From: Takayuki 'January June' Suwa Date: Sat, 2 May 2026 18:11:30 +0000 (+0900) Subject: xtensa: Implement "__force_l32" named address space X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2379d07acec0e5d3df49678b8ea2e10cfc1b47cc;p=thirdparty%2Fgcc.git xtensa: Implement "__force_l32" named address space 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_extendsi2_internal): Rename from zero_extendsi2. (zero_extendsi2): 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. --- diff --git a/gcc/config/xtensa/xtensa-protos.h b/gcc/config/xtensa/xtensa-protos.h index 034549b61e0..71323354e79 100644 --- a/gcc/config/xtensa/xtensa-protos.h +++ b/gcc/config/xtensa/xtensa-protos.h @@ -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); diff --git a/gcc/config/xtensa/xtensa.cc b/gcc/config/xtensa/xtensa.cc index e4c28071a0d..bf7e1dd41f5 100644 --- a/gcc/config/xtensa/xtensa.cc +++ b/gcc/config/xtensa/xtensa.cc @@ -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 &, vec &, vec &, vec &, vec &, vec &, 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); @@ -292,7 +297,7 @@ static rtx_insn *xtensa_md_asm_adjust (vec &, vec &, #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 &, vec &, #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; @@ -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 &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, diff --git a/gcc/config/xtensa/xtensa.h b/gcc/config/xtensa/xtensa.h index bdedf0ea9d8..2bf43f7af42 100644 --- a/gcc/config/xtensa/xtensa.h +++ b/gcc/config/xtensa/xtensa.h @@ -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. */ diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md index a96ea6feb60..f22191a61e4 100644 --- a/gcc/config/xtensa/xtensa.md +++ b/gcc/config/xtensa/xtensa.md @@ -919,7 +919,19 @@ ;; Zero-extend instructions. -(define_insn "zero_extendsi2" +(define_expand "zero_extendsi2" + [(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, true)) + ; + else + emit_insn (gen_zero_extendsi2_internal (operands[0], operands[1])); + DONE; +}) + +(define_insn "zero_extendsi2_internal" [(set (match_operand:SI 0 "register_operand") (zero_extend:SI (match_operand:HQI 1 "nonimmed_operand")))] "" @@ -937,7 +949,9 @@ (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]); @@ -959,7 +973,9 @@ (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]); @@ -1302,6 +1318,8 @@ (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; }) @@ -1331,6 +1349,8 @@ (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; }) @@ -1621,7 +1641,7 @@ [(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])) {