Skip to content

Commit f445e39

Browse files
authored
[SimplifyCFG] Use isWritableObject() API (#110127)
SimplifyCFG store speculation currently has some homegrown code to check for a writable object, handling the alloca special case only. Switch it to use the generic isWritableObject() API, which means that we also support byval arguments, allocator return values, and writable arguments. I've adjusted isWritableObject() to also check for the noalias attribute when handling writable. Otherwise, I don't think that we can generalize from at-entry writability. This was not relevant for previous uses of the function, because they'd already require noalias for other reasons anyway.
1 parent dd2792a commit f445e39

File tree

3 files changed

+20
-24
lines changed

3 files changed

+20
-24
lines changed

llvm/lib/Analysis/AliasAnalysis.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,10 @@ bool llvm::isWritableObject(const Value *Object,
892892
return true;
893893

894894
if (auto *A = dyn_cast<Argument>(Object)) {
895-
if (A->hasAttribute(Attribute::Writable)) {
895+
// Also require noalias, otherwise writability at function entry cannot be
896+
// generalized to writability at other program points, even if the pointer
897+
// does not escape.
898+
if (A->hasAttribute(Attribute::Writable) && A->hasNoAliasAttr()) {
896899
ExplicitlyDereferenceableOnly = true;
897900
return true;
898901
}

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/Analysis/DomTreeUpdater.h"
2929
#include "llvm/Analysis/GuardUtils.h"
3030
#include "llvm/Analysis/InstructionSimplify.h"
31+
#include "llvm/Analysis/Loads.h"
3132
#include "llvm/Analysis/MemorySSA.h"
3233
#include "llvm/Analysis/MemorySSAUpdater.h"
3334
#include "llvm/Analysis/TargetTransformInfo.h"
@@ -3057,16 +3058,17 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
30573058
if (auto *LI = dyn_cast<LoadInst>(&CurI)) {
30583059
if (LI->getPointerOperand() == StorePtr && LI->getType() == StoreTy &&
30593060
LI->isSimple() && LI->getAlign() >= StoreToHoist->getAlign()) {
3060-
// Local objects (created by an `alloca` instruction) are always
3061-
// writable, so once we are past a read from a location it is valid to
3062-
// also write to that same location.
3063-
// If the address of the local object never escapes the function, that
3064-
// means it's never concurrently read or written, hence moving the store
3065-
// from under the condition will not introduce a data race.
3066-
auto *AI = dyn_cast<AllocaInst>(getUnderlyingObject(StorePtr));
3067-
if (AI && !PointerMayBeCaptured(AI, false, true))
3061+
Value *Obj = getUnderlyingObject(StorePtr);
3062+
bool ExplicitlyDereferenceableOnly;
3063+
if (isWritableObject(Obj, ExplicitlyDereferenceableOnly) &&
3064+
!PointerMayBeCaptured(Obj, /*ReturnCaptures=*/false,
3065+
/*StoreCaptures=*/true) &&
3066+
(!ExplicitlyDereferenceableOnly ||
3067+
isDereferenceablePointer(StorePtr, StoreTy,
3068+
LI->getDataLayout()))) {
30683069
// Found a previous load, return it.
30693070
return LI;
3071+
}
30703072
}
30713073
// The load didn't work out, but we may still find a store.
30723074
}

llvm/test/Transforms/SimplifyCFG/speculate-store.ll

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,8 @@ define i64 @load_before_store_noescape_byval(ptr byval([2 x i32]) %a, i64 %i, i3
201201
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i64 0, i64 [[I:%.*]]
202202
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
203203
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[V]], [[B:%.*]]
204-
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
205-
; CHECK: if.then:
206-
; CHECK-NEXT: store i32 [[B]], ptr [[ARRAYIDX]], align 4
207-
; CHECK-NEXT: br label [[IF_END]]
208-
; CHECK: if.end:
204+
; CHECK-NEXT: [[SPEC_STORE_SELECT:%.*]] = select i1 [[CMP]], i32 [[B]], i32 [[V]]
205+
; CHECK-NEXT: store i32 [[SPEC_STORE_SELECT]], ptr [[ARRAYIDX]], align 4
209206
; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[A]], align 8
210207
; CHECK-NEXT: ret i64 [[V2]]
211208
;
@@ -235,11 +232,8 @@ define i64 @load_before_store_noescape_malloc(i64 %i, i32 %b) {
235232
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i64 0, i64 [[I:%.*]]
236233
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
237234
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[V]], [[B:%.*]]
238-
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
239-
; CHECK: if.then:
240-
; CHECK-NEXT: store i32 [[B]], ptr [[ARRAYIDX]], align 4
241-
; CHECK-NEXT: br label [[IF_END]]
242-
; CHECK: if.end:
235+
; CHECK-NEXT: [[SPEC_STORE_SELECT:%.*]] = select i1 [[CMP]], i32 [[B]], i32 [[V]]
236+
; CHECK-NEXT: store i32 [[SPEC_STORE_SELECT]], ptr [[ARRAYIDX]], align 4
243237
; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[A]], align 8
244238
; CHECK-NEXT: ret i64 [[V2]]
245239
;
@@ -267,11 +261,8 @@ define i64 @load_before_store_noescape_writable(ptr noalias writable dereference
267261
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i64 0, i64 1
268262
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
269263
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[V]], [[B:%.*]]
270-
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
271-
; CHECK: if.then:
272-
; CHECK-NEXT: store i32 [[B]], ptr [[ARRAYIDX]], align 4
273-
; CHECK-NEXT: br label [[IF_END]]
274-
; CHECK: if.end:
264+
; CHECK-NEXT: [[SPEC_STORE_SELECT:%.*]] = select i1 [[CMP]], i32 [[B]], i32 [[V]]
265+
; CHECK-NEXT: store i32 [[SPEC_STORE_SELECT]], ptr [[ARRAYIDX]], align 4
275266
; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[A]], align 8
276267
; CHECK-NEXT: ret i64 [[V2]]
277268
;

0 commit comments

Comments
 (0)