diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/config/riscv/riscv-protos.h | 1 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.cc | 69 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.md | 11 |
3 files changed, 81 insertions, 0 deletions
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 07a4d42..3d8775e 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -136,6 +136,7 @@ extern void riscv_legitimize_poly_move (machine_mode, rtx, rtx, rtx); extern void riscv_expand_usadd (rtx, rtx, rtx); extern void riscv_expand_ssadd (rtx, rtx, rtx); extern void riscv_expand_ussub (rtx, rtx, rtx); +extern void riscv_expand_sssub (rtx, rtx, rtx); extern void riscv_expand_ustrunc (rtx, rtx); #ifdef RTX_CODE diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 7be3939..8708a7b 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -12329,6 +12329,75 @@ riscv_expand_ussub (rtx dest, rtx x, rtx y) emit_move_insn (dest, gen_lowpart (mode, xmode_dest)); } +/* Implements the signed saturation sub standard name ssadd for int mode. + + z = SAT_SUB(x, y). + => + 1. minus = x - y + 2. xor_0 = x ^ y + 3. xor_1 = x ^ minus + 4. lt_0 = xor_1 < 0 + 5. lt_1 = xor_0 < 0 + 6. and = lt_0 & lt_1 + 7. lt = x < 0 + 8. neg = -lt + 9. max = INT_MAX + 10. max = max ^ neg + 11. neg = -and + 12. max = max & neg + 13. and = and - 1 + 14. z = minus & and + 15. z = z | max */ + +void +riscv_expand_sssub (rtx dest, rtx x, rtx y) +{ + machine_mode mode = GET_MODE (dest); + unsigned bitsize = GET_MODE_BITSIZE (mode).to_constant (); + rtx shift_bits = GEN_INT (bitsize - 1); + rtx xmode_x = gen_lowpart (Xmode, x); + rtx xmode_y = gen_lowpart (Xmode, y); + rtx xmode_minus = gen_reg_rtx (Xmode); + rtx xmode_xor_0 = gen_reg_rtx (Xmode); + rtx xmode_xor_1 = gen_reg_rtx (Xmode); + rtx xmode_lt_0 = gen_reg_rtx (Xmode); + rtx xmode_lt_1 = gen_reg_rtx (Xmode); + rtx xmode_and = gen_reg_rtx (Xmode); + rtx xmode_lt = gen_reg_rtx (Xmode); + rtx xmode_neg = gen_reg_rtx (Xmode); + rtx xmode_max = gen_reg_rtx (Xmode); + rtx xmode_dest = gen_reg_rtx (Xmode); + + /* Step-1: mins = x - y, xor_0 = x ^ y, xor_1 = x ^ minus. */ + riscv_emit_binary (MINUS, xmode_minus, xmode_x, xmode_y); + riscv_emit_binary (XOR, xmode_xor_0, xmode_x, xmode_y); + riscv_emit_binary (XOR, xmode_xor_1, xmode_x, xmode_minus); + + /* Step-2: and = xor_0 < 0 & xor_1 < 0. */ + riscv_emit_binary (LSHIFTRT, xmode_lt_0, xmode_xor_0, shift_bits); + riscv_emit_binary (LSHIFTRT, xmode_lt_1, xmode_xor_1, shift_bits); + riscv_emit_binary (AND, xmode_and, xmode_lt_0, xmode_lt_1); + riscv_emit_binary (AND, xmode_and, xmode_and, CONST1_RTX (Xmode)); + + /* Step-3: lt = x < 0, neg = -lt. */ + riscv_emit_binary (LT, xmode_lt, xmode_x, CONST0_RTX (Xmode)); + riscv_emit_unary (NEG, xmode_neg, xmode_lt); + + /* Step-4: max = 0x7f..., max = max ^ neg, neg = -and, max = max & neg. */ + riscv_emit_move (xmode_max, riscv_gen_sign_max_cst (mode)); + riscv_emit_binary (XOR, xmode_max, xmode_max, xmode_neg); + riscv_emit_unary (NEG, xmode_neg, xmode_and); + riscv_emit_binary (AND, xmode_max, xmode_max, xmode_neg); + + /* Step-5: and = and - 1, dest = minus & and. */ + riscv_emit_binary (PLUS, xmode_and, xmode_and, CONSTM1_RTX (Xmode)); + riscv_emit_binary (AND, xmode_dest, xmode_minus, xmode_and); + + /* Step-6: dest = dest | max. */ + riscv_emit_binary (IOR, xmode_dest, xmode_dest, xmode_max); + emit_move_insn (dest, gen_lowpart (mode, xmode_dest)); +} + /* Implement the unsigned saturation truncation for int mode. b = SAT_TRUNC (a); diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 0410d99..067c241 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -4392,6 +4392,17 @@ } ) +(define_expand "sssub<mode>3" + [(match_operand:ANYI 0 "register_operand") + (match_operand:ANYI 1 "register_operand") + (match_operand:ANYI 2 "register_operand")] + "" + { + riscv_expand_sssub (operands[0], operands[1], operands[2]); + DONE; + } +) + (define_expand "ustrunc<mode><anyi_double_truncated>2" [(match_operand:<ANYI_DOUBLE_TRUNCATED> 0 "register_operand") (match_operand:ANYI_DOUBLE_TRUNC 1 "register_operand")] |