diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp @@ -660,7 +660,8 @@ // FIXME: This is overconservative; this transform is allowed in some cases // for atomic operations. - if (FirstLI->isAtomic()) + const bool isAtomic = FirstLI->isAtomic(); + if (isAtomic) return nullptr; // When processing loads, we need to propagate two bits of information to the @@ -668,9 +669,9 @@ // don't sink loads when some have their alignment specified and some don't. // visitLoadInst will propagate an alignment onto the load when TD is around, // and if TD isn't around, we can't handle the mixed case. - bool isVolatile = FirstLI->isVolatile(); + const bool isVolatile = FirstLI->isVolatile(); Align LoadAlignment = FirstLI->getAlign(); - unsigned LoadAddrSpace = FirstLI->getPointerAddressSpace(); + const unsigned LoadAddrSpace = FirstLI->getPointerAddressSpace(); // We can't sink the load if the loaded value could be modified between the // load and the PHI. @@ -685,17 +686,19 @@ FirstLI->getParent()->getTerminator()->getNumSuccessors() != 1) return nullptr; - // Check to see if all arguments are the same operation. for (unsigned i = 1, e = PN.getNumIncomingValues(); i != e; ++i) { LoadInst *LI = dyn_cast(PN.getIncomingValue(i)); if (!LI || !LI->hasOneUser()) return nullptr; + // Make sure all arguments are the same type of operation. + if (LI->isAtomic() != isAtomic || LI->isVolatile() != isVolatile || + LI->getPointerAddressSpace() != LoadAddrSpace) + return nullptr; + // We can't sink the load if the loaded value could be modified between // the load and the PHI. - if (LI->isVolatile() != isVolatile || - LI->getParent() != PN.getIncomingBlock(i) || - LI->getPointerAddressSpace() != LoadAddrSpace || + if (LI->getParent() != PN.getIncomingBlock(i) || !isSafeAndProfitableToSinkLoad(LI)) return nullptr; diff --git a/llvm/test/Transforms/InstCombine/pr51435.ll b/llvm/test/Transforms/InstCombine/pr51435.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/pr51435.ll @@ -0,0 +1,48 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +; Generated from: +; +; #include +; +; struct foo { +; unsigned int non_atomic; +; atomic_uint atomic; +; }; +; +; unsigned int foo(struct foo* foo, int c) { +; return c ? foo->non_atomic +; : atomic_load_explicit(&foo->atomic, memory_order_acquire); +; } + +%struct.foo = type { i32, i32 } + +; Atomic and non-atomic loads should not be combined. +define dso_local i32 @foo(%struct.foo* %foo, i32 %c) { +; CHECK-LABEL: @foo +; CHECK: cond.true: +; CHECK-NEXT: [[NON_ATOMIC_PTR:%.*]] = getelementptr inbounds %struct.foo, %struct.foo* %foo, i64 0, i32 0 +; CHECK-NEXT: [[NON_ATOMIC:%.*]] = load i32, i32* [[NON_ATOMIC_PTR]], align 4 +; CHECK: cond.false: +; CHECK-NEXT: [[ATOMIC_PTR:%.*]] = getelementptr inbounds %struct.foo, %struct.foo* %foo, i64 0, i32 1 +; CHECK-NEXT: [[ATOMIC:%.*]] = load atomic i32, i32* [[ATOMIC_PTR]] acquire, align 4 +; CHECK: cond.end: +; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[NON_ATOMIC]], %cond.true ], [ [[ATOMIC]], %cond.false ] +; CHECK-NEXT: ret i32 [[COND]] +entry: + %tobool = icmp ne i32 %c, 0 + br i1 %tobool, label %cond.true, label %cond.false + +cond.true: + %non_atomic = getelementptr inbounds %struct.foo, %struct.foo* %foo, i64 0, i32 0 + %0 = load i32, i32* %non_atomic, align 4 + br label %cond.end + +cond.false: + %atomic = getelementptr inbounds %struct.foo, %struct.foo* %foo, i64 0, i32 1 + %1 = load atomic i32, i32* %atomic acquire, align 4 + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ %1, %cond.false ] + ret i32 %cond +}