diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -168,6 +168,7 @@ Instruction *visitExtractValueInst(ExtractValueInst &EV); Instruction *visitLandingPadInst(LandingPadInst &LI); Instruction *visitVAEndInst(VAEndInst &I); + Value *PushFreezeToPreventPoisonFromPropagate(FreezeInst& FI); Instruction *visitFreeze(FreezeInst &I); /// Specify what to return for unhandled instructions. diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3513,6 +3513,48 @@ return nullptr; } +Value *InstCombinerImpl::PushFreezeToPreventPoisonFromPropagate(FreezeInst& OrigFI) { + // Try to push freeze through instructions that propagate but don't produce + // poison as far as possible. If operand of freeze follows three conditions + // 1) one-use, 2) does not produce poison and 3) has all but one + // guaranteed-non-poison operands then push the freeze through to the one + // operand that is not guaranteed non poison. The actual transform is as + // follows. + // Op1 = ... ; ; Op1 can be posion + // Op0 = Inst(Op1, NonPoisonOps...) ; Op0 has only one use and only have + // ; single guaranteed-non-poison operands + // ... = Freeze(Op0) + // => + // Op1 = ... + // Op1.fr = Freeze(Op1) + // ... = Inst(Op1.fr, NonPoisonOps...) + auto* OrigOp = OrigFI.getOperand(0); + if (auto *OrigOpInst = dyn_cast(OrigOp)) { + if (OrigOpInst->hasOneUse() && !canCreateUndefOrPoison(dyn_cast(OrigOp))) { + Optional MaybePoisonOperand; + for (Use &U : OrigOpInst->operands()) { + if (isGuaranteedNotToBeUndefOrPoison(U.get())) + continue; + if (!MaybePoisonOperand) + MaybePoisonOperand = &U; + else if(MaybePoisonOperand) + return nullptr; + } + + if (!MaybePoisonOperand.hasValue()) + return nullptr; + + auto *MaybePoisonUse = MaybePoisonOperand.getValue(); + auto *FrozenMaybePoisonOperand = new FreezeInst(MaybePoisonUse->get(), MaybePoisonUse->getUser()->getName() + ".fr"); + + replaceUse(*MaybePoisonUse, FrozenMaybePoisonOperand); + FrozenMaybePoisonOperand->insertBefore(OrigOpInst); + return OrigOp; + } + } + return nullptr; +} + Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) { Value *Op0 = I.getOperand(0); @@ -3525,6 +3567,9 @@ return NV; } + if (Value *V = PushFreezeToPreventPoisonFromPropagate(I)) + return replaceInstUsesWith(I, V); + if (match(Op0, m_Undef())) { // If I is freeze(undef), see its uses and fold it to the best constant. // - or: pick -1 diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll --- a/llvm/test/Transforms/InstCombine/freeze.ll +++ b/llvm/test/Transforms/InstCombine/freeze.ll @@ -86,3 +86,35 @@ call void @use_i32_i1(i32 %a, i1 %b) ret void } + +; Move the freeze forward to prevent poison from spreading. + +define i32 @early_freeze_test1(i32 %x, i32 %y) { +; CHECK-LABEL: @early_freeze_test1( +; CHECK-NEXT: [[V1:%.*]] = add i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1]] +; CHECK-NEXT: [[V2:%.*]] = shl i32 [[V1_FR]], 1 +; CHECK-NEXT: [[V3:%.*]] = and i32 [[V2]], 2 +; CHECK-NEXT: ret i32 [[V3]] +; + %v1 = add i32 %x, %y + %v2 = shl i32 %v1, 1 + %v3 = and i32 %v2, 2 + %v3.fr = freeze i32 %v3 + ret i32 %v3.fr +} + +define i1 @early_freeze_test2(i32* %ptr) { +; CHECK-LABEL: @early_freeze_test2( +; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1]] +; CHECK-NEXT: [[V2:%.*]] = and i32 [[V1_FR]], 1 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[V2]], 0 +; CHECK-NEXT: ret i1 [[COND]] +; + %v1 = load i32, i32* %ptr + %v2 = and i32 %v1, 1 + %cond = icmp eq i32 %v2, 0 + %cond.fr = freeze i1 %cond + ret i1 %cond.fr +}