]> granicus.if.org Git - clang/commitdiff
Add CodeGen support for indirect goto.
authorDaniel Dunbar <daniel@zuster.org>
Mon, 4 Aug 2008 16:51:22 +0000 (16:51 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Mon, 4 Aug 2008 16:51:22 +0000 (16:51 +0000)
 - Follows emission scheme used by llvm-gcc, i.e. invent an id for
   each label whose address is taken and replace each indirect goto by
   a switch to each possible target.
 - Currently we emit a switch for each indirect goto instead of
   merging them as llvm-gcc does.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@54318 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGExprScalar.cpp
lib/CodeGen/CGStmt.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/Sema/SemaExpr.cpp
test/CodeGen/indirect-goto.c [new file with mode: 0644]

index c44af4b8f17ac68ee63ac5f14da452b36e807675..e8adc59e9fd9a32e3b7ea30412fc55861757e2cf 100644 (file)
@@ -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) {
index 9743c7cadf37860b3db7c464b087e7471685801f..a5cf946ea62d17e9cbd1a2935e6ac820eda6718b 100644 (file)
@@ -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());
index e153ad9fd8d753dd6918c7238bd737dbcf6982b1..9814ae71374b08f3b1d327106b6ae9fbc99d37f4 100644 (file)
@@ -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));
+    }
+  }         
+}
index 38e10e90cefd04233108f2dfcd72b10696f9566a..374cdf6cd7beb003a2cae8a4ce539786f86a6c79 100644 (file)
@@ -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
index 6c6d6e2610adec61e3cc718bb6d727b1f27437ea..1f544927b790a9cf860976377f5a06b8758ed97a 100644 (file)
@@ -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 (file)
index 0000000..c0dc8a8
--- /dev/null
@@ -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);
+}