Support output constraints on "asm goto"
authorBill Wendling <isanbard@gmail.com>
Tue, 25 Feb 2020 02:32:50 +0000 (18:32 -0800)
committerBill Wendling <isanbard@gmail.com>
Tue, 25 Feb 2020 02:51:29 +0000 (18:51 -0800)
Summary:
Clang's "asm goto" feature didn't initially support outputs constraints. That
was the same behavior as gcc's implementation. The decision by gcc not to
support outputs was based on a restriction in their IR regarding terminators.
LLVM doesn't restrict terminators from returning values (e.g. 'invoke'), so
it made sense to support this feature.

Output values are valid only on the 'fallthrough' path. If an output value's used
on an indirect branch, then it's 'poisoned'.

In theory, outputs *could* be valid on the 'indirect' paths, but it's very
difficult to guarantee that the original semantics would be retained. E.g.
because indirect labels could be used as data, we wouldn't be able to split
critical edges in situations where two 'callbr' instructions have the same
indirect label, because the indirect branch's destination would no longer be
the same.

Reviewers: jyknight, nickdesaulniers, hfinkel

Reviewed By: jyknight, nickdesaulniers

Subscribers: MaskRay, rsmith, hiraditya, llvm-commits, cfe-commits, craig.topper, rnk

Tags: #clang, #llvm

Differential Revision: https://reviews.llvm.org/D69876

13 files changed:
clang/docs/LanguageExtensions.rst
clang/include/clang/AST/Stmt.h
clang/include/clang/Basic/Features.def
clang/lib/AST/Stmt.cpp
clang/lib/Analysis/UninitializedValues.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/lib/Parse/ParseStmtAsm.cpp
clang/lib/Sema/SemaStmtAsm.cpp
clang/test/Analysis/uninit-asm-goto.cpp [new file with mode: 0644]
clang/test/CodeGen/asm-goto.c
clang/test/Parser/asm-goto.c
clang/test/Parser/asm-goto.cpp
clang/test/Sema/asm-goto.cpp

index 7c7f862..a15bf50 100644 (file)
@@ -1255,6 +1255,34 @@ the clang implementation are in :doc:`Block-ABI-Apple<Block-ABI-Apple>`.
 
 Query for this feature with ``__has_extension(blocks)``.
 
+ASM Goto with Output Constraints
+================================
+
+In addition to the functionality provided by `GCC's extended
+assembly`<https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html>`_, clang
+supports output constraints with the `goto` form.
+
+The goto form of GCC's extended assembly allows the programmer to branch to a C
+label from within an inline assembly block. Clang extends this behavior by
+allowing the programmer to use output constraints:
+
+.. code-block:: c++
+
+  int foo(int x) {
+      int y;
+      asm goto("# %0 %1 %l2" : "=r"(y) : "r"(x) : : err);
+      return y;
+    err:
+      return -1;
+  }
+
+It's important to note that outputs are valid only on the "fallthrough" branch.
+Using outputs on an indirect branch may result in undefined behavior. For
+example, in the function above, use of the value assigned to `y` in the `err`
+block is undefined behavior.
+
+Query for this feature with ``__has_extension(gnu_asm_goto_with_outputs)``.
+
 Objective-C Features
 ====================
 
index 24c1abb..5cc4eb8 100644 (file)
@@ -3032,7 +3032,7 @@ public:
   }
 
   IdentifierInfo *getLabelIdentifier(unsigned i) const {
-    return Names[i + NumInputs];
+    return Names[i + NumOutputs + NumInputs];
   }
 
   AddrLabelExpr *getLabelExpr(unsigned i) const;
@@ -3043,11 +3043,11 @@ public:
   using labels_const_range = llvm::iterator_range<const_labels_iterator>;
 
   labels_iterator begin_labels() {
-    return &Exprs[0] + NumInputs;
+    return &Exprs[0] + NumOutputs + NumInputs;
   }
 
   labels_iterator end_labels() {
-    return &Exprs[0] + NumInputs + NumLabels;
+    return &Exprs[0] + NumOutputs + NumInputs + NumLabels;
   }
 
   labels_range labels() {
@@ -3055,11 +3055,11 @@ public:
   }
 
   const_labels_iterator begin_labels() const {
-    return &Exprs[0] + NumInputs;
+    return &Exprs[0] + NumOutputs + NumInputs;
   }
 
   const_labels_iterator end_labels() const {
-    return &Exprs[0] + NumInputs + NumLabels;
+    return &Exprs[0] + NumOutputs + NumInputs + NumLabels;
   }
 
   labels_const_range labels() const {
index 28eb694..20e1b14 100644 (file)
@@ -252,6 +252,7 @@ EXTENSION(overloadable_unmarked, true)
 EXTENSION(pragma_clang_attribute_namespaces, true)
 EXTENSION(pragma_clang_attribute_external_declaration, true)
 EXTENSION(gnu_asm, LangOpts.GNUAsm)
+EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm)
 
 #undef EXTENSION
 #undef FEATURE
index 1aea9ac..550f5aa 100644 (file)
@@ -457,7 +457,7 @@ void GCCAsmStmt::setInputExpr(unsigned i, Expr *E) {
 }
 
 AddrLabelExpr *GCCAsmStmt::getLabelExpr(unsigned i) const {
-  return cast<AddrLabelExpr>(Exprs[i + NumInputs]);
+  return cast<AddrLabelExpr>(Exprs[i + NumOutputs + NumInputs]);
 }
 
 StringRef GCCAsmStmt::getLabelName(unsigned i) const {
@@ -523,7 +523,7 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const {
 
   for (unsigned i = 0, e = getNumLabels(); i != e; ++i)
     if (getLabelName(i) == SymbolicName)
-      return i + getNumInputs();
+      return i + getNumOutputs() + getNumInputs();
 
   // Not found.
   return -1;
index ff14e18..a4a900c 100644 (file)
@@ -475,6 +475,7 @@ public:
   void VisitCallExpr(CallExpr *ce);
   void VisitDeclRefExpr(DeclRefExpr *dr);
   void VisitDeclStmt(DeclStmt *ds);
+  void VisitGCCAsmStmt(GCCAsmStmt *as);
   void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);
   void VisitObjCMessageExpr(ObjCMessageExpr *ME);
   void VisitOMPExecutableDirective(OMPExecutableDirective *ED);
@@ -760,6 +761,16 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
   }
 }
 
+void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) {
+  // An "asm goto" statement is a terminator that may initialize some variables.
+  if (!as->isAsmGoto())
+    return;
+
+  for (const auto &o : as->outputs())
+    if (const VarDecl *VD = findVar(o).getDecl())
+      vals[VD] = Initialized;
+}
+
 void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
   // If the Objective-C message expression is an implicit no-return that
   // is not modeled in the CFG, set the tracked dataflow values to Unknown.
@@ -797,6 +808,10 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
     if (Optional<CFGStmt> cs = I.getAs<CFGStmt>())
       tf.Visit(const_cast<Stmt *>(cs->getStmt()));
   }
+  CFGTerminator terminator = block->getTerminator();
+  if (GCCAsmStmt *as = dyn_cast_or_null<GCCAsmStmt>(terminator.getStmt()))
+    if (as->isAsmGoto())
+      tf.Visit(as);
   return vals.updateValueVectorWithScratch(block);
 }
 
index bf63a1f..a49601f 100644 (file)
@@ -2211,13 +2211,6 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
     Constraints += InputConstraint;
   }
 
-  // Append the "input" part of inout constraints last.
-  for (unsigned i = 0, e = InOutArgs.size(); i != e; i++) {
-    ArgTypes.push_back(InOutArgTypes[i]);
-    Args.push_back(InOutArgs[i]);
-  }
-  Constraints += InOutConstraints;
-
   // Labels
   SmallVector<llvm::BasicBlock *, 16> Transfer;
   llvm::BasicBlock *Fallthrough = nullptr;
@@ -2225,7 +2218,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
   if (const auto *GS =  dyn_cast<GCCAsmStmt>(&S)) {
     IsGCCAsmGoto = GS->isAsmGoto();
     if (IsGCCAsmGoto) {
-      for (auto *E : GS->labels()) {
+      for (const auto *E : GS->labels()) {
         JumpDest Dest = getJumpDestForLabel(E->getLabel());
         Transfer.push_back(Dest.getBlock());
         llvm::BlockAddress *BA =
@@ -2236,11 +2229,17 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
           Constraints += ',';
         Constraints += 'X';
       }
-      StringRef Name = "asm.fallthrough";
-      Fallthrough = createBasicBlock(Name);
+      Fallthrough = createBasicBlock("asm.fallthrough");
     }
   }
 
+  // Append the "input" part of inout constraints last.
+  for (unsigned i = 0, e = InOutArgs.size(); i != e; i++) {
+    ArgTypes.push_back(InOutArgTypes[i]);
+    Args.push_back(InOutArgs[i]);
+  }
+  Constraints += InOutConstraints;
+
   // Clobbers
   for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) {
     StringRef Clobber = S.getClobber(i);
@@ -2293,9 +2292,9 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
   if (IsGCCAsmGoto) {
     llvm::CallBrInst *Result =
         Builder.CreateCallBr(IA, Fallthrough, Transfer, Args);
+    EmitBlock(Fallthrough);
     UpdateAsmCallInst(cast<llvm::CallBase>(*Result), HasSideEffect, ReadOnly,
                       ReadNone, S, ResultRegTypes, *this, RegResults);
-    EmitBlock(Fallthrough);
   } else {
     llvm::CallInst *Result =
         Builder.CreateCall(IA, Args, getBundlesForFunclet(IA));
index ea2c871..b596ade 100644 (file)
@@ -781,12 +781,6 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
     AteExtraColon = Tok.is(tok::coloncolon);
     ConsumeToken();
 
-    if (!AteExtraColon && isGotoAsm && Tok.isNot(tok::colon)) {
-      Diag(Tok, diag::err_asm_goto_cannot_have_output);
-      SkipUntil(tok::r_paren, StopAtSemi);
-      return StmtError();
-    }
-
     if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
       return StmtError();
   }
index 93faf2d..82a308f 100644 (file)
@@ -478,10 +478,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
 
     // Look for the correct constraint index.
     unsigned ConstraintIdx = Piece.getOperandNo();
+    unsigned NumOperands = NS->getNumOutputs() + NS->getNumInputs();
     // Labels are the last in the Exprs list.
-    if (NS->isAsmGoto() && ConstraintIdx >= NS->getNumInputs())
+    if (NS->isAsmGoto() && ConstraintIdx >= NumOperands)
       continue;
-    unsigned NumOperands = NS->getNumOutputs() + NS->getNumInputs();
     // Look for the (ConstraintIdx - NumOperands + 1)th constraint with
     // modifier '+'.
     if (ConstraintIdx >= NumOperands) {
diff --git a/clang/test/Analysis/uninit-asm-goto.cpp b/clang/test/Analysis/uninit-asm-goto.cpp
new file mode 100644 (file)
index 0000000..ce2ca81
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -std=c++11 -Wuninitialized -verify %s
+// expected-no-diagnostics
+
+int test1(int x) {
+    int y;
+    asm goto("# %0 %1 %2" : "=r"(y) : "r"(x) : : err);
+    return y;
+  err:
+    return -1;
+}
index 7692fbc..c322d45 100644 (file)
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O0 -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -triple i386-pc-linux-gnu -O0 -emit-llvm %s -o - | FileCheck %s
 
-int foo(int cond)
-{
+int test1(int cond) {
+  // CHECK-LABEL: define i32 @test1(
   // CHECK: callbr void asm sideeffect
   // CHECK: to label %asm.fallthrough [label %label_true, label %loop]
-  // CHECK: asm.fallthrough:
-  asm volatile goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
+  // CHECK-LABEL: asm.fallthrough:
   asm volatile goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
+  asm volatile goto("testl %0, %0; jne %l2;" :: "r"(cond)::label_true, loop);
   // CHECK: callbr void asm sideeffect
   // CHECK: to label %asm.fallthrough1 [label %label_true, label %loop]
-  // CHECK: asm.fallthrough1:
+  // CHECK-LABEL: asm.fallthrough1:
+  return 0;
+loop:
+  return 0;
+label_true:
+  return 1;
+}
+
+int test2(int cond) {
+  // CHECK-LABEL: define i32 @test2(
+  // CHECK: callbr i32 asm sideeffect
+  // CHECK: to label %asm.fallthrough [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough:
+  asm volatile goto("testl %0, %0; jne %l2;" : "=r"(cond) : "r"(cond) :: label_true, loop);
+  asm volatile goto("testl %0, %0; jne %l3;" : "=r"(cond) : "r"(cond) :: label_true, loop);
+  // CHECK: callbr i32 asm sideeffect
+  // CHECK: to label %asm.fallthrough1 [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough1:
+  return 0;
+loop:
+  return 0;
+label_true:
+  return 1;
+}
+
+int test3(int out1, int out2) {
+  // CHECK-LABEL: define i32 @test3(
+  // CHECK: callbr { i32, i32 } asm sideeffect
+  // CHECK: to label %asm.fallthrough [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough:
+  asm volatile goto("testl %0, %0; jne %l3;" : "=r"(out1), "=r"(out2) : "r"(out1) :: label_true, loop);
+  asm volatile goto("testl %0, %0; jne %l4;" : "=r"(out1), "=r"(out2) : "r"(out1) :: label_true, loop);
+  // CHECK: callbr { i32, i32 } asm sideeffect
+  // CHECK: to label %asm.fallthrough2 [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough2:
   return 0;
 loop:
   return 0;
 label_true:
   return 1;
 }
+
+int test4(int out1, int out2) {
+  // CHECK-LABEL: define i32 @test4(
+  // CHECK: callbr { i32, i32 } asm sideeffect "jne ${3:l}", "={si},={di},r,X,X,0,1
+  // CHECK: to label %asm.fallthrough [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough:
+  if (out1 < out2)
+    asm volatile goto("jne %l3" : "+S"(out1), "+D"(out2) : "r"(out1) :: label_true, loop);
+  else
+    asm volatile goto("jne %l5" : "+S"(out1), "+D"(out2) : "r"(out1), "r"(out2) :: label_true, loop);
+  // CHECK: callbr { i32, i32 } asm sideeffect "jne ${5:l}", "={si},={di},r,r,X,X,0,1
+  // CHECK: to label %asm.fallthrough2 [label %label_true, label %loop]
+  // CHECK-LABEL: asm.fallthrough2:
+  return out1 + out2;
+loop:
+  return -1;
+label_true:
+  return -2;
+}
+
+int test5(int addr, int size, int limit) {
+  // CHECK-LABEL: define i32 @test5(
+  // CHECK: callbr i32 asm "add $1,$0 ; jc ${3:l} ; cmp $2,$0 ; ja ${3:l} ; ", "=r,imr,imr,X,0
+  // CHECK: to label %asm.fallthrough [label %t_err]
+  // CHECK-LABEL: asm.fallthrough:
+  asm goto(
+      "add %1,%0 ; "
+      "jc %l[t_err] ; "
+      "cmp %2,%0 ; "
+      "ja %l[t_err] ; "
+      : "+r" (addr)
+      : "g" (size), "g" (limit)
+      : : t_err);
+  return 0;
+t_err:
+  return 1;
+}
+
+int test6(int out1) {
+  // CHECK-LABEL: define i32 @test6(
+  // CHECK: callbr i32 asm sideeffect "testl $0, $0; testl $1, $1; jne ${2:l}", "={si},r,X,X,0,{{.*}} i8* blockaddress(@test6, %label_true), i8* blockaddress(@test6, %landing)
+  // CHECK: to label %asm.fallthrough [label %label_true, label %landing]
+  // CHECK-LABEL: asm.fallthrough:
+  // CHECK-LABEL: landing:
+  int out2 = 42;
+  asm volatile goto("testl %0, %0; testl %1, %1; jne %l2" : "+S"(out2) : "r"(out1) :: label_true, landing);
+landing:
+  return out1 + out2;
+label_true:
+  return -2;
+}
index 7f8edb1..f9ad6c5 100644 (file)
@@ -4,13 +4,13 @@
 #if !__has_extension(gnu_asm)
 #error Extension 'gnu_asm' should be available by default
 #endif
-
+#if !__has_extension(gnu_asm_goto_with_outputs)
+#error Extension 'gnu_asm_goto_with_outputs' should be available by default
+#endif
 
 int a, b, c, d, e, f, g, h, i, j, k, l;
 
-void
-fgoto1 (void)
-{
+void test(void) {
   __asm__ volatile goto (""
             :: [a] "r" (a), [b] "r" (b), [c] "r" (c), [d] "r" (d),
                [e] "r" (e), [f] "r" (f), [g] "r" (g), [h] "r" (h),
@@ -20,9 +20,7 @@ lab1: return;
 lab2: return;
 }
 
-void
-fgoto2 (void)
-{
+void test2(void) {
   __asm__ volatile goto (""
             :: [a] "r,m" (a), [b] "r,m" (b), [c] "r,m" (c), [d] "r,m" (d),
                [e] "r,m" (e), [f] "r,m" (f), [g] "r,m" (g), [h] "r,m" (h),
@@ -31,14 +29,33 @@ fgoto2 (void)
   lab: return;
 }
 
-int zoo ()
-{
+int test3(int x) {
+  __asm__ volatile goto ("decl %0; jnz %l[a]"
+                         : "=r" (x) : "m" (x) : "memory" : a);
+a:
+  return -x;
+}
+
+int test4(int x) {
+  int y;
+  if (x > 42)
+    __asm__ volatile goto ("decl %0; jnz %l[a]"
+                           : "=r" (x), "=r" (y) : "m" (x) : "memory" : a);
+  else
+    __asm__ volatile goto ("decl %0; jnz %l[b]"
+                           : "=r" (x), "=r" (y) : "m" (x) : "memory" : b);
+  x = y + 42;
+a:
+  return -x;
+b:
+  return +x;
+}
+
+int test5(void) {
   int x,cond,*e;
   // expected-error@+1 {{expected ')'}}
   asm ("mov %[e], %[e]" : : [e] "rm" (*e)::a)
-  // expected-error@+1 {{'asm goto' cannot have output constraints}}
-  asm goto ("decl %0; jnz %l[a]" :"=r"(x): "m"(x) : "memory" : a);
-  // expected-error@+1 {{expected identifie}}
+  // expected-error@+1 {{expected identifier}}
   asm goto ("decl %0;" :: "m"(x) : "memory" : );
   // expected-error@+1 {{expected ':'}}
   asm goto ("decl %0;" :: "m"(x) : "memory" );
@@ -55,3 +72,25 @@ label_true:
 loop:
   return 0;
 }
+
+int test6(int y) {
+  int x,cond,*e;
+  // expected-error@+1 {{expected ')'}}
+  asm ("mov %[e], %[e]" : "=r" (y) : [e] "rm" (*e), "r" (y) :: a)
+  // expected-error@+1 {{expected identifier}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" :);
+  // expected-error@+1  {{expected ':'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory");
+  // expected-error@+1 {{use of undeclared label 'x'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : x);
+  // expected-error@+1 {{use of undeclared label 'b'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : b);
+  // expected-error@+1 {{invalid operand number in inline asm string}}
+  asm goto ("testl %0, %0; jne %l5;" : "=r" (y) : "r" (cond), "r" (y) :: label_true, loop);
+  // expected-error@+1 {{unknown symbolic operand name in inline assembly string}}
+  asm goto ("decl %0; jnz %l[b]" : "=r" (y) : "m" (x), "r" (y) : "memory" : a);
+label_true:
+loop:
+a:
+  return 0;
+}
index f09466c..faff611 100644 (file)
@@ -1,14 +1,54 @@
 // RUN: %clang_cc1 -triple i386-pc-linux-gnu -fsyntax-only -verify -std=c++11 %s
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify -std=c++11 %s
 
-int zoo ()
-{
+int a, b, c, d, e, f, g, h, i, j, k, l;
+
+void test1(void) {
+  __asm__ volatile goto (""
+            :: [a] "r" (a), [b] "r" (b), [c] "r" (c), [d] "r" (d),
+               [e] "r" (e), [f] "r" (f), [g] "r" (g), [h] "r" (h),
+               [i] "r" (i), [j] "r" (j), [k] "r" (k), [l] "r" (l)
+            ::lab1,lab2);
+lab1: return;
+lab2: return;
+}
+
+void test2(void) {
+  __asm__ volatile goto (""
+            :: [a] "r,m" (a), [b] "r,m" (b), [c] "r,m" (c), [d] "r,m" (d),
+               [e] "r,m" (e), [f] "r,m" (f), [g] "r,m" (g), [h] "r,m" (h),
+               [i] "r,m" (i), [j] "r,m" (j), [k] "r,m" (k), [l] "r,m" (l)
+            :: lab);
+  lab: return;
+}
+
+int test3(int x) {
+  __asm__ volatile goto ("decl %0; jnz %l[a]"
+                         : "=r" (x) : "m" (x) : "memory" : a);
+a:
+  return -x;
+}
+
+int test4(int x) {
+  int y;
+  if (x > 42)
+    __asm__ volatile goto ("decl %0; jnz %l[a]"
+                           : "=r" (x), "=r" (y) : "m" (x) : "memory" : a);
+  else
+    __asm__ volatile goto ("decl %0; jnz %l[b]"
+                           : "=r" (x), "=r" (y) : "m" (x) : "memory" : b);
+  x = y + 42;
+a:
+  return -x;
+b:
+  return +x;
+}
+
+int test5(void) {
   int x,cond,*e;
   // expected-error@+1 {{expected ')'}}
   asm ("mov %[e], %[e]" : : [e] "rm" (*e)::a)
-  // expected-error@+1  {{'asm goto' cannot have output constraints}}
-  asm goto ("decl %0; jnz %l[a]" :"=r"(x): "m"(x) : "memory" : a);
-  // expected-error@+1 {{expected identifie}}
+  // expected-error@+1 {{expected identifier}}
   asm goto ("decl %0;" :: "m"(x) : "memory" : );
   // expected-error@+1  {{expected ':'}}
   asm goto ("decl %0;" :: "m"(x) : "memory" );
@@ -26,28 +66,24 @@ a:
   return 0;
 }
 
-
-int a, b, c, d, e, f, g, h, i, j, k, l;
-
-void
-fgoto1 (void)
-{
-  __asm__ volatile goto (""
-            :: [a] "r" (a), [b] "r" (b), [c] "r" (c), [d] "r" (d),
-               [e] "r" (e), [f] "r" (f), [g] "r" (g), [h] "r" (h),
-               [i] "r" (i), [j] "r" (j), [k] "r" (k), [l] "r" (l)
-            ::lab1,lab2);
-lab1: return;
-lab2: return;
-}
-
-void
-fgoto2 (void)
-{
-  __asm__ volatile goto (""
-            :: [a] "r,m" (a), [b] "r,m" (b), [c] "r,m" (c), [d] "r,m" (d),
-               [e] "r,m" (e), [f] "r,m" (f), [g] "r,m" (g), [h] "r,m" (h),
-               [i] "r,m" (i), [j] "r,m" (j), [k] "r,m" (k), [l] "r,m" (l)
-            :: lab);
-  lab: return;
+int test6(int y) {
+  int x,cond,*e;
+  // expected-error@+1 {{expected ')'}}
+  asm ("mov %[e], %[e]" : "=r" (y) : [e] "rm" (*e), "r" (y) :: a)
+  // expected-error@+1 {{expected identifier}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" :);
+  // expected-error@+1  {{expected ':'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory");
+  // expected-error@+1 {{use of undeclared label 'x'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : x);
+  // expected-error@+1 {{use of undeclared label 'b'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : b);
+  // expected-error@+1 {{invalid operand number in inline asm string}}
+  asm goto ("testl %0, %0; jne %l5;" : "=r" (y) : "r" (cond), "r" (y) :: label_true, loop);
+  // expected-error@+1 {{unknown symbolic operand name in inline assembly string}}
+  asm goto ("decl %0; jnz %l[b]" : "=r" (y) : "m" (x), "r" (y) : "memory" : a);
+label_true:
+loop:
+a:
+  return 0;
 }
index d857309..64addd9 100644 (file)
@@ -1,38 +1,38 @@
 // RUN: %clang_cc1 %s -triple i386-pc-linux-gnu -verify -fsyntax-only
 // RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -verify -fsyntax-only
 
-struct NonTrivial {
-  ~NonTrivial();
+struct S {
+  ~S();
   int f(int);
 private:
   int k;
 };
-void JumpDiagnostics(int n) {
+void test1(int n) {
 // expected-error@+1 {{cannot jump from this goto statement to its label}}
   goto DirectJump;
 // expected-note@+1 {{jump bypasses variable with a non-trivial destructor}}
-  NonTrivial tnp1;
+  S s1;
 
 DirectJump:
 // expected-error@+1 {{cannot jump from this asm goto statement to one of its possible targets}}
   asm goto("jmp %l0;" ::::Later);
 // expected-note@+1 {{jump bypasses variable with a non-trivial destructor}}
-  NonTrivial tnp2;
+  S s2;
 // expected-note@+1 {{possible target of asm goto statement}}
 Later:
   return;
 }
 
-struct S { ~S(); };
-void foo(int a) {
+struct T { ~T(); };
+void test2(int a) {
   if (a) {
 FOO:
 // expected-note@+2 {{jump exits scope of variable with non-trivial destructor}}
 // expected-note@+1 {{jump exits scope of variable with non-trivial destructor}}
-    S s;
+    T t;
     void *p = &&BAR;
 // expected-error@+1 {{cannot jump from this asm goto statement to one of its possible targets}}
-  asm goto("jmp %l0;" ::::BAR);
+    asm goto("jmp %l0;" ::::BAR);
 // expected-error@+1 {{cannot jump from this indirect goto statement to one of its possible targets}}
     goto *p;
     p = &&FOO;
@@ -45,9 +45,7 @@ BAR:
   return;
 }
 
-
-//Asm goto:
-int test16(int n)
+int test3(int n)
 {
   // expected-error@+2 {{cannot jump from this asm goto statement to one of its possible targets}}
   // expected-error@+1 {{cannot jump from this asm goto statement to one of its possible targets}}