diff options
| author | Stefan Schulze Frielinghaus <stefansf@gcc.gnu.org> | 2025-07-21 13:05:26 +0200 |
|---|---|---|
| committer | Stefan Schulze Frielinghaus <stefansf@gcc.gnu.org> | 2025-07-21 13:05:26 +0200 |
| commit | cbf17db978c663817e4cd3337bbc80f59fa05bb6 (patch) | |
| tree | 18241ae07554e9237b20ffc4daf032bffd2b4e14 /gcc/gimplify.cc | |
| parent | a51c146ebce41b5e4326b222f2d9e04bb22d276f (diff) | |
Error handling for hard register constraints
This implements error handling for hard register constraints including
potential conflicts with register asm operands.
In contrast to register asm operands, hard register constraints allow
more than just one register per operand. Even more than just one
register per alternative. For example, a valid constraint for an
operand is "{r0}{r1}m,{r2}". However, this also means that we have to
make sure that each register is used at most once in each alternative
over all outputs and likewise over all inputs. For asm statements this
is done by this patch during gimplification. For hard register
constraints used in machine description, error handling is still a todo
and I haven't investigated this so far and consider this rather a low
priority.
gcc/ada/ChangeLog:
* gcc-interface/trans.cc (gnat_to_gnu): Pass null pointer to
parse_{input,output}_constraint().
gcc/analyzer/ChangeLog:
* region-model-asm.cc (region_model::on_asm_stmt): Pass null
pointer to parse_{input,output}_constraint().
gcc/c/ChangeLog:
* c-typeck.cc (build_asm_expr): Pass null pointer to
parse_{input,output}_constraint().
gcc/ChangeLog:
* cfgexpand.cc (n_occurrences): Move this ...
(check_operand_nalternatives): and this ...
(expand_asm_stmt): and the call to gimplify.cc.
* config/s390/s390.cc (s390_md_asm_adjust): Pass null pointer to
parse_{input,output}_constraint().
* gimple-walk.cc (walk_gimple_asm): Pass null pointer to
parse_{input,output}_constraint().
(walk_stmt_load_store_addr_ops): Ditto.
* gimplify-me.cc (gimple_regimplify_operands): Ditto.
* gimplify.cc (num_occurrences): Moved from cfgexpand.cc.
(num_alternatives): Ditto.
(gimplify_asm_expr): Deal with hard register constraints.
* stmt.cc (eliminable_regno_p): New helper.
(hardreg_ok_p): Perform a similar check as done in
make_decl_rtl().
(parse_output_constraint): Add parameter for gimplify_reg_info
and validate hard register constrained operands.
(parse_input_constraint): Ditto.
* stmt.h (class gimplify_reg_info): Forward declaration.
(parse_output_constraint): Add parameter.
(parse_input_constraint): Ditto.
* tree-ssa-operands.cc
(operands_scanner::get_asm_stmt_operands): Pass null pointer
to parse_{input,output}_constraint().
* tree-ssa-structalias.cc (find_func_aliases): Pass null pointer
to parse_{input,output}_constraint().
* varasm.cc (assemble_asm): Pass null pointer to
parse_{input,output}_constraint().
* gimplify_reg_info.h: New file.
gcc/cp/ChangeLog:
* semantics.cc (finish_asm_stmt): Pass null pointer to
parse_{input,output}_constraint().
gcc/d/ChangeLog:
* toir.cc: Pass null pointer to
parse_{input,output}_constraint().
gcc/testsuite/ChangeLog:
* gcc.dg/pr87600-2.c: Split test into two files since errors for
functions test{0,1} are thrown during expand, and for
test{2,3} during gimplification.
* lib/scanasm.exp: On s390, skip lines beginning with #.
* gcc.dg/asm-hard-reg-error-1.c: New test.
* gcc.dg/asm-hard-reg-error-2.c: New test.
* gcc.dg/asm-hard-reg-error-3.c: New test.
* gcc.dg/asm-hard-reg-error-4.c: New test.
* gcc.dg/asm-hard-reg-error-5.c: New test.
* gcc.dg/pr87600-3.c: New test.
* gcc.target/aarch64/asm-hard-reg-2.c: New test.
* gcc.target/s390/asm-hard-reg-7.c: New test.
Diffstat (limited to 'gcc/gimplify.cc')
| -rw-r--r-- | gcc/gimplify.cc | 142 |
1 files changed, 126 insertions, 16 deletions
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index 910314bd54c..fbf47dd9b60 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -71,6 +71,10 @@ along with GCC; see the file COPYING3. If not see #include "context.h" #include "tree-nested.h" #include "gcc-urlifier.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 @@ -7823,6 +7827,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. */ @@ -7853,6 +7893,36 @@ 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); + + gimplify_reg_info reg_info (num_alternatives, noutputs); + + 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) @@ -7869,8 +7939,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; @@ -8001,8 +8072,8 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) *end = '\0'; beg[-1] = '='; tem = beg - 1; - parse_output_constraint (&tem, i, 0, 0, - &mem_p, ®_p, &inout_p); + parse_output_constraint (&tem, i, 0, 0, &mem_p, ®_p, + &inout_p, nullptr); if (dst != str) *dst++ = ','; if (reg_p) @@ -8041,13 +8112,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)); @@ -8129,15 +8247,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; |
