clang support gnu asm goto.

Syntax:
  asm [volatile] goto ( AssemblerTemplate
                      :
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

New llvm IR is "callbr" for inline asm goto instead "call" for inline asm
For:
asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
IR:
callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@foo, %label_true), i8* blockaddress(@foo, %loop)) #1
          to label %asm.fallthrough [label %label_true, label %loop], !srcloc !3

asm.fallthrough:                                

Compiler need to generate:
1> a dummy constarint 'X' for each label.
2> an unique fallthrough label for each asm goto stmt " asm.fallthrough%number".


Diagnostic 
1>	duplicate asm operand name are used in output, input and label.
2>	goto out of scope.

llvm-svn: 362045
This commit is contained in:
Jennifer Yu 2019-05-30 01:05:46 +00:00
parent 192dd7df2f
commit 954ec09aed
28 changed files with 732 additions and 148 deletions

View File

@ -46,6 +46,7 @@ class Attr;
class CapturedDecl; class CapturedDecl;
class Decl; class Decl;
class Expr; class Expr;
class AddrLabelExpr;
class LabelDecl; class LabelDecl;
class ODRHash; class ODRHash;
class PrinterHelper; class PrinterHelper;
@ -2816,13 +2817,15 @@ class GCCAsmStmt : public AsmStmt {
StringLiteral **Constraints = nullptr; StringLiteral **Constraints = nullptr;
StringLiteral **Clobbers = nullptr; StringLiteral **Clobbers = nullptr;
IdentifierInfo **Names = nullptr; IdentifierInfo **Names = nullptr;
unsigned NumLabels = 0;
public: public:
GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple,
bool isvolatile, unsigned numoutputs, unsigned numinputs, bool isvolatile, unsigned numoutputs, unsigned numinputs,
IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, IdentifierInfo **names, StringLiteral **constraints, Expr **exprs,
StringLiteral *asmstr, unsigned numclobbers, StringLiteral *asmstr, unsigned numclobbers,
StringLiteral **clobbers, SourceLocation rparenloc); StringLiteral **clobbers, unsigned numlabels,
SourceLocation rparenloc);
/// Build an empty inline-assembly statement. /// Build an empty inline-assembly statement.
explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {} explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {}
@ -2947,6 +2950,51 @@ public:
return const_cast<GCCAsmStmt*>(this)->getInputExpr(i); return const_cast<GCCAsmStmt*>(this)->getInputExpr(i);
} }
//===--- Labels ---===//
bool isAsmGoto() const {
return NumLabels > 0;
}
unsigned getNumLabels() const {
return NumLabels;
}
IdentifierInfo *getLabelIdentifier(unsigned i) const {
return Names[i + NumInputs];
}
AddrLabelExpr *getLabelExpr(unsigned i) const;
StringRef getLabelName(unsigned i) const;
using labels_iterator = CastIterator<AddrLabelExpr>;
using const_labels_iterator = ConstCastIterator<AddrLabelExpr>;
using labels_range = llvm::iterator_range<labels_iterator>;
using labels_const_range = llvm::iterator_range<const_labels_iterator>;
labels_iterator begin_labels() {
return &Exprs[0] + NumInputs;
}
labels_iterator end_labels() {
return &Exprs[0] + NumInputs + NumLabels;
}
labels_range labels() {
return labels_range(begin_labels(), end_labels());
}
const_labels_iterator begin_labels() const {
return &Exprs[0] + NumInputs;
}
const_labels_iterator end_labels() const {
return &Exprs[0] + NumInputs + NumLabels;
}
labels_const_range labels() const {
return labels_const_range(begin_labels(), end_labels());
}
private: private:
void setOutputsAndInputsAndClobbers(const ASTContext &C, void setOutputsAndInputsAndClobbers(const ASTContext &C,
IdentifierInfo **Names, IdentifierInfo **Names,
@ -2954,6 +3002,7 @@ private:
Stmt **Exprs, Stmt **Exprs,
unsigned NumOutputs, unsigned NumOutputs,
unsigned NumInputs, unsigned NumInputs,
unsigned NumLabels,
StringLiteral **Clobbers, StringLiteral **Clobbers,
unsigned NumClobbers); unsigned NumClobbers);

View File

@ -27,8 +27,8 @@ def err_msasm_unable_to_create_target : Error<
"MS-style inline assembly is not available: %0">; "MS-style inline assembly is not available: %0">;
def err_gnu_inline_asm_disabled : Error< def err_gnu_inline_asm_disabled : Error<
"GNU-style inline assembly is disabled">; "GNU-style inline assembly is disabled">;
def err_asm_goto_not_supported_yet : Error< def err_asm_goto_cannot_have_output : Error<
"'asm goto' constructs are not supported yet">; "'asm goto' cannot have output constraints">;
} }
let CategoryName = "Parse Issue" in { let CategoryName = "Parse Issue" in {

View File

@ -5064,12 +5064,12 @@ def warn_cxx98_compat_switch_into_protected_scope : Warning<
def err_indirect_goto_without_addrlabel : Error< def err_indirect_goto_without_addrlabel : Error<
"indirect goto in function with no address-of-label expressions">; "indirect goto in function with no address-of-label expressions">;
def err_indirect_goto_in_protected_scope : Error< def err_indirect_goto_in_protected_scope : Error<
"cannot jump from this indirect goto statement to one of its possible targets">; "cannot jump from this %select{indirect|asm}0 goto statement to one of its possible targets">;
def warn_cxx98_compat_indirect_goto_in_protected_scope : Warning< def warn_cxx98_compat_indirect_goto_in_protected_scope : Warning<
"jump from this indirect goto statement to one of its possible targets " "jump from this %select{indirect|asm}0 goto statement to one of its possible targets "
"is incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; "is incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore;
def note_indirect_goto_target : Note< def note_indirect_goto_target : Note<
"possible target of indirect goto statement">; "possible target of %select{indirect|asm}0 goto statement">;
def note_protected_by_variable_init : Note< def note_protected_by_variable_init : Note<
"jump bypasses variable initialization">; "jump bypasses variable initialization">;
def note_protected_by_variable_nontriv_destructor : Note< def note_protected_by_variable_nontriv_destructor : Note<
@ -7497,6 +7497,10 @@ let CategoryName = "Inline Assembly Issue" in {
"use constraint modifier \"%0\"">; "use constraint modifier \"%0\"">;
def note_asm_input_duplicate_first : Note< def note_asm_input_duplicate_first : Note<
"constraint '%0' is already present here">; "constraint '%0' is already present here">;
def error_duplicate_asm_operand_name : Error<
"duplicate use of asm operand name \"%0\"">;
def note_duplicate_asm_operand_name : Note<
"asm operand name \"%0\" first referenced here">;
} }
def error_inoutput_conflict_with_clobber : Error< def error_inoutput_conflict_with_clobber : Error<

View File

@ -3971,6 +3971,7 @@ public:
unsigned NumInputs, IdentifierInfo **Names, unsigned NumInputs, IdentifierInfo **Names,
MultiExprArg Constraints, MultiExprArg Exprs, MultiExprArg Constraints, MultiExprArg Exprs,
Expr *AsmString, MultiExprArg Clobbers, Expr *AsmString, MultiExprArg Clobbers,
unsigned NumLabels,
SourceLocation RParenLoc); SourceLocation RParenLoc);
void FillInlineAsmIdentifierInfo(Expr *Res, void FillInlineAsmIdentifierInfo(Expr *Res,

View File

@ -5592,12 +5592,17 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
return InputOrErr.takeError(); return InputOrErr.takeError();
} }
SmallVector<Expr *, 4> Exprs(S->getNumOutputs() + S->getNumInputs()); SmallVector<Expr *, 4> Exprs(S->getNumOutputs() + S->getNumInputs() +
S->getNumLabels());
if (Error Err = ImportContainerChecked(S->outputs(), Exprs)) if (Error Err = ImportContainerChecked(S->outputs(), Exprs))
return std::move(Err); return std::move(Err);
if (Error Err =
ImportArrayChecked(S->inputs(), Exprs.begin() + S->getNumOutputs()))
return std::move(Err);
if (Error Err = ImportArrayChecked( if (Error Err = ImportArrayChecked(
S->inputs(), Exprs.begin() + S->getNumOutputs())) S->labels(), Exprs.begin() + S->getNumOutputs() + S->getNumInputs()))
return std::move(Err); return std::move(Err);
ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc()); ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc());
@ -5623,6 +5628,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
*AsmStrOrErr, *AsmStrOrErr,
S->getNumClobbers(), S->getNumClobbers(),
Clobbers.data(), Clobbers.data(),
S->getNumLabels(),
*RParenLocOrErr); *RParenLocOrErr);
} }

View File

@ -444,6 +444,14 @@ void GCCAsmStmt::setInputExpr(unsigned i, Expr *E) {
Exprs[i + NumOutputs] = E; Exprs[i + NumOutputs] = E;
} }
AddrLabelExpr *GCCAsmStmt::getLabelExpr(unsigned i) const {
return cast<AddrLabelExpr>(Exprs[i + NumInputs]);
}
StringRef GCCAsmStmt::getLabelName(unsigned i) const {
return getLabelExpr(i)->getLabel()->getName();
}
/// getInputConstraint - Return the specified input constraint. Unlike output /// getInputConstraint - Return the specified input constraint. Unlike output
/// constraints, these can be empty. /// constraints, these can be empty.
StringRef GCCAsmStmt::getInputConstraint(unsigned i) const { StringRef GCCAsmStmt::getInputConstraint(unsigned i) const {
@ -456,13 +464,16 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C,
Stmt **Exprs, Stmt **Exprs,
unsigned NumOutputs, unsigned NumOutputs,
unsigned NumInputs, unsigned NumInputs,
unsigned NumLabels,
StringLiteral **Clobbers, StringLiteral **Clobbers,
unsigned NumClobbers) { unsigned NumClobbers) {
this->NumOutputs = NumOutputs; this->NumOutputs = NumOutputs;
this->NumInputs = NumInputs; this->NumInputs = NumInputs;
this->NumClobbers = NumClobbers; this->NumClobbers = NumClobbers;
this->NumLabels = NumLabels;
assert(!(NumOutputs && NumLabels) && "asm goto cannot have outputs");
unsigned NumExprs = NumOutputs + NumInputs; unsigned NumExprs = NumOutputs + NumInputs + NumLabels;
C.Deallocate(this->Names); C.Deallocate(this->Names);
this->Names = new (C) IdentifierInfo*[NumExprs]; this->Names = new (C) IdentifierInfo*[NumExprs];
@ -497,6 +508,10 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const {
if (getInputName(i) == SymbolicName) if (getInputName(i) == SymbolicName)
return getNumOutputs() + NumPlusOperands + i; return getNumOutputs() + NumPlusOperands + i;
for (unsigned i = 0, e = getNumLabels(); i != e; ++i)
if (getLabelName(i) == SymbolicName)
return i + getNumInputs();
// Not found. // Not found.
return -1; return -1;
} }
@ -614,8 +629,8 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
while (CurPtr != StrEnd && isDigit(*CurPtr)) while (CurPtr != StrEnd && isDigit(*CurPtr))
N = N*10 + ((*CurPtr++)-'0'); N = N*10 + ((*CurPtr++)-'0');
unsigned NumOperands = unsigned NumOperands = getNumOutputs() + getNumPlusOperands() +
getNumOutputs() + getNumPlusOperands() + getNumInputs(); getNumInputs() + getNumLabels();
if (N >= NumOperands) { if (N >= NumOperands) {
DiagOffs = CurPtr-StrStart-1; DiagOffs = CurPtr-StrStart-1;
return diag::err_asm_invalid_operand_number; return diag::err_asm_invalid_operand_number;
@ -728,10 +743,12 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc,
unsigned numinputs, IdentifierInfo **names, unsigned numinputs, IdentifierInfo **names,
StringLiteral **constraints, Expr **exprs, StringLiteral **constraints, Expr **exprs,
StringLiteral *asmstr, unsigned numclobbers, StringLiteral *asmstr, unsigned numclobbers,
StringLiteral **clobbers, SourceLocation rparenloc) StringLiteral **clobbers, unsigned numlabels,
SourceLocation rparenloc)
: AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, : AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs,
numinputs, numclobbers), RParenLoc(rparenloc), AsmStr(asmstr) { numinputs, numclobbers),
unsigned NumExprs = NumOutputs + NumInputs; RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
unsigned NumExprs = NumOutputs + NumInputs + NumLabels;
Names = new (C) IdentifierInfo*[NumExprs]; Names = new (C) IdentifierInfo*[NumExprs];
std::copy(names, names + NumExprs, Names); std::copy(names, names + NumExprs, Names);

View File

@ -414,12 +414,15 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
if (Node->isVolatile()) if (Node->isVolatile())
OS << "volatile "; OS << "volatile ";
if (Node->isAsmGoto())
OS << "goto ";
OS << "("; OS << "(";
VisitStringLiteral(Node->getAsmString()); VisitStringLiteral(Node->getAsmString());
// Outputs // Outputs
if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 || if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 ||
Node->getNumClobbers() != 0) Node->getNumClobbers() != 0 || Node->getNumLabels() != 0)
OS << " : "; OS << " : ";
for (unsigned i = 0, e = Node->getNumOutputs(); i != e; ++i) { for (unsigned i = 0, e = Node->getNumOutputs(); i != e; ++i) {
@ -439,7 +442,8 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
} }
// Inputs // Inputs
if (Node->getNumInputs() != 0 || Node->getNumClobbers() != 0) if (Node->getNumInputs() != 0 || Node->getNumClobbers() != 0 ||
Node->getNumLabels() != 0)
OS << " : "; OS << " : ";
for (unsigned i = 0, e = Node->getNumInputs(); i != e; ++i) { for (unsigned i = 0, e = Node->getNumInputs(); i != e; ++i) {
@ -459,7 +463,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
} }
// Clobbers // Clobbers
if (Node->getNumClobbers() != 0) if (Node->getNumClobbers() != 0 || Node->getNumLabels())
OS << " : "; OS << " : ";
for (unsigned i = 0, e = Node->getNumClobbers(); i != e; ++i) { for (unsigned i = 0, e = Node->getNumClobbers(); i != e; ++i) {
@ -469,6 +473,16 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
VisitStringLiteral(Node->getClobberStringLiteral(i)); VisitStringLiteral(Node->getClobberStringLiteral(i));
} }
// Labels
if (Node->getNumLabels() != 0)
OS << " : ";
for (unsigned i = 0, e = Node->getNumLabels(); i != e; ++i) {
if (i != 0)
OS << ", ";
OS << Node->getLabelName(i);
}
OS << ");"; OS << ");";
if (Policy.IncludeNewlines) OS << NL; if (Policy.IncludeNewlines) OS << NL;
} }

View File

@ -321,6 +321,9 @@ void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) {
ID.AddInteger(S->getNumClobbers()); ID.AddInteger(S->getNumClobbers());
for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
VisitStringLiteral(S->getClobberStringLiteral(I)); VisitStringLiteral(S->getClobberStringLiteral(I));
ID.AddInteger(S->getNumLabels());
for (auto *L : S->labels())
VisitDecl(L->getLabel());
} }
void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) { void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) {

View File

@ -549,6 +549,7 @@ private:
CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc); CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
CFGBlock *VisitForStmt(ForStmt *F); CFGBlock *VisitForStmt(ForStmt *F);
CFGBlock *VisitGotoStmt(GotoStmt *G); CFGBlock *VisitGotoStmt(GotoStmt *G);
CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc);
CFGBlock *VisitIfStmt(IfStmt *I); CFGBlock *VisitIfStmt(IfStmt *I);
CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc); CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
CFGBlock *VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc); CFGBlock *VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc);
@ -1478,22 +1479,38 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
E = BackpatchBlocks.end(); I != E; ++I ) { E = BackpatchBlocks.end(); I != E; ++I ) {
CFGBlock *B = I->block; CFGBlock *B = I->block;
const GotoStmt *G = cast<GotoStmt>(B->getTerminator()); if (auto *G = dyn_cast<GotoStmt>(B->getTerminator())) {
LabelMapTy::iterator LI = LabelMap.find(G->getLabel()); LabelMapTy::iterator LI = LabelMap.find(G->getLabel());
// If there is no target for the goto, then we are looking at an
// If there is no target for the goto, then we are looking at an // incomplete AST. Handle this by not registering a successor.
// incomplete AST. Handle this by not registering a successor. if (LI == LabelMap.end())
if (LI == LabelMap.end()) continue; continue;
JumpTarget JT = LI->second;
JumpTarget JT = LI->second; prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition, JT.scopePosition);
JT.scopePosition); prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
prependAutomaticObjDtorsWithTerminator(B, I->scopePosition, JT.scopePosition);
JT.scopePosition); const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator(
const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator( B, I->scopePosition, JT.scopePosition);
B, I->scopePosition, JT.scopePosition); appendScopeBegin(JT.block, VD, G);
appendScopeBegin(JT.block, VD, G); addSuccessor(B, JT.block);
addSuccessor(B, JT.block); };
if (auto *G = dyn_cast<GCCAsmStmt>(B->getTerminator())) {
CFGBlock *Successor = (I+1)->block;
for (auto *L : G->labels()) {
LabelMapTy::iterator LI = LabelMap.find(L->getLabel());
// If there is no target for the goto, then we are looking at an
// incomplete AST. Handle this by not registering a successor.
if (LI == LabelMap.end())
continue;
JumpTarget JT = LI->second;
// Successor has been added, so skip it.
if (JT.block == Successor)
continue;
addSuccessor(B, JT.block);
}
I++;
}
} }
// Add successors to the Indirect Goto Dispatch block (if we have one). // Add successors to the Indirect Goto Dispatch block (if we have one).
@ -2142,6 +2159,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::GotoStmtClass: case Stmt::GotoStmtClass:
return VisitGotoStmt(cast<GotoStmt>(S)); return VisitGotoStmt(cast<GotoStmt>(S));
case Stmt::GCCAsmStmtClass:
return VisitGCCAsmStmt(cast<GCCAsmStmt>(S), asc);
case Stmt::IfStmtClass: case Stmt::IfStmtClass:
return VisitIfStmt(cast<IfStmt>(S)); return VisitIfStmt(cast<IfStmt>(S));
@ -3146,6 +3166,28 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {
return Block; return Block;
} }
CFGBlock *CFGBuilder::VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc) {
// Goto is a control-flow statement. Thus we stop processing the current
// block and create a new one.
if (!G->isAsmGoto())
return VisitStmt(G, asc);
if (Block) {
Succ = Block;
if (badCFG)
return nullptr;
}
Block = createBlock();
Block->setTerminator(G);
// We will backpatch this block later for all the labels.
BackpatchBlocks.push_back(JumpSource(Block, ScopePos));
// Save "Succ" in BackpatchBlocks. In the backpatch processing, "Succ" is
// used to avoid adding "Succ" again.
BackpatchBlocks.push_back(JumpSource(Succ, ScopePos));
return Block;
}
CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
CFGBlock *LoopSuccessor = nullptr; CFGBlock *LoopSuccessor = nullptr;

View File

@ -1896,6 +1896,55 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str,
return llvm::MDNode::get(CGF.getLLVMContext(), Locs); return llvm::MDNode::get(CGF.getLLVMContext(), Locs);
} }
static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect,
bool ReadOnly, bool ReadNone, const AsmStmt &S,
const std::vector<llvm::Type *> &ResultRegTypes,
CodeGenFunction &CGF,
std::vector<llvm::Value *> &RegResults) {
Result.addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoUnwind);
// Attach readnone and readonly attributes.
if (!HasSideEffect) {
if (ReadNone)
Result.addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadNone);
else if (ReadOnly)
Result.addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadOnly);
}
// Slap the source location of the inline asm into a !srcloc metadata on the
// call.
if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S))
Result.setMetadata("srcloc",
getAsmSrcLocInfo(gccAsmStmt->getAsmString(), CGF));
else {
// At least put the line number on MS inline asm blobs.
llvm::Constant *Loc = llvm::ConstantInt::get(CGF.Int32Ty,
S.getAsmLoc().getRawEncoding());
Result.setMetadata("srcloc",
llvm::MDNode::get(CGF.getLLVMContext(),
llvm::ConstantAsMetadata::get(Loc)));
}
if (CGF.getLangOpts().assumeFunctionsAreConvergent())
// Conservatively, mark all inline asm blocks in CUDA or OpenCL as
// convergent (meaning, they may call an intrinsically convergent op, such
// as bar.sync, and so can't have certain optimizations applied around
// them).
Result.addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::Convergent);
// Extract all of the register value results from the asm.
if (ResultRegTypes.size() == 1) {
RegResults.push_back(&Result);
} else {
for (unsigned i = 0, e = ResultRegTypes.size(); i != e; ++i) {
llvm::Value *Tmp = CGF.Builder.CreateExtractValue(&Result, i, "asmresult");
RegResults.push_back(Tmp);
}
}
}
void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
// Assemble the final asm string. // Assemble the final asm string.
std::string AsmString = S.generateAsmString(getContext()); std::string AsmString = S.generateAsmString(getContext());
@ -2138,6 +2187,29 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
} }
Constraints += InOutConstraints; Constraints += InOutConstraints;
// Labels
SmallVector<llvm::BasicBlock *, 16> Transfer;
llvm::BasicBlock *Fallthrough = nullptr;
bool IsGCCAsmGoto = false;
if (const auto *GS = dyn_cast<GCCAsmStmt>(&S)) {
IsGCCAsmGoto = GS->isAsmGoto();
if (IsGCCAsmGoto) {
for (auto *E : GS->labels()) {
JumpDest Dest = getJumpDestForLabel(E->getLabel());
Transfer.push_back(Dest.getBlock());
llvm::BlockAddress *BA =
llvm::BlockAddress::get(CurFn, Dest.getBlock());
Args.push_back(BA);
ArgTypes.push_back(BA->getType());
if (!Constraints.empty())
Constraints += ',';
Constraints += 'X';
}
StringRef Name = "asm.fallthrough";
Fallthrough = createBasicBlock(Name);
}
}
// Clobbers // Clobbers
for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) {
StringRef Clobber = S.getClobber(i); StringRef Clobber = S.getClobber(i);
@ -2180,52 +2252,18 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
llvm::InlineAsm *IA = llvm::InlineAsm *IA =
llvm::InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect, llvm::InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect,
/* IsAlignStack */ false, AsmDialect); /* IsAlignStack */ false, AsmDialect);
llvm::CallInst *Result =
Builder.CreateCall(IA, Args, getBundlesForFunclet(IA));
Result->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoUnwind);
// Attach readnone and readonly attributes.
if (!HasSideEffect) {
if (ReadNone)
Result->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadNone);
else if (ReadOnly)
Result->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadOnly);
}
// Slap the source location of the inline asm into a !srcloc metadata on the
// call.
if (const GCCAsmStmt *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S)) {
Result->setMetadata("srcloc", getAsmSrcLocInfo(gccAsmStmt->getAsmString(),
*this));
} else {
// At least put the line number on MS inline asm blobs.
auto Loc = llvm::ConstantInt::get(Int32Ty, S.getAsmLoc().getRawEncoding());
Result->setMetadata("srcloc",
llvm::MDNode::get(getLLVMContext(),
llvm::ConstantAsMetadata::get(Loc)));
}
if (getLangOpts().assumeFunctionsAreConvergent()) {
// Conservatively, mark all inline asm blocks in CUDA or OpenCL as
// convergent (meaning, they may call an intrinsically convergent op, such
// as bar.sync, and so can't have certain optimizations applied around
// them).
Result->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::Convergent);
}
// Extract all of the register value results from the asm.
std::vector<llvm::Value*> RegResults; std::vector<llvm::Value*> RegResults;
if (ResultRegTypes.size() == 1) { if (IsGCCAsmGoto) {
RegResults.push_back(Result); llvm::CallBrInst *Result =
Builder.CreateCallBr(IA, Fallthrough, Transfer, Args);
UpdateAsmCallInst(cast<llvm::CallBase>(*Result), HasSideEffect, ReadOnly,
ReadNone, S, ResultRegTypes, *this, RegResults);
EmitBlock(Fallthrough);
} else { } else {
for (unsigned i = 0, e = ResultRegTypes.size(); i != e; ++i) { llvm::CallInst *Result =
llvm::Value *Tmp = Builder.CreateExtractValue(Result, i, "asmresult"); Builder.CreateCall(IA, Args, getBundlesForFunclet(IA));
RegResults.push_back(Tmp); UpdateAsmCallInst(cast<llvm::CallBase>(*Result), HasSideEffect, ReadOnly,
} ReadNone, S, ResultRegTypes, *this, RegResults);
} }
assert(RegResults.size() == ResultRegTypes.size()); assert(RegResults.size() == ResultRegTypes.size());

View File

@ -710,12 +710,12 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
// Remember if this was a volatile asm. // Remember if this was a volatile asm.
bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile;
// Remember if this was a goto asm.
bool isGotoAsm = false;
// TODO: support "asm goto" constructs (PR#9295).
if (Tok.is(tok::kw_goto)) { if (Tok.is(tok::kw_goto)) {
Diag(Tok, diag::err_asm_goto_not_supported_yet); isGotoAsm = true;
SkipUntil(tok::r_paren, StopAtSemi); ConsumeToken();
return StmtError();
} }
if (Tok.isNot(tok::l_paren)) { if (Tok.isNot(tok::l_paren)) {
@ -753,7 +753,8 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
return Actions.ActOnGCCAsmStmt(AsmLoc, /*isSimple*/ true, isVolatile, return Actions.ActOnGCCAsmStmt(AsmLoc, /*isSimple*/ true, isVolatile,
/*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr,
Constraints, Exprs, AsmString.get(), Constraints, Exprs, AsmString.get(),
Clobbers, T.getCloseLocation()); Clobbers, /*NumLabels*/ 0,
T.getCloseLocation());
} }
// Parse Outputs, if present. // Parse Outputs, if present.
@ -763,6 +764,12 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
AteExtraColon = Tok.is(tok::coloncolon); AteExtraColon = Tok.is(tok::coloncolon);
ConsumeToken(); 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)) if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
return StmtError(); return StmtError();
} }
@ -789,12 +796,15 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
unsigned NumInputs = Names.size() - NumOutputs; unsigned NumInputs = Names.size() - NumOutputs;
// Parse the clobbers, if present. // Parse the clobbers, if present.
if (AteExtraColon || Tok.is(tok::colon)) { if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
if (!AteExtraColon) if (AteExtraColon)
AteExtraColon = false;
else {
AteExtraColon = Tok.is(tok::coloncolon);
ConsumeToken(); ConsumeToken();
}
// Parse the asm-string list for clobbers if present. // Parse the asm-string list for clobbers if present.
if (Tok.isNot(tok::r_paren)) { if (!AteExtraColon && isTokenStringLiteral()) {
while (1) { while (1) {
ExprResult Clobber(ParseAsmStringLiteral()); ExprResult Clobber(ParseAsmStringLiteral());
@ -808,11 +818,49 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
} }
} }
} }
if (!isGotoAsm && (Tok.isNot(tok::r_paren) || AteExtraColon)) {
Diag(Tok, diag::err_expected) << tok::r_paren;
SkipUntil(tok::r_paren, StopAtSemi);
return StmtError();
}
// Parse the goto label, if present.
unsigned NumLabels = 0;
if (AteExtraColon || Tok.is(tok::colon)) {
if (!AteExtraColon)
ConsumeToken();
while (true) {
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
SkipUntil(tok::r_paren, StopAtSemi);
return StmtError();
}
LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
Tok.getLocation());
Names.push_back(Tok.getIdentifierInfo());
if (!LD) {
SkipUntil(tok::r_paren, StopAtSemi);
return StmtError();
}
ExprResult Res =
Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD);
Exprs.push_back(Res.get());
NumLabels++;
ConsumeToken();
if (!TryConsumeToken(tok::comma))
break;
}
} else if (isGotoAsm) {
Diag(Tok, diag::err_expected) << tok::colon;
SkipUntil(tok::r_paren, StopAtSemi);
return StmtError();
}
T.consumeClose(); T.consumeClose();
return Actions.ActOnGCCAsmStmt( return Actions.ActOnGCCAsmStmt(
AsmLoc, false, isVolatile, NumOutputs, NumInputs, Names.data(), AsmLoc, false, isVolatile, NumOutputs, NumInputs, Names.data(),
Constraints, Exprs, AsmString.get(), Clobbers, T.getCloseLocation()); Constraints, Exprs, AsmString.get(), Clobbers, NumLabels,
T.getCloseLocation());
} }
/// ParseAsmOperands - Parse the asm-operands production as used by /// ParseAsmOperands - Parse the asm-operands production as used by

View File

@ -65,8 +65,10 @@ class JumpScopeChecker {
llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes; llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes;
SmallVector<Stmt*, 16> Jumps; SmallVector<Stmt*, 16> Jumps;
SmallVector<IndirectGotoStmt*, 4> IndirectJumps; SmallVector<Stmt*, 4> IndirectJumps;
SmallVector<Stmt*, 4> AsmJumps;
SmallVector<LabelDecl*, 4> IndirectJumpTargets; SmallVector<LabelDecl*, 4> IndirectJumpTargets;
SmallVector<LabelDecl*, 4> AsmJumpTargets;
public: public:
JumpScopeChecker(Stmt *Body, Sema &S); JumpScopeChecker(Stmt *Body, Sema &S);
private: private:
@ -76,10 +78,10 @@ private:
void BuildScopeInformation(Stmt *S, unsigned &origParentScope); void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
void VerifyJumps(); void VerifyJumps();
void VerifyIndirectJumps(); void VerifyIndirectOrAsmJumps(bool IsAsmGoto);
void NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes); void NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes);
void DiagnoseIndirectJump(IndirectGotoStmt *IG, unsigned IGScope, void DiagnoseIndirectOrAsmJump(Stmt *IG, unsigned IGScope, LabelDecl *Target,
LabelDecl *Target, unsigned TargetScope); unsigned TargetScope);
void CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc, void CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc,
unsigned JumpDiag, unsigned JumpDiagWarning, unsigned JumpDiag, unsigned JumpDiagWarning,
unsigned JumpDiagCXX98Compat); unsigned JumpDiagCXX98Compat);
@ -103,7 +105,8 @@ JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s)
// Check that all jumps we saw are kosher. // Check that all jumps we saw are kosher.
VerifyJumps(); VerifyJumps();
VerifyIndirectJumps(); VerifyIndirectOrAsmJumps(false);
VerifyIndirectOrAsmJumps(true);
} }
/// GetDeepestCommonScope - Finds the innermost scope enclosing the /// GetDeepestCommonScope - Finds the innermost scope enclosing the
@ -316,7 +319,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
} }
LabelAndGotoScopes[S] = ParentScope; LabelAndGotoScopes[S] = ParentScope;
IndirectJumps.push_back(cast<IndirectGotoStmt>(S)); IndirectJumps.push_back(S);
break; break;
case Stmt::SwitchStmtClass: case Stmt::SwitchStmtClass:
@ -339,6 +342,18 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
Jumps.push_back(S); Jumps.push_back(S);
break; break;
case Stmt::GCCAsmStmtClass:
if (auto *GS = dyn_cast<GCCAsmStmt>(S))
if (GS->isAsmGoto()) {
// Remember both what scope a goto is in as well as the fact that we
// have it. This makes the second scan not have to walk the AST again.
LabelAndGotoScopes[S] = ParentScope;
AsmJumps.push_back(GS);
for (auto *E : GS->labels())
AsmJumpTargets.push_back(E->getLabel());
}
break;
case Stmt::IfStmtClass: { case Stmt::IfStmtClass: {
IfStmt *IS = cast<IfStmt>(S); IfStmt *IS = cast<IfStmt>(S);
if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck())) if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck()))
@ -629,14 +644,13 @@ void JumpScopeChecker::VerifyJumps() {
} }
} }
/// VerifyIndirectJumps - Verify whether any possible indirect jump /// VerifyIndirectOrAsmJumps - Verify whether any possible indirect goto or
/// might cross a protection boundary. Unlike direct jumps, indirect /// asm goto jump might cross a protection boundary. Unlike direct jumps,
/// jumps count cleanups as protection boundaries: since there's no /// indirect or asm goto jumps count cleanups as protection boundaries:
/// way to know where the jump is going, we can't implicitly run the /// since there's no way to know where the jump is going, we can't implicitly
/// right cleanups the way we can with direct jumps. /// run the right cleanups the way we can with direct jumps.
/// /// Thus, an indirect/asm jump is "trivial" if it bypasses no
/// Thus, an indirect jump is "trivial" if it bypasses no /// initializations and no teardowns. More formally, an indirect/asm jump
/// initializations and no teardowns. More formally, an indirect jump
/// from A to B is trivial if the path out from A to DCA(A,B) is /// from A to B is trivial if the path out from A to DCA(A,B) is
/// trivial and the path in from DCA(A,B) to B is trivial, where /// trivial and the path in from DCA(A,B) to B is trivial, where
/// DCA(A,B) is the deepest common ancestor of A and B. /// DCA(A,B) is the deepest common ancestor of A and B.
@ -648,36 +662,41 @@ void JumpScopeChecker::VerifyJumps() {
/// Under these definitions, this function checks that the indirect /// Under these definitions, this function checks that the indirect
/// jump between A and B is trivial for every indirect goto statement A /// jump between A and B is trivial for every indirect goto statement A
/// and every label B whose address was taken in the function. /// and every label B whose address was taken in the function.
void JumpScopeChecker::VerifyIndirectJumps() { void JumpScopeChecker::VerifyIndirectOrAsmJumps(bool IsAsmGoto) {
if (IndirectJumps.empty()) return; SmallVector<Stmt*, 4> GotoJumps = IsAsmGoto ? AsmJumps : IndirectJumps;
if (GotoJumps.empty())
return;
SmallVector<LabelDecl *, 4> JumpTargets =
IsAsmGoto ? AsmJumpTargets : IndirectJumpTargets;
// If there aren't any address-of-label expressions in this function, // If there aren't any address-of-label expressions in this function,
// complain about the first indirect goto. // complain about the first indirect goto.
if (IndirectJumpTargets.empty()) { if (JumpTargets.empty()) {
S.Diag(IndirectJumps[0]->getGotoLoc(), assert(!IsAsmGoto &&"only indirect goto can get here");
S.Diag(GotoJumps[0]->getBeginLoc(),
diag::err_indirect_goto_without_addrlabel); diag::err_indirect_goto_without_addrlabel);
return; return;
} }
// Collect a single representative of every scope containing an // Collect a single representative of every scope containing an
// indirect goto. For most code bases, this substantially cuts // indirect or asm goto. For most code bases, this substantially cuts
// down on the number of jump sites we'll have to consider later. // down on the number of jump sites we'll have to consider later.
typedef std::pair<unsigned, IndirectGotoStmt*> JumpScope; typedef std::pair<unsigned, Stmt*> JumpScope;
SmallVector<JumpScope, 32> JumpScopes; SmallVector<JumpScope, 32> JumpScopes;
{ {
llvm::DenseMap<unsigned, IndirectGotoStmt*> JumpScopesMap; llvm::DenseMap<unsigned, Stmt*> JumpScopesMap;
for (SmallVectorImpl<IndirectGotoStmt*>::iterator for (SmallVectorImpl<Stmt *>::iterator I = GotoJumps.begin(),
I = IndirectJumps.begin(), E = IndirectJumps.end(); I != E; ++I) { E = GotoJumps.end();
IndirectGotoStmt *IG = *I; I != E; ++I) {
Stmt *IG = *I;
if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG))) if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG)))
continue; continue;
unsigned IGScope = LabelAndGotoScopes[IG]; unsigned IGScope = LabelAndGotoScopes[IG];
IndirectGotoStmt *&Entry = JumpScopesMap[IGScope]; Stmt *&Entry = JumpScopesMap[IGScope];
if (!Entry) Entry = IG; if (!Entry) Entry = IG;
} }
JumpScopes.reserve(JumpScopesMap.size()); JumpScopes.reserve(JumpScopesMap.size());
for (llvm::DenseMap<unsigned, IndirectGotoStmt*>::iterator for (llvm::DenseMap<unsigned, Stmt *>::iterator I = JumpScopesMap.begin(),
I = JumpScopesMap.begin(), E = JumpScopesMap.end(); I != E; ++I) E = JumpScopesMap.end();
I != E; ++I)
JumpScopes.push_back(*I); JumpScopes.push_back(*I);
} }
@ -685,8 +704,8 @@ void JumpScopeChecker::VerifyIndirectJumps() {
// label whose address was taken somewhere in the function. // label whose address was taken somewhere in the function.
// For most code bases, there will be only one such scope. // For most code bases, there will be only one such scope.
llvm::DenseMap<unsigned, LabelDecl*> TargetScopes; llvm::DenseMap<unsigned, LabelDecl*> TargetScopes;
for (SmallVectorImpl<LabelDecl*>::iterator for (SmallVectorImpl<LabelDecl *>::iterator I = JumpTargets.begin(),
I = IndirectJumpTargets.begin(), E = IndirectJumpTargets.end(); E = JumpTargets.end();
I != E; ++I) { I != E; ++I) {
LabelDecl *TheLabel = *I; LabelDecl *TheLabel = *I;
if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(TheLabel->getStmt()))) if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(TheLabel->getStmt())))
@ -763,7 +782,7 @@ void JumpScopeChecker::VerifyIndirectJumps() {
// Only diagnose if we didn't find something. // Only diagnose if we didn't find something.
if (IsReachable) continue; if (IsReachable) continue;
DiagnoseIndirectJump(I->second, I->first, TargetLabel, TargetScope); DiagnoseIndirectOrAsmJump(I->second, I->first, TargetLabel, TargetScope);
} }
} }
} }
@ -784,12 +803,15 @@ static bool IsCXX98CompatWarning(Sema &S, unsigned InDiagNote) {
} }
/// Produce primary diagnostic for an indirect jump statement. /// Produce primary diagnostic for an indirect jump statement.
static void DiagnoseIndirectJumpStmt(Sema &S, IndirectGotoStmt *Jump, static void DiagnoseIndirectOrAsmJumpStmt(Sema &S, Stmt *Jump,
LabelDecl *Target, bool &Diagnosed) { LabelDecl *Target, bool &Diagnosed) {
if (Diagnosed) if (Diagnosed)
return; return;
S.Diag(Jump->getGotoLoc(), diag::err_indirect_goto_in_protected_scope); bool IsAsmGoto = isa<GCCAsmStmt>(Jump);
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target); S.Diag(Jump->getBeginLoc(), diag::err_indirect_goto_in_protected_scope)
<< IsAsmGoto;
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target)
<< IsAsmGoto;
Diagnosed = true; Diagnosed = true;
} }
@ -803,10 +825,9 @@ void JumpScopeChecker::NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes) {
} }
/// Diagnose an indirect jump which is known to cross scopes. /// Diagnose an indirect jump which is known to cross scopes.
void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump, void JumpScopeChecker::DiagnoseIndirectOrAsmJump(Stmt *Jump, unsigned JumpScope,
unsigned JumpScope, LabelDecl *Target,
LabelDecl *Target, unsigned TargetScope) {
unsigned TargetScope) {
if (CHECK_PERMISSIVE(JumpScope == TargetScope)) if (CHECK_PERMISSIVE(JumpScope == TargetScope))
return; return;
@ -816,7 +837,7 @@ void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump,
// Walk out the scope chain until we reach the common ancestor. // Walk out the scope chain until we reach the common ancestor.
for (unsigned I = JumpScope; I != Common; I = Scopes[I].ParentScope) for (unsigned I = JumpScope; I != Common; I = Scopes[I].ParentScope)
if (Scopes[I].OutDiag) { if (Scopes[I].OutDiag) {
DiagnoseIndirectJumpStmt(S, Jump, Target, Diagnosed); DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);
S.Diag(Scopes[I].Loc, Scopes[I].OutDiag); S.Diag(Scopes[I].Loc, Scopes[I].OutDiag);
} }
@ -827,15 +848,18 @@ void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump,
if (IsCXX98CompatWarning(S, Scopes[I].InDiag)) if (IsCXX98CompatWarning(S, Scopes[I].InDiag))
ToScopesCXX98Compat.push_back(I); ToScopesCXX98Compat.push_back(I);
else if (Scopes[I].InDiag) { else if (Scopes[I].InDiag) {
DiagnoseIndirectJumpStmt(S, Jump, Target, Diagnosed); DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);
S.Diag(Scopes[I].Loc, Scopes[I].InDiag); S.Diag(Scopes[I].Loc, Scopes[I].InDiag);
} }
// Diagnose this jump if it would be ill-formed in C++98. // Diagnose this jump if it would be ill-formed in C++98.
if (!Diagnosed && !ToScopesCXX98Compat.empty()) { if (!Diagnosed && !ToScopesCXX98Compat.empty()) {
S.Diag(Jump->getGotoLoc(), bool IsAsmGoto = isa<GCCAsmStmt>(Jump);
diag::warn_cxx98_compat_indirect_goto_in_protected_scope); S.Diag(Jump->getBeginLoc(),
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target); diag::warn_cxx98_compat_indirect_goto_in_protected_scope)
<< IsAsmGoto;
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target)
<< IsAsmGoto;
NoteJumpIntoScopes(ToScopesCXX98Compat); NoteJumpIntoScopes(ToScopesCXX98Compat);
} }
} }

View File

@ -209,11 +209,12 @@ static StringRef extractRegisterName(const Expr *Expression,
static SourceLocation static SourceLocation
getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints,
StringLiteral **Clobbers, int NumClobbers, StringLiteral **Clobbers, int NumClobbers,
unsigned NumLabels,
const TargetInfo &Target, ASTContext &Cont) { const TargetInfo &Target, ASTContext &Cont) {
llvm::StringSet<> InOutVars; llvm::StringSet<> InOutVars;
// Collect all the input and output registers from the extended asm // Collect all the input and output registers from the extended asm
// statement in order to check for conflicts with the clobber list // statement in order to check for conflicts with the clobber list
for (unsigned int i = 0; i < Exprs.size(); ++i) { for (unsigned int i = 0; i < Exprs.size() - NumLabels; ++i) {
StringRef Constraint = Constraints[i]->getString(); StringRef Constraint = Constraints[i]->getString();
StringRef InOutReg = Target.getConstraintRegister( StringRef InOutReg = Target.getConstraintRegister(
Constraint, extractRegisterName(Exprs[i], Target)); Constraint, extractRegisterName(Exprs[i], Target));
@ -241,6 +242,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
unsigned NumInputs, IdentifierInfo **Names, unsigned NumInputs, IdentifierInfo **Names,
MultiExprArg constraints, MultiExprArg Exprs, MultiExprArg constraints, MultiExprArg Exprs,
Expr *asmString, MultiExprArg clobbers, Expr *asmString, MultiExprArg clobbers,
unsigned NumLabels,
SourceLocation RParenLoc) { SourceLocation RParenLoc) {
unsigned NumClobbers = clobbers.size(); unsigned NumClobbers = clobbers.size();
StringLiteral **Constraints = StringLiteral **Constraints =
@ -269,7 +271,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return new (Context) return new (Context)
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs.data(), AsmString, NumInputs, Names, Constraints, Exprs.data(), AsmString,
NumClobbers, Clobbers, RParenLoc); NumClobbers, Clobbers, NumLabels, RParenLoc);
} }
ExprResult ER = CheckPlaceholderExpr(Exprs[i]); ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
@ -330,7 +332,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return new (Context) return new (Context)
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs.data(), AsmString, NumInputs, Names, Constraints, Exprs.data(), AsmString,
NumClobbers, Clobbers, RParenLoc); NumClobbers, Clobbers, NumLabels, RParenLoc);
} }
} }
@ -352,7 +354,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return new (Context) return new (Context)
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs.data(), AsmString, NumInputs, Names, Constraints, Exprs.data(), AsmString,
NumClobbers, Clobbers, RParenLoc); NumClobbers, Clobbers, NumLabels, RParenLoc);
} }
ExprResult ER = CheckPlaceholderExpr(Exprs[i]); ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
@ -451,14 +453,15 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return new (Context) return new (Context)
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs.data(), AsmString, NumInputs, Names, Constraints, Exprs.data(), AsmString,
NumClobbers, Clobbers, RParenLoc); NumClobbers, Clobbers, NumLabels, RParenLoc);
} }
} }
GCCAsmStmt *NS = GCCAsmStmt *NS =
new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs.data(), NumInputs, Names, Constraints, Exprs.data(),
AsmString, NumClobbers, Clobbers, RParenLoc); AsmString, NumClobbers, Clobbers, NumLabels,
RParenLoc);
// Validate the asm string, ensuring it makes sense given the operands we // Validate the asm string, ensuring it makes sense given the operands we
// have. // have.
SmallVector<GCCAsmStmt::AsmStringPiece, 8> Pieces; SmallVector<GCCAsmStmt::AsmStringPiece, 8> Pieces;
@ -476,8 +479,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
// Look for the correct constraint index. // Look for the correct constraint index.
unsigned ConstraintIdx = Piece.getOperandNo(); 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(); unsigned NumOperands = NS->getNumOutputs() + NS->getNumInputs();
// Look for the (ConstraintIdx - NumOperands + 1)th constraint with // Look for the (ConstraintIdx - NumOperands + 1)th constraint with
// modifier '+'. // modifier '+'.
if (ConstraintIdx >= NumOperands) { if (ConstraintIdx >= NumOperands) {
@ -660,10 +665,39 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
// Check for conflicts between clobber list and input or output lists // Check for conflicts between clobber list and input or output lists
SourceLocation ConstraintLoc = SourceLocation ConstraintLoc =
getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers, getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers,
NumLabels,
Context.getTargetInfo(), Context); Context.getTargetInfo(), Context);
if (ConstraintLoc.isValid()) if (ConstraintLoc.isValid())
targetDiag(ConstraintLoc, diag::error_inoutput_conflict_with_clobber); targetDiag(ConstraintLoc, diag::error_inoutput_conflict_with_clobber);
// Check for duplicate asm operand name between input, output and label lists.
typedef std::pair<StringRef , Expr *> NamedOperand;
SmallVector<NamedOperand, 4> NamedOperandList;
for (unsigned i = 0, e = NumOutputs + NumInputs + NumLabels; i != e; ++i)
if (Names[i])
NamedOperandList.emplace_back(
std::make_pair(Names[i]->getName(), Exprs[i]));
// Sort NamedOperandList.
std::stable_sort(NamedOperandList.begin(), NamedOperandList.end(),
[](const NamedOperand &LHS, const NamedOperand &RHS) {
return LHS.first < RHS.first;
});
// Find adjacent duplicate operand.
SmallVector<NamedOperand, 4>::iterator Found =
std::adjacent_find(begin(NamedOperandList), end(NamedOperandList),
[](const NamedOperand &LHS, const NamedOperand &RHS) {
return LHS.first == RHS.first;
});
if (Found != NamedOperandList.end()) {
Diag((Found + 1)->second->getBeginLoc(),
diag::error_duplicate_asm_operand_name)
<< (Found + 1)->first;
Diag(Found->second->getBeginLoc(), diag::note_duplicate_asm_operand_name)
<< Found->first;
return StmtError();
}
if (NS->isAsmGoto())
setFunctionHasBranchIntoScope();
return NS; return NS;
} }

View File

@ -1373,10 +1373,11 @@ public:
unsigned NumInputs, IdentifierInfo **Names, unsigned NumInputs, IdentifierInfo **Names,
MultiExprArg Constraints, MultiExprArg Exprs, MultiExprArg Constraints, MultiExprArg Exprs,
Expr *AsmString, MultiExprArg Clobbers, Expr *AsmString, MultiExprArg Clobbers,
unsigned NumLabels,
SourceLocation RParenLoc) { SourceLocation RParenLoc) {
return getSema().ActOnGCCAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs, return getSema().ActOnGCCAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, Constraints, Exprs, NumInputs, Names, Constraints, Exprs,
AsmString, Clobbers, RParenLoc); AsmString, Clobbers, NumLabels, RParenLoc);
} }
/// Build a new MS style inline asm statement. /// Build a new MS style inline asm statement.
@ -7051,6 +7052,16 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
Exprs.push_back(Result.get()); Exprs.push_back(Result.get());
} }
// Go through the Labels.
for (unsigned I = 0, E = S->getNumLabels(); I != E; ++I) {
Names.push_back(S->getLabelIdentifier(I));
ExprResult Result = getDerived().TransformExpr(S->getLabelExpr(I));
if (Result.isInvalid())
return StmtError();
ExprsChanged |= Result.get() != S->getLabelExpr(I);
Exprs.push_back(Result.get());
}
if (!getDerived().AlwaysRebuild() && !ExprsChanged) if (!getDerived().AlwaysRebuild() && !ExprsChanged)
return S; return S;
@ -7064,7 +7075,8 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
S->isVolatile(), S->getNumOutputs(), S->isVolatile(), S->getNumOutputs(),
S->getNumInputs(), Names.data(), S->getNumInputs(), Names.data(),
Constraints, Exprs, AsmString.get(), Constraints, Exprs, AsmString.get(),
Clobbers, S->getRParenLoc()); Clobbers, S->getNumLabels(),
S->getRParenLoc());
} }
template<typename Derived> template<typename Derived>

View File

@ -370,12 +370,14 @@ void ASTStmtReader::VisitAsmStmt(AsmStmt *S) {
void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
VisitAsmStmt(S); VisitAsmStmt(S);
S->NumLabels = Record.readInt();
S->setRParenLoc(ReadSourceLocation()); S->setRParenLoc(ReadSourceLocation());
S->setAsmString(cast_or_null<StringLiteral>(Record.readSubStmt())); S->setAsmString(cast_or_null<StringLiteral>(Record.readSubStmt()));
unsigned NumOutputs = S->getNumOutputs(); unsigned NumOutputs = S->getNumOutputs();
unsigned NumInputs = S->getNumInputs(); unsigned NumInputs = S->getNumInputs();
unsigned NumClobbers = S->getNumClobbers(); unsigned NumClobbers = S->getNumClobbers();
unsigned NumLabels = S->getNumLabels();
// Outputs and inputs // Outputs and inputs
SmallVector<IdentifierInfo *, 16> Names; SmallVector<IdentifierInfo *, 16> Names;
@ -392,9 +394,14 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
for (unsigned I = 0; I != NumClobbers; ++I) for (unsigned I = 0; I != NumClobbers; ++I)
Clobbers.push_back(cast_or_null<StringLiteral>(Record.readSubStmt())); Clobbers.push_back(cast_or_null<StringLiteral>(Record.readSubStmt()));
// Labels
for (unsigned I = 0, N = NumLabels; I != N; ++I)
Exprs.push_back(Record.readSubStmt());
S->setOutputsAndInputsAndClobbers(Record.getContext(), S->setOutputsAndInputsAndClobbers(Record.getContext(),
Names.data(), Constraints.data(), Names.data(), Constraints.data(),
Exprs.data(), NumOutputs, NumInputs, Exprs.data(), NumOutputs, NumInputs,
NumLabels,
Clobbers.data(), NumClobbers); Clobbers.data(), NumClobbers);
} }

View File

@ -283,6 +283,7 @@ void ASTStmtWriter::VisitAsmStmt(AsmStmt *S) {
void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) {
VisitAsmStmt(S); VisitAsmStmt(S);
Record.push_back(S->getNumLabels());
Record.AddSourceLocation(S->getRParenLoc()); Record.AddSourceLocation(S->getRParenLoc());
Record.AddStmt(S->getAsmString()); Record.AddStmt(S->getAsmString());
@ -304,6 +305,9 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) {
for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
Record.AddStmt(S->getClobberStringLiteral(I)); Record.AddStmt(S->getClobberStringLiteral(I));
// Labels
for (auto *E : S->labels()) Record.AddStmt(E);
Code = serialization::STMT_GCCASM; Code = serialization::STMT_GCCASM;
} }

View File

@ -0,0 +1,52 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s
int foo(int cond)
{
label_true:
asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
return 0;
loop:
return 0;
}
// CHECK-LABEL: loop
// CHECK-NEXT: 0
// CHECK-NEXT: return
// CHECK-NEXT: Preds (1): B3
// CHECK-NEXT: Succs (1): B0
// CHECK-LABEL: label_true
// CHECK-NEXT: asm goto
// CHECK-NEXT: Preds (2): B3 B4
// CHECK-NEXT: Succs (3): B2 B3 B1
int bar(int cond)
{
asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::L1, L2);
return 0;
L1:
L2:
return 0;
}
// CHECK: [B4]
// CHECK-NEXT: asm goto
// CHECK-NEXT: Preds (1): B5
// CHECK-NEXT: Succs (3): B3 B2 B1
int zoo(int n)
{
A5:
A1:
asm goto("testl %0, %0; jne %l1;" :: "r"(n)::A1, A2, A3, A4, A5);
A2:
A3:
A4:
return 0;
}
// CHECK-LABEL: A1
// CHECK-NEXT: asm goto
// CHECK-NEXT: Preds (2): B5 B4
// CHECK-NEXT: Succs (5): B3 B4 B2 B1 B5

View File

@ -0,0 +1,18 @@
// RUN: %clang_cc1 -O0 -emit-llvm %s -o - | FileCheck %s
int foo(int cond)
{
// CHECK: callbr void asm sideeffect
// CHECK: to label %asm.fallthrough [label %label_true, label %loop], !srcloc !2
// CHECK: asm.fallthrough:
asm volatile goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
asm volatile goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
// CHECK: callbr void asm sideeffect
// CHECK: to label %asm.fallthrough1 [label %label_true, label %loop], !srcloc !3
// CHECK: asm.fallthrough1:
return 0;
loop:
return 0;
label_true:
return 1;
}

View File

@ -262,3 +262,15 @@ void t31(int len) {
// CHECK: @t31 // CHECK: @t31
// CHECK: call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}" // CHECK: call void asm sideeffect "", "=*%rm,=*rm,0,1,~{dirflag},~{fpsr},~{flags}"
} }
// CHECK: @t32
int t32(int cond)
{
asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
// CHECK: callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@t32, %label_true), i8* blockaddress(@t32, %loop)) #1
return 0;
loop:
return 0;
label_true:
return 1;
}

View File

@ -1,4 +1,3 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -fasm-blocks -fsyntax-only -verify %s -DCHECK_ASM_GOTO
// RUN: %clang_cc1 -triple i386-unknown-unknown -fasm-blocks -O0 -emit-llvm -S %s -o - | FileCheck %s // RUN: %clang_cc1 -triple i386-unknown-unknown -fasm-blocks -O0 -emit-llvm -S %s -o - | FileCheck %s
// REQUIRES: x86-registered-target // REQUIRES: x86-registered-target
@ -20,10 +19,11 @@ void f() {
// CHECK: movl %ebx, %eax // CHECK: movl %ebx, %eax
// CHECK: movl %ecx, %edx // CHECK: movl %ecx, %edx
#ifdef CHECK_ASM_GOTO __asm volatile goto ("movl %ecx, %edx");
__asm volatile goto ("movl %ecx, %edx"); // expected-error {{'asm goto' constructs are not supported yet}} // CHECK: movl %ecx, %edx
__asm mov eax, ebx __asm mov eax, ebx
__asm goto ("movl %ecx, %edx"); // expected-error {{'asm goto' constructs are not supported yet}} __asm goto ("movl %ecx, %edx");
#endif // CHECK: movl %ebx, %eax
// CHECK: movl %ecx, %edx
} }

View File

@ -71,7 +71,9 @@ theif:
} }
asm ("nop"); asm ("nop");
int cond;
asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true);
label_true:
return; return;
} }

View File

@ -1,10 +1,14 @@
// Header for the PCH test asm.c // Header for the PCH test asm.c
void f() { void f() {
int i; int i,cond;
asm ("foo\n" : : "a" (i + 2)); asm ("foo\n" : : "a" (i + 2));
asm ("foo\n" : [symbolic_name] "=a" (i) : "[symbolic_name]" (i)); asm ("foo\n" : [symbolic_name] "=a" (i) : "[symbolic_name]" (i));
asm volatile goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true, loop);
label_true:
loop:
return;
} }
void clobbers() { void clobbers() {

View File

@ -21,6 +21,56 @@ void f2() {
} }
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 zoo ()
{
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}}
asm goto ("decl %0;" :: "m"(x) : "memory" : );
// expected-error@+1 {{expected ':'}}
asm goto ("decl %0;" :: "m"(x) : "memory" );
// expected-error@+1 {{use of undeclared label 'x'}}
asm goto ("decl %0;" :: "m"(x) : "memory" :x);
// expected-error@+1 {{use of undeclared label 'b'}}
asm goto ("decl %0;" :: "m"(x) : "memory" :b);
// expected-error@+1 {{invalid operand number in inline asm string}}
asm goto ("testl %0, %0; jne %l3;" :: "r"(cond)::label_true, loop);
// expected-error@+1 {{unknown symbolic operand name in inline assembly string}}
asm goto ("decl %0; jnz %l[b]" :: "m"(x) : "memory" : a);
a:
label_true:
loop:
return 0;
}
// rdar://5952468 // rdar://5952468
__asm ; // expected-error {{expected '(' after 'asm'}} __asm ; // expected-error {{expected '(' after 'asm'}}

View File

@ -7,3 +7,54 @@ int foo4 asm (u"bar4"); // expected-error {{cannot use unicode string literal in
int foo5 asm (U"bar5"); // expected-error {{cannot use unicode string literal in 'asm'}} int foo5 asm (U"bar5"); // expected-error {{cannot use unicode string literal in 'asm'}}
int foo6 asm ("bar6"_x); // expected-error {{string literal with user-defined suffix cannot be used here}} int foo6 asm ("bar6"_x); // expected-error {{string literal with user-defined suffix cannot be used here}}
int foo6 asm ("" L"bar7"); // expected-error {{cannot use wide string literal in 'asm'}} int foo6 asm ("" L"bar7"); // expected-error {{cannot use wide string literal in 'asm'}}
int zoo ()
{
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}}
asm goto ("decl %0;" :: "m"(x) : "memory" : );
// expected-error@+1 {{expected ':'}}
asm goto ("decl %0;" :: "m"(x) : "memory" );
// expected-error@+1 {{use of undeclared label 'x'}}
asm goto ("decl %0;" :: "m"(x) : "memory" :x);
// expected-error@+1 {{use of undeclared label 'b'}}
asm goto ("decl %0;" :: "m"(x) : "memory" :b);
// expected-error@+1 {{invalid operand number in inline asm string}}
asm goto ("testl %0, %0; jne %l3;" :: "r"(cond)::label_true, loop);
// expected-error@+1 {{unknown symbolic operand name in inline assembly string}}
asm goto ("decl %0; jnz %l[b]" :: "m"(x) : "memory" : a);
label_true:
loop:
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;
}

View File

@ -0,0 +1,45 @@
// RUN: %clang_cc1 %s -triple i386-pc-linux-gnu -verify -fsyntax-only
struct NonTrivial {
~NonTrivial();
int f(int);
private:
int k;
};
void JumpDiagnostics(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;
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;
// expected-note@+1 {{possible target of asm goto statement}}
Later:
return;
}
struct S { ~S(); };
void foo(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;
void *p = &&BAR;
// expected-error@+1 {{cannot jump from this asm goto statement to one of its possible targets}}
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;
goto *p;
return;
}
// expected-note@+2 {{possible target of asm goto statement}}
// expected-note@+1 {{possible target of indirect goto statement}}
BAR:
return;
}

View File

@ -295,3 +295,24 @@ int test17(int t0)
return r0 + r1; return r0 + r1;
} }
void test18()
{
// expected-error@+2 {{duplicate use of asm operand name "lab"}}
// expected-note@+1 {{asm operand name "lab" first referenced here}}
asm goto ("" : : : : lab, lab, lab2, lab);
// expected-error@+2 {{duplicate use of asm operand name "lab"}}
// expected-note@+1 {{asm operand name "lab" first referenced here}}
asm goto ("xorw %[lab], %[lab]; je %l[lab]" : : [lab] "i" (0) : : lab);
lab:;
lab2:;
int x,x1;
// expected-error@+2 {{duplicate use of asm operand name "lab"}}
// expected-note@+1 {{asm operand name "lab" first referenced here}}
asm ("" : [lab] "=r" (x),[lab] "+r" (x) : [lab1] "r" (x));
// expected-error@+2 {{duplicate use of asm operand name "lab"}}
// expected-note@+1 {{asm operand name "lab" first referenced here}}
asm ("" : [lab] "=r" (x1) : [lab] "r" (x));
// expected-error@+1 {{invalid operand number in inline asm string}}
asm ("jne %l0":::);
asm goto ("jne %l0"::::lab);
}

View File

@ -23,3 +23,13 @@ template <int N> void testc(int value)
asm("rol %1, %0" :"=r"(value): "I"(N + 1)); asm("rol %1, %0" :"=r"(value): "I"(N + 1));
} }
int foo() { testc<2>(10); } int foo() { testc<2>(10); }
// these should compile without error
template <int N> bool testd()
{
__asm goto ("" : : : : lab);
return true;
lab:
return false;
}
bool foox() { return testd<0> (); }

View File

@ -232,3 +232,19 @@ void test15(int n, void *pc) {
// rdar://9024687 // rdar://9024687
int test16(int [sizeof &&z]); // expected-error {{use of address-of-label extension outside of a function body}} int test16(int [sizeof &&z]); // expected-error {{use of address-of-label extension outside of a function body}}
//Asm goto:
int test16(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}}
asm volatile goto("testl %0, %0; jne %l1;" :: "r"(n)::label_true, loop);
// expected-note@+2 {{jump bypasses initialization of variable length array}}
// expected-note@+1 {{possible target of asm goto statement}}
return ({int a[n];label_true: 2;});
// expected-note@+1 {{jump bypasses initialization of variable length array}}
int b[n];
// expected-note@+1 {{possible target of asm goto statement}}
loop:
return 0;
}