Index: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -351,6 +351,12 @@ !TI->hasOneUse() || !FI->hasOneUse()) return nullptr; + // Do not fold GEP leading to an alloca, foldSelectGEP() will revert it. + if (isa(TI) && + (isa(GetUnderlyingObject(TI, DL)) || + isa(GetUnderlyingObject(FI, DL)))) + return nullptr; + // Figure out if the operations have any operands in common. Value *MatchOp, *OtherOpT, *OtherOpF; bool MatchIsOpZero; Index: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1712,28 +1712,36 @@ /// Thread a GEP operation with constant indices through the constant true/false /// arms of a select. static Instruction *foldSelectGEP(GetElementPtrInst &GEP, - InstCombiner::BuilderTy &Builder) { + InstCombiner::BuilderTy &Builder, + const DataLayout &DL) { if (!GEP.hasAllConstantIndices()) return nullptr; Instruction *Sel; Value *Cond; - Constant *TrueC, *FalseC; + Value *True, *False; if (!match(GEP.getPointerOperand(), m_Instruction(Sel)) || !match(Sel, - m_Select(m_Value(Cond), m_Constant(TrueC), m_Constant(FalseC)))) + m_Select(m_Value(Cond), m_Value(True), m_Value(False)))) return nullptr; - // gep (select Cond, TrueC, FalseC), IndexC --> select Cond, TrueC', FalseC' + // Allow transformation if both sides are constant or if either refers + // to alloca to help SROA. + if ((!isa(True) || !isa(False)) && + !isa(GetUnderlyingObject(True, DL)) && + !isa(GetUnderlyingObject(False, DL))) + return nullptr; + + // gep (select Cond, True, False), IndexC --> select Cond, True', False' // Propagate 'inbounds' and metadata from existing instructions. // Note: using IRBuilder to create the constants for efficiency. SmallVector IndexC(GEP.idx_begin(), GEP.idx_end()); bool IsInBounds = GEP.isInBounds(); - Value *NewTrueC = IsInBounds ? Builder.CreateInBoundsGEP(TrueC, IndexC) - : Builder.CreateGEP(TrueC, IndexC); - Value *NewFalseC = IsInBounds ? Builder.CreateInBoundsGEP(FalseC, IndexC) - : Builder.CreateGEP(FalseC, IndexC); - return SelectInst::Create(Cond, NewTrueC, NewFalseC, "", nullptr, Sel); + Value *NewTrue = IsInBounds ? Builder.CreateInBoundsGEP(True, IndexC) + : Builder.CreateGEP(True, IndexC); + Value *NewFalse = IsInBounds ? Builder.CreateInBoundsGEP(False, IndexC) + : Builder.CreateGEP(False, IndexC); + return SelectInst::Create(Cond, NewTrue, NewFalse, "", nullptr, Sel); } Instruction *InstCombiner::visitGetElementPtrInst(GetElementPtrInst &GEP) { @@ -2386,7 +2394,7 @@ } } - if (Instruction *R = foldSelectGEP(GEP, Builder)) + if (Instruction *R = foldSelectGEP(GEP, Builder, DL)) return R; return nullptr; Index: llvm/test/Transforms/InstCombine/getelementptr.ll =================================================================== --- llvm/test/Transforms/InstCombine/getelementptr.ll +++ llvm/test/Transforms/InstCombine/getelementptr.ll @@ -1237,4 +1237,31 @@ ret i32* %gep } +define i32* @test_gep_select_arg(i1 %cond, %struct.f* %a, %struct.f* %b) { +; CHECK-LABEL: @test_gep_select_arg( +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], %struct.f* [[A:%.*]], %struct.f* [[B:%.*]], !prof !0 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_F:%.*]], %struct.f* [[SEL]], i64 0, i32 0 +; CHECK-NEXT: ret i32* [[GEP]] +; + %sel = select i1 %cond, %struct.f* %a, %struct.f* %b, !prof !0 + %gep = getelementptr inbounds %struct.f, %struct.f* %sel, i64 0, i32 0 + ret i32* %gep +} + +define i32* @test_gep_select_alloca(i1 %cond) { +; CHECK-LABEL: @test_gep_select_alloca( +; CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_F:%.*]], align 8 +; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_F]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_F]], %struct.f* [[A]], i64 0, i32 0 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_F]], %struct.f* [[B]], i64 0, i32 0 +; CHECK-NEXT: [[GEP:%.*]] = select i1 [[COND:%.*]], i32* [[TMP1]], i32* [[TMP2]], !prof !0 +; CHECK-NEXT: ret i32* [[GEP]] +; + %a = alloca %struct.f + %b = alloca %struct.f + %sel = select i1 %cond, %struct.f* %a, %struct.f* %b, !prof !0 + %gep = getelementptr inbounds %struct.f, %struct.f* %sel, i64 0, i32 0 + ret i32* %gep +} + !0 = !{!"branch_weights", i32 2, i32 10} Index: llvm/test/Transforms/SROA/select-gep.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/SROA/select-gep.ll @@ -0,0 +1,19 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine -sroa < %s | FileCheck %s + +%pair = type { i32, i32 } + +define i32 @test_sroa_select_gep(i1 %cond) { +; CHECK-LABEL: @test_sroa_select_gep( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[LOAD:%.*]] = select i1 [[COND:%.*]], i32 undef, i32 undef +; CHECK-NEXT: ret i32 [[LOAD]] +; +bb: + %a = alloca %pair, align 4 + %b = alloca %pair, align 4 + %select = select i1 %cond, %pair* %a, %pair* %b + %gep = getelementptr inbounds %pair, %pair* %select, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +}