diff options
-rw-r--r-- | lib/CodeGen/CGExprScalar.cpp | 6 | ||||
-rw-r--r-- | lib/CodeGen/CGStmt.cpp | 20 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.cpp | 35 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 25 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 3 | ||||
-rw-r--r-- | test/CodeGen/indirect-goto.c | 20 |
6 files changed, 106 insertions, 3 deletions
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index c44af4b8f1..e8adc59e9f 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -117,6 +117,12 @@ public: Value *VisitSizeOfAlignOfTypeExpr(const SizeOfAlignOfTypeExpr *E) { return EmitSizeAlignOf(E->getArgumentType(), E->getType(), E->isSizeOf()); } + Value *VisitAddrLabelExpr(const AddrLabelExpr *E) { + Value *V = llvm::ConstantInt::get(llvm::Type::Int32Ty, + CGF.GetIDForAddrOfLabel(E->getLabel())); + return Builder.CreateIntToPtr(V, + llvm::PointerType::getUnqual(llvm::Type::Int8Ty)); + } // l-values. Value *VisitDeclRefExpr(DeclRefExpr *E) { diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index 9743c7cadf..a5cf946ea6 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -62,6 +62,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::CompoundStmtClass: EmitCompoundStmt(cast<CompoundStmt>(*S)); break; case Stmt::LabelStmtClass: EmitLabelStmt(cast<LabelStmt>(*S)); break; case Stmt::GotoStmtClass: EmitGotoStmt(cast<GotoStmt>(*S)); break; + case Stmt::IndirectGotoStmtClass: + EmitIndirectGotoStmt(cast<IndirectGotoStmt>(*S)); break; case Stmt::IfStmtClass: EmitIfStmt(cast<IfStmt>(*S)); break; case Stmt::WhileStmtClass: EmitWhileStmt(cast<WhileStmt>(*S)); break; @@ -157,7 +159,25 @@ void CodeGenFunction::EmitGotoStmt(const GotoStmt &S) { Builder.SetInsertPoint(llvm::BasicBlock::Create("", CurFn)); } +void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) { + // Emit initial switch which will be patched up later by + // EmitIndirectSwitches(). We need a default dest, so we use the + // current BB, but this is overwritten. + llvm::Value *V = Builder.CreatePtrToInt(EmitScalarExpr(S.getTarget()), + llvm::Type::Int32Ty, + "addr"); + llvm::SwitchInst *I = Builder.CreateSwitch(V, Builder.GetInsertBlock()); + IndirectSwitches.push_back(I); + + // Emit a block after the branch so that dead code after a goto has some place + // to go. + Builder.SetInsertPoint(llvm::BasicBlock::Create("", CurFn)); +} + void CodeGenFunction::EmitIfStmt(const IfStmt &S) { + // FIXME: It would probably be nice for us to skip emission of if + // (0) code here. + // C99 6.8.4.1: The first substatement is executed if the expression compares // unequal to 0. The condition must be a scalar type. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index e153ad9fd8..9814ae7137 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -68,7 +68,10 @@ bool CodeGenFunction::hasAggregateLLVMType(QualType T) { void CodeGenFunction::GenerateFunction(const Stmt *Body) { // Emit the function body. EmitStmt(Body); - + + // Finish emission of indirect switches. + EmitIndirectSwitches(); + // Emit debug descriptor for function end. CGDebugInfo *DI = CGM.getDebugInfo(); if (DI) { @@ -179,3 +182,33 @@ void CodeGenFunction::WarnUnsupported(const Stmt *S, const char *Type) { CGM.WarnUnsupported(S, Type); } +unsigned CodeGenFunction::GetIDForAddrOfLabel(const LabelStmt *L) { + // Use LabelIDs.size() as the new ID if one hasn't been assigned. + return LabelIDs.insert(std::make_pair(L, LabelIDs.size())).first->second; +} + +void CodeGenFunction::EmitIndirectSwitches() { + llvm::BasicBlock *Default; + + if (!LabelIDs.empty()) { + Default = getBasicBlockForLabel(LabelIDs.begin()->first); + } else { + // No possible targets for indirect goto, just emit an infinite + // loop. + Default = llvm::BasicBlock::Create("indirectgoto.loop", CurFn); + llvm::BranchInst::Create(Default, Default); + } + + for (std::vector<llvm::SwitchInst*>::iterator i = IndirectSwitches.begin(), + e = IndirectSwitches.end(); i != e; ++i) { + llvm::SwitchInst *I = *i; + + I->setSuccessor(0, Default); + for (std::map<const LabelStmt*,unsigned>::iterator LI = LabelIDs.begin(), + LE = LabelIDs.end(); LI != LE; ++LI) { + I->addCase(llvm::ConstantInt::get(llvm::Type::Int32Ty, + LI->second), + getBasicBlockForLabel(LI->first)); + } + } +} diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 38e10e90ce..374cdf6cd7 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -22,6 +22,7 @@ #include "clang/AST/ExprObjC.h" #include <vector> +#include <map> namespace llvm { class Module; @@ -240,11 +241,21 @@ public: /// AllocaInsertPoint - This is an instruction in the entry block before which /// we prefer to insert allocas. llvm::Instruction *AllocaInsertPt; - + const llvm::Type *LLVMIntTy; uint32_t LLVMPointerWidth; private: + /// LabelIDs - Track arbitrary ids assigned to labels for use in + /// implementing the GCC address-of-label extension and indirect + /// goto. IDs are assigned to labels inside getIDForAddrOfLabel(). + std::map<const LabelStmt*, unsigned> LabelIDs; + + /// IndirectSwitches - Record the list of switches for indirect + /// gotos. Emission of the actual switching code needs to be delayed + /// until all AddrLabelExprs have been seen. + std::vector<llvm::SwitchInst*> IndirectSwitches; + /// LocalDeclMap - This keeps track of the LLVM allocas or globals for local C /// decls. llvm::DenseMap<const Decl*, llvm::Value*> LocalDeclMap; @@ -342,6 +353,8 @@ public: /// the input field number being accessed. static unsigned getAccessedFieldNo(unsigned Idx, const llvm::Constant *Elts); + unsigned GetIDForAddrOfLabel(const LabelStmt *L); + //===--------------------------------------------------------------------===// // Declaration Emission //===--------------------------------------------------------------------===// @@ -363,6 +376,7 @@ public: void EmitLabel(const LabelStmt &S); // helper for EmitLabelStmt. void EmitLabelStmt(const LabelStmt &S); void EmitGotoStmt(const GotoStmt &S); + void EmitIndirectGotoStmt(const IndirectGotoStmt &S); void EmitIfStmt(const IfStmt &S); void EmitWhileStmt(const WhileStmt &S); void EmitDoStmt(const DoStmt &S); @@ -502,6 +516,15 @@ public: llvm::GlobalValue *GenerateStaticBlockVarDecl(const VarDecl &D, bool NoInit, const char *Separator); + + //===--------------------------------------------------------------------===// + // Internal Helpers + //===--------------------------------------------------------------------===// + +private: + /// EmitIndirectSwitches - Emit code for all of the switch + /// instructions in IndirectSwitches. + void EmitIndirectSwitches(); }; } // end namespace CodeGen } // end namespace clang diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6c6d6e2610..1f544927b7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2313,7 +2313,8 @@ Sema::ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, // Look up the record for this label identifier. LabelStmt *&LabelDecl = LabelMap[LabelII]; - // If we haven't seen this label yet, create a forward reference. + // If we haven't seen this label yet, create a forward reference. It + // will be validated and/or cleaned up in ActOnFinishFunctionBody. if (LabelDecl == 0) LabelDecl = new LabelStmt(LabLoc, LabelII, 0); diff --git a/test/CodeGen/indirect-goto.c b/test/CodeGen/indirect-goto.c new file mode 100644 index 0000000000..c0dc8a8298 --- /dev/null +++ b/test/CodeGen/indirect-goto.c @@ -0,0 +1,20 @@ +// RUN: clang -emit-llvm-bc -o - %s | opt -std-compile-opts | llvm-dis > %t && +// RUN: grep "ret i32" %t | count 1 && +// RUN: grep "ret i32 210" %t | count 1 + +static int foo(unsigned i) { + const void *addrs[] = { &&L1, &&L2, &&L3, &&L4, &&L5 }; + int res = 1; + + goto *addrs[i]; + L5: res *= 11; + L4: res *= 7; + L3: res *= 5; + L2: res *= 3; + L1: res *= 2; + return res; +} + +int bar() { + return foo(3); +} |