diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -549,6 +549,51 @@ match(L2, m_Load(m_Specific(LHS)))); } +// Fold away bit casts of the loaded value by loading the desired type. +// We can do this for BitCastInsts as well as casts from and to pointer types, +// as long as those are noops (i.e., the source or dest type have the same +// bitwidth as the target's pointers). +static Instruction *combineIdenticalCastsIntoLoad(InstCombiner &IC, + LoadInst &LI) { + llvm::SmallVector Casts; + Type *DestTy = nullptr; + + // Visit every user of this load instruction, + for (User *U : LI.users()) { + // All users must be casts. + auto *CI = dyn_cast(U); + if (!CI) + return nullptr; + // If this is the first cast we've encountered, capture the dest type. + if (!DestTy) { + DestTy = CI->getDestTy(); + // Note that if load is atomic, we might not be able to change load type. + if (LI.isAtomic() && !isSupportedAtomicType(DestTy)) + return nullptr; + } + // All casts must have identical destination type. + if (DestTy != CI->getDestTy()) + return nullptr; + // The cast must actually be something that can be folded into load. + if (!CI->isNoopCast(IC.getDataLayout())) + return nullptr; + // Great, cast appears to be identical. + Casts.emplace_back(CI); + } + if (Casts.empty()) + return nullptr; // No users? + + // Great, can CSE casts and fold it into load. + LoadInst *NewLoad = IC.combineLoadToNewType(LI, DestTy); + for (CastInst *CI : Casts) { + CI->replaceAllUsesWith(NewLoad); + IC.eraseInstFromFunction(*CI); + } + assert(LI.use_empty() && "Failed to remove all users of the load!"); + + return &LI; +} + /// Combine loads to match the type of their uses' value after looking /// through intervening bitcasts. /// @@ -617,19 +662,9 @@ } } - // Fold away bit casts of the loaded value by loading the desired type. - // We can do this for BitCastInsts as well as casts from and to pointer types, - // as long as those are noops (i.e., the source or dest type have the same - // bitwidth as the target's pointers). - if (LI.hasOneUse()) - if (auto* CI = dyn_cast(LI.user_back())) - if (CI->isNoopCast(DL)) - if (!LI.isAtomic() || isSupportedAtomicType(CI->getDestTy())) { - LoadInst *NewLoad = IC.combineLoadToNewType(LI, CI->getDestTy()); - CI->replaceAllUsesWith(NewLoad); - IC.eraseInstFromFunction(*CI); - return &LI; - } + // If all users of this load are identical casts, fold them into load. + if (Instruction *NewLoad = combineIdenticalCastsIntoLoad(IC, LI)) + return NewLoad; // FIXME: We should also canonicalize loads of vectors when their elements are // cast to other types. diff --git a/llvm/test/Transforms/InstCombine/multi-use-load-casts.ll b/llvm/test/Transforms/InstCombine/multi-use-load-casts.ll --- a/llvm/test/Transforms/InstCombine/multi-use-load-casts.ll +++ b/llvm/test/Transforms/InstCombine/multi-use-load-casts.ll @@ -7,7 +7,8 @@ define void @t0(i1 zeroext %c0, i1 zeroext %c1, i64* nocapture readonly %src) { ; CHECK-LABEL: @t0( ; CHECK-NEXT: bb: -; CHECK-NEXT: [[DATA:%.*]] = load i64, i64* [[SRC:%.*]], align 8 +; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64* [[SRC:%.*]] to i32** +; CHECK-NEXT: [[DATA1:%.*]] = load i32*, i32** [[TMP0]], align 8 ; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB3:%.*]], label [[BB7:%.*]] ; CHECK: bb3: ; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB4:%.*]], label [[BB5:%.*]] @@ -15,12 +16,10 @@ ; CHECK-NEXT: tail call void @abort() ; CHECK-NEXT: unreachable ; CHECK: bb5: -; CHECK-NEXT: [[PTR0:%.*]] = inttoptr i64 [[DATA]] to i32* -; CHECK-NEXT: tail call void @sink0(i32* [[PTR0]]) +; CHECK-NEXT: tail call void @sink0(i32* [[DATA1]]) ; CHECK-NEXT: br label [[BB9:%.*]] ; CHECK: bb7: -; CHECK-NEXT: [[PTR1:%.*]] = inttoptr i64 [[DATA]] to i32* -; CHECK-NEXT: tail call void @sink1(i32* [[PTR1]]) +; CHECK-NEXT: tail call void @sink1(i32* [[DATA1]]) ; CHECK-NEXT: br label [[BB9]] ; CHECK: bb9: ; CHECK-NEXT: ret void