void updated this revision to Diff 240720.
void added a comment.

Merge "__has_extension" patch into this patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69876/new/

https://reviews.llvm.org/D69876

Files:
  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
  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: clang/test/Sema/asm-goto.cpp
===================================================================
--- clang/test/Sema/asm-goto.cpp
+++ 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@+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 @@
   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}}
Index: clang/test/Parser/asm-goto.cpp
===================================================================
--- clang/test/Parser/asm-goto.cpp
+++ 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@+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 @@
   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: clang/test/Parser/asm-goto.c
===================================================================
--- clang/test/Parser/asm-goto.c
+++ 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 @@
 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 @@
   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 @@
 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: clang/test/CodeGen/asm-goto.c
===================================================================
--- clang/test/CodeGen/asm-goto.c
+++ 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;
+}
Index: clang/test/Analysis/uninit-asm-goto.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/lib/Sema/SemaStmtAsm.cpp
===================================================================
--- clang/lib/Sema/SemaStmtAsm.cpp
+++ clang/lib/Sema/SemaStmtAsm.cpp
@@ -478,10 +478,10 @@
 
     // Look for the correct constraint index.
     unsigned ConstraintIdx = Piece.getOperandNo();
-    // Labels are the last in the Exprs list.
-    if (NS->isAsmGoto() && ConstraintIdx >= NS->getNumInputs())
-      continue;
     unsigned NumOperands = NS->getNumOutputs() + NS->getNumInputs();
+    // Labels are the last in the Exprs list.
+    if (NS->isAsmGoto() && ConstraintIdx >= NumOperands)
+      continue;
     // Look for the (ConstraintIdx - NumOperands + 1)th constraint with
     // modifier '+'.
     if (ConstraintIdx >= NumOperands) {
Index: clang/lib/Parse/ParseStmtAsm.cpp
===================================================================
--- clang/lib/Parse/ParseStmtAsm.cpp
+++ clang/lib/Parse/ParseStmtAsm.cpp
@@ -781,12 +781,6 @@
     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: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -2211,13 +2211,6 @@
     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 @@
   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 @@
           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);
@@ -2287,9 +2286,9 @@
   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: clang/lib/Analysis/UninitializedValues.cpp
===================================================================
--- clang/lib/Analysis/UninitializedValues.cpp
+++ clang/lib/Analysis/UninitializedValues.cpp
@@ -475,6 +475,7 @@
   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::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 @@
     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: clang/lib/AST/Stmt.cpp
===================================================================
--- clang/lib/AST/Stmt.cpp
+++ clang/lib/AST/Stmt.cpp
@@ -457,7 +457,7 @@
 }
 
 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 @@
 
   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: clang/include/clang/Basic/Features.def
===================================================================
--- clang/include/clang/Basic/Features.def
+++ clang/include/clang/Basic/Features.def
@@ -252,6 +252,7 @@
 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: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -3029,7 +3029,7 @@
   }
 
   IdentifierInfo *getLabelIdentifier(unsigned i) const {
-    return Names[i + NumInputs];
+    return Names[i + NumOutputs + NumInputs];
   }
 
   AddrLabelExpr *getLabelExpr(unsigned i) const;
@@ -3040,11 +3040,11 @@
   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() {
@@ -3052,11 +3052,11 @@
   }
 
   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: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -1255,6 +1255,34 @@
 
 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 constriants:
+
+.. 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
 ====================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to