By default, the RTX generation pass conservatively expands bit-field
insertion to an insn sequence consisting of the bit mask and shift of
the inserted value, the bit-inversion mask of the destination, and finally
a logical-OR.

However, if the logical-AND operation on an inverted bit-field mask is
relatively expensive, it is more advantageous to shift the inserted value
without masking it and then follow the idiom '(A & M) | (B & ~M)' ->
'((A ^ B) & M) ^ B'.

    /* example */
    struct foo {
      unsigned int x:10;
      unsigned int y:11;
      unsigned int z:11;
    };
    struct foo test0(struct foo a, unsigned int b) {
      a.x = b;
      return a;
    }
    struct foo test1(struct foo a, unsigned int b) {
      a.y = b;
      return a;
    }

    ;; before (-Os ; !BITS_BIG_ENDIAN | !TARGET_DEPBITS)
    test0:
        entry   sp, 32
        movi    a8, -0x400      ;; = ~0x000003FF
        extui   a3, a3, 0, 10   ;; mask
        and     a2, a2, a8      ;; inverted mask
        or      a2, a2, a3      ;; logical-OR
        retw.n
        .literal_position
        .literal .LC0, -2096129 ;; = ~0x001FFC00
    test1:
        entry   sp, 32
        l32r    a8, .LC0
        extui   a3, a3, 0, 11   ;; mask
        slli    a3, a3, 10      ;;
        and     a2, a2, a8      ;; inverted mask
        or      a2, a2, a3      ;; logical-OR
        retw.n

    ;; after (-Os ; !BITS_BIG_ENDIAN | !TARGET_DEPBITS)
    test0:
        entry   sp, 32
        xor     a3, a2, a3
        extui   a3, a3, 0, 10   ;; mask
        xor     a2, a2, a3
        retw.n
    test1:
        entry   sp, 32
        slli    a3, a3, 10      ;; bit-position alignment
        xor     a3, a2, a3
        extui   a3, a3, 10, 11  ;; mask
        slli    a3, a3, 10      ;;
        xor     a2, a2, a3
        retw.n

gcc/ChangeLog:

        * config/xtensa/xtensa.md (insvsi_intermal):
        Rename from 'insvsi'.
        (insvsi): New expansion pattern that also addresses situations
        where the DEPBITS machine instruction is unavailable.
---
 gcc/config/xtensa/xtensa.md | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md
index 8354ab250a1..31abbc8aa2d 100644
--- a/gcc/config/xtensa/xtensa.md
+++ b/gcc/config/xtensa/xtensa.md
@@ -1079,7 +1079,35 @@
    (set_attr "mode"  "SI")
    (set_attr "length"        "6")])
-(define_insn "insvsi"
+(define_expand "insvsi"
+  [(set (zero_extract:SI (match_operand:SI 0 "register_operand")
+                        (match_operand:SI 1 "extui_fldsz_operand")
+                        (match_operand:SI 2 "const_int_operand"))
+       (match_operand:SI 3 "register_operand"))]
+  ""
+{
+  if (TARGET_DEPBITS)
+    emit_insn (gen_insvsi_internal (operands[0], operands[1], operands[2],
+                                   operands[3]));
+  else
+    {
+      int fldsz = INTVAL (operands[1]), shift;
+      rtx temp = gen_reg_rtx (SImode), mask;
+      if (BITS_BIG_ENDIAN)
+       shift = (32 - (fldsz + INTVAL (operands[2]))) & 0x1f;
+      else
+       shift = INTVAL (operands[2]) & 0x1f;
+      mask = GEN_INT (((1 << fldsz) - 1) << shift);
+      emit_insn (shift ? gen_ashlsi3 (temp, operands[3], GEN_INT (shift))
+                      : gen_rtx_SET (temp, operands[3]));
+      emit_insn (gen_xorsi3 (temp, operands[0], temp));
+      emit_insn (gen_andsi3 (temp, temp, force_reg (SImode, mask)));
+      emit_insn (gen_xorsi3 (operands[0], operands[0], temp));
+    }
+  DONE;
+})
+
+(define_insn "insvsi_internal"
   [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+a")
                         (match_operand:SI 1 "extui_fldsz_operand" "")
                         (match_operand:SI 2 "const_int_operand" ""))
--
2.39.5

Reply via email to