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
====================
}
IdentifierInfo *getLabelIdentifier(unsigned i) const {
- return Names[i + NumInputs];
+ return Names[i + NumOutputs + NumInputs];
}
AddrLabelExpr *getLabelExpr(unsigned i) const;
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() {
}
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 {
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
}
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 {
for (unsigned i = 0, e = getNumLabels(); i != e; ++i)
if (getLabelName(i) == SymbolicName)
- return i + getNumInputs();
+ return i + getNumOutputs() + getNumInputs();
// Not found.
return -1;
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);
}
}
+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.
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);
}
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;
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 =
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);
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));
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();
}
// 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) {
--- /dev/null
+// 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;
+}
// 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;
+}
#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),
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),
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" );
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;
+}
// 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" );
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;
}
// 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;
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}}