Index: lib/CodeGen/CodeGenPrepare.cpp =================================================================== --- lib/CodeGen/CodeGenPrepare.cpp +++ lib/CodeGen/CodeGenPrepare.cpp @@ -2676,17 +2676,12 @@ struct ExtAddrMode : public TargetLowering::AddrMode { Value *BaseReg = nullptr; Value *ScaledReg = nullptr; + Value *OriginalValue = nullptr; ExtAddrMode() = default; void print(raw_ostream &OS) const; void dump() const; - - bool operator==(const ExtAddrMode& O) const { - return (BaseReg == O.BaseReg) && (ScaledReg == O.ScaledReg) && - (BaseGV == O.BaseGV) && (BaseOffs == O.BaseOffs) && - (HasBaseReg == O.HasBaseReg) && (Scale == O.Scale); - } }; } // end anonymous namespace @@ -4360,6 +4355,75 @@ return false; } +// Convert a null Value to a null value constant +static Value *NullValueToNullConstant(Value *V, Type *T) { + if (V) + return V; + else + return Constant::getNullValue(T); +} + +static Value *CreateSelectOrPHI(Value *Addr, + SmallVectorImpl &AddrModes, + IRBuilder<> &Builder, + std::function GetVal) { + // Start off by figuring out the type + Type *T = nullptr; + for (ExtAddrMode &AM : AddrModes) { + Value *V = GetVal(AM); + if (V) { + Type *NewT = V->getType(); + if (!T) + T = NewT; + else if (T != NewT) + return nullptr; // We can't handle mismatched types + } + } + Value *Result = nullptr; + if (PHINode *OrigPHI = dyn_cast(Addr)) { + // We can't do anything if addrmodes doesn't match the number of incoming + // values + if (AddrModes.size() != OrigPHI->getNumIncomingValues()) + return nullptr; + PHINode *NewPHI = PHINode::Create(T, OrigPHI->getNumIncomingValues(), + OrigPHI->getName(), + OrigPHI->getParent()->getFirstNonPHI()); + for (BasicBlock *BB : OrigPHI->blocks()) { + Value *IV = OrigPHI->getIncomingValueForBlock(BB); + // Find the AddrMode this corresponds to + for (ExtAddrMode &AM : AddrModes) { + if (AM.OriginalValue == IV) { + Value *V = NullValueToNullConstant(GetVal(AM), T); + NewPHI->addIncoming(V, BB); + break; + } + } + } + // If we failed to add an incoming value for each in the original then we + // can't proceed + if (NewPHI->getNumIncomingValues() != OrigPHI->getNumIncomingValues()) { + NewPHI->eraseFromParent(); + return nullptr; + } + Result = NewPHI; + } else if (SelectInst *OrigSelect = dyn_cast(Addr)) { + // Can't do anything if we don't have exactly two addrmodes + if (AddrModes.size() != 2) + return nullptr; + // The two addrmodes we have must correspond to the operands of the select + if (AddrModes[0].OriginalValue != OrigSelect->getTrueValue()) + return nullptr; + if (AddrModes[1].OriginalValue != OrigSelect->getFalseValue()) + return nullptr; + Value *X = NullValueToNullConstant(GetVal(AddrModes[0]), T); + Value *Y = NullValueToNullConstant(GetVal(AddrModes[1]), T); + Result = Builder.CreateSelect(OrigSelect->getCondition(), X, Y); + } else { + llvm_unreachable("Addr should be PHI or Select"); + } + return Result; +} + /// Sink addressing mode computation immediate before MemoryInst if doing so /// can be done without increasing register pressure. The need for the /// register pressure constraint means this can end up being an all or nothing @@ -4389,13 +4453,21 @@ SmallPtrSet Visited; worklist.push_back(Addr); + // When examining PHIs and selects we need to keep track of how the + // addressing modes differ from each other. + bool DifferentBaseReg = false; + bool DifferentBaseGV = false; + bool DifferentBaseOffs = false; + bool DifferentScaledReg = false; + bool DifferentScale = false; + bool TrivialAddrMode = true; + // Use a worklist to iteratively look through PHI nodes, and ensure that // the addressing mode obtained from the non-PHI roots of the graph - // are equivalent. - bool AddrModeFound = false; + // are compatible. bool PhiSeen = false; SmallVector AddrModeInsts; - ExtAddrMode AddrMode; + SmallVector AddrModes; TypePromotionTransaction TPT(RemovedInsts); TypePromotionTransaction::ConstRestorationPt LastKnownGood = TPT.getRestorationPoint(); @@ -4422,6 +4494,13 @@ PhiSeen = true; continue; } + // Similar for select. + if (SelectInst *SI = dyn_cast(V)) { + worklist.push_back(SI->getFalseValue()); + worklist.push_back(SI->getTrueValue()); + PhiSeen = true; + continue; + } // For non-PHIs, determine the addressing mode being computed. Note that // the result may differ depending on what other uses our candidate @@ -4430,27 +4509,73 @@ ExtAddrMode NewAddrMode = AddressingModeMatcher::Match( V, AccessTy, AddrSpace, MemoryInst, AddrModeInsts, *TLI, *TRI, InsertedInsts, PromotedInsts, TPT); - - if (!AddrModeFound) { - AddrModeFound = true; - AddrMode = NewAddrMode; + NewAddrMode.OriginalValue = V; + + // AddrModes with a base reg or gv where the reg/gv is just the original + // value are trivial. We need to detect these to avoid introducing a phi or + // select which just duplicates what's already there. + if ((!NewAddrMode.BaseReg && !NewAddrMode.BaseGV) || + (NewAddrMode.BaseGV && + NewAddrMode.BaseGV != NewAddrMode.OriginalValue) || + (NewAddrMode.BaseReg && + NewAddrMode.BaseReg != NewAddrMode.OriginalValue)) + TrivialAddrMode = false; + + // If this is the first addrmode then everything is fine. + if (AddrModes.size() == 0) { + AddrModes.emplace_back(NewAddrMode); continue; } - if (NewAddrMode == AddrMode) + + // Figure out how different this is from the other address modes. + for (ExtAddrMode &AM : AddrModes) { + if (AM.BaseReg != NewAddrMode.BaseReg) + DifferentBaseReg = true; + if (AM.BaseGV != NewAddrMode.BaseGV) + DifferentBaseGV = true; + if (AM.BaseOffs != NewAddrMode.BaseOffs) + DifferentBaseOffs = true; + if (AM.ScaledReg != NewAddrMode.ScaledReg) + DifferentScaledReg = true; + // Don't count 0 as being a different scale, because that actually means + // unscaled (which will already be counted by having no ScaledReg). + if (AM.Scale && NewAddrMode.Scale && AM.Scale != NewAddrMode.Scale) + DifferentScale = true; + } + unsigned DifferCount = DifferentBaseReg + DifferentBaseGV + + DifferentBaseOffs + DifferentScaledReg + + DifferentScale; + + // If this addrmode is the same as all the others then everything is fine + // (which should only happen when there is actually only one addrmode). + if (DifferCount == 0) continue; - AddrModeFound = false; + // If NewAddrMode differs in only one dimension and comes from a phi or + // select then we can handle it by inserting a phi/select later on. + if ((isa(Addr) || isa(Addr)) && Addr->hasOneUse() && + DifferCount == 1) { + AddrModes.emplace_back(NewAddrMode); + continue; + } + + AddrModes.clear(); break; } // If the addressing mode couldn't be determined, or if multiple different - // ones were determined, bail out now. - if (!AddrModeFound) { + // ones were determined but we can't do anything useful with them, bail out + // now. + if (AddrModes.size() == 0 || (AddrModes.size() > 1 && TrivialAddrMode)) { TPT.rollback(LastKnownGood); return false; } TPT.commit(); + // If we have only one AddrMode then it will be first in AddrModes, otherwise + // we adjust the elements as we go. + ExtAddrMode AddrMode = AddrModes.front(); + // If all the instructions matched are already in this BB, don't do anything. // If we saw Phi node then it is not local definitely. if (!PhiSeen && none_of(AddrModeInsts, [&](Value *V) { @@ -4485,7 +4610,58 @@ Type *IntPtrTy = DL->getIntPtrType(Addr->getType()); Value *ResultPtr = nullptr, *ResultIndex = nullptr; - // First, find the pointer. + // If we have more than one AddrMode then insert a select or phi for each + // field in which the AddrModes differ. + if (AddrModes.size() > 1) { + // We can't cope with scale being different + if (DifferentScale) + return false; + // Set BaseReg if bases differ + if (DifferentBaseReg) { + AddrMode.BaseReg = + CreateSelectOrPHI(Addr, AddrModes, Builder, + [&](ExtAddrMode &AM) { return AM.BaseReg; }); + if (!AddrMode.BaseReg) + return false; + } + // Set BaseReg if we have different global variables + if (DifferentBaseGV) { + assert(!AddrMode.BaseReg); + AddrMode.BaseReg = + CreateSelectOrPHI(Addr, AddrModes, Builder, + [&](ExtAddrMode &AM) { return AM.BaseGV; }); + if (!AddrMode.BaseReg) + return false; + AddrMode.BaseGV = nullptr; + } + // Set ResultIndex if offsets differ + if (DifferentBaseOffs) { + ResultIndex = + CreateSelectOrPHI(Addr, AddrModes, Builder, [&](ExtAddrMode &AM) { + return ConstantInt::get(IntPtrTy, AM.BaseOffs); + }); + if (!ResultIndex) + return false; + AddrMode.BaseOffs = 0; + } + // Adjust ScaledReg to a select if it differs + if (DifferentScaledReg) { + AddrMode.ScaledReg = + CreateSelectOrPHI(Addr, AddrModes, Builder, + [&](ExtAddrMode &AM) { return AM.ScaledReg; }); + if (!AddrMode.ScaledReg) + return false; + // If we have a mix of scaled and unscaled addrmodes then we want scale + // to be the scale and not zero + if (!AddrMode.Scale) + for (ExtAddrMode &AM : AddrModes) + if (AM.Scale) { + AddrMode.Scale = AM.Scale; + break; + } + } + } + if (AddrMode.BaseReg && AddrMode.BaseReg->getType()->isPointerTy()) { ResultPtr = AddrMode.BaseReg; AddrMode.BaseReg = nullptr; Index: test/CodeGen/X86/tail-merge-identical.ll =================================================================== --- test/CodeGen/X86/tail-merge-identical.ll +++ test/CodeGen/X86/tail-merge-identical.ll @@ -8,8 +8,8 @@ ; %else1 and %then2 end up lowering to identical blocks. These blocks should be ; merged during tail-merging. ; CHECK-LABEL: merge_identical_blocks -; CHECK: movl $data+4 -; CHECK-NOT: movl $data+4 +; CHECK: movl $4 +; CHECK-NOT: movl $4 ; CHECK: retq define void @merge_identical_blocks(i1 %a, i1 %b) { entry: Index: test/CodeGen/X86/x86-cmov-converter.ll =================================================================== --- test/CodeGen/X86/x86-cmov-converter.ll +++ test/CodeGen/X86/x86-cmov-converter.ll @@ -234,7 +234,9 @@ } ; CHECK-LABEL: BinarySearch -; CHECK: cmov +; CHECK-NOT: cmov +; CHECK: setae %[[REG:[a-z]]]l +; CHECK-NEXT: movq 8(%rdx,%r[[REG]]x,8) define i32 @BinarySearch(i32 %Mask, %struct.Node* nocapture readonly %Curr, %struct.Node* nocapture readonly %Next) #0 { entry: Index: test/Transforms/CodeGenPrepare/ARM/sink-addrmode.ll =================================================================== --- /dev/null +++ test/Transforms/CodeGenPrepare/ARM/sink-addrmode.ll @@ -0,0 +1,367 @@ +; RUN: opt -S -codegenprepare -mtriple=thumbv7m < %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" + +@gv1 = common global i32 0, align 4 +@gv2 = common global i32 0, align 4 + +; Phi selects between ptr and gep with ptr as base and constant offset +define void @test_phi_onegep_offset(i32* %ptr, i32 %value) { +; CHECK-LABEL: @test_phi_onegep_offset +; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if.then ] +; CHECK: phi i32 [ 0, %entry ], [ 4, %if.then ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: + %gep = getelementptr inbounds i32, i32* %ptr, i32 1 + br label %if.end + +if.end: + %phi = phi i32* [ %ptr, %entry ], [ %gep, %if.then ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between two geps with same base, different constant offsets +define void @test_phi_twogep_offset(i32* %ptr, i32 %value) { +; CHECK-LABEL: @test_phi_twogep_offset +; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] +; CHECK: phi i32 [ 4, %if.then ], [ 8, %if.else ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1 + br label %if.end + +if.else: + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between ptr and gep with ptr as base and nonconstant offset +define void @test_phi_onegep_nonconst_offset(i32* %ptr, i32 %value, i32 %off) { +; CHECK-LABEL: @test_phi_onegep_nonconst_offset +; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if.then ] +; CHECK: phi i32 [ 0, %entry ], [ %off, %if.then ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: + %gep = getelementptr inbounds i32, i32* %ptr, i32 %off + br label %if.end + +if.end: + %phi = phi i32* [ %ptr, %entry ], [ %gep, %if.then ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between two geps with same base, different nonconstant offsets +define void @test_phi_twogep_nonconst_offset(i32* %ptr, i32 %value, i32 %off1, i32 %off2) { +; CHECK-LABEL: @test_phi_twogep_nonconst_offset +; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] +; CHECK: phi i32 [ %off1, %if.then ], [ %off2, %if.else ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off1 + br label %if.end + +if.else: + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 %off2 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between two geps with different base, same constant offset +define void @test_phi_twogep_base(i32* %ptr1, i32* %ptr2, i32 %value) { +; CHECK-LABEL: @test_phi_twogep_base +; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] +; CHECK: phi i32* [ %ptr1, %if.then ], [ %ptr2, %if.else ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* %ptr1, i32 1 + br label %if.end + +if.else: + %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 1 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between two geps with different base global variables, same constant offset +define void @test_phi_twogep_base_gv(i32 %value) { +; CHECK-LABEL: @test_phi_twogep_base_gv +; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] +; CHECK: phi i32* [ @gv1, %if.then ], [ @gv2, %if.else ] +entry: + %cmp = icmp sgt i32 %value, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* @gv1, i32 1 + br label %if.end + +if.else: + %gep2 = getelementptr inbounds i32, i32* @gv2, i32 1 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ] + store i32 %value, i32* %phi, align 4 + ret void +} + +; Phi selects between ptr and gep with ptr as base and constant offset +define void @test_select_onegep_offset(i32* %ptr, i32 %value) { +; CHECK-LABEL: @test_select_onegep_offset +; CHECK-NOT: select i1 %cmp, i32* %ptr, i32* %gep +; CHECK: select i1 %cmp, i32 0, i32 4 +entry: + %cmp = icmp sgt i32 %value, 0 + %gep = getelementptr inbounds i32, i32* %ptr, i32 1 + %select = select i1 %cmp, i32* %ptr, i32* %gep + store i32 %value, i32* %select, align 4 + ret void +} + +; Select between two geps with same base, different constant offsets +define void @test_select_twogep_offset(i32* %ptr, i32 %value) { +; CHECK-LABEL: @test_select_twogep_offset +; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2 +; CHECK: select i1 %cmp, i32 4, i32 8 +entry: + %cmp = icmp sgt i32 %value, 0 + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1 + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2 + %select = select i1 %cmp, i32* %gep1, i32* %gep2 + store i32 %value, i32* %select, align 4 + ret void +} + +; Select between ptr and gep with ptr as base and nonconstant offset +define void @test_select_onegep_nonconst_offset(i32* %ptr, i32 %value, i32 %off) { +; CHECK-LABEL: @test_select_onegep_nonconst_offset +; CHECK-NOT: select i1 %cmp, i32* %ptr, i32* %gep +; CHECK: select i1 %cmp, i32 0, i32 %off +entry: + %cmp = icmp sgt i32 %value, 0 + %gep = getelementptr inbounds i32, i32* %ptr, i32 %off + %select = select i1 %cmp, i32* %ptr, i32* %gep + store i32 %value, i32* %select, align 4 + ret void +} + +; Select between two geps with same base, different nonconstant offsets +define void @test_select_twogep_nonconst_offset(i32* %ptr, i32 %value, i32 %off1, i32 %off2) { +; CHECK-LABEL: @test_select_twogep_nonconst_offset +; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2 +; CHECK: select i1 %cmp, i32 %off1, i32 %off2 +entry: + %cmp = icmp sgt i32 %value, 0 + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off1 + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 %off2 + %select = select i1 %cmp, i32* %gep1, i32* %gep2 + store i32 %value, i32* %select, align 4 + ret void +} + +; Select between two geps with different base, same constant offset +define void @test_select_twogep_base(i32* %ptr1, i32* %ptr2, i32 %value) { +; CHECK-LABEL: @test_select_twogep_base +; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2 +; CHECK: select i1 %cmp, i32* %ptr1, i32* %ptr2 +entry: + %cmp = icmp sgt i32 %value, 0 + %gep1 = getelementptr inbounds i32, i32* %ptr1, i32 1 + %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 1 + %select = select i1 %cmp, i32* %gep1, i32* %gep2 + store i32 %value, i32* %select, align 4 + ret void +} + +; Select between two geps with different base global variables, same constant offset +define void @test_select_twogep_base_gv(i32 %value) { +; CHECK-LABEL: @test_select_twogep_base_gv +; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2 +; CHECK: select i1 %cmp, i32* @gv1, i32* @gv2 +entry: + %cmp = icmp sgt i32 %value, 0 + %gep1 = getelementptr inbounds i32, i32* @gv1, i32 1 + %gep2 = getelementptr inbounds i32, i32* @gv2, i32 1 + %select = select i1 %cmp, i32* %gep1, i32* %gep2 + store i32 %value, i32* %select, align 4 + ret void +} + +; If the phi is in a different block to where the gep will be, the phi goes where +; the original phi was bit where the gep is. +; CHECK-LABEL: @test_phi_different_block +; CHECK-LABEL: if1.end +; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if1.then ] +; CHECK: phi i32 [ 0, %entry ], [ 4, %if1.then ] +define void @test_phi_different_block(i32* %ptr, i32 %value1, i32 %value2) { +entry: + %cmp1 = icmp sgt i32 %value1, 0 + br i1 %cmp1, label %if1.then, label %if1.end + +if1.then: + %gep = getelementptr inbounds i32, i32* %ptr, i32 1 + br label %if1.end + +if1.end: + %phi = phi i32* [ %ptr, %entry ], [ %gep, %if1.then ] + %cmp2 = icmp sgt i32 %value2, 0 + br i1 %cmp2, label %if2.then, label %if2.end + +if2.then: + store i32 %value1, i32* %ptr, align 4 + br label %if2.end + +if2.end: + store i32 %value2, i32* %phi, align 4 + ret void +} + +; A phi with three incoming values should be optimised +; CHECK-LABEL: @test_phi_threegep +; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else.then ], [ %gep3, %if.else.else ] +; CHECK: phi i32 [ 4, %if.then ], [ 8, %if.else.then ], [ 12, %if.else.else ] +define void @test_phi_threegep(i32* %ptr, i32 %value1, i32 %value2) { +entry: + %cmp1 = icmp sgt i32 %value1, 0 + br i1 %cmp1, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1 + br label %if.end + +if.else: + %cmp2 = icmp sgt i32 %value2, 0 + br i1 %cmp2, label %if.else.then, label %if.else.else + +if.else.then: + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2 + br label %if.end + +if.else.else: + %gep3 = getelementptr inbounds i32, i32* %ptr, i32 3 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else.then ], [ %gep3, %if.else.else ] + store i32 %value1, i32* %phi, align 4 + ret void +} + +; A phi with two incoming values but three geps due to nesting shouldn't be +; optimised +; CHECK-LABEL: @test_phi_threegep_nested +; CHECK: phi i32* [ %gep2, %if.else.then ], [ %gep3, %if.else.else ] +define void @test_phi_threegep_nested(i32* %ptr, i32 %value1, i32 %value2) { +entry: + %cmp1 = icmp sgt i32 %value1, 0 + br i1 %cmp1, label %if.then, label %if.else + +if.then: + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1 + br label %if.end + +if.else: + %cmp2 = icmp sgt i32 %value2, 0 + br i1 %cmp2, label %if.else.then, label %if.else.else + +if.else.then: + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2 + br label %if.else.end + +if.else.else: + %gep3 = getelementptr inbounds i32, i32* %ptr, i32 3 + br label %if.else.end + +if.else.end: + %gep4 = phi i32* [ %gep2, %if.else.then ], [ %gep3, %if.else.else ] + store i32 %value2, i32* %ptr, align 4 + br label %if.end + +if.end: + %phi = phi i32* [ %gep1, %if.then ], [ %gep4, %if.else.end ] + store i32 %value1, i32* %phi, align 4 + ret void +} + +; A nested select is expected to not be optimised +; CHECK-LABEL: @test_nested_select +; CHECK: select i1 %cmp2, i32* %gep1, i32* %gep2 +; CHECK: select i1 %cmp1, i32* %gep1, i32* %select1 +define void @test_nested_select(i32* %ptr, i32 %value1, i32 %value2) { +entry: + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1 + %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2 + %cmp1 = icmp sgt i32 %value1, 0 + %cmp2 = icmp sgt i32 %value2, 0 + %select1 = select i1 %cmp2, i32* %gep1, i32* %gep2 + %select2 = select i1 %cmp1, i32* %gep1, i32* %select1 + store i32 %value1, i32* %select2, align 4 + ret void +} + +; Scaling the offset by a different amount is expected not to be optimised +; CHECK-LABEL: @test_select_different_scale +; CHECK: select i1 %cmp, i32* %gep1, i32* %castgep +define void @test_select_different_scale(i32* %ptr, i32 %value, i32 %off) { +entry: + %cmp = icmp sgt i32 %value, 0 + %castptr = bitcast i32* %ptr to i16* + %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off + %gep2 = getelementptr inbounds i16, i16* %castptr, i32 %off + %castgep = bitcast i16* %gep2 to i32* + %select = select i1 %cmp, i32* %gep1, i32* %castgep + store i32 %value, i32* %select, align 4 + ret void +} + +; A select between two values is already the best we can do +; CHECK-LABEL: @test_select_trivial +; CHECK: select i1 %cmp, i32* %ptr1, i32* %ptr2 +define void @test_select_trivial(i32* %ptr1, i32* %ptr2, i32 %value) { +entey: + %cmp = icmp sgt i32 %value, 0 + %select = select i1 %cmp, i32* %ptr1, i32* %ptr2 + store i32 %value, i32* %select, align 4 + ret void +} + +; A select between two global variables is already the best we can do +; CHECK-LABEL: @test_select_trivial_gv +; CHECK: select i1 %cmp, i32* @gv1, i32* @gv2 +define void @test_select_trivial_gv(i32 %value) { +entey: + %cmp = icmp sgt i32 %value, 0 + %select = select i1 %cmp, i32* @gv1, i32* @gv2 + store i32 %value, i32* %select, align 4 + ret void +}