https://gcc.gnu.org/g:e096b2205f81b3db9d06f4ee4da9badbb0c6de20

commit r17-3906-ge096b2205f81b3db9d06f4ee4da9badbb0c6de20
Author: Robin Dapp <[email protected]>
Date:   Tue Aug 25 13:55:00 2026 +0200

    RISC-V: Add TImode and OImode move expanders.
    
    In the discussion around REGMODE_NATURAL_SIZE Richard Sandiford argued
    that rather than potentially introducing spills during expand we should
    require targets to provide appropriate expanders.
    
    Currently, as we allow TImode via MAX_FIXED_MODE_SIZE (and
    REGMODE_NATURAL_SIZE set to the size of one vector for vector modes),
    (subreg:TI (reg:V4SF R) 0) is valid but cannot be split nor moved.
    
    Therefore, define TImode and OImode moves.
    
    gcc/ChangeLog:
    
            * config/riscv/riscv-protos.h (riscv_split_quadword_move):
            Declare.
            * config/riscv/riscv.cc (riscv_subpart): New function to get
            TImode sub-parts from OImode regs.
            (subreg_word_splittable_p): New function to check if a subreg
            (spill_subreg_move): New function.
            (riscv_split_doubleword_move): Add unsplittable-subreg handling.
            (riscv_split_quadword_move): New function to split OImode.
            * config/riscv/riscv.md (movti): New expander.
            (movoi): Likewise.

Diff:
---
 gcc/config/riscv/riscv-protos.h |   1 +
 gcc/config/riscv/riscv.cc       | 138 ++++++++++++++++++++++++++++++++++++++++
 gcc/config/riscv/riscv.md       |  26 ++++++++
 3 files changed, 165 insertions(+)

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index e9612cca1a1d..b0c1d8d4d05c 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -124,6 +124,7 @@ extern bool riscv_legitimize_move (machine_mode, rtx, rtx);
 extern rtx riscv_subword (rtx, bool);
 extern bool riscv_split_64bit_move_p (rtx, rtx);
 extern void riscv_split_doubleword_move (rtx, rtx);
+extern void riscv_split_quadword_move (rtx, rtx);
 extern const char *riscv_output_move (rtx, rtx);
 extern const char *riscv_output_return ();
 extern void riscv_declare_function_name (FILE *, const char *, tree);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index aafbcd5db0a3..a1474b05ad8a 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -5315,6 +5315,84 @@ riscv_subword (rtx op, bool high_p)
   return simplify_gen_subreg (word_mode, op, mode, byte);
 }
 
+/* Same as riscv_subword, just only for OImode.  */
+
+static rtx
+riscv_subpart (rtx op, bool high_p)
+{
+  unsigned int byte = (high_p != BYTES_BIG_ENDIAN) ? 16 : 0;
+  machine_mode mode = GET_MODE (op);
+
+  if (mode == VOIDmode)
+    mode = OImode;
+
+  gcc_assert (mode == OImode);
+
+  if (MEM_P (op))
+    return adjust_address (op, TImode, byte);
+
+  return simplify_gen_subreg (TImode, op, mode, byte);
+}
+
+/* Return true if OP is a subreg that we cannot split into words or false
+   otherwise.  */
+
+static inline bool
+subreg_word_unsplittable_p (rtx op)
+{
+  return SUBREG_P ((op))
+    && maybe_lt ((unsigned) UNITS_PER_WORD,
+                riscv_regmode_natural_size (GET_MODE (SUBREG_REG ((op)))));
+}
+
+/* Given a move from SRC to DEST where either SRC, DEST, or both are
+   not splittable at word boundaries, emit the necessary spill code to handle
+   the move.  */
+
+static void
+spill_subreg_move (rtx dest, rtx src)
+{
+  bool spill_dest = subreg_word_unsplittable_p (dest);
+  bool spill_src = subreg_word_unsplittable_p (src);
+
+  gcc_assert (spill_dest || spill_src);
+  gcc_assert (can_create_pseudo_p ());
+  gcc_assert (!paradoxical_subreg_p (src) && !paradoxical_subreg_p (dest));
+
+  rtx tmp_src = src;
+  if (spill_src)
+    {
+      /* Get a stack slot of inner mode, move the inner reg to it and
+        "view" in outer mode.  We can only get here when handling
+        subreg-punned registers for which the subreg byte is 0.  */
+      rtx inner = SUBREG_REG (src);
+      gcc_assert (known_eq (SUBREG_BYTE (src), 0));
+      machine_mode mode_inner = GET_MODE (inner);
+      rtx mem = assign_stack_temp (mode_inner, GET_MODE_SIZE (mode_inner));
+      emit_move_insn (mem, inner);
+      tmp_src = adjust_address (mem, GET_MODE (src), 0);
+    }
+
+  if (!spill_dest)
+    {
+      emit_move_insn (dest, tmp_src);
+      return;
+    }
+
+  /* Similar to the source, first get a stack slot of inner mode.
+     Then, view it as outer mode and move the source to it.
+     Finally, store it in the inner subreg.  */
+  rtx inner = SUBREG_REG (dest);
+  gcc_assert (known_eq (SUBREG_BYTE (dest), 0));
+  machine_mode mode_inner = GET_MODE (inner);
+  rtx mem = assign_stack_temp (mode_inner, GET_MODE_SIZE (mode_inner));
+  rtx mem_outer = adjust_address (mem, GET_MODE (dest), 0);
+  if (maybe_gt (GET_MODE_SIZE (mode_inner), GET_MODE_SIZE (GET_MODE (dest))))
+    emit_move_insn (mem, inner);
+  emit_move_insn (mem_outer, tmp_src);
+  emit_move_insn (inner, mem);
+}
+
 /* Return true if a 64-bit move from SRC to DEST should be split into two.  */
 
 bool
@@ -5449,6 +5527,22 @@ riscv_split_doubleword_move (rtx dest, rtx src)
        }
     }
 
+  /* Nothing to do for highwords of paradoxical subregs.  */
+  if (paradoxical_subreg_p (src) || paradoxical_subreg_p (dest))
+    {
+      riscv_emit_move (riscv_subword (dest, false),
+                      riscv_subword (src, false));
+      return;
+    }
+
+  /* We cannot build DI subregs of larger-sized vector regs (see
+     riscv_regmode_natural_size), spill instead.  */
+  if (subreg_word_unsplittable_p (src) || subreg_word_unsplittable_p (dest))
+    {
+      spill_subreg_move (dest, src);
+      return;
+    }
+
    /* The operation can be split into two normal moves.  Decide in
       which order to do them.  */
    rtx low_dest = riscv_subword (dest, false);
@@ -5464,6 +5558,50 @@ riscv_split_doubleword_move (rtx dest, rtx src)
      }
 }
 
+/* This just splits OImode into two TImode halves and lets
+   riscv_split_doubleword_move do the rest.  */
+
+void
+riscv_split_quadword_move (rtx dest, rtx src)
+{
+  gcc_assert (GET_MODE (dest) == OImode);
+  gcc_assert (!BYTES_BIG_ENDIAN);
+
+  /* Nothing to do for highwords of paradoxical subregs.  */
+  if (paradoxical_subreg_p (dest) || paradoxical_subreg_p (src))
+    {
+      riscv_split_doubleword_move (riscv_subpart (dest, false),
+                                  riscv_subpart (src, false));
+      return;
+    }
+
+  /* We cannot build TI subregs of larger-sized vector regs (see
+     riscv_regmode_natural_size), spill instead.  */
+  if (subreg_word_unsplittable_p (src) || subreg_word_unsplittable_p (dest))
+    {
+      spill_subreg_move (dest, src);
+      return;
+    }
+
+  /* Split into TImode hi/lo parts and hand off to
+     riscv_split_doubleword_move.  */
+  rtx src_lo = riscv_subpart (src, false);
+  rtx dest_lo = riscv_subpart (dest, false);
+  rtx src_hi = riscv_subpart (src, true);
+  rtx dest_hi = riscv_subpart (dest, true);
+
+  if (reg_overlap_mentioned_p (dest_lo, src))
+    {
+      riscv_split_doubleword_move (dest_hi, src_hi);
+      riscv_split_doubleword_move (dest_lo, src_lo);
+    }
+  else
+    {
+      riscv_split_doubleword_move (dest_lo, src_lo);
+      riscv_split_doubleword_move (dest_hi, src_hi);
+    }
+}
+
 /* Constant VAL is known to be sum of two S12 constants.  Break it into
    comprising BASE and OFF.
    Numerically S12 is -2048 to 2047, however it uses the more conservative
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index a56d6cbc592f..bd308e3d056d 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -2877,6 +2877,32 @@
   DONE;
 })
 
+;; We don't have a real TImode move but the middle-end demands it if we're
+;; allowing TImode.  Just move the individual registers.
+(define_expand "movti"
+  [(set (match_operand:TI 0 "nonimmediate_operand")
+       (match_operand:TI 1 "general_operand"))]
+  "TARGET_64BIT"
+{
+  /* TODO: Rather than spilling everything we could catch loads or stores
+     of subreg-punned vector registers like
+      (set (mem:TI ...) (subreg:TI (reg:V4SI ...)))
+     and emit a vector load/store right away.  The same is true for the
+     OImode expander.  */
+  riscv_split_doubleword_move (operands[0], operands[1]);
+  DONE;
+})
+
+;; Similar for OImode.
+(define_expand "movoi"
+  [(set (match_operand:OI 0 "nonimmediate_operand")
+       (match_operand:OI 1 "general_operand"))]
+  "TARGET_64BIT"
+{
+  riscv_split_quadword_move (operands[0], operands[1]);
+  DONE;
+})
+
 (define_expand "cmpmemsi"
   [(parallel [(set (match_operand:SI 0)
                (compare:SI (match_operand:BLK 1)

Reply via email to