Skip to content

Commit d710f14

Browse files
jroelofsmemfrob
authored andcommitted
[GlobalOpt] Fix a miscompile when evaluating struct initializers.
The bug was that evaluateBitcastFromPtr attempts a narrowing to a struct's 0th element of a store that covers other elements. While this is okay on the load side, applying it to stores causes us to miss the writes to the additionally covered elements. rdar://79503568 Differential revision: https://reviews.llvm.org/D105838
1 parent bad161f commit d710f14

File tree

2 files changed

+59
-13
lines changed

2 files changed

+59
-13
lines changed

llvm/lib/Transforms/Utils/Evaluator.cpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,16 @@ static bool isSimpleEnoughPointerToCommit(Constant *C, const DataLayout &DL) {
174174
return false;
175175
}
176176

177-
/// Apply 'Func' to Ptr. If this returns nullptr, introspect the pointer's
178-
/// type and walk down through the initial elements to obtain additional
179-
/// pointers to try. Returns the first non-null return value from Func, or
180-
/// nullptr if the type can't be introspected further.
177+
/// Apply \p TryLoad to Ptr. If this returns \p nullptr, introspect the
178+
/// pointer's type and walk down through the initial elements to obtain
179+
/// additional pointers to try. Returns the first non-null return value from
180+
/// \p TryLoad, or \p nullptr if the type can't be introspected further.
181181
static Constant *
182182
evaluateBitcastFromPtr(Constant *Ptr, const DataLayout &DL,
183183
const TargetLibraryInfo *TLI,
184-
std::function<Constant *(Constant *)> Func) {
184+
std::function<Constant *(Constant *)> TryLoad) {
185185
Constant *Val;
186-
while (!(Val = Func(Ptr))) {
186+
while (!(Val = TryLoad(Ptr))) {
187187
// If Ty is a non-opaque struct, we can convert the pointer to the struct
188188
// into a pointer to its first member.
189189
// FIXME: This could be extended to support arrays as well.
@@ -211,9 +211,11 @@ static Constant *getInitializer(Constant *C) {
211211
Constant *Evaluator::ComputeLoadResult(Constant *P, Type *Ty) {
212212
// If this memory location has been recently stored, use the stored value: it
213213
// is the most up-to-date.
214-
auto findMemLoc = [this](Constant *Ptr) { return MutatedMemory.lookup(Ptr); };
214+
auto TryFindMemLoc = [this](Constant *Ptr) {
215+
return MutatedMemory.lookup(Ptr);
216+
};
215217

216-
if (Constant *Val = findMemLoc(P))
218+
if (Constant *Val = TryFindMemLoc(P))
217219
return Val;
218220

219221
// Access it.
@@ -237,7 +239,7 @@ Constant *Evaluator::ComputeLoadResult(Constant *P, Type *Ty) {
237239
// If it hasn't, we may still be able to find a stored pointer by
238240
// introspecting the type.
239241
Constant *Val =
240-
evaluateBitcastFromPtr(CE->getOperand(0), DL, TLI, findMemLoc);
242+
evaluateBitcastFromPtr(CE->getOperand(0), DL, TLI, TryFindMemLoc);
241243
if (!Val)
242244
Val = getInitializer(CE->getOperand(0));
243245
if (Val)
@@ -369,17 +371,25 @@ bool Evaluator::EvaluateBlock(BasicBlock::iterator CurInst, BasicBlock *&NextBB,
369371
// legal. If it's not, we can try introspecting the type to find a
370372
// legal conversion.
371373

372-
auto castValTy = [&](Constant *P) -> Constant * {
373-
Type *Ty = cast<PointerType>(P->getType())->getElementType();
374-
if (Constant *FV = ConstantFoldLoadThroughBitcast(Val, Ty, DL)) {
374+
auto TryCastValTy = [&](Constant *P) -> Constant * {
375+
// The conversion is illegal if the store is wider than the
376+
// pointee proposed by `evaluateBitcastFromPtr`, since that would
377+
// drop stores to other struct elements when the caller attempts to
378+
// look through a struct's 0th element.
379+
Type *NewTy = cast<PointerType>(P->getType())->getElementType();
380+
Type *STy = Val->getType();
381+
if (DL.getTypeSizeInBits(NewTy) < DL.getTypeSizeInBits(STy))
382+
return nullptr;
383+
384+
if (Constant *FV = ConstantFoldLoadThroughBitcast(Val, NewTy, DL)) {
375385
Ptr = P;
376386
return FV;
377387
}
378388
return nullptr;
379389
};
380390

381391
Constant *NewVal =
382-
evaluateBitcastFromPtr(CE->getOperand(0), DL, TLI, castValTy);
392+
evaluateBitcastFromPtr(CE->getOperand(0), DL, TLI, TryCastValTy);
383393
if (!NewVal) {
384394
LLVM_DEBUG(dbgs() << "Failed to bitcast constant ptr, can not "
385395
"evaluate.\n");
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; RUN: opt < %s -globalopt -S -o - | FileCheck %s
2+
3+
%class.Class = type { i8, i8, i8, i8 }
4+
@A = local_unnamed_addr global %class.Class undef, align 4
5+
@B = local_unnamed_addr global %class.Class undef, align 4
6+
7+
@llvm.global_ctors = appending global [2 x { i32, void ()*, i8* }] [
8+
{ i32, void ()*, i8* } { i32 65535, void ()* @initA, i8* null },
9+
{ i32, void ()*, i8* } { i32 65535, void ()* @initB, i8* null }
10+
]
11+
12+
define internal void @initA() section "__TEXT,__StaticInit,regular,pure_instructions" {
13+
entry:
14+
store i32 -1, i32* bitcast (%class.Class* @A to i32*), align 4
15+
ret void
16+
}
17+
18+
define internal void @initB() section "__TEXT,__StaticInit,regular,pure_instructions" {
19+
entry:
20+
store i8 -1, i8* bitcast (%class.Class* @B to i8*), align 4
21+
ret void
22+
}
23+
24+
; rdar://79503568
25+
; Check that we don't miscompile when the store covers the whole struct.
26+
; CHECK-NOT: @A = local_unnamed_addr global %class.Class { i8 -1, i8 undef, i8 undef, i8 undef }, align 4
27+
28+
; FIXME: We could optimzie this as { i8 -1, i8 -1, i8 -1, i8 -1 } if constant folding were a little smarter.
29+
; CHECK: @A = local_unnamed_addr global %class.Class undef, align 4
30+
31+
; Check that we still perform the transform when store is smaller than the width of the 0th element.
32+
; CHECK: @B = local_unnamed_addr global %class.Class { i8 -1, i8 undef, i8 undef, i8 undef }, align 4
33+
34+
; CHECK: define internal void @initA()
35+
; CHECK-NOT: define internal void @initB()
36+

0 commit comments

Comments
 (0)