1/* 2 * RISC-V translation routines for the RV64M Standard Extension. 3 * 4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu 5 * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de 6 * Bastian Koppelmann, kbastian@mail.uni-paderborn.de 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2 or later, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 22static bool trans_mul(DisasContext *ctx, arg_mul *a) 23{ 24 REQUIRE_EXT(ctx, RVM); 25 return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL); 26} 27 28static void gen_mulh(TCGv ret, TCGv s1, TCGv s2) 29{ 30 TCGv discard = tcg_temp_new(); 31 32 tcg_gen_muls2_tl(discard, ret, s1, s2); 33 tcg_temp_free(discard); 34} 35 36static void gen_mulh_w(TCGv ret, TCGv s1, TCGv s2) 37{ 38 tcg_gen_mul_tl(ret, s1, s2); 39 tcg_gen_sari_tl(ret, ret, 32); 40} 41 42static bool trans_mulh(DisasContext *ctx, arg_mulh *a) 43{ 44 REQUIRE_EXT(ctx, RVM); 45 return gen_arith_per_ol(ctx, a, EXT_SIGN, gen_mulh, gen_mulh_w, NULL); 46} 47 48static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) 49{ 50 TCGv rl = tcg_temp_new(); 51 TCGv rh = tcg_temp_new(); 52 53 tcg_gen_mulu2_tl(rl, rh, arg1, arg2); 54 /* fix up for one negative */ 55 tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1); 56 tcg_gen_and_tl(rl, rl, arg2); 57 tcg_gen_sub_tl(ret, rh, rl); 58 59 tcg_temp_free(rl); 60 tcg_temp_free(rh); 61} 62 63static void gen_mulhsu_w(TCGv ret, TCGv arg1, TCGv arg2) 64{ 65 TCGv t1 = tcg_temp_new(); 66 TCGv t2 = tcg_temp_new(); 67 68 tcg_gen_ext32s_tl(t1, arg1); 69 tcg_gen_ext32u_tl(t2, arg2); 70 tcg_gen_mul_tl(ret, t1, t2); 71 tcg_temp_free(t1); 72 tcg_temp_free(t2); 73 tcg_gen_sari_tl(ret, ret, 32); 74} 75 76static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a) 77{ 78 REQUIRE_EXT(ctx, RVM); 79 return gen_arith_per_ol(ctx, a, EXT_NONE, gen_mulhsu, gen_mulhsu_w, NULL); 80} 81 82static void gen_mulhu(TCGv ret, TCGv s1, TCGv s2) 83{ 84 TCGv discard = tcg_temp_new(); 85 86 tcg_gen_mulu2_tl(discard, ret, s1, s2); 87 tcg_temp_free(discard); 88} 89 90static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) 91{ 92 REQUIRE_EXT(ctx, RVM); 93 /* gen_mulh_w works for either sign as input. */ 94 return gen_arith_per_ol(ctx, a, EXT_ZERO, gen_mulhu, gen_mulh_w, NULL); 95} 96 97static void gen_div(TCGv ret, TCGv source1, TCGv source2) 98{ 99 TCGv temp1, temp2, zero, one, mone, min; 100 101 temp1 = tcg_temp_new(); 102 temp2 = tcg_temp_new(); 103 zero = tcg_constant_tl(0); 104 one = tcg_constant_tl(1); 105 mone = tcg_constant_tl(-1); 106 min = tcg_constant_tl(1ull << (TARGET_LONG_BITS - 1)); 107 108 /* 109 * If overflow, set temp2 to 1, else source2. 110 * This produces the required result of min. 111 */ 112 tcg_gen_setcond_tl(TCG_COND_EQ, temp1, source1, min); 113 tcg_gen_setcond_tl(TCG_COND_EQ, temp2, source2, mone); 114 tcg_gen_and_tl(temp1, temp1, temp2); 115 tcg_gen_movcond_tl(TCG_COND_NE, temp2, temp1, zero, one, source2); 116 117 /* 118 * If div by zero, set temp1 to -1 and temp2 to 1 to 119 * produce the required result of -1. 120 */ 121 tcg_gen_movcond_tl(TCG_COND_EQ, temp1, source2, zero, mone, source1); 122 tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, temp2); 123 124 tcg_gen_div_tl(ret, temp1, temp2); 125 126 tcg_temp_free(temp1); 127 tcg_temp_free(temp2); 128} 129 130static bool trans_div(DisasContext *ctx, arg_div *a) 131{ 132 REQUIRE_EXT(ctx, RVM); 133 return gen_arith(ctx, a, EXT_SIGN, gen_div, NULL); 134} 135 136static void gen_divu(TCGv ret, TCGv source1, TCGv source2) 137{ 138 TCGv temp1, temp2, zero, one, max; 139 140 temp1 = tcg_temp_new(); 141 temp2 = tcg_temp_new(); 142 zero = tcg_constant_tl(0); 143 one = tcg_constant_tl(1); 144 max = tcg_constant_tl(~0); 145 146 /* 147 * If div by zero, set temp1 to max and temp2 to 1 to 148 * produce the required result of max. 149 */ 150 tcg_gen_movcond_tl(TCG_COND_EQ, temp1, source2, zero, max, source1); 151 tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, source2); 152 tcg_gen_divu_tl(ret, temp1, temp2); 153 154 tcg_temp_free(temp1); 155 tcg_temp_free(temp2); 156} 157 158static bool trans_divu(DisasContext *ctx, arg_divu *a) 159{ 160 REQUIRE_EXT(ctx, RVM); 161 return gen_arith(ctx, a, EXT_ZERO, gen_divu, NULL); 162} 163 164static void gen_rem(TCGv ret, TCGv source1, TCGv source2) 165{ 166 TCGv temp1, temp2, zero, one, mone, min; 167 168 temp1 = tcg_temp_new(); 169 temp2 = tcg_temp_new(); 170 zero = tcg_constant_tl(0); 171 one = tcg_constant_tl(1); 172 mone = tcg_constant_tl(-1); 173 min = tcg_constant_tl(1ull << (TARGET_LONG_BITS - 1)); 174 175 /* 176 * If overflow, set temp1 to 0, else source1. 177 * This avoids a possible host trap, and produces the required result of 0. 178 */ 179 tcg_gen_setcond_tl(TCG_COND_EQ, temp1, source1, min); 180 tcg_gen_setcond_tl(TCG_COND_EQ, temp2, source2, mone); 181 tcg_gen_and_tl(temp1, temp1, temp2); 182 tcg_gen_movcond_tl(TCG_COND_NE, temp1, temp1, zero, zero, source1); 183 184 /* 185 * If div by zero, set temp2 to 1, else source2. 186 * This avoids a possible host trap, but produces an incorrect result. 187 */ 188 tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, source2); 189 190 tcg_gen_rem_tl(temp1, temp1, temp2); 191 192 /* If div by zero, the required result is the original dividend. */ 193 tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp1); 194 195 tcg_temp_free(temp1); 196 tcg_temp_free(temp2); 197} 198 199static bool trans_rem(DisasContext *ctx, arg_rem *a) 200{ 201 REQUIRE_EXT(ctx, RVM); 202 return gen_arith(ctx, a, EXT_SIGN, gen_rem, NULL); 203} 204 205static void gen_remu(TCGv ret, TCGv source1, TCGv source2) 206{ 207 TCGv temp, zero, one; 208 209 temp = tcg_temp_new(); 210 zero = tcg_constant_tl(0); 211 one = tcg_constant_tl(1); 212 213 /* 214 * If div by zero, set temp to 1, else source2. 215 * This avoids a possible host trap, but produces an incorrect result. 216 */ 217 tcg_gen_movcond_tl(TCG_COND_EQ, temp, source2, zero, one, source2); 218 219 tcg_gen_remu_tl(temp, source1, temp); 220 221 /* If div by zero, the required result is the original dividend. */ 222 tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp); 223 224 tcg_temp_free(temp); 225} 226 227static bool trans_remu(DisasContext *ctx, arg_remu *a) 228{ 229 REQUIRE_EXT(ctx, RVM); 230 return gen_arith(ctx, a, EXT_ZERO, gen_remu, NULL); 231} 232 233static bool trans_mulw(DisasContext *ctx, arg_mulw *a) 234{ 235 REQUIRE_64BIT(ctx); 236 REQUIRE_EXT(ctx, RVM); 237 ctx->ol = MXL_RV32; 238 return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL); 239} 240 241static bool trans_divw(DisasContext *ctx, arg_divw *a) 242{ 243 REQUIRE_64BIT(ctx); 244 REQUIRE_EXT(ctx, RVM); 245 ctx->ol = MXL_RV32; 246 return gen_arith(ctx, a, EXT_SIGN, gen_div, NULL); 247} 248 249static bool trans_divuw(DisasContext *ctx, arg_divuw *a) 250{ 251 REQUIRE_64BIT(ctx); 252 REQUIRE_EXT(ctx, RVM); 253 ctx->ol = MXL_RV32; 254 return gen_arith(ctx, a, EXT_ZERO, gen_divu, NULL); 255} 256 257static bool trans_remw(DisasContext *ctx, arg_remw *a) 258{ 259 REQUIRE_64BIT(ctx); 260 REQUIRE_EXT(ctx, RVM); 261 ctx->ol = MXL_RV32; 262 return gen_arith(ctx, a, EXT_SIGN, gen_rem, NULL); 263} 264 265static bool trans_remuw(DisasContext *ctx, arg_remuw *a) 266{ 267 REQUIRE_64BIT(ctx); 268 REQUIRE_EXT(ctx, RVM); 269 ctx->ol = MXL_RV32; 270 return gen_arith(ctx, a, EXT_ZERO, gen_remu, NULL); 271} 272