]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
xtensa: Implement "__force_l32" named address space
authorTakayuki 'January June' Suwa <jjsuwa_sys3175@yahoo.co.jp>
Sat, 2 May 2026 18:11:30 +0000 (03:11 +0900)
committerMax Filippov <jcmvbkbc@gmail.com>
Mon, 4 May 2026 07:27:49 +0000 (00:27 -0700)
In the Xtensa ISA, unless the memory regions for placing machine instructions
are configured as "unified," instructions other than specific 32-bit width
load/store ones are not defined to be able to access data in such regions.

In such cases, data residing in the same memory area as the instructions,
eg., pre-configured constant tables or string literals, cannot be read using
the usual sub-word memory load instructions when reading them in units of
1- or 2-bytes.  Instead, a series of alternative instructions are needed to
extract the desired sub-word bit by bit from the result of loading an aligned
full-word.

This patch introduces a new target-specific named address space "__force_l32"
which indicates that such considerations are necessary when loading sub-words
from memory.

     /* example #1 */
     struct foo {
       short a, b, c, d;
     };
     int test(void) {
       extern __force_l32 struct foo *p;
       return p->a * p->d;
     }

     ;; result #1 (-O2 -mlittle-endian)
      .literal_position
      .literal .LC0, p
     test:
      entry sp, 32
      l32r a9, .LC0 ;; the address of p
      movi.n a8, -4 ;; consolidated by fwprop/CSE
      l32i.n a9, a9, 0 ;; the value of p
      addi.n a10, a9, 6
      and a2, a9, a8 ;; p->a : align to SImode
      and a8, a10, a8 ;; p->d : align to SImode
      l32i.n a2, a2, 0 ;; p->a : load:SI
      l32i.n a8, a8, 0 ;; p->d : load:SI
      ssa8l a9 ;; p->a : shift to bit position 0
      srl a2, a2
      ssa8l a10 ;; p->d : shift to bit position 0
      srl a8, a8
      mul16s a2, a2, a8 ;; mulhisi3
      retw.n

     /* example #2 */
     char *strcpy_irom(char *dst, __force_l32 const char *src) {
       char *p = dst;
       while (*p = *src)
         ++p, ++src;
       return dst;
     }

     ;; result #2 (-Os -mbig-endian)
     strcpy_irom:
      entry sp, 32
      mov.n a9, a2
      movi.n a10, -4 ;; hoisted out
      j .L2
     .L3:
      addi.n a9, a9, 1
      addi.n a3, a3, 1
     .L2:
      and a8, a3, a10 ;; *src : align to SImode
      l32i.n a8, a8, 0 ;; *src : load:SI
      ssa8b a3 ;; *src : shift to bit position 0
      sll a8, a8
      extui a8, a8, 24, 8 ;; *src : zero_extract:QI
      s8i a8, a9, 0 ;; *p   : store:QI
      bnez.n a8, .L3
      retw.n

gcc/ChangeLog:

* config/xtensa/xtensa-protos.h
(xtensa_expand_load_force_l32): New function prototype.
* config/xtensa/xtensa.cc (#include): Add "expmed.h".
(TARGET_LEGITIMATE_ADDRESS_P):
Change a whitespace delimiter from HTAB to SPACE.
(TARGET_ADDR_SPACE_SUBSET_P, TARGET_ADDR_SPACE_CONVERT,
TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P):
New macro definitions for named address space.
(xtensa_addr_space_subset_p, xtensa_addr_space_convert,
xtensa_addr_space_legitimate_address_p):
New hook function prototypes and definitions required for
implementing the named address space.
(xtensa_expand_load_force_l32): New function that generates RTXes
that perform loads from memory belonging to the named address
space.
* config/xtensa/xtensa.h (ADDR_SPACE_FORCE_L32):
New macro for the ID# of the named address space.
(REGISTER_TARGET_PRAGMAS): New hook for registering C language
identifier for the named address space.
* config/xtensa/xtensa.md
(zero_extend<mode>si2_internal): Rename from zero_extend<mode>si2.
(zero_extend<mode>si2): New RTL generation pattern that calls
xtensa_expand_load_force_l32().
(extendhisi2, extendqisi2, movhi, movqi):
Change to call xtensa_expand_load_force_l32() first.
(*shift_per_byte): Delete the insn condition.

gcc/config/xtensa/xtensa-protos.h
gcc/config/xtensa/xtensa.cc
gcc/config/xtensa/xtensa.h
gcc/config/xtensa/xtensa.md

index 034549b61e0617c07e1f6548b55a8e2acc00799d..71323354e79c1fcf2aa4dfb622bed4e354208a12 100644 (file)
@@ -60,6 +60,8 @@ extern bool xtensa_tls_referenced_p (rtx);
 extern enum rtx_code xtensa_shlrd_which_direction (rtx, rtx);
 extern bool xtensa_postreload_completed_p (void);
 extern char *xtensa_bswapsi2_output (rtx_insn *, const char *);
+extern bool xtensa_expand_load_force_l32 (rtx *, machine_mode, machine_mode,
+                                         int);
 
 #ifdef TREE_CODE
 extern void init_cumulative_args (CUMULATIVE_ARGS *, int);
index e4c28071a0d561e3db03a89f637b056fff890e77..bf7e1dd41f5df8bf351e9a82b7d0fe4acc6f1c0c 100644 (file)
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "alias.h"
 #include "explow.h"
+#include "expmed.h"
 #include "expr.h"
 #include "langhooks.h"
 #include "gimplify.h"
@@ -201,6 +202,10 @@ static rtx_insn *xtensa_md_asm_adjust (vec<rtx> &, vec<rtx> &,
                                       vec<machine_mode> &, vec<const char *> &,
                                       vec<rtx> &, vec<rtx> &, HARD_REG_SET &,
                                       location_t);
+static bool xtensa_addr_space_subset_p (addr_space_t, addr_space_t);
+static rtx xtensa_addr_space_convert (rtx, tree, tree);
+static bool xtensa_addr_space_legitimate_address_p (machine_mode, rtx, bool,
+                                                   addr_space_t, code_helper);
 
 \f
 
@@ -292,7 +297,7 @@ static rtx_insn *xtensa_md_asm_adjust (vec<rtx> &, vec<rtx> &,
 #define TARGET_CANNOT_FORCE_CONST_MEM xtensa_cannot_force_const_mem
 
 #undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P    xtensa_legitimate_address_p
+#define TARGET_LEGITIMATE_ADDRESS_P xtensa_legitimate_address_p
 
 #undef TARGET_FRAME_POINTER_REQUIRED
 #define TARGET_FRAME_POINTER_REQUIRED xtensa_frame_pointer_required
@@ -375,6 +380,16 @@ static rtx_insn *xtensa_md_asm_adjust (vec<rtx> &, vec<rtx> &,
 #undef TARGET_MD_ASM_ADJUST
 #define TARGET_MD_ASM_ADJUST xtensa_md_asm_adjust
 
+#undef TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P xtensa_addr_space_subset_p
+
+#undef TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT xtensa_addr_space_convert
+
+#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
+       xtensa_addr_space_legitimate_address_p
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 \f
@@ -2597,6 +2612,70 @@ xtensa_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm, rtx scratch,
 }
 
 
+/* Expand a 1- or 2-byte width memory load into an aligned 4-byte width
+   load with bit-extraction of the required bytes.  */
+
+bool
+xtensa_expand_load_force_l32 (rtx *operands, machine_mode dest_mode,
+                             machine_mode src_mode, int unsignedp)
+{
+  rtx dest, src, addr, temp, x;
+
+  gcc_assert (src_mode == QImode || src_mode == HImode);
+
+  /* Reject sub-word store to memory within the __force_l32 address space.  */
+  if (mem_operand (dest = operands[0], dest_mode)
+      && MEM_ADDR_SPACE (dest) == ADDR_SPACE_FORCE_L32)
+    {
+      error ("Storing 1- and 2-byte quantities to memory within the "
+            "%<__force_l32%> address space is not supported");
+      return false;
+    }
+
+  /* Exclude insns that do not load memory.  */
+  if (! register_operand (dest, dest_mode)
+      || ! mem_operand (src = operands[1], src_mode))
+    return false;
+
+  /* Exclude insns that do not perform memory loading with "force_l32".  */
+  if (MEM_ADDR_SPACE (src) != ADDR_SPACE_FORCE_L32)
+    return false;
+
+  /* Addressing in the __force_l32 address space is only valid with a base
+     register without offset.  */
+  addr = XEXP (src, 0);
+  gcc_assert (REG_P (addr));
+
+  /* First, Load the aligned SImode memory containing the desired [HQ]Imode
+     value.  */
+  emit_insn (gen_andsi3 (temp = gen_reg_rtx (Pmode),
+                        addr, force_reg (SImode, GEN_INT (-4))));
+  x = gen_rtx_MEM (SImode, temp);
+  MEM_VOLATILE_P (x) = MEM_VOLATILE_P (src);
+  emit_insn (gen_rtx_SET (temp, x));
+
+  /* Then, shift the bit-image of the desired [HQ]Imode value to bit-
+     position 0, ie., the least significant side for little-endian, the
+     most significant side for big-endian.
+     This process requires a shift of 8-times the amount, ie., a per-byte
+     shift instruction.  Therefore, with the implementation of this, the
+     instruction is modified to be usable regardless of whether optimization
+     and/or debugging is enabled or disabled (see "*shift_per_byte" insn
+     pattern in xtensa.md).  */
+  x = gen_rtx_ASHIFT (SImode, addr, GEN_INT (3));
+  x = BITS_BIG_ENDIAN ? gen_rtx_ASHIFT (SImode, temp, x)
+                     : gen_rtx_LSHIFTRT (SImode, temp, x);
+  emit_insn (gen_rtx_SET (temp, x));
+
+  /* Finally, extract the necessary part from the shifted result.  */
+  x = extract_bit_field (temp, GET_MODE_BITSIZE (src_mode), 0, unsignedp,
+                        NULL_RTX, dest_mode, dest_mode, false, NULL);
+  emit_insn (gen_rtx_SET (dest, x));
+
+  return true;
+}
+
+
 /* Implement TARGET_CANNOT_FORCE_CONST_MEM.  */
 
 static bool
@@ -5479,6 +5558,68 @@ xtensa_md_asm_adjust (vec<rtx> &outputs ATTRIBUTE_UNUSED,
   return NULL;
 }
 
+/* Implement TARGET_ADDR_SPACE_SUBSET_P.  */
+
+static bool
+xtensa_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+  /* Just obvious.  */
+  if (subset == superset)
+     return true;
+
+  /* A __force_l32 pointer can point to any location in the generic
+     address space, though its efficiency is another matter.  */
+  if (superset == ADDR_SPACE_FORCE_L32 && subset == ADDR_SPACE_GENERIC)
+    return true;
+
+  return false;
+}
+
+/* Implement TARGET_ADDR_SPACE_CONVERT.  */
+
+static rtx
+xtensa_addr_space_convert (rtx op, tree from_type, tree to_type)
+{
+  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
+  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
+
+  /* A __force_l32 pointer can point to any location in the generic
+     address space, though its efficiency is another matter.  */
+  if (to_as == ADDR_SPACE_FORCE_L32 && from_as == ADDR_SPACE_GENERIC)
+    ;
+  /* However, the reverse conversion carries risks.  */
+  else if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_FORCE_L32)
+    warning (0, "converting from the %<__force_l32%> address space to the "
+               "generic one is generally not safe");
+  /* Unimplemented conversions are of course not supported.  */
+  else
+    error ("conversion between those address spaces is not supported");
+
+  return op;
+}
+
+/* Implement TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P.  */
+
+static bool
+xtensa_addr_space_legitimate_address_p (machine_mode mode, rtx addr,
+                                       bool strict, addr_space_t as,
+                                       code_helper ch)
+{
+  switch (as)
+    {
+    case ADDR_SPACE_FORCE_L32:
+      /* The __force_l32 address space.  */
+
+      while (SUBREG_P (addr))
+       addr = SUBREG_REG (addr);
+
+      /* Only valid with a base register without offset.  */
+      return REG_P (addr) && BASE_REG_P (addr, strict);
+    }
+
+  return xtensa_legitimate_address_p (mode, addr, strict, ch);
+}
+
 /* Machine-specific pass in order to replace all assignments of large
    integer constants (i.e., that do not fit into the immediate field which
    can hold signed 12 bits) with other legitimate forms, specifically,
index bdedf0ea9d806d4a45ad5a16da4d16b803e56db7..2bf43f7af42ce3ffc46997265f8f7bf44fb62ddc 100644 (file)
@@ -317,6 +317,13 @@ along with GCC; see the file COPYING3.  If not see
    call an address kept in a register.  */
 #define NO_FUNCTION_CSE 1
 
+/* Named address spaces.  */
+#define ADDR_SPACE_FORCE_L32 1
+#define REGISTER_TARGET_PRAGMAS()                                      \
+  do {                                                                 \
+    c_register_addr_space ("__force_l32", ADDR_SPACE_FORCE_L32);       \
+  } while (0)
+
 /* Xtensa processors have "register windows".  GCC does not currently
    take advantage of the possibility for variable-sized windows; instead,
    we use a fixed window size of 8.  */
index a96ea6feb60f71a43e73e26b1b960cf8bdd11ffa..f22191a61e471d39a5e664ff9eaf9dc90ec10b1e 100644 (file)
 \f
 ;; Zero-extend instructions.
 
-(define_insn "zero_extend<mode>si2"
+(define_expand "zero_extend<mode>si2"
+  [(set (match_operand:SI 0 "register_operand")
+       (zero_extend:SI (match_operand:HQI 1 "nonimmed_operand")))]
+  ""
+{
+  if (xtensa_expand_load_force_l32 (operands, SImode, <MODE>mode, true))
+    ;
+  else
+    emit_insn (gen_zero_extend<mode>si2_internal (operands[0], operands[1]));
+  DONE;
+})
+
+(define_insn "zero_extend<mode>si2_internal"
   [(set (match_operand:SI 0 "register_operand")
        (zero_extend:SI (match_operand:HQI 1 "nonimmed_operand")))]
   ""
        (sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
   ""
 {
-  if (sext_operand (operands[1], HImode))
+  if (xtensa_expand_load_force_l32 (operands, SImode, HImode, false))
+    ;
+  else if (sext_operand (operands[1], HImode))
     emit_insn (gen_extendhisi2_internal (operands[0], operands[1]));
   else
     xtensa_extend_reg (operands[0], operands[1]);
        (sign_extend:SI (match_operand:QI 1 "register_operand" "")))]
   ""
 {
-  if (TARGET_SEXT)
+  if (xtensa_expand_load_force_l32 (operands, SImode, QImode, false))
+    ;
+  else if (TARGET_SEXT)
     emit_insn (gen_extendqisi2_internal (operands[0], operands[1]));
   else
     xtensa_extend_reg (operands[0], operands[1]);
        (match_operand:HI 1 "general_operand" ""))]
   ""
 {
+  if (xtensa_expand_load_force_l32 (operands, HImode, HImode, true))
+    DONE;
   if (xtensa_emit_move_sequence (operands, HImode))
     DONE;
 })
        (match_operand:QI 1 "general_operand" ""))]
   ""
 {
+  if (xtensa_expand_load_force_l32 (operands, QImode, QImode, true))
+    DONE;
   if (xtensa_emit_move_sequence (operands, QImode))
     DONE;
 })
                [(match_operand:SI 1 "register_operand" "r")
                 (ashift:SI (match_operand:SI 2 "register_operand" "r")
                            (const_int 3))]))]
-  "!optimize_debug && optimize"
+  ""
 {
   switch (GET_CODE (operands[3]))
     {