diff --git a/clang/CodeGen/CGStmt.cpp b/clang/CodeGen/CGStmt.cpp index f1f915e7c19c..2b9a72be531f 100644 --- a/clang/CodeGen/CGStmt.cpp +++ b/clang/CodeGen/CGStmt.cpp @@ -57,6 +57,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::BreakStmtClass: EmitBreakStmt(); break; case Stmt::ContinueStmtClass: EmitContinueStmt(); break; + case Stmt::SwitchStmtClass: EmitSwitchStmt(cast(*S)); break; + case Stmt::DefaultStmtClass: EmitDefaultStmt(cast(*S)); break; + case Stmt::CaseStmtClass: EmitCaseStmt(cast(*S)); break; } } @@ -350,3 +353,50 @@ void CodeGenFunction::EmitContinueStmt() { Builder.CreateBr(Block); EmitBlock(new llvm::BasicBlock()); } + +void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) { + StartBlock("sw.bb"); + llvm::Value *V = EmitScalarExpr(S.getLHS()); + SwitchInsn->addCase(cast(V), Builder.GetInsertBlock()); + assert (!S.getRHS() && "Case statement range is not yet supported"); + EmitStmt(S.getSubStmt()); +} + +void CodeGenFunction::EmitDefaultStmt(const DefaultStmt &S) { + StartBlock("sw.default"); + // Current insert block is the default destination. + SwitchInsn->setSuccessor(0, Builder.GetInsertBlock()); + EmitStmt(S.getSubStmt()); +} + +void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { + llvm::Value *CondV = EmitScalarExpr(S.getCond()); + + // Handle nested switch statements. + llvm::SwitchInst *SavedSwitchInsn = SwitchInsn; + + // Create basic block to hold stuff that comes after switch statement. + // Initially use it to hold DefaultStmt. + llvm::BasicBlock *NextBlock = new llvm::BasicBlock("after.sw", CurFn); + SwitchInsn = Builder.CreateSwitch(CondV, NextBlock); + + // All break statements jump to NextBlock. If BreakContinueStack is non empty then + // reuse last ContinueBlock. + llvm::BasicBlock *ContinueBlock = NULL; + if (!BreakContinueStack.empty()) + ContinueBlock = BreakContinueStack.back().ContinueBlock; + BreakContinueStack.push_back(BreakContinue(NextBlock, ContinueBlock)); + + // Emit switch body. + EmitStmt(S.getBody()); + BreakContinueStack.pop_back(); + + // Prune insert block if it is dummy. + llvm::BasicBlock *BB = Builder.GetInsertBlock(); + if (isDummyBlock(BB)) + BB->eraseFromParent(); + + // Place NextBlock as the new insert point. + Builder.SetInsertPoint(NextBlock); + SwitchInsn = SavedSwitchInsn; +} diff --git a/clang/CodeGen/CodeGenFunction.cpp b/clang/CodeGen/CodeGenFunction.cpp index 363d299c2604..e8a00c89ea0b 100644 --- a/clang/CodeGen/CodeGenFunction.cpp +++ b/clang/CodeGen/CodeGenFunction.cpp @@ -115,3 +115,13 @@ bool CodeGenFunction::isDummyBlock(const llvm::BasicBlock *BB) { return false; } +/// StartBlock - Start new block named N. If insert block is a dummy block +/// then reuse it. +void CodeGenFunction::StartBlock(const char *N) { + llvm::BasicBlock *BB = Builder.GetInsertBlock(); + if (!isDummyBlock(BB)) + EmitBlock(new llvm::BasicBlock(N)); + else + BB->setName(N); +} + diff --git a/clang/CodeGen/CodeGenFunction.h b/clang/CodeGen/CodeGenFunction.h index 2f7e0736e021..f804aaa6262b 100644 --- a/clang/CodeGen/CodeGenFunction.h +++ b/clang/CodeGen/CodeGenFunction.h @@ -41,7 +41,10 @@ namespace clang { class ForStmt; class ReturnStmt; class DeclStmt; - + class CaseStmt; + class DefaultStmt; + class SwitchStmt; + class Expr; class DeclRefExpr; class StringLiteral; @@ -237,6 +240,10 @@ private: }; llvm::SmallVector BreakContinueStack; + // SwitchInsn - This is used by EmitCaseStmt() and EmitDefaultStmt() to + // populate switch instruction + llvm::SwitchInst *SwitchInsn; + public: CodeGenFunction(CodeGenModule &cgm); @@ -281,6 +288,10 @@ public: /// with no predecessors. static bool isDummyBlock(const llvm::BasicBlock *BB); + /// StartBlock - Start new block named N. If insert block is a dummy block + /// then reuse it. + void StartBlock(const char *N); + //===--------------------------------------------------------------------===// // Declaration Emission //===--------------------------------------------------------------------===// @@ -308,7 +319,10 @@ public: void EmitDeclStmt(const DeclStmt &S); void EmitBreakStmt(); void EmitContinueStmt(); - + void EmitSwitchStmt(const SwitchStmt &S); + void EmitDefaultStmt(const DefaultStmt &S); + void EmitCaseStmt(const CaseStmt &S); + //===--------------------------------------------------------------------===// // LValue Expression Emission //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index ff93d22296e5..2f3544e6e474 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -271,6 +271,13 @@ public: Expr *getRHS() { return reinterpret_cast(SubExprs[RHS]); } Stmt *getSubStmt() { return SubExprs[SUBSTMT]; } virtual Stmt* v_getSubStmt() { return getSubStmt(); } + const Expr *getLHS() const { + return reinterpret_cast(SubExprs[LHS]); + } + const Expr *getRHS() const { + return reinterpret_cast(SubExprs[RHS]); + } + const Stmt *getSubStmt() const { return SubExprs[SUBSTMT]; } virtual SourceRange getSourceRange() const { return SourceRange(CaseLoc, SubExprs[SUBSTMT]->getLocEnd()); @@ -294,6 +301,7 @@ public: Stmt *getSubStmt() { return SubStmt; } virtual Stmt* v_getSubStmt() { return getSubStmt(); } + const Stmt *getSubStmt() const { return SubStmt; } SourceLocation getDefaultLoc() const { return DefaultLoc; } diff --git a/clang/test/CodeGen/switch.c b/clang/test/CodeGen/switch.c new file mode 100644 index 000000000000..690c11e9f36c --- /dev/null +++ b/clang/test/CodeGen/switch.c @@ -0,0 +1,17 @@ +// RUN: clang %s -emit-llvm | llvm-as | opt -std-compile-opts -disable-output + +int foo(int i) { + int j = 0; + switch (i) { + case 1 : + j = 2; break; + case 2: + j = 3; break; + default: + j = 42; break; + } + j = j + 1; + return j; +} + +