From 5846cacafa3a592f1745aaa20297d744c40dc56c Mon Sep 17 00:00:00 2001 From: Jennifer Yu Date: Thu, 30 May 2019 01:05:46 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@362045 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Stmt.h | 51 +++++++- include/clang/Basic/DiagnosticParseKinds.td | 4 +- include/clang/Basic/DiagnosticSemaKinds.td | 10 +- include/clang/Sema/Sema.h | 1 + lib/AST/ASTImporter.cpp | 10 +- lib/AST/Stmt.cpp | 29 ++++- lib/AST/StmtPrinter.cpp | 20 +++- lib/AST/StmtProfile.cpp | 3 + lib/Analysis/CFG.cpp | 74 +++++++++--- lib/CodeGen/CGStmt.cpp | 126 +++++++++++++------- lib/Parse/ParseStmtAsm.cpp | 68 +++++++++-- lib/Sema/JumpDiagnostics.cpp | 114 +++++++++++------- lib/Sema/SemaStmtAsm.cpp | 48 ++++++-- lib/Sema/TreeTransform.h | 16 ++- lib/Serialization/ASTReaderStmt.cpp | 7 ++ lib/Serialization/ASTWriterStmt.cpp | 4 + test/Analysis/asm-goto.cpp | 52 ++++++++ test/CodeGen/asm-goto.c | 18 +++ test/CodeGen/asm.c | 12 ++ test/CodeGen/inline-asm-mixed-style.c | 10 +- test/Coverage/c-language-features.inc | 4 +- test/PCH/asm.h | 6 +- test/Parser/asm.c | 50 ++++++++ test/Parser/asm.cpp | 51 ++++++++ test/Sema/asm-goto.cpp | 45 +++++++ test/Sema/asm.c | 21 ++++ test/Sema/inline-asm-validate-tmpl.cpp | 10 ++ test/Sema/scope-check.c | 16 +++ 28 files changed, 732 insertions(+), 148 deletions(-) create mode 100644 test/Analysis/asm-goto.cpp create mode 100644 test/CodeGen/asm-goto.c create mode 100644 test/Sema/asm-goto.cpp diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 77b2173fcb..fe5d802688 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -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(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; + using const_labels_iterator = ConstCastIterator; + using labels_range = llvm::iterator_range; + using labels_const_range = llvm::iterator_range; + + 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); diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index fb281a5be8..15a5ecf177 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -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 { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7e03174b3d..e750b062f8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5064,12 +5064,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, 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< @@ -7497,6 +7497,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< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d7486ec1c2..d25d7decf3 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3971,6 +3971,7 @@ public: unsigned NumInputs, IdentifierInfo **Names, MultiExprArg Constraints, MultiExprArg Exprs, Expr *AsmString, MultiExprArg Clobbers, + unsigned NumLabels, SourceLocation RParenLoc); void FillInlineAsmIdentifierInfo(Expr *Res, diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 2e4c304b3d..1f1ec1d687 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -5592,12 +5592,17 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) { return InputOrErr.takeError(); } - SmallVector Exprs(S->getNumOutputs() + S->getNumInputs()); + SmallVector 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); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 4796ee87f7..e9a2c58f1a 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -444,6 +444,14 @@ void GCCAsmStmt::setInputExpr(unsigned i, Expr *E) { Exprs[i + NumOutputs] = E; } +AddrLabelExpr *GCCAsmStmt::getLabelExpr(unsigned i) const { + return cast(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]; @@ -497,6 +508,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; } @@ -614,8 +629,8 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&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; @@ -728,10 +743,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); diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 7fe0be5217..563095f89b 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -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; } diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index 93bdcac8b5..c5da5bfda9 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -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) { diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 1d83359341..b53bfcca37 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -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 CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { E = BackpatchBlocks.end(); I != E; ++I ) { CFGBlock *B = I->block; - const GotoStmt *G = cast(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(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(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(S)); + case Stmt::GCCAsmStmtClass: + return VisitGCCAsmStmt(cast(S), asc); + case Stmt::IfStmtClass: return VisitIfStmt(cast(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; diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index c617b198d7..5c24db7092 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -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 &ResultRegTypes, + CodeGenFunction &CGF, + std::vector &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(&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 Transfer; + llvm::BasicBlock *Fallthrough = nullptr; + bool IsGCCAsmGoto = false; + if (const auto *GS = dyn_cast(&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(&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 RegResults; - if (ResultRegTypes.size() == 1) { - RegResults.push_back(Result); + if (IsGCCAsmGoto) { + llvm::CallBrInst *Result = + Builder.CreateCallBr(IA, Fallthrough, Transfer, Args); + UpdateAsmCallInst(cast(*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(*Result), HasSideEffect, ReadOnly, + ReadNone, S, ResultRegTypes, *this, RegResults); } assert(RegResults.size() == ResultRegTypes.size()); diff --git a/lib/Parse/ParseStmtAsm.cpp b/lib/Parse/ParseStmtAsm.cpp index c63808a472..75f3ac396e 100644 --- a/lib/Parse/ParseStmtAsm.cpp +++ b/lib/Parse/ParseStmtAsm.cpp @@ -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 diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp index 2234d6ba9b..c8743df90e 100644 --- a/lib/Sema/JumpDiagnostics.cpp +++ b/lib/Sema/JumpDiagnostics.cpp @@ -65,8 +65,10 @@ class JumpScopeChecker { llvm::DenseMap LabelAndGotoScopes; SmallVector Jumps; - SmallVector IndirectJumps; + SmallVector IndirectJumps; + SmallVector AsmJumps; SmallVector IndirectJumpTargets; + SmallVector 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 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(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(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(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 GotoJumps = IsAsmGoto ? AsmJumps : IndirectJumps; + if (GotoJumps.empty()) + return; + SmallVector 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 JumpScope; + typedef std::pair JumpScope; SmallVector JumpScopes; { - llvm::DenseMap JumpScopesMap; - for (SmallVectorImpl::iterator - I = IndirectJumps.begin(), E = IndirectJumps.end(); I != E; ++I) { - IndirectGotoStmt *IG = *I; + llvm::DenseMap JumpScopesMap; + for (SmallVectorImpl::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::iterator - I = JumpScopesMap.begin(), E = JumpScopesMap.end(); I != E; ++I) + for (llvm::DenseMap::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 TargetScopes; - for (SmallVectorImpl::iterator - I = IndirectJumpTargets.begin(), E = IndirectJumpTargets.end(); + for (SmallVectorImpl::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(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 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(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); } } diff --git a/lib/Sema/SemaStmtAsm.cpp b/lib/Sema/SemaStmtAsm.cpp index 8c6012573c..ec8958c3c5 100644 --- a/lib/Sema/SemaStmtAsm.cpp +++ b/lib/Sema/SemaStmtAsm.cpp @@ -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 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 NamedOperand; + SmallVector 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::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; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 6620885f23..609a3cfcfc 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1373,10 +1373,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. @@ -7051,6 +7052,16 @@ TreeTransform::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; @@ -7064,7 +7075,8 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { S->isVolatile(), S->getNumOutputs(), S->getNumInputs(), Names.data(), Constraints, Exprs, AsmString.get(), - Clobbers, S->getRParenLoc()); + Clobbers, S->getNumLabels(), + S->getRParenLoc()); } template diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 4d879b46e1..52aa3d961d 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -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(Record.readSubStmt())); unsigned NumOutputs = S->getNumOutputs(); unsigned NumInputs = S->getNumInputs(); unsigned NumClobbers = S->getNumClobbers(); + unsigned NumLabels = S->getNumLabels(); // Outputs and inputs SmallVector Names; @@ -392,9 +394,14 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { for (unsigned I = 0; I != NumClobbers; ++I) Clobbers.push_back(cast_or_null(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); } diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index b0a35cf2f5..776aab6bf5 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -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; } diff --git a/test/Analysis/asm-goto.cpp b/test/Analysis/asm-goto.cpp new file mode 100644 index 0000000000..3d4babc57b --- /dev/null +++ b/test/Analysis/asm-goto.cpp @@ -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 diff --git a/test/CodeGen/asm-goto.c b/test/CodeGen/asm-goto.c new file mode 100644 index 0000000000..2c4a1a0c4d --- /dev/null +++ b/test/CodeGen/asm-goto.c @@ -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; +} diff --git a/test/CodeGen/asm.c b/test/CodeGen/asm.c index 038d346e99..7de79639bf 100644 --- a/test/CodeGen/asm.c +++ b/test/CodeGen/asm.c @@ -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; +} diff --git a/test/CodeGen/inline-asm-mixed-style.c b/test/CodeGen/inline-asm-mixed-style.c index 6b830d9fa7..a9e111cd5d 100644 --- a/test/CodeGen/inline-asm-mixed-style.c +++ b/test/CodeGen/inline-asm-mixed-style.c @@ -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 } diff --git a/test/Coverage/c-language-features.inc b/test/Coverage/c-language-features.inc index 356687907d..ea3b96f600 100644 --- a/test/Coverage/c-language-features.inc +++ b/test/Coverage/c-language-features.inc @@ -71,7 +71,9 @@ theif: } asm ("nop"); - + int cond; + asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::label_true); +label_true: return; } diff --git a/test/PCH/asm.h b/test/PCH/asm.h index a568058d58..5a7268eff6 100644 --- a/test/PCH/asm.h +++ b/test/PCH/asm.h @@ -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() { diff --git a/test/Parser/asm.c b/test/Parser/asm.c index 637f9d7ed4..489b545ebe 100644 --- a/test/Parser/asm.c +++ b/test/Parser/asm.c @@ -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 __asm ; // expected-error {{expected '(' after 'asm'}} diff --git a/test/Parser/asm.cpp b/test/Parser/asm.cpp index 9f64dfea47..9c4d62a255 100644 --- a/test/Parser/asm.cpp +++ b/test/Parser/asm.cpp @@ -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 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 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; +} diff --git a/test/Sema/asm-goto.cpp b/test/Sema/asm-goto.cpp new file mode 100644 index 0000000000..f61a8096b8 --- /dev/null +++ b/test/Sema/asm-goto.cpp @@ -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; +} diff --git a/test/Sema/asm.c b/test/Sema/asm.c index 67da197426..29a55c610d 100644 --- a/test/Sema/asm.c +++ b/test/Sema/asm.c @@ -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); +} diff --git a/test/Sema/inline-asm-validate-tmpl.cpp b/test/Sema/inline-asm-validate-tmpl.cpp index cf7eac3d83..9e234caa9c 100644 --- a/test/Sema/inline-asm-validate-tmpl.cpp +++ b/test/Sema/inline-asm-validate-tmpl.cpp @@ -23,3 +23,13 @@ template void testc(int value) asm("rol %1, %0" :"=r"(value): "I"(N + 1)); } int foo() { testc<2>(10); } + +// these should compile without error +template bool testd() +{ + __asm goto ("" : : : : lab); + return true; +lab: + return false; +} +bool foox() { return testd<0> (); } diff --git a/test/Sema/scope-check.c b/test/Sema/scope-check.c index fa37d10d07..0622450e2e 100644 --- a/test/Sema/scope-check.c +++ b/test/Sema/scope-check.c @@ -232,3 +232,19 @@ void test15(int n, void *pc) { // rdar://9024687 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; +} -- 2.40.0