@@ -2966,44 +2966,6 @@ expand_asm_loc (tree string, int vol, location_t locus)
emit_insn (body);
}
-/* Return the number of times character C occurs in string S. */
-static int
-n_occurrences (int c, const char *s)
-{
- int n = 0;
- while (*s)
- n += (*s++ == c);
- return n;
-}
-
-/* A subroutine of expand_asm_operands. Check that all operands have
- the same number of alternatives. Return true if so. */
-
-static bool
-check_operand_nalternatives (const vec<const char *> &constraints)
-{
- unsigned len = constraints.length();
- if (len > 0)
- {
- int nalternatives = n_occurrences (',', constraints[0]);
-
- if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
- {
- error ("too many alternatives in %<asm%>");
- return false;
- }
-
- for (unsigned i = 1; i < len; ++i)
- if (n_occurrences (',', constraints[i]) != nalternatives)
- {
- error ("operand constraints for %<asm%> differ "
- "in number of alternatives");
- return false;
- }
- }
- return true;
-}
-
/* Check for overlap between registers marked in CLOBBERED_REGS and
anything inappropriate in T. Emit error and return the register
variable definition for error, NULL_TREE for ok. */
@@ -3169,10 +3131,6 @@ expand_asm_stmt (gasm *stmt)
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
}
- /* ??? Diagnose during gimplification? */
- if (! check_operand_nalternatives (constraints))
- return;
-
/* Count the number of meaningful clobbered registers, ignoring what
we would ignore later. */
auto_vec<rtx> clobber_rvec;
@@ -70,6 +70,10 @@ along with GCC; see the file COPYING3. If not see
#include "omp-offload.h"
#include "context.h"
#include "tree-nested.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+#include "gimplify_reg_info.h"
/* Identifier for a basic condition, mapping it to other basic conditions of
its Boolean expression. Basic conditions given the same uid (in the same
@@ -7021,6 +7025,42 @@ gimplify_addr_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
return ret;
}
+/* Return the number of times character C occurs in string S. */
+
+static int
+num_occurrences (int c, const char *s)
+{
+ int n = 0;
+ while (*s)
+ n += (*s++ == c);
+ return n;
+}
+
+/* A subroutine of gimplify_asm_expr. Check that all operands have
+ the same number of alternatives. Return -1 if this is violated. Otherwise
+ return the number of alternatives. */
+
+static int
+num_alternatives (const_tree link)
+{
+ if (link == nullptr)
+ return 0;
+
+ const char *constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ int num = num_occurrences (',', constraint);
+
+ if (num + 1 > MAX_RECOG_ALTERNATIVES)
+ return -1;
+
+ for (link = TREE_CHAIN (link); link; link = TREE_CHAIN (link))
+ {
+ constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ if (num_occurrences (',', constraint) != num)
+ return -1;
+ }
+ return num + 1;
+}
+
/* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple
value; output operands should be a gimple lvalue. */
@@ -7051,6 +7091,47 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
clobbers = NULL;
labels = NULL;
+ int num_alternatives_out = num_alternatives (ASM_OUTPUTS (expr));
+ int num_alternatives_in = num_alternatives (ASM_INPUTS (expr));
+ if (num_alternatives_out == -1 || num_alternatives_in == -1
+ || (num_alternatives_out > 0 && num_alternatives_in > 0
+ && num_alternatives_out != num_alternatives_in))
+ {
+ error ("operand constraints for %<asm%> differ "
+ "in number of alternatives");
+ return GS_ERROR;
+ }
+ int num_alternatives = MAX (num_alternatives_out, num_alternatives_in);
+
+ int ninputs = list_length (ASM_INPUTS (expr));
+ /* Normalization turns in/out operands into explicit output and input
+ operands. Thus, count those implicit inputs, too. */
+ for (tree link = ASM_OUTPUTS (expr); link; link = TREE_CHAIN (link))
+ {
+ const char *constraint
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ if (strchr (constraint, '+') != nullptr)
+ ++ninputs;
+ }
+
+ gimplify_reg_info reg_info (num_alternatives, noutputs, ninputs);
+
+ link_next = NULL_TREE;
+ for (link = ASM_CLOBBERS (expr); link; link = link_next)
+ {
+ /* The clobber entry could also be an error marker. */
+ if (TREE_CODE (TREE_VALUE (link)) == STRING_CST)
+ {
+ const char *regname= TREE_STRING_POINTER (TREE_VALUE (link));
+ int regno = decode_reg_name (regname);
+ if (regno >= 0)
+ reg_info.set_clobbered (regno);
+ }
+ link_next = TREE_CHAIN (link);
+ TREE_CHAIN (link) = NULL_TREE;
+ vec_safe_push (clobbers, link);
+ }
+
ret = GS_ALL_DONE;
link_next = NULL_TREE;
for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = link_next)
@@ -7067,8 +7148,9 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
if (constraint_len == 0)
continue;
- ok = parse_output_constraint (&constraint, i, 0, 0,
- &allows_mem, &allows_reg, &is_inout);
+ reg_info.operand = TREE_VALUE (link);
+ ok = parse_output_constraint (&constraint, i, 0, 0, &allows_mem,
+ &allows_reg, &is_inout, ®_info);
if (!ok)
{
ret = GS_ERROR;
@@ -7238,13 +7320,60 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
}
}
+ /* After all output operands have been gimplified, verify that each output
+ operand is used at most once in case of hard register constraints. Thus,
+ error out in cases like
+ asm ("" : "={0}" (x), "={1}" (x));
+ or even for
+ asm ("" : "=r" (x), "={1}" (x));
+
+ FIXME: Ideally we would also error out for cases like
+ int x;
+ asm ("" : "=r" (x), "=r" (x));
+ However, since code like that was previously accepted, erroring out now might
+ break existing code. On the other hand, we already error out for register
+ asm like
+ register int x asm ("0");
+ asm ("" : "=r" (x), "=r" (x));
+ Thus, maybe it wouldn't be too bad to also error out in the former
+ non-register-asm case.
+ */
+ for (unsigned i = 0; i < vec_safe_length (outputs); ++i)
+ {
+ tree link = (*outputs)[i];
+ tree op1 = TREE_VALUE (link);
+ const char *constraint
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ if (strchr (constraint, '{') != nullptr)
+ for (unsigned j = 0; j < vec_safe_length (outputs); ++j)
+ {
+ if (i == j)
+ continue;
+ tree link2 = (*outputs)[j];
+ tree op2 = TREE_VALUE (link2);
+ if (op1 == op2)
+ {
+ error ("multiple outputs to lvalue %qE", op2);
+ return GS_ERROR;
+ }
+ }
+ }
+
link_next = NULL_TREE;
- for (link = ASM_INPUTS (expr); link; ++i, link = link_next)
+ int input_num = 0;
+ for (link = ASM_INPUTS (expr); link; ++input_num, ++i, link = link_next)
{
link_next = TREE_CHAIN (link);
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
- parse_input_constraint (&constraint, 0, 0, noutputs, 0,
- oconstraints, &allows_mem, &allows_reg);
+ reg_info.operand = TREE_VALUE (link);
+ bool ok = parse_input_constraint (&constraint, input_num, 0, noutputs, 0,
+ oconstraints, &allows_mem, &allows_reg,
+ ®_info);
+ if (!ok)
+ {
+ ret = GS_ERROR;
+ is_inout = false;
+ }
/* If we can't make copies, we can only accept memory. */
tree intype = TREE_TYPE (TREE_VALUE (link));
@@ -7325,15 +7454,7 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
}
link_next = NULL_TREE;
- for (link = ASM_CLOBBERS (expr); link; ++i, link = link_next)
- {
- link_next = TREE_CHAIN (link);
- TREE_CHAIN (link) = NULL_TREE;
- vec_safe_push (clobbers, link);
- }
-
- link_next = NULL_TREE;
- for (link = ASM_LABELS (expr); link; ++i, link = link_next)
+ for (link = ASM_LABELS (expr); link; link = link_next)
{
link_next = TREE_CHAIN (link);
TREE_CHAIN (link) = NULL_TREE;
new file mode 100644
@@ -0,0 +1,169 @@
+/* gimplify_reg_info is used during gimplification while walking over
+ operands and their corresponding constraints of asm statements in order to
+ detect errors.
+
+ m_alt_output is a mapping describing which registers are potentially used in
+ which alternative over all outputs. Similarly for m_alt_input but over all
+ inputs.
+
+ m_early_clobbered_alt is a mapping describing which register is early
+ clobbered in which alternative over all outputs.
+
+ m_early_clobbered_output is the counter part to the prior one, i.e., it
+ is a mapping describing which register is early clobbered in which operand
+ over all alternatives.
+
+ m_reg_asm_input describes the register in case an input is a register asm
+ and -1 in case it is not. */
+
+#include "regs.h"
+
+class gimplify_reg_info
+{
+ HARD_REG_SET *m_buf;
+ HARD_REG_SET *m_alt_output;
+ HARD_REG_SET *m_alt_input;
+ HARD_REG_SET *m_early_clobbered_alt;
+ HARD_REG_SET *m_early_clobbered_output;
+ int *m_reg_asm_input;
+ const unsigned m_num_alternatives;
+ const unsigned m_num_outputs;
+ const unsigned m_num_inputs = 0;
+ /* Member m_clobbered describes all the registers marked as clobbered in an
+ asm statement, i.e., this is the clobbers list of an extended asm
+
+ asm asm-qualifiers ( AssemblerTemplate
+ : OutputOperands
+ [ : InputOperands
+ [ : Clobbers ] ])
+
+ and is not to be confused with the early clobbers sets. */
+ HARD_REG_SET m_clobbered;
+
+public:
+ tree operand;
+
+ gimplify_reg_info (unsigned num_alternatives,
+ unsigned num_outputs,
+ unsigned num_inputs)
+ : m_num_alternatives{num_alternatives}
+ , m_num_outputs{num_outputs}
+ , m_num_inputs{num_inputs}
+ {
+ CLEAR_HARD_REG_SET (m_clobbered);
+
+ /* If there are no alternatives, then there are no outputs/inputs and there
+ is nothing to do on our end. Thus, we are dealing most likely with a
+ basic asm. */
+ if (num_alternatives == 0)
+ return;
+
+ unsigned buf_size = num_alternatives * 3 + num_outputs;
+ m_buf = new HARD_REG_SET[buf_size];
+ for (unsigned i = 0; i < buf_size; ++i)
+ CLEAR_HARD_REG_SET (m_buf[i]);
+ m_alt_output = &m_buf[0];
+ m_alt_input = &m_buf[num_alternatives];
+ m_early_clobbered_alt = &m_buf[num_alternatives * 2];
+ if (num_outputs > 0)
+ m_early_clobbered_output = &m_buf[num_alternatives * 3];
+ else
+ m_early_clobbered_output = nullptr;
+ if (num_inputs > 0)
+ {
+ m_reg_asm_input = new int[num_inputs];
+ for (unsigned i = 0; i < num_inputs; ++i)
+ m_reg_asm_input[i] = -1;
+ }
+ else
+ m_reg_asm_input = nullptr;
+ }
+
+ ~gimplify_reg_info ()
+ {
+ if (m_num_alternatives > 0)
+ delete[] m_buf;
+ if (m_num_inputs > 0)
+ delete[] m_reg_asm_input;
+ }
+
+ void set_output (unsigned alt, int regno)
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_alt_output[alt], mode, regno);
+ }
+
+ void set_input (unsigned alt, int regno)
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_alt_input[alt], mode, regno);
+ }
+
+ bool test_alt_output (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_alt_output[alt], regno);
+ }
+
+ bool test_alt_input (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_alt_input[alt], regno);
+ }
+
+ void set_reg_asm_input (unsigned input, int regno)
+ {
+ gcc_checking_assert (input < m_num_inputs);
+ m_reg_asm_input[input] = regno;
+ }
+
+ bool used_by_different_reg_asm_input (unsigned input, int regno)
+ {
+ gcc_checking_assert (input < m_num_inputs);
+ for (unsigned i = 0; i < m_num_inputs; ++i)
+ {
+ if (i == input)
+ continue;
+ if (m_reg_asm_input[i] == regno)
+ return true;
+ }
+ return false;
+ }
+
+ void set_early_clobbered (unsigned alt, unsigned output, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ gcc_checking_assert (output < m_num_outputs);
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_early_clobbered_alt[alt], mode, regno);
+ add_to_hard_reg_set (&m_early_clobbered_output[output], mode, regno);
+ }
+
+ bool test_early_clobbered_alt (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_early_clobbered_alt[alt], regno);
+ }
+
+ bool is_early_clobbered_in_any_output_unequal (unsigned operand,
+ int regno) const
+ {
+ gcc_checking_assert (operand < m_num_outputs);
+ for (unsigned op = 0; op < m_num_outputs; ++op)
+ if (op != operand
+ && TEST_HARD_REG_BIT (m_early_clobbered_output[op], regno))
+ return true;
+ return false;
+ }
+
+ void set_clobbered (int regno)
+ {
+ SET_HARD_REG_BIT (m_clobbered, regno);
+ }
+
+ bool is_clobbered (int regno) const
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ return overlaps_hard_reg_set_p (m_clobbered, mode, regno);
+ }
+};
@@ -39,6 +39,8 @@ along with GCC; see the file COPYING3. If not see
#include "emit-rtl.h"
#include "pretty-print.h"
#include "diagnostic-core.h"
+#include "output.h"
+#include "gimplify_reg_info.h"
#include "fold-const.h"
#include "varasm.h"
@@ -201,6 +203,51 @@ decode_hard_reg_constraint (const char *begin)
return regno;
}
+static bool
+eliminable_regno_p (int regnum)
+{
+ static const struct
+ {
+ const int from;
+ const int to;
+ } eliminables[] = ELIMINABLE_REGS;
+ for (size_t i = 0; i < ARRAY_SIZE (eliminables); i++)
+ if (regnum == eliminables[i].from)
+ return true;
+ return false;
+}
+
+/* Perform a similar check as done in make_decl_rtl(). */
+
+static bool
+hardreg_ok_p (int reg_number, machine_mode mode, int operand_num)
+{
+ if (mode == BLKmode)
+ error ("data type isn%'t suitable for register %s of operand %i",
+ reg_names[reg_number], operand_num);
+ else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number))
+ error ("register %s for operand %i cannot be accessed"
+ " by the current target", reg_names[reg_number], operand_num);
+ else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number))
+ error ("register %s for operand %i is not general enough"
+ " to be used as a register variable", reg_names[reg_number], operand_num);
+ else if (!targetm.hard_regno_mode_ok (reg_number, mode))
+ error ("register %s for operand %i isn%'t suitable for data type",
+ reg_names[reg_number], operand_num);
+ else if (reg_number != HARD_FRAME_POINTER_REGNUM
+ && (reg_number == FRAME_POINTER_REGNUM
+#ifdef RETURN_ADDRESS_POINTER_REGNUM
+ || reg_number == RETURN_ADDRESS_POINTER_REGNUM
+#endif
+ || reg_number == ARG_POINTER_REGNUM)
+ && eliminable_regno_p (reg_number))
+ error ("register for operand %i is an internal GCC "
+ "implementation detail", operand_num);
+ else
+ return true;
+ return false;
+}
+
/* Parse the output constraint pointed to by *CONSTRAINT_P. It is the
OPERAND_NUMth output operand, indexed from zero. There are NINPUTS
inputs and NOUTPUTS outputs to this extended-asm. Upon return,
@@ -217,7 +264,8 @@ decode_hard_reg_constraint (const char *begin)
bool
parse_output_constraint (const char **constraint_p, int operand_num,
int ninputs, int noutputs, bool *allows_mem,
- bool *allows_reg, bool *is_inout)
+ bool *allows_reg, bool *is_inout,
+ gimplify_reg_info *reg_info)
{
const char *constraint = *constraint_p;
const char *p;
@@ -271,6 +319,9 @@ parse_output_constraint (const char **constraint_p, int operand_num,
constraint = *constraint_p;
}
+ unsigned int alt = 0;
+ bool early_clobbered = false;
+
/* Loop through the constraint string. */
for (p = constraint + 1; *p; )
{
@@ -290,12 +341,21 @@ parse_output_constraint (const char **constraint_p, int operand_num,
}
break;
- case '?': case '!': case '*': case '&': case '#':
+ case '&':
+ early_clobbered = true;
+ break;
+
+ case '?': case '!': case '*': case '#':
case '$': case '^':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
- case 'N': case 'O': case 'P': case ',':
+ case 'N': case 'O': case 'P':
+ break;
+
+ case ',':
+ ++alt;
+ early_clobbered = false;
break;
case '0': case '1': case '2': case '3': case '4':
@@ -318,6 +378,54 @@ parse_output_constraint (const char **constraint_p, int operand_num,
case '{':
{
+ if (!targetm.lra_p ())
+ {
+ error ("hard register constraints are only supported while using LRA");
+ return false;
+ }
+ if (reg_info)
+ {
+ int regno = decode_hard_reg_constraint (p);
+ if (regno < 0)
+ {
+ error ("invalid output constraint: %s", p);
+ return false;
+ }
+ if (reg_info->test_alt_output (alt, regno))
+ {
+ error ("multiple outputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ {
+ reg_info->set_output (alt, regno);
+ if (early_clobbered)
+ reg_info->set_early_clobbered (alt, operand_num, regno);
+ }
+ if (reg_info->is_clobbered (regno))
+ {
+ error ("hard register constraint for output %i conflicts "
+ "with %<asm%> clobber list", operand_num);
+ return false;
+ }
+ if (VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno_op = decode_reg_name (asmspec);
+ if (regno != regno_op)
+ {
+ error ("constraint and register %<asm%> for output "
+ "operand %i are unsatisfiable", operand_num);
+ return false;
+ }
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, operand_num))
+ return false;
+ }
*allows_reg = true;
break;
}
@@ -333,6 +441,34 @@ parse_output_constraint (const char **constraint_p, int operand_num,
*allows_mem = true;
else
insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem);
+ if (reg_info && *allows_reg
+ && VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno = decode_reg_name (asmspec);
+ if (regno < 0)
+ {
+ error ("invalid register name for %q+D", reg_info->operand);
+ return false;
+ }
+ if (reg_info->test_alt_output (alt, regno))
+ {
+ error ("multiple outputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ {
+ reg_info->set_output (alt, regno);
+ if (early_clobbered)
+ reg_info->set_early_clobbered (alt, operand_num, regno);
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, operand_num))
+ return false;
+ }
break;
}
@@ -350,7 +486,8 @@ bool
parse_input_constraint (const char **constraint_p, int input_num,
int ninputs, int noutputs, int ninout,
const char * const * constraints,
- bool *allows_mem, bool *allows_reg)
+ bool *allows_mem, bool *allows_reg,
+ gimplify_reg_info *reg_info)
{
const char *constraint = *constraint_p;
const char *orig_constraint = constraint;
@@ -365,6 +502,9 @@ parse_input_constraint (const char **constraint_p, int input_num,
/* Make sure constraint has neither `=', `+', nor '&'. */
+ unsigned int alt = 0;
+ unsigned long match = 0;
+
for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
switch (constraint[j])
{
@@ -391,7 +531,11 @@ parse_input_constraint (const char **constraint_p, int input_num,
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
- case 'N': case 'O': case 'P': case ',':
+ case 'N': case 'O': case 'P':
+ break;
+
+ case ',':
+ ++alt;
break;
/* Whether or not a numeric constraint allows a register is
@@ -403,7 +547,6 @@ parse_input_constraint (const char **constraint_p, int input_num,
case '5': case '6': case '7': case '8': case '9':
{
char *end;
- unsigned long match;
saw_match = true;
@@ -443,6 +586,58 @@ parse_input_constraint (const char **constraint_p, int input_num,
case '{':
{
+ if (!targetm.lra_p ())
+ {
+ error ("hard register constraints are only supported while using LRA");
+ return false;
+ }
+ if (reg_info)
+ {
+ int regno = decode_hard_reg_constraint (constraint + j);
+ if (regno < 0)
+ {
+ error ("invalid input constraint: %s", constraint + j);
+ return false;
+ }
+ if (reg_info->test_alt_input (alt, regno)
+ || reg_info->used_by_different_reg_asm_input (input_num, regno))
+ {
+ error ("multiple inputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ reg_info->set_input (alt, regno);
+ if (reg_info->is_clobbered (regno))
+ {
+ error ("hard register constraint for input %i conflicts "
+ "with %<asm%> clobber list", input_num);
+ return false;
+ }
+ if (constraint == orig_constraint
+ && reg_info->test_early_clobbered_alt (alt, regno))
+ {
+ error ("invalid hard register usage between earlyclobber "
+ "operand and input operand");
+ return false;
+ }
+ if (VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno_op = decode_reg_name (asmspec);
+ if (regno != regno_op)
+ {
+ error ("constraint and register %<asm%> for input "
+ "operand %i are unsatisfiable", input_num);
+ return false;
+ }
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, input_num))
+ return false;
+ }
*allows_reg = true;
break;
}
@@ -463,6 +658,38 @@ parse_input_constraint (const char **constraint_p, int input_num,
*allows_mem = true;
else
insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem);
+ if (reg_info && *allows_reg
+ && VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno = decode_reg_name (asmspec);
+ if (regno < 0)
+ {
+ error ("invalid register name for %q+D", reg_info->operand);
+ return false;
+ }
+ if (reg_info->test_alt_input (alt, regno))
+ {
+ error ("multiple inputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ reg_info->set_reg_asm_input (input_num, regno);
+ if ((constraint == orig_constraint
+ && reg_info->test_early_clobbered_alt (alt, regno))
+ || (constraint != orig_constraint
+ && reg_info->is_early_clobbered_in_any_output_unequal
+ (match, regno)))
+ {
+ error ("invalid hard register usage between earlyclobber operand and input operand");
+ return false;
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, input_num))
+ return false;
+ }
break;
}
@@ -20,11 +20,15 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_STMT_H
#define GCC_STMT_H
+class gimplify_reg_info;
+
extern void expand_label (tree);
extern bool parse_output_constraint (const char **, int, int, int,
- bool *, bool *, bool *);
+ bool *, bool *, bool *,
+ gimplify_reg_info * = nullptr);
extern bool parse_input_constraint (const char **, int, int, int, int,
- const char * const *, bool *, bool *);
+ const char * const *, bool *, bool *,
+ gimplify_reg_info * = nullptr);
extern int decode_hard_reg_constraint (const char *);
extern tree resolve_asm_operand_names (tree, tree, tree, tree);
#ifdef HARD_CONST
new file mode 100644
@@ -0,0 +1,83 @@
+/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */
+
+#if defined (__aarch64__)
+# define GPR1_RAW "x0"
+# define GPR2 "{x1}"
+# define GPR3 "{x2}"
+# define INVALID_GPR_A "{x31}"
+#elif defined (__arm__)
+# define GPR1_RAW "r0"
+# define GPR2 "{r1}"
+# define GPR3 "{r2}"
+# define INVALID_GPR_A "{r16}"
+#elif defined (__i386__)
+# define GPR1_RAW "%eax"
+# define GPR2 "{%ebx}"
+# define GPR3 "{%edx}"
+# define INVALID_GPR_A "{%eex}"
+#elif defined (__powerpc__) || defined (__POWERPC__)
+# define GPR1_RAW "r4"
+# define GPR2 "{r5}"
+# define GPR3 "{r6}"
+# define INVALID_GPR_A "{r33}"
+#elif defined (__riscv)
+# define GPR1_RAW "t4"
+# define GPR2 "{t5}"
+# define GPR3 "{t6}"
+# define INVALID_GPR_A "{t7}"
+#elif defined (__s390__)
+# define GPR1_RAW "r4"
+# define GPR2 "{r5}"
+# define GPR3 "{r6}"
+# define INVALID_GPR_A "{r17}"
+#elif defined (__x86_64__)
+# define GPR1_RAW "rax"
+# define GPR2 "{rbx}"
+# define GPR3 "{rcx}"
+# define INVALID_GPR_A "{rex}"
+#endif
+
+#define GPR1 "{"GPR1_RAW"}"
+#define INVALID_GPR_B "{"GPR1_RAW
+
+struct { int a[128]; } s = {0};
+
+void
+test (void)
+{
+ int x, y;
+ register int gpr1 __asm__ (GPR1_RAW) = 0;
+
+ __asm__ ("" :: "{}" (42)); /* { dg-error "invalid input constraint: \{\}" } */
+ __asm__ ("" :: INVALID_GPR_A (42)); /* { dg-error "invalid input constraint" } */
+ __asm__ ("" :: INVALID_GPR_B (42)); /* { dg-error "invalid input constraint" } */
+
+ __asm__ ("" :: GPR1 (s)); /* { dg-error "data type isn't suitable for register .* of operand 0" } */
+
+ __asm__ ("" :: "r" (gpr1), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 (42), "r" (gpr1)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 (42), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1","GPR2 (42), GPR2","GPR3 (42));
+ __asm__ ("" :: GPR1","GPR2 (42), GPR3","GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1","GPR2 (42), GPR1","GPR3 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 GPR2 (42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" : "+"GPR1 (x), "="GPR1 (y)); /* { dg-error "multiple outputs to hard register" } */
+ __asm__ ("" : "="GPR1 (y) : GPR1 (42), "0" (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" : "+"GPR1 (x) : GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+
+ __asm__ ("" : "="GPR1 (gpr1));
+ __asm__ ("" : "="GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for output operand 0 are unsatisfiable" } */
+ __asm__ ("" :: GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */
+ __asm__ ("" : "="GPR1 (x) : "0" (gpr1));
+ __asm__ ("" : "="GPR1 GPR2 (x) : "0" (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */
+
+ __asm__ ("" : "=&"GPR1 (x) : "0" (gpr1));
+ __asm__ ("" : "=&"GPR1 (x) : "0" (42));
+ __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,"GPR1 (42));
+ __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,"GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR1 (x) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,r" (gpr1));
+ __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,r" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&r" (gpr1) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR1 (x), "=r" (y) : "1" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+}
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { { aarch64*-*-* s390x-*-* } && int128 } } } */
+/* { dg-options "-O2" } get rid of -ansi since we use __int128 */
+
+/* Test register pairs. */
+
+#if defined (__aarch64__)
+# define GPR1 "{x4}"
+# define GPR2_RAW "x5"
+#elif defined (__s390__)
+# define GPR1 "{r4}"
+# define GPR2_RAW "r5"
+#endif
+
+#define GPR2 "{"GPR2_RAW"}"
+
+void
+test (void)
+{
+ __asm__ ("" :: GPR1 ((__int128) 42));
+ __asm__ ("" :: GPR2 ((__int128) 42)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */
+ __asm__ ("" :: GPR1 ((__int128) 42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+
+ __int128 x;
+ __asm__ ("" : "="GPR1 (x) :: GPR2_RAW); /* { dg-error "hard register constraint for output 0 conflicts with 'asm' clobber list" } */
+ __asm__ ("" : "=r" (x) : GPR1 (x) : GPR2_RAW); /* { dg-error "hard register constraint for input 0 conflicts with 'asm' clobber list" } */
+}
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile { target arm-*-* s390-*-* } } */
+/* { dg-options "-std=c99" } we need long long */
+/* { dg-additional-options "-march=armv7-a" { target arm-*-* } } */
+
+/* Test register pairs. */
+
+#if defined (__arm__)
+# define GPR1 "{r4}"
+# define GPR2_RAW "r5"
+#elif defined (__s390__)
+# define GPR1 "{r4}"
+# define GPR2_RAW "r5"
+#endif
+
+#define GPR2 "{"GPR2_RAW"}"
+
+void
+test (void)
+{
+ __asm__ ("" :: GPR1 (42ll));
+ __asm__ ("" :: GPR2 (42ll)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */
+ __asm__ ("" :: GPR1 (42ll), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+
+ long long x;
+ __asm__ ("" : "="GPR1 (x) :: GPR2_RAW); /* { dg-error "hard register constraint for output 0 conflicts with 'asm' clobber list" } */
+ __asm__ ("" : "=r" (x) : GPR1 (x) : GPR2_RAW); /* { dg-error "hard register constraint for input 0 conflicts with 'asm' clobber list" } */
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+
+/* Verify output operands. */
+
+int
+test (void)
+{
+ int x;
+ register int y __asm__ ("0");
+
+ /* Preserve status quo and don't error out. */
+ __asm__ ("" : "=r" (x), "=r" (x));
+
+ /* Although it is somewhat inconsistent, we error out in case of register asm for status quo. */
+ __asm__ ("" : "=r" (y), "=r" (y)); /* { dg-error "multiple outputs to hard register" } */
+
+ /* Be more strict for hard register constraints and error out. */
+ __asm__ ("" : "={0}" (x), "={1}" (x)); /* { dg-error "multiple outputs to lvalue 'x'" } */
+
+ /* Still error out in case of a mixture. */
+ __asm__ ("" : "=r" (x), "={1}" (x)); /* { dg-error "multiple outputs to lvalue 'x'" } */
+
+ return x + y;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+/* Test clobbers.
+ See asm-hard-reg-error-{2,3}.c for tests involving register pairs. */
+
+int
+test (void)
+{
+ int x, y;
+ __asm__ ("" : "={0}" (x), "={1}" (y) : : "1"); /* { dg-error "hard register constraint for output 1 conflicts with 'asm' clobber list" } */
+ __asm__ ("" : "={0}" (x) : "{0}" (y), "{1}" (y) : "1"); /* { dg-error "hard register constraint for input 1 conflicts with 'asm' clobber list" } */
+ return x + y;
+}
@@ -11,34 +11,6 @@ test0 (void)
{
register long var1 asm (REG1);
register long var2 asm (REG1);
- asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "invalid hard register usage between output operands" } */
+ asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "multiple outputs to hard register" } */
return var1;
}
-
-long
-test1 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG2);
- asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */
- return var1;
-}
-
-long
-test2 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG1);
- asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
- return var1;
-}
-
-long
-test3 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG1);
- long var3;
- asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
- return var1 + var3;
-}
new file mode 100644
@@ -0,0 +1,35 @@
+/* PR rtl-optimization/87600 */
+/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */
+/* { dg-options "-O2" } */
+
+#include "pr87600.h"
+
+/* The following are all invalid uses of local register variables. */
+
+long
+test1 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG2);
+ asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */
+ return var1;
+}
+
+long
+test2 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG1);
+ asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ return var1;
+}
+
+long
+test3 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG1);
+ long var3;
+ asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ return var1 + var3;
+}
@@ -896,6 +896,10 @@ proc configure_check-function-bodies { config } {
set up_config(fluff) {^\s*(?://)}
} elseif { [istarget *-*-darwin*] } {
set up_config(fluff) {^\s*(?:\.|//|@)|^L[0-9ABCESV]}
+ } elseif { [istarget s390*-*-*] } {
+ # Additionally to the defaults skip lines beginning with a # resulting
+ # from inline asm.
+ set up_config(fluff) {^\s*(?:\.|//|@|$|#)}
} else {
# Skip lines beginning with labels ('.L[...]:') or other directives
# ('.align', '.cfi_startproc', '.quad [...]', '.text', etc.), '//' or