Skip to content

Commit

Permalink
clang support gnu asm goto.
Browse files Browse the repository at this point in the history
Syntax:
  asm [volatile] goto ( AssemblerTemplate
                      :
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

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

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

asm.fallthrough:                                

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


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

llvm-svn: 362045
  • Loading branch information
jyu2-git committed May 30, 2019
1 parent 192dd7d commit 954ec09
Show file tree
Hide file tree
Showing 28 changed files with 732 additions and 148 deletions.
51 changes: 50 additions & 1 deletion clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Attr;
class CapturedDecl;
class Decl;
class Expr;
class AddrLabelExpr;
class LabelDecl;
class ODRHash;
class PrinterHelper;
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -2947,13 +2950,59 @@ class GCCAsmStmt : public AsmStmt {
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,
StringLiteral **Constraints,
Stmt **Exprs,
unsigned NumOutputs,
unsigned NumInputs,
unsigned NumLabels,
StringLiteral **Clobbers,
unsigned NumClobbers);

Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<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<
Expand Down Expand Up @@ -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<
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3971,6 +3971,7 @@ class Sema {
unsigned NumInputs, IdentifierInfo **Names,
MultiExprArg Constraints, MultiExprArg Exprs,
Expr *AsmString, MultiExprArg Clobbers,
unsigned NumLabels,
SourceLocation RParenLoc);

void FillInlineAsmIdentifierInfo(Expr *Res,
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -5623,6 +5628,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
*AsmStrOrErr,
S->getNumClobbers(),
Clobbers.data(),
S->getNumLabels(),
*RParenLocOrErr);
}

Expand Down
29 changes: 23 additions & 6 deletions clang/lib/AST/Stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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];
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -614,8 +629,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;
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 17 additions & 3 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
74 changes: 58 additions & 16 deletions clang/lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ class CFGBuilder {
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);
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 954ec09

Please sign in to comment.