[clang] 50cac24 - Support output constraints on "asm goto"

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 24 18:57:19 PST 2020


Author: Bill Wendling
Date: 2020-02-24T18:51:29-08:00
New Revision: 50cac248773c3a023e8f6ceb9938bdd5e9f15da2

URL: https://github.com/llvm/llvm-project/commit/50cac248773c3a023e8f6ceb9938bdd5e9f15da2
DIFF: https://github.com/llvm/llvm-project/commit/50cac248773c3a023e8f6ceb9938bdd5e9f15da2.diff

LOG: Support output constraints on "asm goto"

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

Added: 
    clang/test/Analysis/uninit-asm-goto.cpp

Modified: 
    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/CodeGen/asm-goto.c
    clang/test/Parser/asm-goto.c
    clang/test/Parser/asm-goto.cpp
    clang/test/Sema/asm-goto.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 7c7f86214a83..a15bf50f7c08 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -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
 ====================
 

diff  --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 24c1abb2be54..5cc4eb8a46df 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -3032,7 +3032,7 @@ class GCCAsmStmt : public AsmStmt {
   }
 
   IdentifierInfo *getLabelIdentifier(unsigned i) const {
-    return Names[i + NumInputs];
+    return Names[i + NumOutputs + NumInputs];
   }
 
   AddrLabelExpr *getLabelExpr(unsigned i) const;
@@ -3043,11 +3043,11 @@ class GCCAsmStmt : public AsmStmt {
   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 @@ class GCCAsmStmt : public AsmStmt {
   }
 
   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 {

diff  --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 28eb694ba9a8..20e1b141a3ef 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -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

diff  --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index 1aea9acc472b..550f5aa338f1 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -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;

diff  --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp
index ff14e18ec144..a4a900c8146a 100644
--- a/clang/lib/Analysis/UninitializedValues.cpp
+++ b/clang/lib/Analysis/UninitializedValues.cpp
@@ -475,6 +475,7 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
   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);
 }
 

diff  --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index bf63a1f7128b..a49601f49936 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -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));

diff  --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp
index ea2c871d6a82..b596aded867d 100644
--- a/clang/lib/Parse/ParseStmtAsm.cpp
+++ b/clang/lib/Parse/ParseStmtAsm.cpp
@@ -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();
   }

diff  --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 93faf2d151f9..82a308f35c21 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -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
index 000000000000..ce2ca81d944f
--- /dev/null
+++ b/clang/test/Analysis/uninit-asm-goto.cpp
@@ -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;
+}

diff  --git a/clang/test/CodeGen/asm-goto.c b/clang/test/CodeGen/asm-goto.c
index 7692fbc6797b..c322d4560f5a 100644
--- a/clang/test/CodeGen/asm-goto.c
+++ b/clang/test/CodeGen/asm-goto.c
@@ -2,19 +2,104 @@
 // 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;
+}

diff  --git a/clang/test/Parser/asm-goto.c b/clang/test/Parser/asm-goto.c
index 7f8edb111563..f9ad6c5ee9a4 100644
--- a/clang/test/Parser/asm-goto.c
+++ b/clang/test/Parser/asm-goto.c
@@ -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 at +1 {{expected ')'}}
   asm ("mov %[e], %[e]" : : [e] "rm" (*e)::a)
-  // expected-error at +1 {{'asm goto' cannot have output constraints}}
-  asm goto ("decl %0; jnz %l[a]" :"=r"(x): "m"(x) : "memory" : a);
-  // expected-error at +1 {{expected identifie}}
+  // expected-error at +1 {{expected identifier}}
   asm goto ("decl %0;" :: "m"(x) : "memory" : );
   // expected-error at +1 {{expected ':'}}
   asm goto ("decl %0;" :: "m"(x) : "memory" );
@@ -55,3 +72,25 @@ int zoo ()
 loop:
   return 0;
 }
+
+int test6(int y) {
+  int x,cond,*e;
+  // expected-error at +1 {{expected ')'}}
+  asm ("mov %[e], %[e]" : "=r" (y) : [e] "rm" (*e), "r" (y) :: a)
+  // expected-error at +1 {{expected identifier}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" :);
+  // expected-error at +1  {{expected ':'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory");
+  // expected-error at +1 {{use of undeclared label 'x'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : x);
+  // expected-error at +1 {{use of undeclared label 'b'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : b);
+  // expected-error at +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 at +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;
+}

diff  --git a/clang/test/Parser/asm-goto.cpp b/clang/test/Parser/asm-goto.cpp
index f09466ca488d..faff6113cad6 100644
--- a/clang/test/Parser/asm-goto.cpp
+++ b/clang/test/Parser/asm-goto.cpp
@@ -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 at +1 {{expected ')'}}
   asm ("mov %[e], %[e]" : : [e] "rm" (*e)::a)
-  // expected-error at +1  {{'asm goto' cannot have output constraints}}
-  asm goto ("decl %0; jnz %l[a]" :"=r"(x): "m"(x) : "memory" : a);
-  // expected-error at +1 {{expected identifie}}
+  // expected-error at +1 {{expected identifier}}
   asm goto ("decl %0;" :: "m"(x) : "memory" : );
   // expected-error at +1  {{expected ':'}}
   asm goto ("decl %0;" :: "m"(x) : "memory" );
@@ -26,28 +66,24 @@ int zoo ()
   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 at +1 {{expected ')'}}
+  asm ("mov %[e], %[e]" : "=r" (y) : [e] "rm" (*e), "r" (y) :: a)
+  // expected-error at +1 {{expected identifier}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" :);
+  // expected-error at +1  {{expected ':'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory");
+  // expected-error at +1 {{use of undeclared label 'x'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : x);
+  // expected-error at +1 {{use of undeclared label 'b'}}
+  asm goto ("decl %0;" : "=r" (y) : "m" (x), "r" (y) : "memory" : b);
+  // expected-error at +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 at +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;
 }

diff  --git a/clang/test/Sema/asm-goto.cpp b/clang/test/Sema/asm-goto.cpp
index d85730974359..64addd9d75b6 100644
--- a/clang/test/Sema/asm-goto.cpp
+++ b/clang/test/Sema/asm-goto.cpp
@@ -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 at +1 {{cannot jump from this goto statement to its label}}
   goto DirectJump;
 // expected-note at +1 {{jump bypasses variable with a non-trivial destructor}}
-  NonTrivial tnp1;
+  S s1;
 
 DirectJump:
 // expected-error at +1 {{cannot jump from this asm goto statement to one of its possible targets}}
   asm goto("jmp %l0;" ::::Later);
 // expected-note at +1 {{jump bypasses variable with a non-trivial destructor}}
-  NonTrivial tnp2;
+  S s2;
 // expected-note at +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 at +2 {{jump exits scope of variable with non-trivial destructor}}
 // expected-note at +1 {{jump exits scope of variable with non-trivial destructor}}
-    S s;
+    T t;
     void *p = &&BAR;
 // expected-error at +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 at +1 {{cannot jump from this indirect goto statement to one of its possible targets}}
     goto *p;
     p = &&FOO;
@@ -45,9 +45,7 @@ void foo(int a) {
   return;
 }
 
-
-//Asm goto:
-int test16(int n)
+int test3(int n)
 {
   // expected-error at +2 {{cannot jump from this asm goto statement to one of its possible targets}}
   // expected-error at +1 {{cannot jump from this asm goto statement to one of its possible targets}}


        


More information about the cfe-commits mailing list