mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-14 19:49:36 +00:00
Re-check in clang support gun asm goto after fixing tests.
llvm-svn: 362410
This commit is contained in:
parent
5099aef869
commit
b8fee677bf
@ -46,6 +46,7 @@ class Attr;
|
||||
class CapturedDecl;
|
||||
class Decl;
|
||||
class Expr;
|
||||
class AddrLabelExpr;
|
||||
class LabelDecl;
|
||||
class ODRHash;
|
||||
class PrinterHelper;
|
||||
@ -2816,13 +2817,15 @@ class GCCAsmStmt : public AsmStmt {
|
||||
StringLiteral **Constraints = nullptr;
|
||||
StringLiteral **Clobbers = nullptr;
|
||||
IdentifierInfo **Names = nullptr;
|
||||
unsigned NumLabels = 0;
|
||||
|
||||
public:
|
||||
GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple,
|
||||
bool isvolatile, unsigned numoutputs, unsigned numinputs,
|
||||
IdentifierInfo **names, StringLiteral **constraints, Expr **exprs,
|
||||
StringLiteral *asmstr, unsigned numclobbers,
|
||||
StringLiteral **clobbers, SourceLocation rparenloc);
|
||||
StringLiteral **clobbers, unsigned numlabels,
|
||||
SourceLocation rparenloc);
|
||||
|
||||
/// Build an empty inline-assembly statement.
|
||||
explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {}
|
||||
@ -2947,6 +2950,51 @@ public:
|
||||
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:
|
||||
void setOutputsAndInputsAndClobbers(const ASTContext &C,
|
||||
IdentifierInfo **Names,
|
||||
@ -2954,6 +3002,7 @@ private:
|
||||
Stmt **Exprs,
|
||||
unsigned NumOutputs,
|
||||
unsigned NumInputs,
|
||||
unsigned NumLabels,
|
||||
StringLiteral **Clobbers,
|
||||
unsigned NumClobbers);
|
||||
|
||||
|
@ -27,8 +27,8 @@ def err_msasm_unable_to_create_target : Error<
|
||||
"MS-style inline assembly is not available: %0">;
|
||||
def err_gnu_inline_asm_disabled : Error<
|
||||
"GNU-style inline assembly is disabled">;
|
||||
def err_asm_goto_not_supported_yet : Error<
|
||||
"'asm goto' constructs are not supported yet">;
|
||||
def err_asm_goto_cannot_have_output : Error<
|
||||
"'asm goto' cannot have output constraints">;
|
||||
}
|
||||
|
||||
let CategoryName = "Parse Issue" in {
|
||||
|
@ -5073,12 +5073,12 @@ def warn_cxx98_compat_switch_into_protected_scope : Warning<
|
||||
def err_indirect_goto_without_addrlabel : Error<
|
||||
"indirect goto in function with no address-of-label expressions">;
|
||||
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<
|
||||
"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;
|
||||
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<
|
||||
"jump bypasses variable initialization">;
|
||||
def note_protected_by_variable_nontriv_destructor : Note<
|
||||
@ -7506,6 +7506,10 @@ let CategoryName = "Inline Assembly Issue" in {
|
||||
"use constraint modifier \"%0\"">;
|
||||
def note_asm_input_duplicate_first : Note<
|
||||
"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<
|
||||
|
@ -3985,6 +3985,7 @@ public:
|
||||
unsigned NumInputs, IdentifierInfo **Names,
|
||||
MultiExprArg Constraints, MultiExprArg Exprs,
|
||||
Expr *AsmString, MultiExprArg Clobbers,
|
||||
unsigned NumLabels,
|
||||
SourceLocation RParenLoc);
|
||||
|
||||
void FillInlineAsmIdentifierInfo(Expr *Res,
|
||||
|
@ -5592,12 +5592,17 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
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))
|
||||
return std::move(Err);
|
||||
|
||||
if (Error Err =
|
||||
ImportArrayChecked(S->inputs(), Exprs.begin() + S->getNumOutputs()))
|
||||
return std::move(Err);
|
||||
|
||||
if (Error Err = ImportArrayChecked(
|
||||
S->inputs(), Exprs.begin() + S->getNumOutputs()))
|
||||
S->labels(), Exprs.begin() + S->getNumOutputs() + S->getNumInputs()))
|
||||
return std::move(Err);
|
||||
|
||||
ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc());
|
||||
@ -5623,6 +5628,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
*AsmStrOrErr,
|
||||
S->getNumClobbers(),
|
||||
Clobbers.data(),
|
||||
S->getNumLabels(),
|
||||
*RParenLocOrErr);
|
||||
}
|
||||
|
||||
|
@ -444,6 +444,14 @@ void GCCAsmStmt::setInputExpr(unsigned i, Expr *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
|
||||
/// constraints, these can be empty.
|
||||
StringRef GCCAsmStmt::getInputConstraint(unsigned i) const {
|
||||
@ -456,13 +464,16 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C,
|
||||
Stmt **Exprs,
|
||||
unsigned NumOutputs,
|
||||
unsigned NumInputs,
|
||||
unsigned NumLabels,
|
||||
StringLiteral **Clobbers,
|
||||
unsigned NumClobbers) {
|
||||
this->NumOutputs = NumOutputs;
|
||||
this->NumInputs = NumInputs;
|
||||
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);
|
||||
this->Names = new (C) IdentifierInfo*[NumExprs];
|
||||
@ -498,6 +509,10 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const {
|
||||
if (getInputName(i) == SymbolicName)
|
||||
return getNumOutputs() + NumPlusOperands + i;
|
||||
|
||||
for (unsigned i = 0, e = getNumLabels(); i != e; ++i)
|
||||
if (getLabelName(i) == SymbolicName)
|
||||
return i + getNumInputs();
|
||||
|
||||
// Not found.
|
||||
return -1;
|
||||
}
|
||||
@ -615,8 +630,8 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
|
||||
while (CurPtr != StrEnd && isDigit(*CurPtr))
|
||||
N = N*10 + ((*CurPtr++)-'0');
|
||||
|
||||
unsigned NumOperands =
|
||||
getNumOutputs() + getNumPlusOperands() + getNumInputs();
|
||||
unsigned NumOperands = getNumOutputs() + getNumPlusOperands() +
|
||||
getNumInputs() + getNumLabels();
|
||||
if (N >= NumOperands) {
|
||||
DiagOffs = CurPtr-StrStart-1;
|
||||
return diag::err_asm_invalid_operand_number;
|
||||
@ -729,10 +744,12 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc,
|
||||
unsigned numinputs, IdentifierInfo **names,
|
||||
StringLiteral **constraints, Expr **exprs,
|
||||
StringLiteral *asmstr, unsigned numclobbers,
|
||||
StringLiteral **clobbers, SourceLocation rparenloc)
|
||||
StringLiteral **clobbers, unsigned numlabels,
|
||||
SourceLocation rparenloc)
|
||||
: AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs,
|
||||
numinputs, numclobbers), RParenLoc(rparenloc), AsmStr(asmstr) {
|
||||
unsigned NumExprs = NumOutputs + NumInputs;
|
||||
numinputs, numclobbers),
|
||||
RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
|
||||
unsigned NumExprs = NumOutputs + NumInputs + NumLabels;
|
||||
|
||||
Names = new (C) IdentifierInfo*[NumExprs];
|
||||
std::copy(names, names + NumExprs, Names);
|
||||
|
@ -414,12 +414,15 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
|
||||
if (Node->isVolatile())
|
||||
OS << "volatile ";
|
||||
|
||||
if (Node->isAsmGoto())
|
||||
OS << "goto ";
|
||||
|
||||
OS << "(";
|
||||
VisitStringLiteral(Node->getAsmString());
|
||||
|
||||
// Outputs
|
||||
if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 ||
|
||||
Node->getNumClobbers() != 0)
|
||||
Node->getNumClobbers() != 0 || Node->getNumLabels() != 0)
|
||||
OS << " : ";
|
||||
|
||||
for (unsigned i = 0, e = Node->getNumOutputs(); i != e; ++i) {
|
||||
@ -439,7 +442,8 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
|
||||
}
|
||||
|
||||
// Inputs
|
||||
if (Node->getNumInputs() != 0 || Node->getNumClobbers() != 0)
|
||||
if (Node->getNumInputs() != 0 || Node->getNumClobbers() != 0 ||
|
||||
Node->getNumLabels() != 0)
|
||||
OS << " : ";
|
||||
|
||||
for (unsigned i = 0, e = Node->getNumInputs(); i != e; ++i) {
|
||||
@ -459,7 +463,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
|
||||
}
|
||||
|
||||
// Clobbers
|
||||
if (Node->getNumClobbers() != 0)
|
||||
if (Node->getNumClobbers() != 0 || Node->getNumLabels())
|
||||
OS << " : ";
|
||||
|
||||
for (unsigned i = 0, e = Node->getNumClobbers(); i != e; ++i) {
|
||||
@ -469,6 +473,16 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
|
||||
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 << ");";
|
||||
if (Policy.IncludeNewlines) OS << NL;
|
||||
}
|
||||
|
@ -321,6 +321,9 @@ void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) {
|
||||
ID.AddInteger(S->getNumClobbers());
|
||||
for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
|
||||
VisitStringLiteral(S->getClobberStringLiteral(I));
|
||||
ID.AddInteger(S->getNumLabels());
|
||||
for (auto *L : S->labels())
|
||||
VisitDecl(L->getLabel());
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) {
|
||||
|
@ -549,6 +549,7 @@ private:
|
||||
CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
|
||||
CFGBlock *VisitForStmt(ForStmt *F);
|
||||
CFGBlock *VisitGotoStmt(GotoStmt *G);
|
||||
CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc);
|
||||
CFGBlock *VisitIfStmt(IfStmt *I);
|
||||
CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *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 ) {
|
||||
|
||||
CFGBlock *B = I->block;
|
||||
const GotoStmt *G = cast<GotoStmt>(B->getTerminator());
|
||||
LabelMapTy::iterator LI = LabelMap.find(G->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;
|
||||
prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
|
||||
JT.scopePosition);
|
||||
prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
|
||||
JT.scopePosition);
|
||||
const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator(
|
||||
B, I->scopePosition, JT.scopePosition);
|
||||
appendScopeBegin(JT.block, VD, G);
|
||||
addSuccessor(B, JT.block);
|
||||
if (auto *G = dyn_cast<GotoStmt>(B->getTerminator())) {
|
||||
LabelMapTy::iterator LI = LabelMap.find(G->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;
|
||||
prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
|
||||
JT.scopePosition);
|
||||
prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
|
||||
JT.scopePosition);
|
||||
const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator(
|
||||
B, I->scopePosition, JT.scopePosition);
|
||||
appendScopeBegin(JT.block, VD, G);
|
||||
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).
|
||||
@ -2142,6 +2159,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
|
||||
case Stmt::GotoStmtClass:
|
||||
return VisitGotoStmt(cast<GotoStmt>(S));
|
||||
|
||||
case Stmt::GCCAsmStmtClass:
|
||||
return VisitGCCAsmStmt(cast<GCCAsmStmt>(S), asc);
|
||||
|
||||
case Stmt::IfStmtClass:
|
||||
return VisitIfStmt(cast<IfStmt>(S));
|
||||
|
||||
@ -3146,6 +3166,28 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {
|
||||
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 *LoopSuccessor = nullptr;
|
||||
|
||||
|
@ -1896,6 +1896,55 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str,
|
||||
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) {
|
||||
// Assemble the final asm string.
|
||||
std::string AsmString = S.generateAsmString(getContext());
|
||||
@ -2138,6 +2187,29 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
|
||||
}
|
||||
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
|
||||
for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) {
|
||||
StringRef Clobber = S.getClobber(i);
|
||||
@ -2180,52 +2252,18 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
|
||||
llvm::InlineAsm *IA =
|
||||
llvm::InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect,
|
||||
/* 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;
|
||||
if (ResultRegTypes.size() == 1) {
|
||||
RegResults.push_back(Result);
|
||||
if (IsGCCAsmGoto) {
|
||||
llvm::CallBrInst *Result =
|
||||
Builder.CreateCallBr(IA, Fallthrough, Transfer, Args);
|
||||
UpdateAsmCallInst(cast<llvm::CallBase>(*Result), HasSideEffect, ReadOnly,
|
||||
ReadNone, S, ResultRegTypes, *this, RegResults);
|
||||
EmitBlock(Fallthrough);
|
||||
} else {
|
||||
for (unsigned i = 0, e = ResultRegTypes.size(); i != e; ++i) {
|
||||
llvm::Value *Tmp = Builder.CreateExtractValue(Result, i, "asmresult");
|
||||
RegResults.push_back(Tmp);
|
||||
}
|
||||
llvm::CallInst *Result =
|
||||
Builder.CreateCall(IA, Args, getBundlesForFunclet(IA));
|
||||
UpdateAsmCallInst(cast<llvm::CallBase>(*Result), HasSideEffect, ReadOnly,
|
||||
ReadNone, S, ResultRegTypes, *this, RegResults);
|
||||
}
|
||||
|
||||
assert(RegResults.size() == ResultRegTypes.size());
|
||||
|
@ -710,12 +710,12 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
|
||||
|
||||
// Remember if this was a volatile asm.
|
||||
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)) {
|
||||
Diag(Tok, diag::err_asm_goto_not_supported_yet);
|
||||
SkipUntil(tok::r_paren, StopAtSemi);
|
||||
return StmtError();
|
||||
isGotoAsm = true;
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::l_paren)) {
|
||||
@ -753,7 +753,8 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
|
||||
return Actions.ActOnGCCAsmStmt(AsmLoc, /*isSimple*/ true, isVolatile,
|
||||
/*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr,
|
||||
Constraints, Exprs, AsmString.get(),
|
||||
Clobbers, T.getCloseLocation());
|
||||
Clobbers, /*NumLabels*/ 0,
|
||||
T.getCloseLocation());
|
||||
}
|
||||
|
||||
// Parse Outputs, if present.
|
||||
@ -763,6 +764,12 @@ 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();
|
||||
}
|
||||
@ -789,12 +796,15 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
|
||||
unsigned NumInputs = Names.size() - NumOutputs;
|
||||
|
||||
// Parse the clobbers, if present.
|
||||
if (AteExtraColon || Tok.is(tok::colon)) {
|
||||
if (!AteExtraColon)
|
||||
if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
|
||||
if (AteExtraColon)
|
||||
AteExtraColon = false;
|
||||
else {
|
||||
AteExtraColon = Tok.is(tok::coloncolon);
|
||||
ConsumeToken();
|
||||
|
||||
}
|
||||
// Parse the asm-string list for clobbers if present.
|
||||
if (Tok.isNot(tok::r_paren)) {
|
||||
if (!AteExtraColon && isTokenStringLiteral()) {
|
||||
while (1) {
|
||||
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();
|
||||
return Actions.ActOnGCCAsmStmt(
|
||||
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
|
||||
|
@ -65,8 +65,10 @@ class JumpScopeChecker {
|
||||
llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes;
|
||||
SmallVector<Stmt*, 16> Jumps;
|
||||
|
||||
SmallVector<IndirectGotoStmt*, 4> IndirectJumps;
|
||||
SmallVector<Stmt*, 4> IndirectJumps;
|
||||
SmallVector<Stmt*, 4> AsmJumps;
|
||||
SmallVector<LabelDecl*, 4> IndirectJumpTargets;
|
||||
SmallVector<LabelDecl*, 4> AsmJumpTargets;
|
||||
public:
|
||||
JumpScopeChecker(Stmt *Body, Sema &S);
|
||||
private:
|
||||
@ -76,10 +78,10 @@ private:
|
||||
void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
|
||||
|
||||
void VerifyJumps();
|
||||
void VerifyIndirectJumps();
|
||||
void VerifyIndirectOrAsmJumps(bool IsAsmGoto);
|
||||
void NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes);
|
||||
void DiagnoseIndirectJump(IndirectGotoStmt *IG, unsigned IGScope,
|
||||
LabelDecl *Target, unsigned TargetScope);
|
||||
void DiagnoseIndirectOrAsmJump(Stmt *IG, unsigned IGScope, LabelDecl *Target,
|
||||
unsigned TargetScope);
|
||||
void CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc,
|
||||
unsigned JumpDiag, unsigned JumpDiagWarning,
|
||||
unsigned JumpDiagCXX98Compat);
|
||||
@ -103,7 +105,8 @@ JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s)
|
||||
|
||||
// Check that all jumps we saw are kosher.
|
||||
VerifyJumps();
|
||||
VerifyIndirectJumps();
|
||||
VerifyIndirectOrAsmJumps(false);
|
||||
VerifyIndirectOrAsmJumps(true);
|
||||
}
|
||||
|
||||
/// GetDeepestCommonScope - Finds the innermost scope enclosing the
|
||||
@ -316,7 +319,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
|
||||
}
|
||||
|
||||
LabelAndGotoScopes[S] = ParentScope;
|
||||
IndirectJumps.push_back(cast<IndirectGotoStmt>(S));
|
||||
IndirectJumps.push_back(S);
|
||||
break;
|
||||
|
||||
case Stmt::SwitchStmtClass:
|
||||
@ -339,6 +342,18 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
|
||||
Jumps.push_back(S);
|
||||
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: {
|
||||
IfStmt *IS = cast<IfStmt>(S);
|
||||
if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck()))
|
||||
@ -629,14 +644,13 @@ void JumpScopeChecker::VerifyJumps() {
|
||||
}
|
||||
}
|
||||
|
||||
/// VerifyIndirectJumps - Verify whether any possible indirect jump
|
||||
/// might cross a protection boundary. Unlike direct jumps, indirect
|
||||
/// jumps count cleanups as protection boundaries: since there's no
|
||||
/// way to know where the jump is going, we can't implicitly run the
|
||||
/// right cleanups the way we can with direct jumps.
|
||||
///
|
||||
/// Thus, an indirect jump is "trivial" if it bypasses no
|
||||
/// initializations and no teardowns. More formally, an indirect jump
|
||||
/// VerifyIndirectOrAsmJumps - Verify whether any possible indirect goto or
|
||||
/// asm goto jump might cross a protection boundary. Unlike direct jumps,
|
||||
/// indirect or asm goto jumps count cleanups as protection boundaries:
|
||||
/// since there's no way to know where the jump is going, we can't implicitly
|
||||
/// run the right cleanups the way we can with direct jumps.
|
||||
/// Thus, an indirect/asm jump is "trivial" if it bypasses no
|
||||
/// initializations and no teardowns. More formally, an indirect/asm jump
|
||||
/// 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
|
||||
/// 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
|
||||
/// jump between A and B is trivial for every indirect goto statement A
|
||||
/// and every label B whose address was taken in the function.
|
||||
void JumpScopeChecker::VerifyIndirectJumps() {
|
||||
if (IndirectJumps.empty()) return;
|
||||
|
||||
void JumpScopeChecker::VerifyIndirectOrAsmJumps(bool IsAsmGoto) {
|
||||
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,
|
||||
// complain about the first indirect goto.
|
||||
if (IndirectJumpTargets.empty()) {
|
||||
S.Diag(IndirectJumps[0]->getGotoLoc(),
|
||||
if (JumpTargets.empty()) {
|
||||
assert(!IsAsmGoto &&"only indirect goto can get here");
|
||||
S.Diag(GotoJumps[0]->getBeginLoc(),
|
||||
diag::err_indirect_goto_without_addrlabel);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
typedef std::pair<unsigned, IndirectGotoStmt*> JumpScope;
|
||||
typedef std::pair<unsigned, Stmt*> JumpScope;
|
||||
SmallVector<JumpScope, 32> JumpScopes;
|
||||
{
|
||||
llvm::DenseMap<unsigned, IndirectGotoStmt*> JumpScopesMap;
|
||||
for (SmallVectorImpl<IndirectGotoStmt*>::iterator
|
||||
I = IndirectJumps.begin(), E = IndirectJumps.end(); I != E; ++I) {
|
||||
IndirectGotoStmt *IG = *I;
|
||||
llvm::DenseMap<unsigned, Stmt*> JumpScopesMap;
|
||||
for (SmallVectorImpl<Stmt *>::iterator I = GotoJumps.begin(),
|
||||
E = GotoJumps.end();
|
||||
I != E; ++I) {
|
||||
Stmt *IG = *I;
|
||||
if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG)))
|
||||
continue;
|
||||
unsigned IGScope = LabelAndGotoScopes[IG];
|
||||
IndirectGotoStmt *&Entry = JumpScopesMap[IGScope];
|
||||
Stmt *&Entry = JumpScopesMap[IGScope];
|
||||
if (!Entry) Entry = IG;
|
||||
}
|
||||
JumpScopes.reserve(JumpScopesMap.size());
|
||||
for (llvm::DenseMap<unsigned, IndirectGotoStmt*>::iterator
|
||||
I = JumpScopesMap.begin(), E = JumpScopesMap.end(); I != E; ++I)
|
||||
for (llvm::DenseMap<unsigned, Stmt *>::iterator I = JumpScopesMap.begin(),
|
||||
E = JumpScopesMap.end();
|
||||
I != E; ++I)
|
||||
JumpScopes.push_back(*I);
|
||||
}
|
||||
|
||||
@ -685,8 +704,8 @@ void JumpScopeChecker::VerifyIndirectJumps() {
|
||||
// label whose address was taken somewhere in the function.
|
||||
// For most code bases, there will be only one such scope.
|
||||
llvm::DenseMap<unsigned, LabelDecl*> TargetScopes;
|
||||
for (SmallVectorImpl<LabelDecl*>::iterator
|
||||
I = IndirectJumpTargets.begin(), E = IndirectJumpTargets.end();
|
||||
for (SmallVectorImpl<LabelDecl *>::iterator I = JumpTargets.begin(),
|
||||
E = JumpTargets.end();
|
||||
I != E; ++I) {
|
||||
LabelDecl *TheLabel = *I;
|
||||
if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(TheLabel->getStmt())))
|
||||
@ -763,7 +782,7 @@ void JumpScopeChecker::VerifyIndirectJumps() {
|
||||
// Only diagnose if we didn't find something.
|
||||
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.
|
||||
static void DiagnoseIndirectJumpStmt(Sema &S, IndirectGotoStmt *Jump,
|
||||
LabelDecl *Target, bool &Diagnosed) {
|
||||
static void DiagnoseIndirectOrAsmJumpStmt(Sema &S, Stmt *Jump,
|
||||
LabelDecl *Target, bool &Diagnosed) {
|
||||
if (Diagnosed)
|
||||
return;
|
||||
S.Diag(Jump->getGotoLoc(), diag::err_indirect_goto_in_protected_scope);
|
||||
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target);
|
||||
bool IsAsmGoto = isa<GCCAsmStmt>(Jump);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -803,10 +825,9 @@ void JumpScopeChecker::NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes) {
|
||||
}
|
||||
|
||||
/// Diagnose an indirect jump which is known to cross scopes.
|
||||
void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump,
|
||||
unsigned JumpScope,
|
||||
LabelDecl *Target,
|
||||
unsigned TargetScope) {
|
||||
void JumpScopeChecker::DiagnoseIndirectOrAsmJump(Stmt *Jump, unsigned JumpScope,
|
||||
LabelDecl *Target,
|
||||
unsigned TargetScope) {
|
||||
if (CHECK_PERMISSIVE(JumpScope == TargetScope))
|
||||
return;
|
||||
|
||||
@ -816,7 +837,7 @@ void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump,
|
||||
// Walk out the scope chain until we reach the common ancestor.
|
||||
for (unsigned I = JumpScope; I != Common; I = Scopes[I].ParentScope)
|
||||
if (Scopes[I].OutDiag) {
|
||||
DiagnoseIndirectJumpStmt(S, Jump, Target, Diagnosed);
|
||||
DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);
|
||||
S.Diag(Scopes[I].Loc, Scopes[I].OutDiag);
|
||||
}
|
||||
|
||||
@ -827,15 +848,18 @@ void JumpScopeChecker::DiagnoseIndirectJump(IndirectGotoStmt *Jump,
|
||||
if (IsCXX98CompatWarning(S, Scopes[I].InDiag))
|
||||
ToScopesCXX98Compat.push_back(I);
|
||||
else if (Scopes[I].InDiag) {
|
||||
DiagnoseIndirectJumpStmt(S, Jump, Target, Diagnosed);
|
||||
DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);
|
||||
S.Diag(Scopes[I].Loc, Scopes[I].InDiag);
|
||||
}
|
||||
|
||||
// Diagnose this jump if it would be ill-formed in C++98.
|
||||
if (!Diagnosed && !ToScopesCXX98Compat.empty()) {
|
||||
S.Diag(Jump->getGotoLoc(),
|
||||
diag::warn_cxx98_compat_indirect_goto_in_protected_scope);
|
||||
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target);
|
||||
bool IsAsmGoto = isa<GCCAsmStmt>(Jump);
|
||||
S.Diag(Jump->getBeginLoc(),
|
||||
diag::warn_cxx98_compat_indirect_goto_in_protected_scope)
|
||||
<< IsAsmGoto;
|
||||
S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target)
|
||||
<< IsAsmGoto;
|
||||
NoteJumpIntoScopes(ToScopesCXX98Compat);
|
||||
}
|
||||
}
|
||||
|
@ -209,11 +209,12 @@ static StringRef extractRegisterName(const Expr *Expression,
|
||||
static SourceLocation
|
||||
getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints,
|
||||
StringLiteral **Clobbers, int NumClobbers,
|
||||
unsigned NumLabels,
|
||||
const TargetInfo &Target, ASTContext &Cont) {
|
||||
llvm::StringSet<> InOutVars;
|
||||
// Collect all the input and output registers from the extended asm
|
||||
// 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 InOutReg = Target.getConstraintRegister(
|
||||
Constraint, extractRegisterName(Exprs[i], Target));
|
||||
@ -241,6 +242,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
||||
unsigned NumInputs, IdentifierInfo **Names,
|
||||
MultiExprArg constraints, MultiExprArg Exprs,
|
||||
Expr *asmString, MultiExprArg clobbers,
|
||||
unsigned NumLabels,
|
||||
SourceLocation RParenLoc) {
|
||||
unsigned NumClobbers = clobbers.size();
|
||||
StringLiteral **Constraints =
|
||||
@ -269,7 +271,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
||||
return new (Context)
|
||||
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
NumInputs, Names, Constraints, Exprs.data(), AsmString,
|
||||
NumClobbers, Clobbers, RParenLoc);
|
||||
NumClobbers, Clobbers, NumLabels, RParenLoc);
|
||||
}
|
||||
|
||||
ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
|
||||
@ -330,7 +332,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
||||
return new (Context)
|
||||
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
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)
|
||||
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
NumInputs, Names, Constraints, Exprs.data(), AsmString,
|
||||
NumClobbers, Clobbers, RParenLoc);
|
||||
NumClobbers, Clobbers, NumLabels, RParenLoc);
|
||||
}
|
||||
|
||||
ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
|
||||
@ -451,14 +453,15 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
||||
return new (Context)
|
||||
GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
NumInputs, Names, Constraints, Exprs.data(), AsmString,
|
||||
NumClobbers, Clobbers, RParenLoc);
|
||||
NumClobbers, Clobbers, NumLabels, RParenLoc);
|
||||
}
|
||||
}
|
||||
|
||||
GCCAsmStmt *NS =
|
||||
new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
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
|
||||
// have.
|
||||
SmallVector<GCCAsmStmt::AsmStringPiece, 8> Pieces;
|
||||
@ -476,8 +479,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();
|
||||
|
||||
// Look for the (ConstraintIdx - NumOperands + 1)th constraint with
|
||||
// modifier '+'.
|
||||
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
|
||||
SourceLocation ConstraintLoc =
|
||||
getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers,
|
||||
NumLabels,
|
||||
Context.getTargetInfo(), Context);
|
||||
if (ConstraintLoc.isValid())
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1381,10 +1381,11 @@ public:
|
||||
unsigned NumInputs, IdentifierInfo **Names,
|
||||
MultiExprArg Constraints, MultiExprArg Exprs,
|
||||
Expr *AsmString, MultiExprArg Clobbers,
|
||||
unsigned NumLabels,
|
||||
SourceLocation RParenLoc) {
|
||||
return getSema().ActOnGCCAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs,
|
||||
NumInputs, Names, Constraints, Exprs,
|
||||
AsmString, Clobbers, RParenLoc);
|
||||
AsmString, Clobbers, NumLabels, RParenLoc);
|
||||
}
|
||||
|
||||
/// Build a new MS style inline asm statement.
|
||||
@ -7059,6 +7060,16 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
|
||||
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)
|
||||
return S;
|
||||
|
||||
@ -7072,7 +7083,8 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
|
||||
S->isVolatile(), S->getNumOutputs(),
|
||||
S->getNumInputs(), Names.data(),
|
||||
Constraints, Exprs, AsmString.get(),
|
||||
Clobbers, S->getRParenLoc());
|
||||
Clobbers, S->getNumLabels(),
|
||||
S->getRParenLoc());
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
|
@ -370,12 +370,14 @@ void ASTStmtReader::VisitAsmStmt(AsmStmt *S) {
|
||||
|
||||
void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
VisitAsmStmt(S);
|
||||
S->NumLabels = Record.readInt();
|
||||
S->setRParenLoc(ReadSourceLocation());
|
||||
S->setAsmString(cast_or_null<StringLiteral>(Record.readSubStmt()));
|
||||
|
||||
unsigned NumOutputs = S->getNumOutputs();
|
||||
unsigned NumInputs = S->getNumInputs();
|
||||
unsigned NumClobbers = S->getNumClobbers();
|
||||
unsigned NumLabels = S->getNumLabels();
|
||||
|
||||
// Outputs and inputs
|
||||
SmallVector<IdentifierInfo *, 16> Names;
|
||||
@ -392,9 +394,14 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
for (unsigned I = 0; I != NumClobbers; ++I)
|
||||
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(),
|
||||
Names.data(), Constraints.data(),
|
||||
Exprs.data(), NumOutputs, NumInputs,
|
||||
NumLabels,
|
||||
Clobbers.data(), NumClobbers);
|
||||
}
|
||||
|
||||
|
@ -283,6 +283,7 @@ void ASTStmtWriter::VisitAsmStmt(AsmStmt *S) {
|
||||
|
||||
void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
VisitAsmStmt(S);
|
||||
Record.push_back(S->getNumLabels());
|
||||
Record.AddSourceLocation(S->getRParenLoc());
|
||||
Record.AddStmt(S->getAsmString());
|
||||
|
||||
@ -304,6 +305,9 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) {
|
||||
for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
|
||||
Record.AddStmt(S->getClobberStringLiteral(I));
|
||||
|
||||
// Labels
|
||||
for (auto *E : S->labels()) Record.AddStmt(E);
|
||||
|
||||
Code = serialization::STMT_GCCASM;
|
||||
}
|
||||
|
||||
|
53
clang/test/Analysis/asm-goto.cpp
Normal file
53
clang/test/Analysis/asm-goto.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// RUN: %clang_analyze_cc1 -triple i386-pc-linux-gnu -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s
|
||||
// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -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
|
20
clang/test/CodeGen/asm-goto.c
Normal file
20
clang/test/CodeGen/asm-goto.c
Normal file
@ -0,0 +1,20 @@
|
||||
// REQUIRES: x86-registered-target
|
||||
// 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)
|
||||
{
|
||||
// CHECK: callbr void asm sideeffect
|
||||
// CHECK: to label %asm.fallthrough [label %label_true, label %loop], !srcloc
|
||||
// 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
|
||||
// CHECK: asm.fallthrough1:
|
||||
return 0;
|
||||
loop:
|
||||
return 0;
|
||||
label_true:
|
||||
return 1;
|
||||
}
|
@ -262,3 +262,15 @@ void t31(int len) {
|
||||
// CHECK: @t31
|
||||
// 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;
|
||||
}
|
||||
|
@ -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
|
||||
// REQUIRES: x86-registered-target
|
||||
|
||||
@ -20,10 +19,11 @@ void f() {
|
||||
// CHECK: movl %ebx, %eax
|
||||
// CHECK: movl %ecx, %edx
|
||||
|
||||
#ifdef CHECK_ASM_GOTO
|
||||
__asm volatile goto ("movl %ecx, %edx"); // expected-error {{'asm goto' constructs are not supported yet}}
|
||||
__asm volatile goto ("movl %ecx, %edx");
|
||||
// CHECK: movl %ecx, %edx
|
||||
|
||||
__asm mov eax, ebx
|
||||
__asm goto ("movl %ecx, %edx"); // expected-error {{'asm goto' constructs are not supported yet}}
|
||||
#endif
|
||||
__asm goto ("movl %ecx, %edx");
|
||||
// CHECK: movl %ebx, %eax
|
||||
// CHECK: movl %ecx, %edx
|
||||
}
|
||||
|
@ -71,7 +71,9 @@ theif:
|
||||
}
|
||||
|
||||
asm ("nop");
|
||||
|
||||
int cond;
|
||||
asm goto("" ::::label_true);
|
||||
label_true:
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,14 @@
|
||||
// Header for the PCH test asm.c
|
||||
|
||||
void f() {
|
||||
int i;
|
||||
int i,cond;
|
||||
|
||||
asm ("foo\n" : : "a" (i + 2));
|
||||
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() {
|
||||
|
57
clang/test/Parser/asm-goto.c
Normal file
57
clang/test/Parser/asm-goto.c
Normal file
@ -0,0 +1,57 @@
|
||||
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify %s
|
||||
|
||||
#if !__has_extension(gnu_asm)
|
||||
#error Extension 'gnu_asm' should be available by default
|
||||
#endif
|
||||
|
||||
|
||||
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;
|
||||
}
|
53
clang/test/Parser/asm-goto.cpp
Normal file
53
clang/test/Parser/asm-goto.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// 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 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;
|
||||
}
|
63
clang/test/Sema/asm-goto.cpp
Normal file
63
clang/test/Sema/asm-goto.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
// 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();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//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;
|
||||
}
|
@ -295,3 +295,24 @@ int test17(int t0)
|
||||
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);
|
||||
}
|
||||
|
@ -23,3 +23,13 @@ template <int N> void testc(int value)
|
||||
asm("rol %1, %0" :"=r"(value): "I"(N + 1));
|
||||
}
|
||||
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> (); }
|
||||
|
Loading…
Reference in New Issue
Block a user