[ConstantFolding] Unify handling of load from uniform value

There are a number of places that specially handle loads from a
uniform value where all the bits are the same (zero, one, undef,
poison), because we a) don't care about the load offset in that
case and b) it bypasses casts that might not be legal generally
but do work with uniform values.

We had multiple implementations of this, with a different set of
supported values each time, as well as incomplete type checks in
some cases. In particular, this fixes the assertion reported in
https://reviews.llvm.org/D114889#3198921, as well as a similar
assertion that could be triggered via constant folding.

Differential Revision: https://reviews.llvm.org/D115924
This commit is contained in:
Nikita Popov 2021-12-17 10:31:50 +01:00
parent d3abb04e14
commit 9fd4f80e33
6 changed files with 60 additions and 29 deletions

View File

@ -134,8 +134,8 @@ void g6() {
f6m(1, 2, 3, 4, 5, s);
}
// CHECK: define{{.*}} void @g6
// CHECK: call void @f6(i32 1, [4 x i32] [i32 6, i32 7, i32 0, i32 0])
// CHECK: call void @f6m(i32 1, i32 2, i32 3, i32 4, i32 5, [4 x i32] [i32 6, i32 7, i32 0, i32 0])
// CHECK: call void @f6(i32 1, [4 x i32] [i32 6, i32 7, i32 0, i32 undef])
// CHECK: call void @f6m(i32 1, i32 2, i32 3, i32 4, i32 5, [4 x i32] [i32 6, i32 7, i32 0, i32 undef])
// CHECK: declare void @f6(i32, [4 x i32])
// CHECK: declare void @f6m(i32, i32, i32, i32, i32, [4 x i32])
}

View File

@ -148,6 +148,12 @@ Constant *ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty, APInt Offset,
Constant *ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty,
const DataLayout &DL);
/// If C is a uniform value where all bits are the same (either all zero, all
/// ones, all undef or all poison), return the corresponding uniform value in
/// the new type. If the value is not uniform or the result cannot be
/// represented, return null.
Constant *ConstantFoldLoadFromUniformValue(Constant *C, Type *Ty);
/// ConstantFoldLoadThroughGEPConstantExpr - Given a constant and a
/// getelementptr constantexpr, return the constant value being addressed by the
/// constant expression, or null if something is funny and we can't decide.

View File

@ -106,11 +106,8 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
"Invalid constantexpr bitcast!");
// Catch the obvious splat cases.
if (C->isNullValue() && !DestTy->isX86_MMXTy() && !DestTy->isX86_AMXTy())
return Constant::getNullValue(DestTy);
if (C->isAllOnesValue() && !DestTy->isX86_MMXTy() && !DestTy->isX86_AMXTy() &&
!DestTy->isPtrOrPtrVectorTy()) // Don't get ones for ptr types!
return Constant::getAllOnesValue(DestTy);
if (Constant *Res = ConstantFoldLoadFromUniformValue(C, DestTy))
return Res;
if (auto *VTy = dyn_cast<VectorType>(C->getType())) {
// Handle a vector->scalar integer/fp cast.
@ -362,16 +359,8 @@ Constant *llvm::ConstantFoldLoadThroughBitcast(Constant *C, Type *DestTy,
// Catch the obvious splat cases (since all-zeros can coerce non-integral
// pointers legally).
if (C->isNullValue() && !DestTy->isX86_MMXTy() && !DestTy->isX86_AMXTy())
return Constant::getNullValue(DestTy);
if (C->isAllOnesValue() &&
(DestTy->isIntegerTy() || DestTy->isFloatingPointTy() ||
DestTy->isVectorTy()) &&
!DestTy->isX86_AMXTy() && !DestTy->isX86_MMXTy() &&
!DestTy->isPtrOrPtrVectorTy())
// Get ones when the input is trivial, but
// only for supported types inside getAllOnesValue.
return Constant::getAllOnesValue(DestTy);
if (Constant *Res = ConstantFoldLoadFromUniformValue(C, DestTy))
return Res;
// If the type sizes are the same and a cast is legal, just directly
// cast the constant.
@ -704,16 +693,13 @@ Constant *llvm::ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty,
Offset, DL))
return Result;
// If this load comes from anywhere in a constant global, and if the global
// is all undef or zero, we know what it loads.
if (auto *GV = dyn_cast<GlobalVariable>(getUnderlyingObject(C))) {
if (GV->isConstant() && GV->hasDefinitiveInitializer()) {
if (GV->getInitializer()->isNullValue())
return Constant::getNullValue(Ty);
if (isa<UndefValue>(GV->getInitializer()))
return UndefValue::get(Ty);
}
}
// If this load comes from anywhere in a uniform constant global, the value
// is always the same, regardless of the loaded offset.
if (auto *GV = dyn_cast<GlobalVariable>(getUnderlyingObject(C)))
if (GV->isConstant() && GV->hasDefinitiveInitializer())
if (Constant *Res =
ConstantFoldLoadFromUniformValue(GV->getInitializer(), Ty))
return Res;
return nullptr;
}
@ -724,6 +710,19 @@ Constant *llvm::ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty,
return ConstantFoldLoadFromConstPtr(C, Ty, Offset, DL);
}
Constant *llvm::ConstantFoldLoadFromUniformValue(Constant *C, Type *Ty) {
if (isa<PoisonValue>(C))
return PoisonValue::get(Ty);
if (isa<UndefValue>(C))
return UndefValue::get(Ty);
if (C->isNullValue() && !Ty->isX86_MMXTy() && !Ty->isX86_AMXTy())
return Constant::getNullValue(Ty);
if (C->isAllOnesValue() &&
(Ty->isIntOrIntVectorTy() || Ty->isFPOrFPVectorTy()))
return Constant::getAllOnesValue(Ty);
return nullptr;
}
namespace {
/// One of Op0/Op1 is a constant expression.

View File

@ -305,8 +305,9 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
else if (auto *LI = dyn_cast<LoadInst>(U)) {
// A load from zeroinitializer is always zeroinitializer, regardless of
// any applied offset.
if (Init->isNullValue()) {
LI->replaceAllUsesWith(Constant::getNullValue(LI->getType()));
if (Constant *Res =
ConstantFoldLoadFromUniformValue(Init, LI->getType())) {
LI->replaceAllUsesWith(Res);
EraseFromParent(LI);
continue;
}

View File

@ -0,0 +1,12 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -globalopt < %s | FileCheck %s
@m64 = internal global <1 x i64> zeroinitializer
define i32 @load_mmx() {
; CHECK-LABEL: @load_mmx(
; CHECK-NEXT: ret i32 0
;
%temp = load x86_mmx, x86_mmx* bitcast (<1 x i64>* @m64 to x86_mmx*)
ret i32 0
}

View File

@ -280,3 +280,16 @@ define { i64, i64 } @test_load_struct() {
%v = load { i64, i64 }, { i64, i64 }* @g3
ret { i64, i64 } %v
}
@m64 = internal constant [2 x i64] zeroinitializer
@idx = external global i32
; This should not try to create an x86_mmx null value.
define x86_mmx @load_mmx() {
; CHECK-LABEL: @load_mmx(
; CHECK-NEXT: [[TEMP:%.*]] = load x86_mmx, x86_mmx* bitcast (i64* getelementptr ([2 x i64], [2 x i64]* @m64, i64 0, i64 ptrtoint (i32* @idx to i64)) to x86_mmx*), align 8
; CHECK-NEXT: ret x86_mmx [[TEMP]]
;
%temp = load x86_mmx, x86_mmx* bitcast (i64* getelementptr ([2 x i64], [2 x i64]* @m64, i64 0, i64 ptrtoint (i32* @idx to i64)) to x86_mmx*)
ret x86_mmx %temp
}