diff options
Diffstat (limited to 'intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h')
-rw-r--r-- | intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h b/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h index cc5ce510..4e948359 100644 --- a/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h +++ b/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h @@ -82,6 +82,81 @@ void MacroAssembler<Assembler>::MacroDiv(Register src) { Bind(done); } + +// Divisor comes in "src", dividend comes in gpr_a, remainder is returned in gpr_d. +// gpr_a and FLAGS are clobbered by that macroinstruction. +template <typename Assembler> +template <typename IntType> +void MacroAssembler<Assembler>::MacroRem(Register src) { + Label* zero = MakeLabel(); + Label* overflow = MakeLabel(); + Label* done = MakeLabel(); + Test<IntType>(src, src); + Jcc(Condition::kZero, *zero); + + if constexpr (std::is_signed_v<IntType>) { + Label* do_idiv = MakeLabel(); + // If min int32_t/int64_t is divided by -1 then in risc-v the result is + // the dividend, but x86 will raise an exception. Handle this case separately. + Cmp<IntType>(src, int8_t{-1}); + Jcc(Condition::kNotEqual, *do_idiv); + + if constexpr (std::is_same_v<IntType, int64_t>) { + Cmp<IntType>(gpr_a, + {.disp = constants_pool::kVectorConst<std::numeric_limits<IntType>::min()>}); + } else { + Cmp<IntType>(gpr_a, std::numeric_limits<IntType>::min()); + } + Jcc(Condition::kEqual, *overflow); + + Bind(do_idiv); + // If we are dealing with 8-bit signed case then we need to sign-extend %al into %ax. + if constexpr (std::is_same_v<IntType, int8_t>) { + Cbw(); + // We need to sign-extend gpr_a into gpr_d to ensure 32bit/64-bit/128-bit dividend is correct. + } else if constexpr (std::is_same_v<IntType, int16_t>) { + Cwd(); + } else if constexpr (std::is_same_v<IntType, int32_t>) { + Cdq(); + } else if constexpr (std::is_same_v<IntType, int64_t>) { + Cqo(); + } else { + static_assert(kDependentTypeFalse<IntType>, "Unsupported format"); + } + } else if constexpr (std::is_same_v<IntType, uint8_t>) { + // For 8bit unsigned case we need “xor %ah, %ah” instruction, but our assembler doesn't support + // %ah register. Use .byte to emit the required machine code. + TwoByte(uint16_t{0xe430}); + } else { + // We need to zero-extend eax into dx/edx/rdx to ensure 32-bit/64-bit/128-bit dividend is + // correct. + Xor<uint64_t>(gpr_d, gpr_d); + } + + Div<IntType>(src); + if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) { + // For 8bit case the result is in %ah, but our assembler doesn't support + // %ah register. move %ah to %al + TwoByte(uint16_t{0xe086}); + } + Jmp(*done); + + Bind(zero); + if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) { + Mov<int8_t>(gpr_a, src); + } else { + Mov<IntType>(gpr_d, src); + } + Jmp(*done); + + Bind(overflow); + if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) { + Xor<int8_t>(gpr_a, gpr_a); + } else { + Xor<IntType>(gpr_d, gpr_d); + } + Bind(done); +} } // namespace berberis #endif // RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_ |