| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | /* |
| 3 | * Copyright (c) 2021 Loongson Technology Corporation Limited |
| 4 | */ |
| 5 | |
| 6 | static bool gen_rrr(DisasContext *ctx, arg_rrr *a, |
| 7 | DisasExtend src1_ext, DisasExtend src2_ext, |
| 8 | DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv)) |
| 9 | { |
| 10 | TCGv dest = gpr_dst(ctx, a->rd, dst_ext); |
| 11 | TCGv src1 = gpr_src(ctx, a->rj, src1_ext); |
| 12 | TCGv src2 = gpr_src(ctx, a->rk, src2_ext); |
| 13 | |
| 14 | func(dest, src1, src2); |
| 15 | gen_set_gpr(a->rd, dest, dst_ext); |
| 16 | |
| 17 | return true; |
| 18 | } |
| 19 | |
| 20 | static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a, |
| 21 | DisasExtend src_ext, DisasExtend dst_ext, |
| 22 | void (*func)(TCGv, TCGv, TCGv)) |
| 23 | { |
| 24 | TCGv dest = gpr_dst(ctx, a->rd, dst_ext); |
| 25 | TCGv src1 = gpr_src(ctx, a->rj, src_ext); |
| 26 | TCGv src2 = tcg_constant_tl(a->imm); |
| 27 | |
| 28 | func(dest, src1, src2); |
| 29 | gen_set_gpr(a->rd, dest, dst_ext); |
| 30 | |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a, |
| 35 | DisasExtend src_ext, DisasExtend dst_ext, |
| 36 | void (*func)(TCGv, TCGv, target_long)) |
| 37 | { |
| 38 | TCGv dest = gpr_dst(ctx, a->rd, dst_ext); |
| 39 | TCGv src1 = gpr_src(ctx, a->rj, src_ext); |
| 40 | |
| 41 | func(dest, src1, a->imm); |
| 42 | gen_set_gpr(a->rd, dest, dst_ext); |
| 43 | |
| 44 | return true; |
| 45 | } |
| 46 | |
| 47 | static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, |
| 48 | DisasExtend src_ext, DisasExtend dst_ext, |
| 49 | void (*func)(TCGv, TCGv, TCGv, target_long)) |
| 50 | { |
| 51 | TCGv dest = gpr_dst(ctx, a->rd, dst_ext); |
| 52 | TCGv src1 = gpr_src(ctx, a->rj, src_ext); |
| 53 | TCGv src2 = gpr_src(ctx, a->rk, src_ext); |
| 54 | |
| 55 | func(dest, src1, src2, a->sa); |
| 56 | gen_set_gpr(a->rd, dest, dst_ext); |
| 57 | |
| 58 | return true; |
| 59 | } |
| 60 | |
| 61 | static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a) |
| 62 | { |
| 63 | TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); |
| 64 | |
| 65 | tcg_gen_movi_tl(dest, a->imm << 12); |
| 66 | gen_set_gpr(a->rd, dest, EXT_NONE); |
| 67 | |
| 68 | return true; |
| 69 | } |
| 70 | |
| 71 | static bool gen_pc(DisasContext *ctx, arg_r_i *a, |
| 72 | target_ulong (*func)(target_ulong, int)) |
| 73 | { |
| 74 | TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); |
| 75 | target_ulong addr = make_address_pc(ctx, func(ctx->base.pc_next, a->imm)); |
| 76 | |
| 77 | tcg_gen_movi_tl(dest, addr); |
| 78 | gen_set_gpr(a->rd, dest, EXT_NONE); |
| 79 | |
| 80 | return true; |
| 81 | } |
| 82 | |
| 83 | static void gen_slt(TCGv dest, TCGv src1, TCGv src2) |
| 84 | { |
| 85 | tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2); |
| 86 | } |
| 87 | |
| 88 | static void gen_sltu(TCGv dest, TCGv src1, TCGv src2) |
| 89 | { |
| 90 | tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2); |
| 91 | } |
| 92 | |
| 93 | static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2) |
| 94 | { |
| 95 | tcg_gen_mul_i64(dest, src1, src2); |
| 96 | tcg_gen_sari_i64(dest, dest, 32); |
| 97 | } |
| 98 | |
| 99 | static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2) |
| 100 | { |
| 101 | TCGv discard = tcg_temp_new(); |
| 102 | tcg_gen_muls2_tl(discard, dest, src1, src2); |
| 103 | } |
| 104 | |
| 105 | static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2) |
| 106 | { |
| 107 | TCGv discard = tcg_temp_new(); |
| 108 | tcg_gen_mulu2_tl(discard, dest, src1, src2); |
| 109 | } |
| 110 | |
| 111 | static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) |
| 112 | { |
| 113 | TCGv t0 = tcg_temp_new(); |
| 114 | TCGv t1 = tcg_temp_new(); |
| 115 | TCGv zero = tcg_constant_tl(0); |
| 116 | |
| 117 | /* |
| 118 | * If min / -1, set the divisor to 1. |
| 119 | * This avoids potential host overflow trap and produces min. |
| 120 | * If x / 0, set the divisor to 1. |
| 121 | * This avoids potential host overflow trap; |
| 122 | * the required result is undefined. |
| 123 | */ |
| 124 | tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN); |
| 125 | tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1); |
| 126 | tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0); |
| 127 | tcg_gen_and_tl(ret, ret, t0); |
| 128 | tcg_gen_or_tl(ret, ret, t1); |
| 129 | tcg_gen_movcond_tl(TCG_COND_NE, ret, ret, zero, ret, src2); |
| 130 | } |
| 131 | |
| 132 | static void prep_divisor_du(TCGv ret, TCGv src2) |
| 133 | { |
| 134 | TCGv zero = tcg_constant_tl(0); |
| 135 | TCGv one = tcg_constant_tl(1); |
| 136 | |
| 137 | /* |
| 138 | * If x / 0, set the divisor to 1. |
| 139 | * This avoids potential host overflow trap; |
| 140 | * the required result is undefined. |
| 141 | */ |
| 142 | tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2); |
| 143 | } |
| 144 | |
| 145 | static void gen_div_d(TCGv dest, TCGv src1, TCGv src2) |
| 146 | { |
| 147 | TCGv t0 = tcg_temp_new(); |
| 148 | prep_divisor_d(t0, src1, src2); |
| 149 | tcg_gen_div_tl(dest, src1, t0); |
| 150 | } |
| 151 | |
| 152 | static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2) |
| 153 | { |
| 154 | TCGv t0 = tcg_temp_new(); |
| 155 | prep_divisor_d(t0, src1, src2); |
| 156 | tcg_gen_rem_tl(dest, src1, t0); |
| 157 | } |
| 158 | |
| 159 | static void gen_div_du(TCGv dest, TCGv src1, TCGv src2) |
| 160 | { |
| 161 | TCGv t0 = tcg_temp_new(); |
| 162 | prep_divisor_du(t0, src2); |
| 163 | tcg_gen_divu_tl(dest, src1, t0); |
| 164 | } |
| 165 | |
| 166 | static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2) |
| 167 | { |
| 168 | TCGv t0 = tcg_temp_new(); |
| 169 | prep_divisor_du(t0, src2); |
| 170 | tcg_gen_remu_tl(dest, src1, t0); |
| 171 | } |
| 172 | |
| 173 | static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) |
| 174 | { |
| 175 | TCGv t0 = tcg_temp_new(); |
| 176 | /* We need not check for integer overflow for div_w. */ |
| 177 | prep_divisor_du(t0, src2); |
| 178 | tcg_gen_div_tl(dest, src1, t0); |
| 179 | } |
| 180 | |
| 181 | static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) |
| 182 | { |
| 183 | TCGv t0 = tcg_temp_new(); |
| 184 | /* We need not check for integer overflow for rem_w. */ |
| 185 | prep_divisor_du(t0, src2); |
| 186 | tcg_gen_rem_tl(dest, src1, t0); |
| 187 | } |
| 188 | |
| 189 | static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa) |
| 190 | { |
| 191 | TCGv t0 = tcg_temp_new(); |
| 192 | tcg_gen_shli_tl(t0, src1, sa); |
| 193 | tcg_gen_add_tl(dest, t0, src2); |
| 194 | } |
| 195 | |
| 196 | static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) |
| 197 | { |
| 198 | TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); |
| 199 | TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); |
| 200 | TCGv src2 = tcg_constant_tl(a->imm); |
| 201 | |
| 202 | if (!avail_64(ctx)) { |
| 203 | return false; |
| 204 | } |
| 205 | |
| 206 | tcg_gen_deposit_tl(dest, src1, src2, 32, 32); |
| 207 | gen_set_gpr(a->rd, dest, EXT_NONE); |
| 208 | |
| 209 | return true; |
| 210 | } |
| 211 | |
| 212 | static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) |
| 213 | { |
| 214 | TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); |
| 215 | TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); |
| 216 | TCGv src2 = tcg_constant_tl(a->imm); |
| 217 | |
| 218 | if (!avail_64(ctx)) { |
| 219 | return false; |
| 220 | } |
| 221 | |
| 222 | tcg_gen_deposit_tl(dest, src1, src2, 52, 12); |
| 223 | gen_set_gpr(a->rd, dest, EXT_NONE); |
| 224 | |
| 225 | return true; |
| 226 | } |
| 227 | |
| 228 | static target_ulong gen_pcaddi(target_ulong pc, int imm) |
| 229 | { |
| 230 | return pc + (imm << 2); |
| 231 | } |
| 232 | |
| 233 | static target_ulong gen_pcalau12i(target_ulong pc, int imm) |
| 234 | { |
| 235 | return (pc + (imm << 12)) & ~0xfff; |
| 236 | } |
| 237 | |
| 238 | static target_ulong gen_pcaddu12i(target_ulong pc, int imm) |
| 239 | { |
| 240 | return pc + (imm << 12); |
| 241 | } |
| 242 | |
| 243 | static target_ulong gen_pcaddu18i(target_ulong pc, int imm) |
| 244 | { |
| 245 | return pc + ((target_ulong)(imm) << 18); |
| 246 | } |
| 247 | |
| 248 | static bool trans_addu16i_d(DisasContext *ctx, arg_addu16i_d *a) |
| 249 | { |
| 250 | TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); |
| 251 | TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); |
| 252 | |
| 253 | if (!avail_64(ctx)) { |
| 254 | return false; |
| 255 | } |
| 256 | |
| 257 | tcg_gen_addi_tl(dest, src1, a->imm << 16); |
| 258 | gen_set_gpr(a->rd, dest, EXT_NONE); |
| 259 | |
| 260 | return true; |
| 261 | } |
| 262 | |
| 263 | TRANS(add_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl) |
| 264 | TRANS(add_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl) |
| 265 | TRANS(sub_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl) |
| 266 | TRANS(sub_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl) |
| 267 | TRANS(and, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl) |
| 268 | TRANS(or, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl) |
| 269 | TRANS(xor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl) |
| 270 | TRANS(nor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl) |
| 271 | TRANS(andn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl) |
| 272 | TRANS(orn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl) |
| 273 | TRANS(slt, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt) |
| 274 | TRANS(sltu, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu) |
| 275 | TRANS(mul_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl) |
| 276 | TRANS(mul_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl) |
| 277 | TRANS(mulh_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w) |
| 278 | TRANS(mulh_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w) |
| 279 | TRANS(mulh_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d) |
| 280 | TRANS(mulh_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du) |
| 281 | TRANS(mulw_d_w, 64, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl) |
| 282 | TRANS(mulw_d_wu, 64, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl) |
| 283 | TRANS(div_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w) |
| 284 | TRANS(mod_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w) |
| 285 | TRANS(div_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du) |
| 286 | TRANS(mod_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du) |
| 287 | TRANS(div_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d) |
| 288 | TRANS(mod_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d) |
| 289 | TRANS(div_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du) |
| 290 | TRANS(mod_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du) |
| 291 | TRANS(slti, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt) |
| 292 | TRANS(sltui, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu) |
| 293 | TRANS(addi_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl) |
| 294 | TRANS(addi_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl) |
| 295 | TRANS(alsl_w, ALL, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl) |
| 296 | TRANS(alsl_wu, 64, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl) |
| 297 | TRANS(alsl_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl) |
| 298 | TRANS(pcaddi, ALL, gen_pc, gen_pcaddi) |
| 299 | TRANS(pcalau12i, ALL, gen_pc, gen_pcalau12i) |
| 300 | TRANS(pcaddu12i, ALL, gen_pc, gen_pcaddu12i) |
| 301 | TRANS(pcaddu18i, 64, gen_pc, gen_pcaddu18i) |
| 302 | TRANS(andi, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl) |
| 303 | TRANS(ori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl) |
| 304 | TRANS(xori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl) |