1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_
18 #define RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_
19
20 #include <climits>
21 #include <type_traits>
22
23 #include "berberis/base/bit_util.h"
24 #include "berberis/intrinsics/macro_assembler.h"
25
26 namespace berberis {
27
28 // Divisor comes in "src", dividend comes in gpr_a, result is returned in gpr_a.
29 // gpr_d and FLAGS are clobbered by that macroinstruction.
30 template <typename Assembler>
31 template <typename IntType>
MacroDiv(Register src)32 void MacroAssembler<Assembler>::MacroDiv(Register src) {
33 Label* zero = MakeLabel();
34 Label* done = MakeLabel();
35 Test<IntType>(src, src);
36 Jcc(Condition::kZero, *zero);
37
38 if constexpr (std::is_signed_v<IntType>) {
39 Label* do_idiv = MakeLabel();
40 // If min int32_t/int64_t is divided by -1 then in risc-v the result is
41 // the dividend, but x86 will raise an exception. Handle this case separately.
42 Cmp<IntType>(src, int8_t{-1});
43 Jcc(Condition::kNotEqual, *do_idiv);
44
45 if constexpr (std::is_same_v<IntType, int64_t>) {
46 Cmp<IntType>(gpr_a,
47 {.disp = constants_pool::kVectorConst<std::numeric_limits<IntType>::min()>});
48 } else {
49 Cmp<IntType>(gpr_a, std::numeric_limits<IntType>::min());
50 }
51 Jcc(Condition::kEqual, *done);
52
53 Bind(do_idiv);
54 // If we are dealing with 8-bit signed case then we need to sign-extend %al into %ax.
55 if constexpr (std::is_same_v<IntType, int8_t>) {
56 Cbw();
57 // We need to sign-extend gpr_a into gpr_d to ensure 32bit/64-bit/128-bit dividend is correct.
58 } else if constexpr (std::is_same_v<IntType, int16_t>) {
59 Cwd();
60 } else if constexpr (std::is_same_v<IntType, int32_t>) {
61 Cdq();
62 } else if constexpr (std::is_same_v<IntType, int64_t>) {
63 Cqo();
64 } else {
65 static_assert(kDependentTypeFalse<IntType>, "Unsupported format");
66 }
67 } else if constexpr (std::is_same_v<IntType, uint8_t>) {
68 // For 8bit unsigned case we need “xor %ah, %ah” instruction, but our assembler doesn't support
69 // %ah register. Use .byte to emit the required machine code.
70 TwoByte(uint16_t{0xe430});
71 } else {
72 // We need to zero-extend eax into dx/edx/rdx to ensure 32-bit/64-bit/128-bit dividend is
73 // correct.
74 Xor<uint32_t>(gpr_d, gpr_d);
75 }
76
77 Div<IntType>(src);
78 Jmp(*done);
79
80 Bind(zero);
81 Mov<IntType>(gpr_a, int64_t{-1});
82
83 Bind(done);
84 }
85
86 // Divisor comes in "src", dividend comes in gpr_a, remainder is returned in gpr_d.
87 // gpr_a and FLAGS are clobbered by that macroinstruction.
88 template <typename Assembler>
89 template <typename IntType>
MacroRem(Register src)90 void MacroAssembler<Assembler>::MacroRem(Register src) {
91 Label* zero = MakeLabel();
92 Label* overflow = MakeLabel();
93 Label* done = MakeLabel();
94 Test<IntType>(src, src);
95 Jcc(Condition::kZero, *zero);
96
97 if constexpr (std::is_signed_v<IntType>) {
98 Label* do_idiv = MakeLabel();
99 // If min int32_t/int64_t is divided by -1 then in risc-v the result is
100 // the dividend, but x86 will raise an exception. Handle this case separately.
101 Cmp<IntType>(src, int8_t{-1});
102 Jcc(Condition::kNotEqual, *do_idiv);
103
104 if constexpr (std::is_same_v<IntType, int64_t>) {
105 Cmp<IntType>(gpr_a,
106 {.disp = constants_pool::kVectorConst<std::numeric_limits<IntType>::min()>});
107 } else {
108 Cmp<IntType>(gpr_a, std::numeric_limits<IntType>::min());
109 }
110 Jcc(Condition::kEqual, *overflow);
111
112 Bind(do_idiv);
113 // If we are dealing with 8-bit signed case then we need to sign-extend %al into %ax.
114 if constexpr (std::is_same_v<IntType, int8_t>) {
115 Cbw();
116 // We need to sign-extend gpr_a into gpr_d to ensure 32bit/64-bit/128-bit dividend is correct.
117 } else if constexpr (std::is_same_v<IntType, int16_t>) {
118 Cwd();
119 } else if constexpr (std::is_same_v<IntType, int32_t>) {
120 Cdq();
121 } else if constexpr (std::is_same_v<IntType, int64_t>) {
122 Cqo();
123 } else {
124 static_assert(kDependentTypeFalse<IntType>, "Unsupported format");
125 }
126 } else if constexpr (std::is_same_v<IntType, uint8_t>) {
127 // For 8bit unsigned case we need “xor %ah, %ah” instruction, but our assembler doesn't support
128 // %ah register. Use .byte to emit the required machine code.
129 TwoByte(uint16_t{0xe430});
130 } else {
131 // We need to zero-extend eax into dx/edx/rdx to ensure 32-bit/64-bit/128-bit dividend is
132 // correct.
133 Xor<uint64_t>(gpr_d, gpr_d);
134 }
135
136 Div<IntType>(src);
137 if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) {
138 // For 8bit case the result is in %ah, but our assembler doesn't support
139 // %ah register. move %ah to %al
140 TwoByte(uint16_t{0xe086});
141 }
142 Jmp(*done);
143
144 Bind(zero);
145 if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) {
146 Mov<int8_t>(gpr_a, src);
147 } else {
148 Mov<IntType>(gpr_d, src);
149 }
150 Jmp(*done);
151
152 Bind(overflow);
153 if constexpr (std::is_same_v<IntType, uint8_t> || std::is_same_v<IntType, int8_t>) {
154 Xor<int8_t>(gpr_a, gpr_a);
155 } else {
156 Xor<IntType>(gpr_d, gpr_d);
157 }
158 Bind(done);
159 }
160 } // namespace berberis
161
162 #endif // RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_
163