Skip to content

Commit

Permalink
[ConstantFold] Remove non-trivial gep-of-gep fold
Browse files Browse the repository at this point in the history
This fold is subtly incorrect, because DL-unaware constant folding
does not know the correct index type to use, and just performs the
addition in the type that happens to already be there. This is
incorrect, since sext(X)+sext(Y) is generally not the same as
sext(X+Y). See the `@constexpr_gep_of_gep_with_narrow_type()` for
a miscompile with the current implementation.

One could try to restrict the fold to cases where no overflow
occurs, but I'm not bothering with that here, because the
DL-aware constant folding will take care of this anyway. I've
only kept the straightforward zero-index case, where we just
concatenate two GEPs.
  • Loading branch information
nikic committed May 30, 2024
1 parent 2b9c158 commit 1f0b92d
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 119 deletions.
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/2011-12-19-init-list-ctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ struct S {
// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr @arr, i32 0, i32 1), ptr noundef @.str)
// CHECK: store i32 1, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 1)
// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr @arr, i64 1, i32 1), ptr noundef @.str.1)
// CHECK: store i32 2, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 2)
// CHECK: store i32 2, ptr getelementptr inbounds (%struct.S, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 1), i64 1)
// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr @arr, i64 2, i32 1), ptr noundef @.str.2)
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ std::initializer_list<std::initializer_list<int>> nested = {
// CHECK-DYNAMIC-BL: store i32 5, ptr @_ZGR6nested2_
// CHECK-DYNAMIC-BL: store i32 {{.*}}, ptr getelementptr inbounds (i32, ptr @_ZGR6nested2_, i64 1)
// CHECK-DYNAMIC-BL: store ptr @_ZGR6nested2_,
// CHECK-DYNAMIC-BL: ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 2), align 8
// CHECK-DYNAMIC-BL: store i64 2, ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 2, i32 1), align 8
// CHECK-DYNAMIC-BL: ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 1), i64 1), align 8
// CHECK-DYNAMIC-BL: store i64 2, ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 1), i64 1, i32 1), align 8
// CHECK-DYNAMIC-BL: store ptr @_ZGR6nested_,
// CHECK-DYNAMIC-BL: ptr @nested, align 8
// CHECK-DYNAMIC-BL: store i64 3, ptr getelementptr inbounds ({{.*}}, ptr @nested, i32 0, i32 1), align 8
Expand Down Expand Up @@ -123,9 +123,9 @@ std::initializer_list<std::initializer_list<int>> nested = {
// CHECK-DYNAMIC-BE: store i32 5, ptr @_ZGR6nested2_
// CHECK-DYNAMIC-BE: store i32 {{.*}}, ptr getelementptr inbounds (i32, ptr @_ZGR6nested2_, i64 1)
// CHECK-DYNAMIC-BE: store ptr @_ZGR6nested2_,
// CHECK-DYNAMIC-BE: ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 2), align 8
// CHECK-DYNAMIC-BE: ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 1), i64 1), align 8
// CHECK-DYNAMIC-BE: store ptr getelementptr inbounds ([2 x i32], ptr @_ZGR6nested2_, i64 0, i64 2),
// CHECK-DYNAMIC-BE: ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 2, i32 1), align 8
// CHECK-DYNAMIC-BE: ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr @_ZGR6nested_, i64 1), i64 1, i32 1), align 8
// CHECK-DYNAMIC-BE: store ptr @_ZGR6nested_,
// CHECK-DYNAMIC-BE: ptr @nested, align 8
// CHECK-DYNAMIC-BE: store ptr getelementptr inbounds ([3 x {{.*}}], ptr @_ZGR6nested_, i64 0, i64 3),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ namespace partly_constant {
//
// Third init list.
// CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
// CHECK: store ptr {{.*}}@[[PARTLY_CONSTANT_THIRD]]{{.*}}, ptr getelementptr inbounds ({{.*}}, ptr {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 2)
// CHECK: store i64 4, ptr getelementptr inbounds ({{.*}}, ptr {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 2, i32 1)
// CHECK: store ptr {{.*}}@[[PARTLY_CONSTANT_THIRD]]{{.*}}, ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 1), i64 1)
// CHECK: store i64 4, ptr getelementptr inbounds ({{.*}}, ptr getelementptr inbounds ({{.*}}, ptr {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 1), i64 1, i32 1)
// CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
//
// Outer init list.
Expand Down
96 changes: 48 additions & 48 deletions clang/test/OpenMP/threadprivate_codegen.cpp

Large diffs are not rendered by default.

56 changes: 4 additions & 52 deletions llvm/lib/IR/ConstantFold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1429,64 +1429,16 @@ static Constant *foldGEPOfGEP(GEPOperator *GEP, Type *PointeeTy, bool InBounds,
if (GEP->getInRange())
return nullptr;

// Only handle simple case with leading zero index. We cannot perform an
// actual addition as we don't know the correct index type size to use.
Constant *Idx0 = cast<Constant>(Idxs[0]);
if (Idx0->isNullValue()) {
// Handle the simple case of a zero index.
SmallVector<Value*, 16> NewIndices;
NewIndices.reserve(Idxs.size() + GEP->getNumIndices());
NewIndices.append(GEP->idx_begin(), GEP->idx_end());
NewIndices.append(Idxs.begin() + 1, Idxs.end());
return ConstantExpr::getGetElementPtr(
GEP->getSourceElementType(), cast<Constant>(GEP->getPointerOperand()),
NewIndices, InBounds && GEP->isInBounds());
}

gep_type_iterator LastI = gep_type_end(GEP);
for (gep_type_iterator I = gep_type_begin(GEP), E = gep_type_end(GEP);
I != E; ++I)
LastI = I;

// We can't combine GEPs if the last index is a struct type.
if (!LastI.isSequential())
return nullptr;
// We could perform the transform with non-constant index, but prefer leaving
// it as GEP of GEP rather than GEP of add for now.
ConstantInt *CI = dyn_cast<ConstantInt>(Idx0);
if (!CI)
return nullptr;

// TODO: This code may be extended to handle vectors as well.
auto *LastIdx = cast<Constant>(GEP->getOperand(GEP->getNumOperands()-1));
Type *LastIdxTy = LastIdx->getType();
if (LastIdxTy->isVectorTy())
if (!Idx0->isNullValue())
return nullptr;

SmallVector<Value*, 16> NewIndices;
NewIndices.reserve(Idxs.size() + GEP->getNumIndices());
NewIndices.append(GEP->idx_begin(), GEP->idx_end() - 1);

// Add the last index of the source with the first index of the new GEP.
// Make sure to handle the case when they are actually different types.
if (LastIdxTy != Idx0->getType()) {
unsigned CommonExtendedWidth =
std::max(LastIdxTy->getIntegerBitWidth(),
Idx0->getType()->getIntegerBitWidth());
CommonExtendedWidth = std::max(CommonExtendedWidth, 64U);

Type *CommonTy =
Type::getIntNTy(LastIdxTy->getContext(), CommonExtendedWidth);
if (Idx0->getType() != CommonTy)
Idx0 = ConstantFoldCastInstruction(Instruction::SExt, Idx0, CommonTy);
if (LastIdx->getType() != CommonTy)
LastIdx =
ConstantFoldCastInstruction(Instruction::SExt, LastIdx, CommonTy);
if (!Idx0 || !LastIdx)
return nullptr;
}

NewIndices.push_back(ConstantExpr::get(Instruction::Add, Idx0, LastIdx));
NewIndices.append(GEP->idx_begin(), GEP->idx_end());
NewIndices.append(Idxs.begin() + 1, Idxs.end());

return ConstantExpr::getGetElementPtr(
GEP->getSourceElementType(), cast<Constant>(GEP->getPointerOperand()),
NewIndices, InBounds && GEP->isInBounds());
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Other/constant-fold-gep.ll
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@

; Fold GEP of a GEP. Very simple cases are folded without targetdata.

; PLAIN: @Y = global ptr getelementptr inbounds ([3 x { i32, i32 }], ptr @ext, i64 2)
; PLAIN: @Y = global ptr getelementptr inbounds ([3 x { i32, i32 }], ptr getelementptr inbounds ([3 x { i32, i32 }], ptr @ext, i64 1), i64 1)
; PLAIN: @Z = global ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([3 x { i32, i32 }], ptr @ext, i64 0, i64 1, i32 0), i64 1)
; OPT: @Y = local_unnamed_addr global ptr getelementptr inbounds (i8, ptr @ext, i64 48)
; OPT: @Z = local_unnamed_addr global ptr getelementptr inbounds (i8, ptr @ext, i64 12)
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/gepgep.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ declare void @use(ptr)

define void @f() {
; CHECK-LABEL: define void @f() {
; CHECK-NEXT: call void @use(ptr getelementptr (i8, ptr @buffer, i64 add (i64 sub (i64 0, i64 ptrtoint (ptr @buffer to i64)), i64 127)))
; CHECK-NEXT: call void @use(ptr getelementptr (i8, ptr getelementptr (i8, ptr @buffer, i64 add (i64 sub (i64 0, i64 ptrtoint (ptr @buffer to i64)), i64 63)), i64 64))
; CHECK-NEXT: ret void
;
call void @use(ptr getelementptr (i8, ptr getelementptr (i8, ptr @buffer, i64 add (i64 sub (i64 0, i64 ptrtoint (ptr @buffer to i64)), i64 63)), i64 64))
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/InstCombine/getelementptr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1683,10 +1683,9 @@ if.else:

@g = external global i8

; FIXME: This is a miscompile
define ptr @constexpr_gep_of_gep_with_narrow_type() {
; CHECK-LABEL: @constexpr_gep_of_gep_with_narrow_type(
; CHECK-NEXT: ret ptr getelementptr (i8, ptr @g, i64 -2)
; CHECK-NEXT: ret ptr getelementptr (i8, ptr @g, i64 254)
;
ret ptr getelementptr (i8, ptr getelementptr (i8, ptr @g, i8 127), i8 127)
}
Expand Down
16 changes: 8 additions & 8 deletions llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ define void @constant_fold_ptrtoint_of_gep_of_nullgep() {
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 1234) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 1234) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 1234) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 0)
; LLPARSER-NEXT: call void @use_i64(i64 0)
; LLPARSER-NEXT: call void @use_i64(i64 0)
; LLPARSER-NEXT: call void @use_i64(i64 0)
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; LLPARSER-NEXT: ret void
;
; INSTSIMPLIFY-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() {
Expand All @@ -83,10 +83,10 @@ define void @constant_fold_ptrtoint_of_gep_of_nullgep() {
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 1234) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 1234) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 1234) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 0)
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 0)
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 0)
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 0)
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i64 -1), i64 1) to i64))
; INSTSIMPLIFY-NEXT: ret void
;
; INSTCOMBINE-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() {
Expand Down

0 comments on commit 1f0b92d

Please sign in to comment.