diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -1975,13 +1975,30 @@ std::swap(LHS, RHS); assert((isa(LHS) || isa(LHS)) && "Unexpected value!"); + // Prefer a freeze instruction on the left-hand side, because its operand + // can be replaced with rhs as well. + // if (freeze(x) == y) { use(x); => use(y); } + // This is valid because the branch raises UB if y is undef or poison. + FreezeInst *LHSFr = dyn_cast(LHS); + if (isa(RHS)) { + if (!LHSFr) { + LHSFr = cast(RHS); + std::swap(LHS, RHS); + } else { + // Both operands are freeze instructions. + // Since the benefit of replacing x with freeze isn't clear, + // don't replace x. + LHSFr = nullptr; + } + } + // If there is no obvious reason to prefer the left-hand side over the // right-hand side, ensure the longest lived term is on the right-hand side, // so the shortest lived term will be replaced by the longest lived. // This tends to expose more simplifications. uint32_t LVN = VN.lookupOrAdd(LHS); - if ((isa(LHS) && isa(RHS)) || - (isa(LHS) && isa(RHS))) { + if (!LHSFr && ((isa(LHS) && isa(RHS)) || + (isa(LHS) && isa(RHS)))) { // Move the 'oldest' value to the right-hand side, using the value number // as a proxy for age. uint32_t RVN = VN.lookupOrAdd(RHS); @@ -2006,12 +2023,25 @@ // Replace all occurrences of 'LHS' with 'RHS' everywhere in the scope. As // LHS always has at least one use that is not dominated by Root, this will // never do anything if LHS has only one use. - if (!LHS->hasOneUse()) { + bool HasOneUse = LHS->hasOneUse(); + if (LHSFr) + HasOneUse &= LHSFr->getOperand(0)->hasOneUse(); + + if (!HasOneUse) { unsigned NumReplacements = DominatesByEdge ? replaceDominatedUsesWith(LHS, RHS, *DT, Root) : replaceDominatedUsesWith(LHS, RHS, *DT, Root.getStart()); + if (LHSFr) { + // Perform "if (freeze(x) == y) { use(x); => use(y); }" + Value *LHS2 = LHSFr->getOperand(0); + NumReplacements += + DominatesByEdge + ? replaceDominatedUsesWith(LHS2, RHS, *DT, Root) + : replaceDominatedUsesWith(LHS2, RHS, *DT, Root.getStart()); + } + Changed |= NumReplacements > 0; NumGVNEqProp += NumReplacements; // Cached information for anything that uses LHS will be invalid. diff --git a/llvm/test/Transforms/GVN/freeze.ll b/llvm/test/Transforms/GVN/freeze.ll --- a/llvm/test/Transforms/GVN/freeze.ll +++ b/llvm/test/Transforms/GVN/freeze.ll @@ -63,7 +63,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X_FR]], [[Y:%.*]] ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: -; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]]) +; CHECK-NEXT: call void @foo(i32 [[Y]], i32 [[Y]]) ; CHECK-NEXT: ret void ; CHECK: B: ; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]]) @@ -86,7 +86,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X:%.*]], [[Y_FR]] ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: -; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]]) +; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[X]]) ; CHECK-NEXT: ret void ; CHECK: B: ; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]]) @@ -111,7 +111,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X]], [[Y_FR]] ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: -; CHECK-NEXT: call void @foo(i32 [[Y_FR]], i32 [[Y]]) +; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[X]]) ; CHECK-NEXT: ret void ; CHECK: B: ; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]]) @@ -139,7 +139,7 @@ ; CHECK-NEXT: [[C:%.*]] = and i1 [[C1]], [[C2:%.*]] ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: -; CHECK-NEXT: call void @foo(i32 [[Y_FR]], i32 [[Y]]) +; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[X]]) ; CHECK-NEXT: ret void ; CHECK: B: ; CHECK-NEXT: call void @foo(i32 [[X]], i32 [[Y]])