Index: llvm/lib/Transforms/Scalar/SROA.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SROA.cpp +++ llvm/lib/Transforms/Scalar/SROA.cpp @@ -3464,6 +3464,47 @@ } bool visitGetElementPtrInst(GetElementPtrInst &GEPI) { + // Fold gep (phi ptr1, ptr2) => phi gep(ptr1), gep(ptr2) + if (PHINode *PHI = dyn_cast(GEPI.getPointerOperand())) { + LLVM_DEBUG(dbgs() << " Rewriting gep(phi) -> phi(gep):" + << "\n original: " << *PHI + << "\n " << GEPI + << "\n to: "); + + SmallVector Index(GEPI.idx_begin(), GEPI.idx_end()); + bool IsInBounds = GEPI.isInBounds(); + IRBuilderTy PHIBuilder(GEPI.getParent()->getFirstNonPHI()); + PHINode *NewPN = PHIBuilder.CreatePHI(GEPI.getType(), + PHI->getNumIncomingValues(), + PHI->getName() + ".sroa.phi"); + for (unsigned I = 0, E = PHI->getNumIncomingValues(); I != E; ++I) { + Value *In = PHI->getIncomingValue(I); + Instruction *ValI = dyn_cast(In); + BasicBlock *InBB = PHI->getIncomingBlock(I); + BasicBlock::iterator Ins = + (ValI && !isa(ValI)) ? std::next(ValI->getIterator()) + : InBB->getFirstInsertionPt(); + IRBuilderTy Builder(InBB, Ins); + Value *NewVal = + IsInBounds + ? Builder.CreateInBoundsGEP(In, Index, + In->getName() + ".sroa.gep") + : Builder.CreateGEP(In, Index, In->getName() + ".sroa.gep"); + NewPN->addIncoming(NewVal, InBB); + if (isa(NewVal)) + visit(cast(NewVal)); + } + + GEPI.replaceAllUsesWith(NewPN); + GEPI.eraseFromParent(); + + LLVM_DEBUG(for (Value *In : NewPN->incoming_values()) + dbgs() << "\n " << *In; + dbgs() << "\n " << *NewPN << '\n'); + + return true; + } + enqueueUsers(GEPI); return false; } Index: llvm/test/Transforms/SROA/phi-and-select.ll =================================================================== --- llvm/test/Transforms/SROA/phi-and-select.ll +++ llvm/test/Transforms/SROA/phi-and-select.ll @@ -636,8 +636,7 @@ if.end: ; preds = %for.cond, %entry %f2 = phi %struct.S* [ %f, %entry ], [ %f, %for.cond ] -; CHECK: phi i32 -; CHECK: %[[cast:.*]] = bitcast i32* %[[alloca]] to %struct.S* +; CHECK: phi i32 [ undef, %entry ], [ undef, %for.cond ] phi i32 [ undef, %entry ], [ undef, %for.cond ] br i1 undef, label %if.then5, label %if.then2 @@ -646,7 +645,7 @@ if.then5: ; preds = %if.then2, %if.end %f1 = phi %struct.S* [ undef, %if.then2 ], [ %f2, %if.end ] -; CHECK: phi {{.*}} %[[cast]] +; CHECK: %f1.sroa.phi = phi i32* [ undef, %if.then2 ], [ %[[alloca]], %if.end ] store %struct.S undef, %struct.S* %f1, align 4 ret void } Index: llvm/test/Transforms/SROA/phi-gep.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/SROA/phi-gep.ll @@ -0,0 +1,149 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -sroa < %s | FileCheck %s + +%pair = type { i32, i32 } + +define i32 @test_sroa_phi_gep(i1 %cond) { +; CHECK-LABEL: @test_sroa_phi_gep( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI_SROA_PHI_SROA_SPECULATED:%.*]] = phi i32 [ undef, [[ENTRY:%.*]] ], [ undef, [[IF_THEN]] ] +; CHECK-NEXT: ret i32 [[PHI_SROA_PHI_SROA_SPECULATED]] +; +entry: + %a = alloca %pair, align 4 + %b = alloca %pair, align 4 + br i1 %cond, label %if.then, label %end + +if.then: + br label %end + +end: + %phi = phi %pair* [ %a, %entry], [ %b, %if.then ] + %gep = getelementptr inbounds %pair, %pair* %phi, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +} + +define i32 @test_sroa_phi_gep_undef(i1 %cond) { +; CHECK-LABEL: @test_sroa_phi_gep_undef( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[PHI_SROA_PHI_SROA_SPECULATE_LOAD_IF_THEN:%.*]] = load i32, i32* undef, align 4 +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI_SROA_PHI_SROA_SPECULATED:%.*]] = phi i32 [ undef, [[ENTRY:%.*]] ], [ [[PHI_SROA_PHI_SROA_SPECULATE_LOAD_IF_THEN]], [[IF_THEN]] ] +; CHECK-NEXT: ret i32 [[PHI_SROA_PHI_SROA_SPECULATED]] +; +entry: + %a = alloca %pair, align 4 + br i1 %cond, label %if.then, label %end + +if.then: + br label %end + +end: + %phi = phi %pair* [ %a, %entry], [ undef, %if.then ] + %gep = getelementptr inbounds %pair, %pair* %phi, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +} + +@g = global %pair zeroinitializer, align 4 + +define i32 @test_sroa_phi_gep_global(i1 %cond) { +; CHECK-LABEL: @test_sroa_phi_gep_global( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[PHI_SROA_PHI_SROA_SPECULATE_LOAD_IF_THEN:%.*]] = load i32, i32* getelementptr inbounds (%pair, %pair* @g, i32 0, i32 1), align 4 +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI_SROA_PHI_SROA_SPECULATED:%.*]] = phi i32 [ undef, [[ENTRY:%.*]] ], [ [[PHI_SROA_PHI_SROA_SPECULATE_LOAD_IF_THEN]], [[IF_THEN]] ] +; CHECK-NEXT: ret i32 [[PHI_SROA_PHI_SROA_SPECULATED]] +; +entry: + %a = alloca %pair, align 4 + br i1 %cond, label %if.then, label %end + +if.then: + br label %end + +end: + %phi = phi %pair* [ %a, %entry], [ @g, %if.then ] + %gep = getelementptr inbounds %pair, %pair* %phi, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +} + +define i32 @test_sroa_phi_gep_arg_phi_inspt(i1 %cond, %pair* %p) { +; CHECK-LABEL: @test_sroa_phi_gep_arg_phi_inspt( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_SROA_0:%.*]] = alloca i32 +; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR:%.*]], label [[END:%.*]] +; CHECK: for: +; CHECK-NEXT: [[PHI_INSPT:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I:%.*]], [[FOR]] ] +; CHECK-NEXT: [[P_SROA_GEP:%.*]] = getelementptr inbounds [[PAIR:%.*]], %pair* [[P:%.*]], i32 0, i32 1 +; CHECK-NEXT: [[I]] = add i32 [[PHI_INSPT]], 1 +; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[I]], 10 +; CHECK-NEXT: br i1 [[LOOP_COND]], label [[FOR]], label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI_SROA_PHI:%.*]] = phi i32* [ [[A_SROA_0]], [[ENTRY]] ], [ [[P_SROA_GEP]], [[FOR]] ] +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[PHI_SROA_PHI]], align 4 +; CHECK-NEXT: ret i32 [[LOAD]] +; +entry: + %a = alloca %pair, align 4 + br i1 %cond, label %for, label %end + +for: + %phi_inspt = phi i32 [ 0, %entry ], [ %i, %for ] + %i = add i32 %phi_inspt, 1 + %loop.cond = icmp ult i32 %i, 10 + br i1 %loop.cond, label %for, label %end + +end: + %phi = phi %pair* [ %a, %entry], [ %p, %for ] + %gep = getelementptr inbounds %pair, %pair* %phi, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +} + +define i32 @test_sroa_phi_gep_phi_inspt(i1 %cond, %pair* %p) { +; CHECK-LABEL: @test_sroa_phi_gep_phi_inspt( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_SROA_0:%.*]] = alloca i32 +; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR:%.*]], label [[END:%.*]] +; CHECK: for: +; CHECK-NEXT: [[PHI_INSPT:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I:%.*]], [[FOR]] ] +; CHECK-NEXT: [[PHI_IN_SROA_PHI:%.*]] = phi i32* [ getelementptr inbounds (%pair, %pair* null, i32 0, i32 1), [[ENTRY]] ], [ [[P_SROA_GEP:%.*]], [[FOR]] ] +; CHECK-NEXT: [[P_SROA_GEP]] = getelementptr inbounds [[PAIR:%.*]], %pair* [[P:%.*]], i32 0, i32 1 +; CHECK-NEXT: [[I]] = add i32 [[PHI_INSPT]], 1 +; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[I]], 10 +; CHECK-NEXT: br i1 [[LOOP_COND]], label [[FOR]], label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI_SROA_PHI:%.*]] = phi i32* [ [[A_SROA_0]], [[ENTRY]] ], [ [[PHI_IN_SROA_PHI]], [[FOR]] ] +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[PHI_SROA_PHI]], align 4 +; CHECK-NEXT: ret i32 [[LOAD]] +; +entry: + %a = alloca %pair, align 4 + br i1 %cond, label %for, label %end + +for: + %phi_in = phi %pair * [ null, %entry ], [ %p, %for ] + %phi_inspt = phi i32 [ 0, %entry ], [ %i, %for ] + %i = add i32 %phi_inspt, 1 + %loop.cond = icmp ult i32 %i, 10 + br i1 %loop.cond, label %for, label %end + +end: + %phi = phi %pair* [ %a, %entry], [ %phi_in, %for ] + %gep = getelementptr inbounds %pair, %pair* %phi, i32 0, i32 1 + %load = load i32, i32* %gep, align 4 + ret i32 %load +}