diff --git a/clang/CodeGen/CGStmt.cpp b/clang/CodeGen/CGStmt.cpp index c07929d107c0..f9d614d91c45 100644 --- a/clang/CodeGen/CGStmt.cpp +++ b/clang/CodeGen/CGStmt.cpp @@ -354,23 +354,73 @@ void CodeGenFunction::EmitContinueStmt() { EmitBlock(new llvm::BasicBlock()); } -void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) { - StartBlock("sw.bb"); - llvm::BasicBlock *CaseDest = Builder.GetInsertBlock(); - llvm::ConstantInt *LV = cast(EmitScalarExpr(S.getLHS())); - SwitchInsn->addCase(LV, CaseDest); - if (const Expr *R = S.getRHS()) { - llvm::ConstantInt *RV = cast(EmitScalarExpr(R)); - llvm::APInt LHS = LV->getValue(); - llvm::APInt RHS = RV->getValue(); +/// EmitCaseStmtRange - If case statement range is not too big then +/// add multiple cases to switch instruction, one for each value within +/// the range. If range is too big then emit "if" condition check. +void CodeGenFunction::EmitCaseStmtRange(const CaseStmt &S) { + assert (S.getRHS() && "Unexpected RHS value in CaseStmt"); + + const Expr *L = S.getLHS(); + const Expr *R = S.getRHS(); + llvm::ConstantInt *LV = cast(EmitScalarExpr(L)); + llvm::ConstantInt *RV = cast(EmitScalarExpr(R)); + llvm::APInt LHS = LV->getValue(); + llvm::APInt RHS = RV->getValue(); + + llvm::APInt Range = RHS - LHS; + if (Range.ult(llvm::APInt(Range.getBitWidth(), 64))) { + // Range is small enough to add multiple switch instruction cases. + StartBlock("sw.bb"); + llvm::BasicBlock *CaseDest = Builder.GetInsertBlock(); + SwitchInsn->addCase(LV, CaseDest); LHS++; while (LHS != RHS) { SwitchInsn->addCase(llvm::ConstantInt::get(LHS), CaseDest); LHS++; } - SwitchInsn->addCase(llvm::ConstantInt::get(LHS), CaseDest); - } + SwitchInsn->addCase(RV, CaseDest); + EmitStmt(S.getSubStmt()); + return; + } + + // The range is too big. Emit "if" condition. + llvm::BasicBlock *FalseDest = NULL; + llvm::BasicBlock *CaseDest = new llvm::BasicBlock("sw.bb"); + // If we have already seen one case statement range for this switch + // instruction then piggy-back otherwise use default block as false + // destination. + if (CaseRangeBlock) + FalseDest = CaseRangeBlock; + else + FalseDest = SwitchInsn->getDefaultDest(); + + // Start new block to hold case statement range check instructions. + StartBlock("case.range"); + CaseRangeBlock = Builder.GetInsertBlock(); + + // Emit range check. + llvm::Value *Diff = + Builder.CreateSub(SwitchInsn->getCondition(), LV, "tmp"); + llvm::Value *Cond = + Builder.CreateICmpULE(Diff, llvm::ConstantInt::get(Range), "tmp"); + Builder.CreateCondBr(Cond, CaseDest, FalseDest); + + // Now emit case statement body. + EmitBlock(CaseDest); + EmitStmt(S.getSubStmt()); +} + +void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) { + if (S.getRHS()) { + EmitCaseStmtRange(S); + return; + } + + StartBlock("sw.bb"); + llvm::BasicBlock *CaseDest = Builder.GetInsertBlock(); + llvm::ConstantInt *LV = cast(EmitScalarExpr(S.getLHS())); + SwitchInsn->addCase(LV, CaseDest); EmitStmt(S.getSubStmt()); } @@ -386,6 +436,8 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { // Handle nested switch statements. llvm::SwitchInst *SavedSwitchInsn = SwitchInsn; + llvm::BasicBlock *SavedCRBlock = CaseRangeBlock; + CaseRangeBlock = NULL; // Create basic block to hold stuff that comes after switch statement. // Initially use it to hold DefaultStmt. @@ -403,6 +455,12 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { EmitStmt(S.getBody()); BreakContinueStack.pop_back(); + // If one or more case statement range is seen then use CaseRangeBlock + // as the default block. False edge of CaseRangeBlock will lead to + // original default block. + if (CaseRangeBlock) + SwitchInsn->setSuccessor(0, CaseRangeBlock); + // Prune insert block if it is dummy. llvm::BasicBlock *BB = Builder.GetInsertBlock(); if (isDummyBlock(BB)) @@ -411,4 +469,5 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { // Place NextBlock as the new insert point. Builder.SetInsertPoint(NextBlock); SwitchInsn = SavedSwitchInsn; + CaseRangeBlock = SavedCRBlock; } diff --git a/clang/CodeGen/CodeGenFunction.cpp b/clang/CodeGen/CodeGenFunction.cpp index e8a00c89ea0b..80c75afdb815 100644 --- a/clang/CodeGen/CodeGenFunction.cpp +++ b/clang/CodeGen/CodeGenFunction.cpp @@ -24,7 +24,8 @@ using namespace clang; using namespace CodeGen; CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) - : CGM(cgm), Target(CGM.getContext().Target) {} + : CGM(cgm), Target(CGM.getContext().Target), SwitchInsn(NULL), + CaseRangeBlock(NULL) {} ASTContext &CodeGenFunction::getContext() const { return CGM.getContext(); diff --git a/clang/CodeGen/CodeGenFunction.h b/clang/CodeGen/CodeGenFunction.h index f804aaa6262b..67806d545a86 100644 --- a/clang/CodeGen/CodeGenFunction.h +++ b/clang/CodeGen/CodeGenFunction.h @@ -240,10 +240,14 @@ private: }; llvm::SmallVector BreakContinueStack; - // SwitchInsn - This is used by EmitCaseStmt() and EmitDefaultStmt() to - // populate switch instruction + /// SwitchInsn - This is used by EmitCaseStmt() and EmitDefaultStmt() to + /// populate switch instruction llvm::SwitchInst *SwitchInsn; + /// CaseRangeBlock - This is used, while constructiong swtich instruction, + /// to hold "if" condition for case statement ranges. + llvm::BasicBlock *CaseRangeBlock; + public: CodeGenFunction(CodeGenModule &cgm); @@ -322,6 +326,7 @@ public: void EmitSwitchStmt(const SwitchStmt &S); void EmitDefaultStmt(const DefaultStmt &S); void EmitCaseStmt(const CaseStmt &S); + void EmitCaseStmtRange(const CaseStmt &S); //===--------------------------------------------------------------------===// // LValue Expression Emission diff --git a/clang/test/CodeGen/switch.c b/clang/test/CodeGen/switch.c index 25602e93c373..68313c0741f0 100644 --- a/clang/test/CodeGen/switch.c +++ b/clang/test/CodeGen/switch.c @@ -30,3 +30,35 @@ int foo2(int i) { } +int foo3(int i) { + int j = 0; + switch (i) { + default: + j = 42; break; + case 111: + j = 111; break; + case 0 ... 100: + j = 1; break; + case 222: + j = 222; break; + } + return j; +} + + +int foo4(int i) { + int j = 0; + switch (i) { + case 111: + j = 111; break; + case 0 ... 100: + j = 1; break; + case 222: + j = 222; break; + default: + j = 42; break; + case 501 ... 600: + j = 5; break; + } + return j; +}