xtensa: Implement "force_l32" target-specific attribute

The previous patch introduced the target-specific named address space
"__force_l32", but this reserved identifier can only be used from C.

Therefore, this patch introduces a new target-specific attribute
"force_l32," which is very similar to the named address space "__force_l32,"
making that feature usable not only in C but also in other languages.

     /* example */
     extern "C" {
       unsigned int test(const char *p) {
         for (const char __attribute__((force_l32)) *q = p; ; ++q)
           if (!*q)
             return q - p;
       }
     }

     ;; result (-Os -mlittle-endian)
     test:
     	entry	sp, 32
     	mov.n	a8, a2
     	movi.n	a10, -4
     .L3:
     	and	a9, a8, a10	;; *q : align to SImode
     	l32i.n	a9, a9, 0	;; *q : load:SI
     	ssa8l	a8		;; *q : shift to bit position 0
     	srl	a9, a9
     	extui	a9, a9, 0, 8	:: *q : zero_extract:QI
     	beqz.n	a9, .L5
     	addi.n	a8, a8, 1
     	j	.L3
     .L5:
     	sub	a2, a8, a2
     	retw.n

gcc/ChangeLog:

	* config/xtensa/xtensa.cc (xtensa_attribute_table,
	TARGET_ATTRIBUTE_TABLE):
	New definitions for target-specific attributes.
	(xtensa_expand_load_force_l32_1): New sub-function for inspecting
	the attribute from the specified MEM rtx.
	(xtensa_expand_load_force_l32): Add handlings for for addresses
	with offsets.
	(xtensa_handle_force_l32_attribute_1,
	xtensa_handle_force_l32_attribute):
	New functions for handling the attribute.
This commit is contained in:
Takayuki 'January June' Suwa
2026-04-28 21:25:38 +09:00
committed by Max Filippov
parent 2379d07ace
commit 9eba97e412

View File

@@ -206,6 +206,7 @@ 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);
static tree xtensa_handle_force_l32_attribute (tree *, tree, tree, int, bool *);
@@ -390,6 +391,17 @@ static bool xtensa_addr_space_legitimate_address_p (machine_mode, rtx, bool,
#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
xtensa_addr_space_legitimate_address_p
TARGET_GNU_ATTRIBUTES (xtensa_attribute_table,
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
affects_type_identity, handler, exclude } */
{ "force_l32", 0, 0, true, false, false,
false, xtensa_handle_force_l32_attribute, NULL }
});
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE xtensa_attribute_table
struct gcc_target targetm = TARGET_INITIALIZER;
@@ -2615,6 +2627,18 @@ 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. */
static bool
xtensa_expand_load_force_l32_1 (rtx mem)
{
tree expr = MEM_EXPR (mem), type;
/* If the "force_l32" attribute is found in the tree associated with
mem RTX, return true. */
return expr && (type = TREE_TYPE (expr))
&& TREE_CODE (type) == INTEGER_TYPE
&& lookup_attribute ("force_l32", TYPE_ATTRIBUTES (type));
}
bool
xtensa_expand_load_force_l32 (rtx *operands, machine_mode dest_mode,
machine_mode src_mode, int unsignedp)
@@ -2623,13 +2647,20 @@ xtensa_expand_load_force_l32 (rtx *operands, machine_mode dest_mode,
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)
/* Reject sub-word store to memory with "force_l32". */
if (mem_operand (dest = operands[0], dest_mode))
{
error ("Storing 1- and 2-byte quantities to memory within the "
"%<__force_l32%> address space is not supported");
return false;
if (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;
}
if (xtensa_expand_load_force_l32_1 (dest))
warning (OPT_Wattributes,
"Storing 1- and 2-byte quantities to memory with the "
"%<force_l32%> attribute is not supported and the attribute "
"ignored");
}
/* Exclude insns that do not load memory. */
@@ -2638,13 +2669,31 @@ xtensa_expand_load_force_l32 (rtx *operands, machine_mode dest_mode,
return false;
/* Exclude insns that do not perform memory loading with "force_l32". */
if (MEM_ADDR_SPACE (src) != ADDR_SPACE_FORCE_L32)
if (MEM_ADDR_SPACE (src) != ADDR_SPACE_FORCE_L32
&& ! xtensa_expand_load_force_l32_1 (src))
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));
/* As a preprocessing, handle cases where addr is (PLUS (REG, OFFSET))
form. */
if (REG_P (addr = XEXP (src, 0)))
;
else if (GET_CODE (addr) == PLUS)
{
rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
HOST_WIDE_INT v;
if (! CONST_INT_P (op1))
std::swap (op0, op1);
if (! REG_P (op0) || ! CONST_INT_P (op1))
return false;
if ((v = INTVAL (op1)) == 0)
addr = op0;
else
xtensa_emit_add_imm (addr = gen_reg_rtx (Pmode),
op0, v, NULL_RTX, false);
}
else
return false;
/* First, Load the aligned SImode memory containing the desired [HQ]Imode
value. */
@@ -5620,6 +5669,76 @@ xtensa_addr_space_legitimate_address_p (machine_mode mode, rtx addr,
return xtensa_legitimate_address_p (mode, addr, strict, ch);
}
/* Implement machine-specific attribute "force_l32" handler. */
static bool
xtensa_handle_force_l32_attribute_1 (tree *type)
{
/* If the type has definitions for fields, then recursively process each
of those types, and return true if any of the processes return true. */
if (RECORD_OR_UNION_TYPE_P (*type))
{
bool f = false;
for (tree field = TYPE_FIELDS (*type);
field; field = DECL_CHAIN (field))
f |= xtensa_handle_force_l32_attribute_1 (&TREE_TYPE (field));
return f;
}
/* If the type has an underlying type, it recursively processes that type
and returns the result. */
if (TREE_TYPE (*type))
return xtensa_handle_force_l32_attribute_1 (&TREE_TYPE (*type));
/* If the type is INTEGER and its machine mode is [HQ]I, add the
"force_l32" attribute to that type and return true. */
if (TREE_CODE (*type) == INTEGER_TYPE
&& (TYPE_MODE_RAW (*type) == QImode
|| TYPE_MODE_RAW (*type) == HImode))
{
tree attrs = tree_cons (get_identifier ("force_l32"),
NULL, TYPE_ATTRIBUTES (*type));
*type = build_type_attribute_variant (*type, attrs);
return true;
}
/* If none of the above apply, simply return false. */
return false;
}
static tree
xtensa_handle_force_l32_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
if (DECL_P (*node))
{
if (TREE_CODE (*node) != TYPE_DECL && TREE_CODE (*node) != VAR_DECL
&& TREE_CODE (*node) != PARM_DECL)
{
warning (OPT_Wattributes,
"%qE attribute only applies to declarations of variables, "
"function parameters, or types", name);
*no_add_attrs = true;
}
/* Traverse all [HQ]Imode INTEGER types nested within the underlying
type of that declaration and attempt to apply the "force_l32"
attribute to them. */
else if (! xtensa_handle_force_l32_attribute_1 (&TREE_TYPE (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
}
return NULL_TREE;
}
/* 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,