]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Allow limited extended asm at toplevel [PR41045]
authorJakub Jelinek <jakub@redhat.com>
Thu, 5 Dec 2024 08:25:06 +0000 (09:25 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 5 Dec 2024 08:25:06 +0000 (09:25 +0100)
In the Cauldron IPA/LTO BoF we've discussed toplevel asms and it was
discussed it would be nice to tell the compiler something about what
the toplevel asm does.  Sure, I'm aware the kernel people said they
aren't willing to use something like that, but perhaps other projects
do.  And for kernel perhaps we should add some new option which allows
some dumb parsing of the toplevel asms and gather something from that
parsing.

The following patch is just a small step towards that, namely, allow
some subset of extended inline asm outside of functions.
The patch is unfinished, LTO streaming (out/in) of the ASM_EXPRs isn't
implemented (it emits a sorry diagnostics), nor any cgraph/varpool
changes to find out references etc.

The patch allows something like:

int a[2], b;
enum { E1, E2, E3, E4, E5 };
struct S { int a; char b; long long c; };
asm (".section blah; .quad %P0, %P1, %P2, %P3, %P4; .previous"
     : : "m" (a), "m" (b), "i" (42), "i" (E4), "i" (sizeof (struct S)));

Even for non-LTO, that could be useful e.g. for getting enumerators from
C/C++ as integers into the toplevel asm, or sizeof/offsetof etc.

The restrictions I've implemented are:
1) asm qualifiers aren't still allowed, so asm goto or asm inline can't be
   specified at toplevel, asm volatile has the volatile ignored for C++ with
   a warning and is an error in C like before
2) I see good use for mainly input operands, output maybe to make it clear
   that the inline asm may write some memory, I don't see a good use for
   clobbers, so the patch doesn't allow those (and of course labels because
   asm goto can't be specified)
3) the patch allows only constraints which don't allow registers, so
   typically "m" or "i" or other memory or immediate constraints; for
   memory, it requires that the operand is addressable and its address
   could be used in static var initializer (so that no code actually
   needs to be emitted for it), for others that they are constants usable
   in the static var initializers
4) the patch disallows + (there is no reload of the operands, so I don't
   see benefits of tying some operands together), nor % (who cares if
   something is commutative in this case), or & (again, no code is emitted
   around the asm), nor the 0-9 constraints

Right now there is no way to tell the compiler that the inline asm defines
some symbol, that is implemented in a later patch, as : constraint.

Similarly, the c modifier doesn't work in all cases and the cc modifier
is implemented separately.

2024-12-05  Jakub Jelinek  <jakub@redhat.com>

PR c/41045
gcc/
* output.h (insn_noperands): Declare.
* final.cc (insn_noperands): No longer static.
* varasm.cc (assemble_asm): Handle ASM_EXPR.
* lto-streamer-out.cc (lto_output_toplevel_asms): Add sorry_at
for non-STRING_CST toplevel asm for now.
* doc/extend.texi (Basic @code{asm}, Extended @code{asm}): Document
that extended asm is now allowed outside of functions with certain
restrictions.
gcc/c/
* c-parser.cc (c_parser_asm_string_literal): Add forward declaration.
(c_parser_asm_definition): Parse also extended asm without
clobbers/labels.
* c-typeck.cc (build_asm_expr): Allow extended asm outside of
functions and check extra restrictions.
gcc/cp/
* cp-tree.h (finish_asm_stmt): Add TOPLEV_P argument.
* parser.cc (cp_parser_asm_definition): Parse also extended asm
without clobbers/labels outside of functions.
* semantics.cc (finish_asm_stmt): Add TOPLEV_P argument, if set,
check extra restrictions for extended asm outside of functions.
* pt.cc (tsubst_stmt): Adjust finish_asm_stmt caller.
gcc/testsuite/
* c-c++-common/toplevel-asm-1.c: New test.
* c-c++-common/toplevel-asm-2.c: New test.
* c-c++-common/toplevel-asm-3.c: New test.

14 files changed:
gcc/c/c-parser.cc
gcc/c/c-typeck.cc
gcc/cp/cp-tree.h
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/cp/semantics.cc
gcc/doc/extend.texi
gcc/final.cc
gcc/lto-streamer-out.cc
gcc/output.h
gcc/testsuite/c-c++-common/toplevel-asm-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/toplevel-asm-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/toplevel-asm-3.c [new file with mode: 0644]
gcc/varasm.cc

index a7719acf0bf2cb12582eda94ecc778354eb11413..e939767a6e62144c6c5bf42d065d545cf21341fb 100644 (file)
@@ -1662,6 +1662,7 @@ static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree,
 static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree,
                                                          tree, bool);
 static struct c_parm *c_parser_parameter_declaration (c_parser *, tree, bool);
+static tree c_parser_asm_string_literal (c_parser *);
 static tree c_parser_simple_asm_expr (c_parser *);
 static tree c_parser_gnu_attributes (c_parser *);
 static struct c_expr c_parser_initializer (c_parser *, tree);
@@ -3142,12 +3143,74 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 
    asm-definition:
      simple-asm-expr ;
-*/
+     asm ( toplevel-asm-argument ) ;
+
+   toplevel-asm-argument:
+     asm-string-literal
+     asm-string-literal : asm-operands[opt]
+     asm-string-literal : asm-operands[opt] : asm-operands[opt]
+
+   The :: token is considered equivalent to two consecutive : tokens.  */
 
 static void
 c_parser_asm_definition (c_parser *parser)
 {
-  tree asm_str = c_parser_simple_asm_expr (parser);
+  location_t asm_loc = c_parser_peek_token (parser)->location;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
+  c_parser_consume_token (parser);
+  matching_parens parens;
+  tree asm_str = NULL_TREE;
+  tree outputs = NULL_TREE, inputs = NULL_TREE;
+  if (!parens.require_open (parser))
+    goto done;
+  asm_str = c_parser_asm_string_literal (parser);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    {
+      parens.require_close (parser);
+      goto done;
+    }
+  for (int section = 0; section < 2; ++section)
+    {
+      if (c_parser_next_token_is (parser, CPP_SCOPE))
+       {
+         ++section;
+         if (section == 2)
+           {
+             c_parser_error (parser, "expected %<)%>");
+           error_close_paren:
+             c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+             asm_str = NULL_TREE;
+             goto done;
+           }
+         c_parser_consume_token (parser);
+       }
+      else if (!c_parser_require (parser, CPP_COLON,
+                                 "expected %<:%> or %<)%>",
+                                 UNKNOWN_LOCATION, false))
+       goto error_close_paren;
+      if (!c_parser_next_token_is (parser, CPP_COLON)
+         && !c_parser_next_token_is (parser, CPP_SCOPE)
+         && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+       {
+         if (section)
+           inputs = c_parser_asm_operands (parser);
+         else
+           outputs = c_parser_asm_operands (parser);
+       }
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+       break;
+    }
+
+  if (!parens.require_close (parser))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      asm_str = NULL_TREE;
+    }
+
+  if (asm_str)
+    asm_str = build_asm_expr (asm_loc, asm_str, outputs, inputs,
+                             NULL_TREE, NULL_TREE, false, false);
+done:
   if (asm_str)
     symtab->finalize_toplevel_asm (asm_str);
   c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
index e60f89a21d99d09b0407323b398c1deb26c3365f..29a88aa1dfe07c67c120088137f63cb8acb0d94c 100644 (file)
@@ -12309,10 +12309,38 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
              error_at (loc, "invalid use of void expression");
              output = error_mark_node;
            }
+         if (allows_reg && current_function_decl == NULL_TREE)
+           {
+             error_at (loc, "constraint allows registers outside of "
+                            "a function");
+             output = error_mark_node;
+           }
        }
       else
        output = error_mark_node;
 
+      if (current_function_decl == NULL_TREE && output != error_mark_node)
+       {
+         if (TREE_SIDE_EFFECTS (output))
+           {
+             error_at (loc, "side-effects in output operand outside "
+                            "of a function");
+             output = error_mark_node;
+           }
+         else
+           {
+             tree addr = build_unary_op (loc, ADDR_EXPR, output, false);
+             if (addr == error_mark_node)
+               output = error_mark_node;
+             else if (!initializer_constant_valid_p (addr, TREE_TYPE (addr)))
+               {
+                 error_at (loc, "output operand outside of a function is not "
+                                "constant");
+                 output = error_mark_node;
+               }
+           }
+       }
+
       TREE_VALUE (tail) = output;
     }
 
@@ -12352,10 +12380,38 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
                  input = error_mark_node;
                }
            }
+         if (allows_reg && current_function_decl == NULL_TREE)
+           {
+             error_at (loc, "constraint allows registers outside of "
+                            "a function");
+             input = error_mark_node;
+           }
        }
       else
        input = error_mark_node;
 
+      if (current_function_decl == NULL_TREE && input != error_mark_node)
+       {
+         if (TREE_SIDE_EFFECTS (input))
+           {
+             error_at (loc, "side-effects in input operand outside "
+                            "of a function");
+             input = error_mark_node;
+           }
+         else
+           {
+             tree tem = input;
+             if (allows_mem && lvalue_p (input))
+               tem = build_unary_op (loc, ADDR_EXPR, input, false);
+             if (!initializer_constant_valid_p (tem, TREE_TYPE (tem)))
+               {
+                 error_at (loc, "input operand outside of a function is not "
+                                "constant");
+                 input = error_mark_node;
+               }
+           }
+       }
+
       TREE_VALUE (tail) = input;
     }
 
index 29f28d5e38338145804d902b1b07c076d072a069..67a5eba683ce43545537925db2640d9c474dcc68 100644 (file)
@@ -7876,7 +7876,7 @@ extern tree begin_compound_stmt                   (unsigned int);
 
 extern void finish_compound_stmt               (tree);
 extern tree finish_asm_stmt                    (location_t, int, tree, tree,
-                                                tree, tree, tree, bool);
+                                                tree, tree, tree, bool, bool);
 extern tree finish_label_stmt                  (tree);
 extern void finish_label_decl                  (tree);
 extern cp_expr finish_parenthesized_expr       (cp_expr);
index 80e14b1bd925c369a922671ca33401b5527d41e2..4ee739ca5c9ca080f6c9eea1a46e285e474f53c7 100644 (file)
@@ -23217,7 +23217,6 @@ cp_parser_asm_definition (cp_parser* parser)
      too.  Doing that means that we have to treat the `::' operator as
      two `:' tokens.  */
   if (cp_parser_allow_gnu_extensions_p (parser)
-      && parser->in_function_body
       && (cp_lexer_next_token_is (parser->lexer, CPP_COLON)
          || cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)))
     {
@@ -23271,13 +23270,15 @@ cp_parser_asm_definition (cp_parser* parser)
                 invalid_inputs_p = true;
             }
        }
-      else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
+      else if (parser->in_function_body
+              && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
        /* The clobbers are coming next.  */
        clobbers_p = true;
 
       /* Look for clobbers.  */
       if (clobbers_p
-         || cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+         || (parser->in_function_body
+             && cp_lexer_next_token_is (parser->lexer, CPP_COLON)))
        {
          clobbers_p = true;
          /* Consume the `:' or `::'.  */
@@ -23323,7 +23324,8 @@ cp_parser_asm_definition (cp_parser* parser)
       if (parser->in_function_body)
        {
          asm_stmt = finish_asm_stmt (asm_loc, volatile_p, string, outputs,
-                                     inputs, clobbers, labels, inline_p);
+                                     inputs, clobbers, labels, inline_p,
+                                     false);
          /* If the extended syntax was not used, mark the ASM_EXPR.  */
          if (!extended_p)
            {
@@ -23334,8 +23336,11 @@ cp_parser_asm_definition (cp_parser* parser)
              ASM_BASIC_P (temp) = 1;
            }
        }
-      else
+      else if (!extended_p)
        symtab->finalize_toplevel_asm (string);
+      else
+       finish_asm_stmt (asm_loc, false, string, outputs, inputs,
+                        NULL_TREE, NULL_TREE, false, true);
     }
 
   if (std_attrs && any_nonignored_attribute_p (std_attrs))
index 182f18d0e2465da84b5af64de6a325d60fc9fcae..1f0f0260328840191217aefc4bf3e8c3479a48c0 100644 (file)
@@ -19066,7 +19066,7 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
                                                complain, in_decl);
        tmp = finish_asm_stmt (EXPR_LOCATION (t), ASM_VOLATILE_P (t), string,
                               outputs, inputs, clobbers, labels,
-                              ASM_INLINE_P (t));
+                              ASM_INLINE_P (t), false);
        tree asm_expr = tmp;
        if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
          asm_expr = TREE_OPERAND (asm_expr, 0);
index efa748c7dc897cbd1ae67425c77e5c9d7addc5e7..01b477e9d480096253ce8a89ef2c3a178cbfd212 100644 (file)
@@ -2136,12 +2136,13 @@ finish_compound_stmt (tree stmt)
 /* Finish an asm-statement, whose components are a STRING, some
    OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some
    LABELS.  Also note whether the asm-statement should be
-   considered volatile, and whether it is asm inline.  */
+   considered volatile, and whether it is asm inline.  TOPLEV_P
+   is true if finishing namespace scope extended asm.  */
 
 tree
 finish_asm_stmt (location_t loc, int volatile_p, tree string,
                 tree output_operands, tree input_operands, tree clobbers,
-                tree labels, bool inline_p)
+                tree labels, bool inline_p, bool toplev_p)
 {
   tree r;
   tree t;
@@ -2215,10 +2216,46 @@ finish_asm_stmt (location_t loc, int volatile_p, tree string,
                 mark it addressable.  */
              if (!allows_reg && !cxx_mark_addressable (*op))
                operand = error_mark_node;
+             if (allows_reg && toplev_p)
+               {
+                 error_at (loc, "constraint allows registers outside of "
+                                "a function");
+                 operand = error_mark_node;
+               }
            }
          else
            operand = error_mark_node;
 
+         if (toplev_p && operand != error_mark_node)
+           {
+             if (TREE_SIDE_EFFECTS (operand))
+               {
+                 error_at (loc, "side-effects in output operand outside "
+                                "of a function");
+                 operand = error_mark_node;
+               }
+             else
+               {
+                 tree addr
+                   = cp_build_addr_expr (operand, tf_warning_or_error);
+                 if (addr == error_mark_node)
+                   operand = error_mark_node;
+                 else
+                   {
+                     addr = maybe_constant_value (addr);
+                     if (!initializer_constant_valid_p (addr,
+                                                        TREE_TYPE (addr)))
+                       {
+                         error_at (loc, "output operand outside of a "
+                                        "function is not constant");
+                         operand = error_mark_node;
+                       }
+                     else
+                       operand = build_fold_indirect_ref (addr);
+                   }
+               }
+           }
+
          TREE_VALUE (t) = operand;
        }
 
@@ -2283,10 +2320,56 @@ finish_asm_stmt (location_t loc, int volatile_p, tree string,
                  if (TREE_CONSTANT (constop))
                    operand = constop;
                }
+             if (allows_reg && toplev_p)
+               {
+                 error_at (loc, "constraint allows registers outside of "
+                                "a function");
+                 operand = error_mark_node;
+               }
            }
          else
            operand = error_mark_node;
 
+         if (toplev_p && operand != error_mark_node)
+           {
+             if (TREE_SIDE_EFFECTS (operand))
+               {
+                 error_at (loc, "side-effects in input operand outside "
+                                "of a function");
+                 operand = error_mark_node;
+               }
+             else if (allows_mem && lvalue_or_else (operand, lv_asm, tf_none))
+               {
+                 tree addr = cp_build_addr_expr (operand, tf_warning_or_error);
+                 if (addr == error_mark_node)
+                   operand = error_mark_node;
+                 else
+                   {
+                     addr = maybe_constant_value (addr);
+                     if (!initializer_constant_valid_p (addr,
+                                                        TREE_TYPE (addr)))
+                       {
+                         error_at (loc, "input operand outside of a "
+                                        "function is not constant");
+                         operand = error_mark_node;
+                       }
+                     else
+                       operand = build_fold_indirect_ref (addr);
+                   }
+               }
+             else
+               {
+                 operand = maybe_constant_value (operand);
+                 if (!initializer_constant_valid_p (operand,
+                                                    TREE_TYPE (operand)))
+                   {
+                     error_at (loc, "input operand outside of a "
+                                    "function is not constant");
+                     operand = error_mark_node;
+                   }
+               }
+           }
+
          TREE_VALUE (t) = operand;
        }
     }
@@ -2296,6 +2379,11 @@ finish_asm_stmt (location_t loc, int volatile_p, tree string,
                  clobbers, labels);
   ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
   ASM_INLINE_P (r) = inline_p;
+  if (toplev_p)
+    {
+      symtab->finalize_toplevel_asm (r);
+      return r;
+    }
   r = maybe_cleanup_point_expr_void (r);
   return add_stmt (r);
 }
index c55991df8f10c19d8929b1a71b12b4c78479a302..de2dc39d5ea05efc5f1d3028dfff44872aa02cd9 100644 (file)
@@ -10841,8 +10841,8 @@ statements.  A @dfn{basic @code{asm}} statement is one with no
 operands (@pxref{Basic Asm}), while an @dfn{extended @code{asm}}
 statement (@pxref{Extended Asm}) includes one or more operands.  
 The extended form is preferred for mixing C and assembly language
-within a function, but to include assembly language at
-top level you must use basic @code{asm}.
+within a function and can be used at top level as well with certain
+restrictions.
 
 You can also use the @code{asm} keyword to override the assembler name
 for a C symbol, or to place a C variable in a specific register.
@@ -10880,6 +10880,8 @@ can be used for code compiled with @option{-fno-asm}.
 @item volatile
 The optional @code{volatile} qualifier has no effect. 
 All basic @code{asm} blocks are implicitly volatile.
+Basic @code{asm} statements outside of functions may not use any
+qualifiers.
 
 @item inline
 If you use the @code{inline} qualifier, then for inlining purposes the size
@@ -10924,25 +10926,19 @@ void function()
 @subsubheading Remarks
 Using extended @code{asm} (@pxref{Extended Asm}) typically produces
 smaller, safer, and more efficient code, and in most cases it is a
-better solution than basic @code{asm}.  However, there are two
-situations where only basic @code{asm} can be used:
+better solution than basic @code{asm}.  However, functions declared
+with the @code{naked} attribute require only basic @code{asm}
+(@pxref{Function Attributes}).
 
-@itemize @bullet
-@item
-Extended @code{asm} statements have to be inside a C
-function, so to write inline assembly language at file scope (``top-level''),
-outside of C functions, you must use basic @code{asm}.
-You can use this technique to emit assembler directives,
+Extended @code{asm} statements may be used both inside a C
+function or at file scope (``top-level''), where
+you can use this technique to emit assembler directives,
 define assembly language macros that can be invoked elsewhere in the file,
 or write entire functions in assembly language.
-Basic @code{asm} statements outside of functions may not use any
-qualifiers.
-
-@item
-Functions declared
-with the @code{naked} attribute also require basic @code{asm}
-(@pxref{Function Attributes}).
-@end itemize
+Extended @code{asm} statements outside of functions may not use any
+qualifiers, may not specify clobbers, may not use @code{%}, @code{+} or
+@code{&} modifiers in constraints and can only use constraints which don't
+allow using any register.
 
 Safely accessing C data and calling functions from basic @code{asm} is more 
 complex than it may appear. To access C data, it is better to use extended 
index ea08c4956fb6162db0f063e0b6ec2dbda20d8e2a..4d3d056cb56e325115ed595109da451b83611878 100644 (file)
@@ -149,7 +149,7 @@ extern const int length_unit_log; /* This is defined in insn-attrtab.cc.  */
 const rtx_insn *this_is_asm_operands;
 
 /* Number of operands of this insn, for an `asm' with operands.  */
-static unsigned int insn_noperands;
+unsigned int insn_noperands;
 
 /* Compare optimization flag.  */
 
index 6dc30daf76e62fc826d777efdd1b9201f15cf2fc..f52b56c42f102ad14df8c0aeb9896bb28231d8a9 100644 (file)
@@ -2542,6 +2542,13 @@ lto_output_toplevel_asms (void)
 
   for (can = symtab->first_asm_symbol (); can; can = can->next)
     {
+      if (TREE_CODE (can->asm_str) != STRING_CST)
+       {
+         sorry_at (EXPR_LOCATION (can->asm_str),
+                   "LTO streaming of toplevel extended %<asm%> "
+                   "unimplemented");
+         continue;
+       }
       streamer_write_string_cst (ob, ob->main_stream, can->asm_str);
       streamer_write_hwi (ob, can->order);
     }
index 46b0033b221052fe9ea656181cecc6b1ae61009f..c7970d6ad2bc6cbfff564f34b991bad5316dabf1 100644 (file)
@@ -338,6 +338,9 @@ extern rtx_insn *current_output_insn;
    The precise value is the insn being output, to pass to error_for_asm.  */
 extern const rtx_insn *this_is_asm_operands;
 
+/* Number of operands of this insn, for an `asm' with operands.  */
+extern unsigned int insn_noperands;
+
 /* Carry information from ASM_DECLARE_OBJECT_NAME
    to ASM_FINISH_DECLARE_OBJECT.  */
 extern int size_directive_output;
diff --git a/gcc/testsuite/c-c++-common/toplevel-asm-1.c b/gcc/testsuite/c-c++-common/toplevel-asm-1.c
new file mode 100644 (file)
index 0000000..12623fc
--- /dev/null
@@ -0,0 +1,25 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+struct S { char a; long long b; int c; };
+enum E { E0, E1 = sizeof (struct S) + 15 };
+int v[42];
+void foo (void) {}
+
+asm ("# %0 %1 %2 %c3 %c4 %5 %% %="
+     :: "i" (sizeof (struct S)),
+       "i" (__builtin_offsetof (struct S, c)),
+       "i" (E1),
+       "s" (foo),
+       "i" (v),
+/* Not all targets can satisfy "m" even in non-pic code.  */
+#if !defined(__i386__) && !defined(__x86_64__)
+       "s" (v));
+#else
+       "m" (v));
+asm ("# %0 %1"
+     : "=m" (v[16])
+     : "m" (v[41]));
+#endif
diff --git a/gcc/testsuite/c-c++-common/toplevel-asm-2.c b/gcc/testsuite/c-c++-common/toplevel-asm-2.c
new file mode 100644 (file)
index 0000000..4e5bffc
--- /dev/null
@@ -0,0 +1,21 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+int v[42], w[42], x;
+void l1 (void);
+
+asm ("# %0" : "=m" (32));              /* { dg-error "lvalue required in 'asm' statement" } */
+asm ("# %0" : "=m" (v) : "0" (v));     /* { dg-warning "matching constraint does not allow a register" } */
+asm ("# %0" : : "m" (v), "0" (v));     /* { dg-error "matching constraint references invalid operand number" } */
+asm ("# %0" :: "i" (0) : "cc");                /* { dg-error "expected '\\\)' before ':' token" } */
+asm ("# %0" : : "i" (0) :: l1);                /* { dg-error "expected '\\\)' before '::?' token" } */
+asm ("# %0" : "=r" (x));               /* { dg-error "constraint allows registers outside of a function" } */
+asm ("# %0" : "=m" (x++));             /* { dg-error "lvalue required in 'asm' statement" } */
+asm ("# %0" : "=m" (v[x]));            /* { dg-error "output operand outside of a function is not constant" } */
+asm ("# %0" :: "r" (x));               /* { dg-error "constraint allows registers outside of a function" } */
+asm ("# %0" : : "m" (x++));            /* { dg-error "side-effects in input operand outside of a function" } */
+asm ("# %0" : : "m" (v[x]));           /* { dg-error "input operand outside of a function is not constant" } */
+asm ("# %0" : : "i" (v[x]));           /* { dg-error "input operand outside of a function is not constant" } */
+asm ("# %0" : : "i" (x++));            /* { dg-error "side-effects in input operand outside of a function" } */
diff --git a/gcc/testsuite/c-c++-common/toplevel-asm-3.c b/gcc/testsuite/c-c++-common/toplevel-asm-3.c
new file mode 100644 (file)
index 0000000..4ea0bcf
--- /dev/null
@@ -0,0 +1,11 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+int v[42], w[42], x;
+
+asm ("# %0" : "+m" (v));               /* { dg-error "'\\\+' in output operand outside of a function" } */
+asm ("# %0" : "=&m" (v));              /* { dg-error "'&' in output operand outside of a function" } */
+asm ("# %0, %1" : "=%m" (v), "=m" (w));        /* { dg-error "'%' in output operand outside of a function" } */
+asm ("# %0, %1" : : "%m" (v), "m" (w));        /* { dg-error "'%' in input operand outside of a function" } */
index 261621a18c730cf4b70ff787ccc3c6d591267d1d..0068ec2ce4dcb33d1a4938a17c683865230d1322 100644 (file)
@@ -62,6 +62,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 #include "opts.h"
 #include "asan.h"
+#include "recog.h"
+#include "gimple-expr.h"
 
 /* The (assembler) name of the first globally-visible object output.  */
 extern GTY(()) const char *first_global_object_name;
@@ -1685,16 +1687,167 @@ make_decl_rtl_for_debug (tree decl)
    for an `asm' keyword used between functions.  */
 
 void
-assemble_asm (tree string)
+assemble_asm (tree asm_str)
 {
   const char *p;
-  app_enable ();
 
-  if (TREE_CODE (string) == ADDR_EXPR)
-    string = TREE_OPERAND (string, 0);
+  if (TREE_CODE (asm_str) != ASM_EXPR)
+    {
+      app_enable ();
+      if (TREE_CODE (asm_str) == ADDR_EXPR)
+       asm_str = TREE_OPERAND (asm_str, 0);
+
+      p = TREE_STRING_POINTER (asm_str);
+      fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p);
+    }
+  else
+    {
+      location_t save_loc = input_location;
+      int save_reload_completed = reload_completed;
+      int save_cse_not_expected = cse_not_expected;
+      input_location = EXPR_LOCATION (asm_str);
+      int noutputs = list_length (ASM_OUTPUTS (asm_str));
+      int ninputs = list_length (ASM_INPUTS (asm_str));
+      const char **constraints = NULL;
+      int i;
+      tree tail;
+      bool allows_mem, allows_reg, is_inout;
+      rtx *ops = NULL;
+      if (noutputs + ninputs > MAX_RECOG_OPERANDS)
+       {
+         error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
+         goto done;
+       }
+      constraints = XALLOCAVEC (const char *, noutputs + ninputs);
+      ops = XALLOCAVEC (rtx, noutputs + ninputs);
+      memset (&recog_data, 0, sizeof (recog_data));
+      recog_data.n_operands = ninputs + noutputs;
+      recog_data.is_asm = true;
+      reload_completed = 0;
+      cse_not_expected = 1;
+      for (i = 0, tail = ASM_OUTPUTS (asm_str); tail;
+          ++i, tail = TREE_CHAIN (tail))
+       {
+         tree output = TREE_VALUE (tail);
+         if (output == error_mark_node)
+           goto done;
+         constraints[i]
+           = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
+         if (!parse_output_constraint (&constraints[i], i, ninputs, noutputs,
+                                       &allows_mem, &allows_reg, &is_inout))
+           goto done;
+         if (is_inout)
+           {
+             error ("%qc in output operand outside of a function", '+');
+             goto done;
+           }
+         if (strchr (constraints[i], '&'))
+           {
+             error ("%qc in output operand outside of a function", '&');
+             goto done;
+           }
+         if (strchr (constraints[i], '%'))
+           {
+             error ("%qc in output operand outside of a function", '%');
+             goto done;
+           }
+         output_addressed_constants (output, 0);
+         if (!is_gimple_addressable (output))
+           {
+             error ("output number %d not directly addressable", i);
+             goto done;
+           }
+         ops[i] = expand_expr (build_fold_addr_expr (output), NULL_RTX,
+                               VOIDmode, EXPAND_INITIALIZER);
+         ops[i] = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (output)), ops[i]);
+
+         recog_data.operand[i] = ops[i];
+         recog_data.operand_loc[i] = &ops[i];
+         recog_data.constraints[i] = constraints[i];
+         recog_data.operand_mode[i] = TYPE_MODE (TREE_TYPE (output));
+       }
+      for (i = 0, tail = ASM_INPUTS (asm_str); tail;
+          ++i, tail = TREE_CHAIN (tail))
+       {
+         tree input = TREE_VALUE (tail);
+         if (input == error_mark_node)
+           goto done;
+         constraints[i + noutputs]
+           = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
+         if (!parse_input_constraint (&constraints[i + noutputs], i,
+                                      ninputs, noutputs, 0, constraints,
+                                      &allows_mem, &allows_reg))
+           goto done;
+         if (strchr (constraints[i], '%'))
+           {
+             error ("%qc in input operand outside of a function", '%');
+             goto done;
+           }
+         const char *constraint = constraints[i + noutputs];
+         size_t c_len = strlen (constraint);
+         for (size_t j = 0; j < c_len;
+              j += CONSTRAINT_LEN (constraint[j], constraint + j))
+           if (constraint[j] >= '0' && constraint[j] <= '9')
+             {
+               error ("matching constraint outside of a function");
+               goto done;
+             }
+         output_addressed_constants (input, 0);
+         if (allows_mem && is_gimple_addressable (input))
+           {
+             ops[i + noutputs]
+               = expand_expr (build_fold_addr_expr (input), NULL_RTX,
+                              VOIDmode, EXPAND_INITIALIZER);
+             ops[i + noutputs] = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (input)),
+                                              ops[i + noutputs]);
+           }
+         else
+           ops[i + noutputs] = expand_expr (input, NULL_RTX, VOIDmode,
+                                            EXPAND_INITIALIZER);
+         if (asm_operand_ok (ops[i + noutputs], constraint, NULL) <= 0)
+           {
+             if (!allows_mem)
+               warning (0, "%<asm%> operand %d probably does not "
+                           "match constraints", i + noutputs);
+           }
+         recog_data.operand[i + noutputs] = ops[i + noutputs];
+         recog_data.operand_loc[i + noutputs] = &ops[i + noutputs];
+         recog_data.constraints[i + noutputs] = constraints[i + noutputs];
+         recog_data.operand_mode[i + noutputs]
+           = TYPE_MODE (TREE_TYPE (input));
+       }
+      if (recog_data.n_operands > 0)
+       {
+         const char *p = recog_data.constraints[0];
+         recog_data.n_alternatives = 1;
+         while (*p)
+           recog_data.n_alternatives += (*p++ == ',');
+       }
+      for (i = 0; i < recog_data.n_operands; i++)
+       recog_data.operand_type[i]
+         = recog_data.constraints[i][0] == '=' ? OP_OUT : OP_IN;
+      reload_completed = 1;
+      constrain_operands (1, ALL_ALTERNATIVES);
+      if (which_alternative < 0)
+       {
+         error ("impossible constraint in %<asm%>");
+         goto done;
+       }
+      this_is_asm_operands = make_insn_raw (gen_nop ());
+      insn_noperands = recog_data.n_operands;
+      if (TREE_STRING_POINTER (ASM_STRING (asm_str))[0])
+       {
+         app_enable ();
+         output_asm_insn (TREE_STRING_POINTER (ASM_STRING (asm_str)), ops);
+       }
+      insn_noperands = 0;
+      this_is_asm_operands = NULL;
 
-  p = TREE_STRING_POINTER (string);
-  fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p);
+    done:
+      input_location = save_loc;
+      reload_completed = save_reload_completed;
+      cse_not_expected = save_cse_not_expected;
+    }
 }
 
 /* Write the address of the entity given by SYMBOL to SEC.  */