diff options
Diffstat (limited to 'gcc/stmt.cc')
-rw-r--r-- | gcc/stmt.cc | 277 |
1 files changed, 271 insertions, 6 deletions
diff --git a/gcc/stmt.cc b/gcc/stmt.cc index a510f8f..7942aa3 100644 --- a/gcc/stmt.cc +++ b/gcc/stmt.cc @@ -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" @@ -53,6 +55,7 @@ along with GCC; see the file COPYING3. If not see #include "dumpfile.h" #include "builtins.h" #include "cfgexpand.h" +#include "output.h" /* Functions and data structures for expanding case statements. */ @@ -175,6 +178,77 @@ expand_label (tree label) maybe_set_first_label_num (label_r); } +/* Parse a hard register constraint and return its number or -1 in case of an + error. BEGIN should point to a string of the form `{regname}`. For the + sake of simplicity assume that a register name is not longer than 31 + characters, if not error out. */ + +int +decode_hard_reg_constraint (const char *begin) +{ + if (*begin != '{') + return -1; + ++begin; + const char *end = begin; + while (*end != '}' && *end != '\0') + ++end; + if (*end != '}' || end == begin) + return -1; + ptrdiff_t len = end - begin; + if (len >= 31) + return -1; + char regname[32]; + memcpy (regname, begin, len); + regname[len] = '\0'; + int regno = decode_reg_name (regname); + 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, @@ -191,7 +265,8 @@ expand_label (tree label) 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; @@ -245,6 +320,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; ) { @@ -264,12 +342,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 '-': + case 'N': case 'O': case 'P': case '-': + break; + + case ',': + ++alt; + early_clobbered = false; break; case '0': case '1': case '2': case '3': case '4': @@ -294,6 +381,60 @@ parse_output_constraint (const char **constraint_p, int operand_num, *allows_mem = true; break; + 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; + } + int overlap_regno = reg_info->test_alt_output (alt, regno); + if (overlap_regno < 0) + overlap_regno = reg_info->test_reg_asm_output (regno); + if (overlap_regno >= 0) + { + error ("multiple outputs to hard register: %s", + reg_names[overlap_regno]); + return false; + } + 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; + } + default: if (!ISALPHA (*p)) break; @@ -305,6 +446,31 @@ 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) + { + location_t loc = DECL_SOURCE_LOCATION (reg_info->operand); + error_at (loc, "invalid register name for %q+D", + reg_info->operand); + return false; + } + int overlap_regno = reg_info->test_alt_output (alt, regno); + if (overlap_regno >= 0) + { + error ("multiple outputs to hard register: %s", + reg_names[overlap_regno]); + return false; + } + reg_info->set_reg_asm_output (regno); + if (early_clobbered) + reg_info->set_early_clobbered (alt, operand_num, regno); + } break; } @@ -322,7 +488,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; @@ -338,6 +505,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]) { @@ -364,7 +534,7 @@ 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 '-': + case 'N': case 'O': case 'P': case '-': break; case ':': @@ -382,6 +552,10 @@ parse_input_constraint (const char **constraint_p, int input_num, } break; + case ',': + ++alt; + break; + /* Whether or not a numeric constraint allows a register is decided by the matching constraint, and so there is no need to do anything special with them. We must handle them in @@ -391,7 +565,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; @@ -429,6 +602,65 @@ parse_input_constraint (const char **constraint_p, int input_num, *allows_mem = true; break; + 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; + } + int overlap_regno = reg_info->test_alt_input (alt, regno); + if (overlap_regno < 0) + overlap_regno = reg_info->test_reg_asm_input (regno); + if (overlap_regno >= 0) + { + error ("multiple inputs to hard register: %s", + reg_names[overlap_regno]); + return false; + } + 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; + } + default: if (! ISALPHA (constraint[j])) { @@ -445,6 +677,39 @@ 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) + { + location_t loc = DECL_SOURCE_LOCATION (reg_info->operand); + error_at (loc, "invalid register name for %q+D", + reg_info->operand); + return false; + } + int overlap_regno = reg_info->test_alt_input (alt, regno); + if (overlap_regno >= 0) + { + error ("multiple inputs to hard register: %s", + reg_names[overlap_regno]); + return false; + } + reg_info->set_reg_asm_input (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; + } + } break; } |