forked from OSchip/llvm-project
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
This commit is contained in:
parent
23c2a5ce33
commit
50cac24877
|
@ -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
|
||||
====================
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -478,10 +478,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
|||
|
||||
// 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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in New Issue