[llvm] [polly] [InstCombine][asan] Don't speculate loads before `select ptr` (PR #100773)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 29 10:35:56 PDT 2024
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/100773
>From dd4531e7e87934589f223e470d133bf1b9cf1a6c Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 26 Jul 2024 16:42:06 -0700
Subject: [PATCH 1/4] [NFC][InstCombine][SROA][Asan] Precommit test affected by
#100773
Some optimization need to be undone with
sanitizers by #100773.
Pull Request: https://github.com/llvm/llvm-project/pull/100844
---
llvm/test/Transforms/InstCombine/load.ll | 10 +++++
.../multiple-uses-load-bitcast-select.ll | 24 ++++++++++
.../InstCombine/ptr-replace-alloca.ll | 15 +++++++
llvm/test/Transforms/InstCombine/strnlen-2.ll | 10 +++++
llvm/test/Transforms/SROA/phi-and-select.ll | 21 +++++++++
.../SROA/phi-with-duplicate-pred.ll | 45 +++++++++++++++++++
llvm/test/Transforms/SROA/select-load.ll | 24 +++++++++-
7 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/InstCombine/load.ll b/llvm/test/Transforms/InstCombine/load.ll
index 7d53c8ee35684..032f9098e1093 100644
--- a/llvm/test/Transforms/InstCombine/load.ll
+++ b/llvm/test/Transforms/InstCombine/load.ll
@@ -56,6 +56,16 @@ define i32 @test5(i1 %C) {
ret i32 %Z
}
+define i32 @test5_asan(i1 %C) sanitize_address {
+; CHECK-LABEL: @test5_asan(
+; CHECK-NEXT: [[Z:%.*]] = select i1 [[C:%.*]], i32 42, i32 47
+; CHECK-NEXT: ret i32 [[Z]]
+;
+ %Y = select i1 %C, ptr @X, ptr @X2 ; <ptr> [#uses=1]
+ %Z = load i32, ptr %Y ; <i32> [#uses=1]
+ ret i32 %Z
+}
+
define i32 @load_gep_null_inbounds(i64 %X) {
; CHECK-LABEL: @load_gep_null_inbounds(
; CHECK-NEXT: store i1 true, ptr poison, align 1
diff --git a/llvm/test/Transforms/InstCombine/multiple-uses-load-bitcast-select.ll b/llvm/test/Transforms/InstCombine/multiple-uses-load-bitcast-select.ll
index b2440bfb07b15..38fca0314ae11 100644
--- a/llvm/test/Transforms/InstCombine/multiple-uses-load-bitcast-select.ll
+++ b/llvm/test/Transforms/InstCombine/multiple-uses-load-bitcast-select.ll
@@ -25,3 +25,27 @@ define void @PR35618(ptr %st1, ptr %st2) {
ret void
}
+define void @PR35618_asan(ptr %st1, ptr %st2) sanitize_address {
+; CHECK-LABEL: @PR35618_asan(
+; CHECK-NEXT: [[Y1:%.*]] = alloca double, align 8
+; CHECK-NEXT: [[Z1:%.*]] = alloca double, align 8
+; CHECK-NEXT: [[LD1:%.*]] = load double, ptr [[Y1]], align 8
+; CHECK-NEXT: [[LD2:%.*]] = load double, ptr [[Z1]], align 8
+; CHECK-NEXT: [[TMP:%.*]] = fcmp olt double [[LD1]], [[LD2]]
+; CHECK-NEXT: [[TMP12_V:%.*]] = select i1 [[TMP]], double [[LD1]], double [[LD2]]
+; CHECK-NEXT: store double [[TMP12_V]], ptr [[ST1:%.*]], align 8
+; CHECK-NEXT: store double [[TMP12_V]], ptr [[ST2:%.*]], align 8
+; CHECK-NEXT: ret void
+;
+ %y1 = alloca double
+ %z1 = alloca double
+ %ld1 = load double, ptr %y1
+ %ld2 = load double, ptr %z1
+ %tmp = fcmp olt double %ld1, %ld2
+ %sel = select i1 %tmp, ptr %y1, ptr %z1
+ %tmp12 = load i64, ptr %sel
+ store i64 %tmp12, ptr %st1
+ store i64 %tmp12, ptr %st2
+ ret void
+}
+
diff --git a/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll b/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
index 7c65a93a00436..00f8f50cb4f02 100644
--- a/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
+++ b/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
@@ -427,6 +427,21 @@ entry:
ret i8 %load
}
+define i8 @select_diff_addrspace_remove_alloca_asan(i1 %cond, ptr %p) sanitize_address {
+; CHECK-LABEL: @select_diff_addrspace_remove_alloca_asan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ %alloca = alloca [32 x i8]
+ call void @llvm.memcpy.p0.p1.i64(ptr %alloca, ptr addrspace(1) @g2, i64 32, i1 false)
+ %gep = getelementptr inbounds [32 x i8], ptr %alloca, i32 0, i32 2
+ %sel = select i1 %cond, ptr %alloca, ptr %gep
+ %gep2 = getelementptr inbounds i8, ptr %sel, i64 4
+ %load = load i8, ptr %gep2
+ ret i8 %load
+}
+
declare i8 @readonly_callee(ptr readonly nocapture)
; FIXME: This should be able to fold to call i8 @readonly_callee(ptr nonnull @g1)
diff --git a/llvm/test/Transforms/InstCombine/strnlen-2.ll b/llvm/test/Transforms/InstCombine/strnlen-2.ll
index 5e95aaf8edcd9..36da6df20528c 100644
--- a/llvm/test/Transforms/InstCombine/strnlen-2.ll
+++ b/llvm/test/Transforms/InstCombine/strnlen-2.ll
@@ -38,6 +38,16 @@ define i64 @fold_strnlen_s3_s5_1(i1 %C) {
ret i64 %len
}
+define i64 @fold_strnlen_s3_s5_1_asan(i1 %C) sanitize_address {
+; CHECK-LABEL: @fold_strnlen_s3_s5_1_asan(
+; CHECK-NEXT: ret i64 1
+;
+ %ptr = select i1 %C, ptr @s3, ptr @s6
+
+ %len = call i64 @strnlen(ptr %ptr, i64 1)
+ ret i64 %len
+}
+
; Fold strnlen (C ? s3 : s5, 3) to 3.
diff --git a/llvm/test/Transforms/SROA/phi-and-select.ll b/llvm/test/Transforms/SROA/phi-and-select.ll
index 7c8b27c9de9c0..d237b437b9ce0 100644
--- a/llvm/test/Transforms/SROA/phi-and-select.ll
+++ b/llvm/test/Transforms/SROA/phi-and-select.ll
@@ -344,6 +344,27 @@ entry:
ret i32 %loaded
}
+; We should not unconditionally load with sanitizers.
+define i32 @test9_asan(i32 %b, ptr %ptr) sanitize_address {
+; Same as @test8 but for a select rather than a PHI node.
+;
+; CHECK-LABEL: @test9_asan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-NEXT: [[LOADED_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i32, ptr [[PTR]], align 4
+; CHECK-NEXT: [[LOADED_SROA_SPECULATED:%.*]] = select i1 [[TEST]], i32 undef, i32 [[LOADED_SROA_SPECULATE_LOAD_FALSE]]
+; CHECK-NEXT: ret i32 [[LOADED_SROA_SPECULATED]]
+;
+entry:
+ %f = alloca float
+ store i32 0, ptr %ptr
+ %test = icmp ne i32 %b, 0
+ %select = select i1 %test, ptr %f, ptr %ptr
+ %loaded = load i32, ptr %select, align 4
+ ret i32 %loaded
+}
+
define float @test10(i32 %b, ptr %ptr) {
; Don't try to promote allocas which are not elligible for it even after
; rewriting due to the necessity of inserting bitcasts when speculating a PHI
diff --git a/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll b/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
index 96262b44f58e0..38ee760c82527 100644
--- a/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
+++ b/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
@@ -52,6 +52,51 @@ cleanup7: ; preds = %cleanup
ret void
}
+define void @f2_hwasan(i1 %c1) sanitize_hwaddress {
+; CHECK-LABEL: @f2_hwasan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C1:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: br label [[CLEANUP:%.*]]
+; CHECK: cleanup:
+; CHECK-NEXT: [[G_0_SROA_SPECULATE_LOAD_CLEANUP:%.*]] = load i16, ptr @a, align 1
+; CHECK-NEXT: switch i32 2, label [[CLEANUP7:%.*]] [
+; CHECK-NEXT: i32 0, label [[LBL1:%.*]]
+; CHECK-NEXT: i32 2, label [[LBL1]]
+; CHECK-NEXT: ]
+; CHECK: if.else:
+; CHECK-NEXT: br label [[LBL1]]
+; CHECK: lbl1:
+; CHECK-NEXT: [[G_0_SROA_SPECULATED:%.*]] = phi i16 [ [[G_0_SROA_SPECULATE_LOAD_CLEANUP]], [[CLEANUP]] ], [ [[G_0_SROA_SPECULATE_LOAD_CLEANUP]], [[CLEANUP]] ], [ undef, [[IF_ELSE]] ]
+; CHECK-NEXT: unreachable
+; CHECK: cleanup7:
+; CHECK-NEXT: ret void
+;
+entry:
+ %e = alloca i16, align 1
+ br i1 %c1, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ br label %cleanup
+
+cleanup: ; preds = %if.then
+ switch i32 2, label %cleanup7 [
+ i32 0, label %lbl1
+ i32 2, label %lbl1
+ ]
+
+if.else: ; preds = %entry
+ br label %lbl1
+
+lbl1: ; preds = %if.else, %cleanup, %cleanup
+ %g.0 = phi ptr [ @a, %cleanup ], [ @a, %cleanup ], [ %e, %if.else ]
+ %0 = load i16, ptr %g.0, align 1
+ unreachable
+
+cleanup7: ; preds = %cleanup
+ ret void
+}
+
define void @f3(i1 %c1) {
; CHECK-LABEL: @f3(
; CHECK-NEXT: entry:
diff --git a/llvm/test/Transforms/SROA/select-load.ll b/llvm/test/Transforms/SROA/select-load.ll
index 7df72419b0ce5..0ebc8d71b83f6 100644
--- a/llvm/test/Transforms/SROA/select-load.ll
+++ b/llvm/test/Transforms/SROA/select-load.ll
@@ -36,7 +36,7 @@ entry:
%st.args = type { i32, ptr }
; A bitcasted load and a direct load of select.
-define void @test_multiple_loads_select(i1 %cmp){
+define void @test_multiple_loads_select(i1 %cmp) {
; CHECK-LABEL: @test_multiple_loads_select(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADDR_I8_SROA_SPECULATED:%.*]] = select i1 [[CMP:%.*]], ptr undef, ptr undef
@@ -57,6 +57,28 @@ entry:
ret void
}
+; Sanitizer will break optimization.
+define void @test_multiple_loads_select_asan(i1 %cmp) sanitize_address {
+; CHECK-LABEL: @test_multiple_loads_select_asan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[ADDR_I8_SROA_SPECULATED:%.*]] = select i1 [[CMP:%.*]], ptr undef, ptr undef
+; CHECK-NEXT: call void @foo_i8(ptr [[ADDR_I8_SROA_SPECULATED]])
+; CHECK-NEXT: [[ADDR_I32_SROA_SPECULATED:%.*]] = select i1 [[CMP]], ptr undef, ptr undef
+; CHECK-NEXT: call void @foo_i32(ptr [[ADDR_I32_SROA_SPECULATED]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %args = alloca [2 x %st.args], align 16
+ %arr1 = getelementptr inbounds [2 x %st.args], ptr %args, i64 0, i64 1
+ %sel = select i1 %cmp, ptr %arr1, ptr %args
+ %addr = getelementptr inbounds %st.args, ptr %sel, i64 0, i32 1
+ %addr.i8 = load ptr, ptr %addr, align 8
+ call void @foo_i8(ptr %addr.i8)
+ %addr.i32 = load ptr, ptr %addr, align 8
+ call void @foo_i32 (ptr %addr.i32)
+ ret void
+}
+
declare void @foo_i8(ptr)
declare void @foo_i32(ptr)
>From a33fda5a7ffc8a032fc12c44cc23e94ff141cf76 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 26 Jul 2024 11:18:44 -0700
Subject: [PATCH 2/4] [NFC][Load] Find better place for
`mustSuppressSpeculation`
And extract `suppressSpeculativeLoadForSanitizers`.
Pull Request: https://github.com/llvm/llvm-project/pull/100794
---
llvm/include/llvm/Analysis/Loads.h | 7 +++++++
llvm/include/llvm/Analysis/ValueTracking.h | 7 -------
llvm/lib/Analysis/Loads.cpp | 13 +++++++++++++
llvm/lib/Analysis/ValueTracking.cpp | 11 -----------
4 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h
index 33e817828b754..38f86f77b4158 100644
--- a/llvm/include/llvm/Analysis/Loads.h
+++ b/llvm/include/llvm/Analysis/Loads.h
@@ -106,6 +106,13 @@ bool isSafeToLoadUnconditionally(Value *V, Type *Ty, Align Alignment,
const DominatorTree *DT = nullptr,
const TargetLibraryInfo *TLI = nullptr);
+/// Return true if speculation of the given load must be suppressed to avoid
+/// ordering or interfering with an active sanitizer. If not suppressed,
+/// dereferenceability and alignment must be proven separately. Note: This
+/// is only needed for raw reasoning; if you use the interface below
+/// (isSafeToSpeculativelyExecute), this is handled internally.
+bool mustSuppressSpeculation(const LoadInst &LI);
+
/// The default number of maximum instructions to scan in the block, used by
/// FindAvailableLoadedValue().
extern cl::opt<unsigned> DefMaxInstsToScan;
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 5ef6e43483906..96fa16970584d 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -792,13 +792,6 @@ bool onlyUsedByLifetimeMarkers(const Value *V);
/// droppable instructions.
bool onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V);
-/// Return true if speculation of the given load must be suppressed to avoid
-/// ordering or interfering with an active sanitizer. If not suppressed,
-/// dereferenceability and alignment must be proven separately. Note: This
-/// is only needed for raw reasoning; if you use the interface below
-/// (isSafeToSpeculativelyExecute), this is handled internally.
-bool mustSuppressSpeculation(const LoadInst &LI);
-
/// Return true if the instruction does not have any effects besides
/// calculating the result and does not have undefined behavior.
///
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 61c6aa5e5a3eb..1704f0db4c599 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -345,6 +345,19 @@ bool llvm::isDereferenceableAndAlignedInLoop(LoadInst *LI, Loop *L,
HeaderFirstNonPHI, AC, &DT);
}
+static bool suppressSpeculativeLoadForSanitizers(const Instruction &CtxI) {
+ const Function &F = *CtxI.getFunction();
+ // Speculative load may create a race that did not exist in the source.
+ return F.hasFnAttribute(Attribute::SanitizeThread) ||
+ // Speculative load may load data from dirty regions.
+ F.hasFnAttribute(Attribute::SanitizeAddress) ||
+ F.hasFnAttribute(Attribute::SanitizeHWAddress);
+}
+
+bool llvm::mustSuppressSpeculation(const LoadInst &LI) {
+ return !LI.isUnordered() || suppressSpeculativeLoadForSanitizers(LI);
+}
+
/// Check if executing a load of this pointer value cannot trap.
///
/// If DT and ScanFrom are specified this method performs context-sensitive
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index bfd26fadd237b..497f6eafd22d8 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6798,17 +6798,6 @@ bool llvm::onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V) {
V, /* AllowLifetime */ true, /* AllowDroppable */ true);
}
-bool llvm::mustSuppressSpeculation(const LoadInst &LI) {
- if (!LI.isUnordered())
- return true;
- const Function &F = *LI.getFunction();
- // Speculative load may create a race that did not exist in the source.
- return F.hasFnAttribute(Attribute::SanitizeThread) ||
- // Speculative load may load data from dirty regions.
- F.hasFnAttribute(Attribute::SanitizeAddress) ||
- F.hasFnAttribute(Attribute::SanitizeHWAddress);
-}
-
bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst,
const Instruction *CtxI,
AssumptionCache *AC,
>From 0fce1bac9c6e4dee341e16d3ed657fe8696334c4 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 26 Jul 2024 11:04:32 -0700
Subject: [PATCH 3/4] [NFC][Load] Make `ScanFrom` required parameters
In #100773 we will go conservative for sanitizers,
so it's better to pinpoint location consciously.
Pull Request: https://github.com/llvm/llvm-project/pull/100789
---
llvm/include/llvm/Analysis/Loads.h | 6 ++----
polly/lib/Analysis/ScopBuilder.cpp | 2 +-
polly/lib/Analysis/ScopDetection.cpp | 3 ++-
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h
index 38f86f77b4158..1f01ff7027fa9 100644
--- a/llvm/include/llvm/Analysis/Loads.h
+++ b/llvm/include/llvm/Analysis/Loads.h
@@ -69,8 +69,7 @@ bool isDereferenceableAndAlignedPointer(const Value *V, Align Alignment,
/// quick local scan of the basic block containing ScanFrom, to determine if
/// the address is already accessed.
bool isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &Size,
- const DataLayout &DL,
- Instruction *ScanFrom = nullptr,
+ const DataLayout &DL, Instruction *ScanFrom,
AssumptionCache *AC = nullptr,
const DominatorTree *DT = nullptr,
const TargetLibraryInfo *TLI = nullptr);
@@ -100,8 +99,7 @@ bool isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE,
/// quick local scan of the basic block containing ScanFrom, to determine if
/// the address is already accessed.
bool isSafeToLoadUnconditionally(Value *V, Type *Ty, Align Alignment,
- const DataLayout &DL,
- Instruction *ScanFrom = nullptr,
+ const DataLayout &DL, Instruction *ScanFrom,
AssumptionCache *AC = nullptr,
const DominatorTree *DT = nullptr,
const TargetLibraryInfo *TLI = nullptr);
diff --git a/polly/lib/Analysis/ScopBuilder.cpp b/polly/lib/Analysis/ScopBuilder.cpp
index d594823410f5f..0b9a1a916e1c1 100644
--- a/polly/lib/Analysis/ScopBuilder.cpp
+++ b/polly/lib/Analysis/ScopBuilder.cpp
@@ -2770,7 +2770,7 @@ isl::set ScopBuilder::getNonHoistableCtx(MemoryAccess *Access,
auto &DL = scop->getFunction().getDataLayout();
if (isSafeToLoadUnconditionally(LI->getPointerOperand(), LI->getType(),
- LI->getAlign(), DL)) {
+ LI->getAlign(), DL, nullptr)) {
SafeToLoad = isl::set::universe(AccessRelation.get_space().range());
} else if (BB != LI->getParent()) {
// Skip accesses in non-affine subregions as they might not be executed
diff --git a/polly/lib/Analysis/ScopDetection.cpp b/polly/lib/Analysis/ScopDetection.cpp
index eab7bd83e6a4e..79db3965de023 100644
--- a/polly/lib/Analysis/ScopDetection.cpp
+++ b/polly/lib/Analysis/ScopDetection.cpp
@@ -490,7 +490,8 @@ bool ScopDetection::onlyValidRequiredInvariantLoads(
for (auto NonAffineRegion : Context.NonAffineSubRegionSet) {
if (isSafeToLoadUnconditionally(Load->getPointerOperand(),
- Load->getType(), Load->getAlign(), DL))
+ Load->getType(), Load->getAlign(), DL,
+ nullptr))
continue;
if (NonAffineRegion->contains(Load) &&
>From 6c404f78e56a3376aebd3be261086ba42c71f571 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 26 Jul 2024 09:18:32 -0700
Subject: [PATCH 4/4] [InstCombine][asan] Don't speculate loads before `select
ptr`
Even if memory is valid from `llvm` point of view,
e.g. local alloca, sanitizers have API for user
specific memory annotations.
This annotations can be used to track size of the
local object, e.g. inline vector like may prevent
accessed beyond the current vector size.
So valid programs should not access those parts of
alloca before checking preconditions.
Fixes #100639.
Pull Request: https://github.com/llvm/llvm-project/pull/100773
---
llvm/lib/Analysis/Loads.cpp | 8 +-
llvm/test/Transforms/InstCombine/load.ll | 4 +-
.../InstCombine/ptr-replace-alloca.ll | 4 +-
.../Transforms/InstCombine/select-load.ll | 101 ++++++++++++++++++
llvm/test/Transforms/InstCombine/strnlen-2.ll | 7 +-
llvm/test/Transforms/SROA/phi-and-select.ll | 27 +++--
.../SROA/phi-with-duplicate-pred.ll | 5 +-
llvm/test/Transforms/SROA/select-load.ll | 41 +++++--
8 files changed, 174 insertions(+), 23 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/select-load.ll
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 1704f0db4c599..a88469ab81a8c 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -378,8 +378,12 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &S
// If DT is not specified we can't make context-sensitive query
const Instruction* CtxI = DT ? ScanFrom : nullptr;
if (isDereferenceableAndAlignedPointer(V, Alignment, Size, DL, CtxI, AC, DT,
- TLI))
- return true;
+ TLI)) {
+ // With sanitizers `Dereferenceable` is not always enough for unconditional
+ // load.
+ if (!ScanFrom || !suppressSpeculativeLoadForSanitizers(*ScanFrom))
+ return true;
+ }
if (!ScanFrom)
return false;
diff --git a/llvm/test/Transforms/InstCombine/load.ll b/llvm/test/Transforms/InstCombine/load.ll
index 032f9098e1093..6c087aa87845f 100644
--- a/llvm/test/Transforms/InstCombine/load.ll
+++ b/llvm/test/Transforms/InstCombine/load.ll
@@ -56,9 +56,11 @@ define i32 @test5(i1 %C) {
ret i32 %Z
}
+; FIXME: Constants should be allowed for this optimization.
define i32 @test5_asan(i1 %C) sanitize_address {
; CHECK-LABEL: @test5_asan(
-; CHECK-NEXT: [[Z:%.*]] = select i1 [[C:%.*]], i32 42, i32 47
+; CHECK-NEXT: [[Y:%.*]] = select i1 [[C:%.*]], ptr @X, ptr @X2
+; CHECK-NEXT: [[Z:%.*]] = load i32, ptr [[Y]], align 4
; CHECK-NEXT: ret i32 [[Z]]
;
%Y = select i1 %C, ptr @X, ptr @X2 ; <ptr> [#uses=1]
diff --git a/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll b/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
index 00f8f50cb4f02..a1d10c274c465 100644
--- a/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
+++ b/llvm/test/Transforms/InstCombine/ptr-replace-alloca.ll
@@ -430,7 +430,9 @@ entry:
define i8 @select_diff_addrspace_remove_alloca_asan(i1 %cond, ptr %p) sanitize_address {
; CHECK-LABEL: @select_diff_addrspace_remove_alloca_asan(
; CHECK-NEXT: entry:
-; CHECK-NEXT: ret i8 0
+; CHECK-NEXT: [[GEP2:%.*]] = select i1 [[COND:%.*]], ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @g2, i64 4), ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @g2, i64 6)
+; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr addrspace(1) [[GEP2]], align 1
+; CHECK-NEXT: ret i8 [[LOAD]]
;
entry:
%alloca = alloca [32 x i8]
diff --git a/llvm/test/Transforms/InstCombine/select-load.ll b/llvm/test/Transforms/InstCombine/select-load.ll
new file mode 100644
index 0000000000000..36883423aea36
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-load.ll
@@ -0,0 +1,101 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-grtev4-linux-gnu"
+
+define i32 @test_plain(i1 %f) {
+; CHECK-LABEL: @test_plain(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 8
+; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 8
+; CHECK-NEXT: [[L:%.*]] = select i1 [[F:%.*]], i32 [[A_VAL]], i32 [[B_VAL]]
+; CHECK-NEXT: ret i32 [[L]]
+;
+entry:
+ %a = alloca i32, align 8
+ %b = alloca i32, align 8
+ %sel = select i1 %f, ptr %a, ptr %b
+ %l = load i32, ptr %sel, align 8
+ ret i32 %l
+}
+
+; Don't speculate as the condition may control which memory is valid from
+; sanitizer perspective.
+define i32 @test_asan(i1 %f) sanitize_address {
+; CHECK-LABEL: @test_asan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[F:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[SEL]], align 8
+; CHECK-NEXT: ret i32 [[L]]
+;
+entry:
+ %a = alloca i32, align 8
+ %b = alloca i32, align 8
+ %sel = select i1 %f, ptr %a, ptr %b
+ %l = load i32, ptr %sel, align 8
+ ret i32 %l
+}
+
+
+; Don't speculate as the condition may control which memory is valid from
+; sanitizer perspective.
+define i32 @test_hwasan(i1 %f) sanitize_hwaddress {
+; CHECK-LABEL: @test_hwasan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[F:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[SEL]], align 8
+; CHECK-NEXT: ret i32 [[L]]
+;
+entry:
+ %a = alloca i32, align 8
+ %b = alloca i32, align 8
+ %sel = select i1 %f, ptr %a, ptr %b
+ %l = load i32, ptr %sel, align 8
+ ret i32 %l
+}
+
+; Don't speculate as the condition may control which memory is valid from
+; sanitizer perspective.
+define i32 @test_tsan(i1 %f) sanitize_thread {
+; CHECK-LABEL: @test_tsan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[F:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[SEL]], align 8
+; CHECK-NEXT: ret i32 [[L]]
+;
+entry:
+ %a = alloca i32, align 8
+ %b = alloca i32, align 8
+ %sel = select i1 %f, ptr %a, ptr %b
+ %l = load i32, ptr %sel, align 8
+ ret i32 %l
+}
+
+; Msan just propagates shadow, even if speculated load accesses uninitialized
+; value, instrumentation will select shadow of the desired value anyway.
+define i32 @test_msan(i1 %f) sanitize_memory {
+; CHECK-LABEL: @test_msan(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca i32, align 8
+; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 8
+; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 8
+; CHECK-NEXT: [[L:%.*]] = select i1 [[F:%.*]], i32 [[A_VAL]], i32 [[B_VAL]]
+; CHECK-NEXT: ret i32 [[L]]
+;
+entry:
+ %a = alloca i32, align 8
+ %b = alloca i32, align 8
+ %sel = select i1 %f, ptr %a, ptr %b
+ %l = load i32, ptr %sel, align 8
+ ret i32 %l
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-2.ll b/llvm/test/Transforms/InstCombine/strnlen-2.ll
index 36da6df20528c..9c042409a43f3 100644
--- a/llvm/test/Transforms/InstCombine/strnlen-2.ll
+++ b/llvm/test/Transforms/InstCombine/strnlen-2.ll
@@ -38,9 +38,14 @@ define i64 @fold_strnlen_s3_s5_1(i1 %C) {
ret i64 %len
}
+; FIXME: Constants should be allowed for this optimization.
define i64 @fold_strnlen_s3_s5_1_asan(i1 %C) sanitize_address {
; CHECK-LABEL: @fold_strnlen_s3_s5_1_asan(
-; CHECK-NEXT: ret i64 1
+; CHECK-NEXT: [[PTR:%.*]] = select i1 [[C:%.*]], ptr @s3, ptr @s6
+; CHECK-NEXT: [[STRNLEN_CHAR0:%.*]] = load i8, ptr [[PTR]], align 1
+; CHECK-NEXT: [[STRNLEN_CHAR0CMP:%.*]] = icmp ne i8 [[STRNLEN_CHAR0]], 0
+; CHECK-NEXT: [[LEN:%.*]] = zext i1 [[STRNLEN_CHAR0CMP]] to i64
+; CHECK-NEXT: ret i64 [[LEN]]
;
%ptr = select i1 %C, ptr @s3, ptr @s6
diff --git a/llvm/test/Transforms/SROA/phi-and-select.ll b/llvm/test/Transforms/SROA/phi-and-select.ll
index d237b437b9ce0..616617b079828 100644
--- a/llvm/test/Transforms/SROA/phi-and-select.ll
+++ b/llvm/test/Transforms/SROA/phi-and-select.ll
@@ -348,13 +348,26 @@ entry:
define i32 @test9_asan(i32 %b, ptr %ptr) sanitize_address {
; Same as @test8 but for a select rather than a PHI node.
;
-; CHECK-LABEL: @test9_asan(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
-; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
-; CHECK-NEXT: [[LOADED_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i32, ptr [[PTR]], align 4
-; CHECK-NEXT: [[LOADED_SROA_SPECULATED:%.*]] = select i1 [[TEST]], i32 undef, i32 [[LOADED_SROA_SPECULATE_LOAD_FALSE]]
-; CHECK-NEXT: ret i32 [[LOADED_SROA_SPECULATED]]
+; CHECK-PRESERVE-CFG-LABEL: @test9_asan(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[F:%.*]] = alloca float, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-PRESERVE-CFG-NEXT: [[SELECT:%.*]] = select i1 [[TEST]], ptr [[F]], ptr [[PTR]]
+; CHECK-PRESERVE-CFG-NEXT: [[LOADED:%.*]] = load i32, ptr [[SELECT]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[LOADED]]
+;
+; CHECK-MODIFY-CFG-LABEL: @test9_asan(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-MODIFY-CFG-NEXT: [[LOADED_ELSE_VAL:%.*]] = load i32, ptr [[PTR]], align 4
+; CHECK-MODIFY-CFG-NEXT: br i1 [[TEST]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[LOADED:%.*]] = phi i32 [ undef, [[ENTRY_THEN]] ], [ [[LOADED_ELSE_VAL]], [[ENTRY:%.*]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[LOADED]]
;
entry:
%f = alloca float
diff --git a/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll b/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
index 38ee760c82527..76e00a95e2caf 100644
--- a/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
+++ b/llvm/test/Transforms/SROA/phi-with-duplicate-pred.ll
@@ -55,11 +55,11 @@ cleanup7: ; preds = %cleanup
define void @f2_hwasan(i1 %c1) sanitize_hwaddress {
; CHECK-LABEL: @f2_hwasan(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[E:%.*]] = alloca i16, align 1
; CHECK-NEXT: br i1 [[C1:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[CLEANUP:%.*]]
; CHECK: cleanup:
-; CHECK-NEXT: [[G_0_SROA_SPECULATE_LOAD_CLEANUP:%.*]] = load i16, ptr @a, align 1
; CHECK-NEXT: switch i32 2, label [[CLEANUP7:%.*]] [
; CHECK-NEXT: i32 0, label [[LBL1:%.*]]
; CHECK-NEXT: i32 2, label [[LBL1]]
@@ -67,7 +67,8 @@ define void @f2_hwasan(i1 %c1) sanitize_hwaddress {
; CHECK: if.else:
; CHECK-NEXT: br label [[LBL1]]
; CHECK: lbl1:
-; CHECK-NEXT: [[G_0_SROA_SPECULATED:%.*]] = phi i16 [ [[G_0_SROA_SPECULATE_LOAD_CLEANUP]], [[CLEANUP]] ], [ [[G_0_SROA_SPECULATE_LOAD_CLEANUP]], [[CLEANUP]] ], [ undef, [[IF_ELSE]] ]
+; CHECK-NEXT: [[G_0:%.*]] = phi ptr [ @a, [[CLEANUP]] ], [ @a, [[CLEANUP]] ], [ [[E]], [[IF_ELSE]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[G_0]], align 1
; CHECK-NEXT: unreachable
; CHECK: cleanup7:
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/SROA/select-load.ll b/llvm/test/Transforms/SROA/select-load.ll
index 0ebc8d71b83f6..9de765071b535 100644
--- a/llvm/test/Transforms/SROA/select-load.ll
+++ b/llvm/test/Transforms/SROA/select-load.ll
@@ -59,13 +59,36 @@ entry:
; Sanitizer will break optimization.
define void @test_multiple_loads_select_asan(i1 %cmp) sanitize_address {
-; CHECK-LABEL: @test_multiple_loads_select_asan(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[ADDR_I8_SROA_SPECULATED:%.*]] = select i1 [[CMP:%.*]], ptr undef, ptr undef
-; CHECK-NEXT: call void @foo_i8(ptr [[ADDR_I8_SROA_SPECULATED]])
-; CHECK-NEXT: [[ADDR_I32_SROA_SPECULATED:%.*]] = select i1 [[CMP]], ptr undef, ptr undef
-; CHECK-NEXT: call void @foo_i32(ptr [[ADDR_I32_SROA_SPECULATED]])
-; CHECK-NEXT: ret void
+; CHECK-PRESERVE-CFG-LABEL: @test_multiple_loads_select_asan(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[ARGS_SROA_0:%.*]] = alloca ptr, align 8
+; CHECK-PRESERVE-CFG-NEXT: [[ARGS_SROA_1:%.*]] = alloca ptr, align 8
+; CHECK-PRESERVE-CFG-NEXT: [[SEL_SROA_SEL:%.*]] = select i1 [[CMP:%.*]], ptr [[ARGS_SROA_1]], ptr [[ARGS_SROA_0]]
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR_I8:%.*]] = load ptr, ptr [[SEL_SROA_SEL]], align 8
+; CHECK-PRESERVE-CFG-NEXT: call void @foo_i8(ptr [[ADDR_I8]])
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR_I32:%.*]] = load ptr, ptr [[SEL_SROA_SEL]], align 8
+; CHECK-PRESERVE-CFG-NEXT: call void @foo_i32(ptr [[ADDR_I32]])
+; CHECK-PRESERVE-CFG-NEXT: ret void
+;
+; CHECK-MODIFY-CFG-LABEL: @test_multiple_loads_select_asan(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.else:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[ADDR_I8:%.*]] = phi ptr [ undef, [[ENTRY_THEN]] ], [ undef, [[ENTRY_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: call void @foo_i8(ptr [[ADDR_I8]])
+; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP]], label [[ENTRY_CONT_THEN:%.*]], label [[ENTRY_CONT_ELSE:%.*]]
+; CHECK-MODIFY-CFG: entry.cont.then:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.cont.else:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT_CONT]]
+; CHECK-MODIFY-CFG: entry.cont.cont:
+; CHECK-MODIFY-CFG-NEXT: [[ADDR_I32:%.*]] = phi ptr [ undef, [[ENTRY_CONT_THEN]] ], [ undef, [[ENTRY_CONT_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: call void @foo_i32(ptr [[ADDR_I32]])
+; CHECK-MODIFY-CFG-NEXT: ret void
;
entry:
%args = alloca [2 x %st.args], align 16
@@ -436,13 +459,13 @@ define void @load_of_select_with_noundef_nonnull(ptr %buffer, i1 %b) {
; CHECK-PRESERVE-CFG-LABEL: @load_of_select_with_noundef_nonnull(
; CHECK-PRESERVE-CFG-NEXT: [[UB_PTR:%.*]] = alloca ptr, align 8
; CHECK-PRESERVE-CFG-NEXT: [[SELECT_PTR:%.*]] = select i1 [[B:%.*]], ptr [[BUFFER:%.*]], ptr [[UB_PTR]]
-; CHECK-PRESERVE-CFG-NEXT: [[LOAD_PTR:%.*]] = load ptr, ptr [[SELECT_PTR]], align 8, !nonnull !1, !noundef !1
+; CHECK-PRESERVE-CFG-NEXT: [[LOAD_PTR:%.*]] = load ptr, ptr [[SELECT_PTR]], align 8, !nonnull [[META1:![0-9]+]], !noundef [[META1]]
; CHECK-PRESERVE-CFG-NEXT: ret void
;
; CHECK-MODIFY-CFG-LABEL: @load_of_select_with_noundef_nonnull(
; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTTHEN:%.*]], label [[DOTCONT:%.*]]
; CHECK-MODIFY-CFG: .then:
-; CHECK-MODIFY-CFG-NEXT: [[LOAD_PTR_THEN_VAL:%.*]] = load ptr, ptr [[BUFFER:%.*]], align 8, !nonnull !2, !noundef !2
+; CHECK-MODIFY-CFG-NEXT: [[LOAD_PTR_THEN_VAL:%.*]] = load ptr, ptr [[BUFFER:%.*]], align 8, !nonnull [[META2:![0-9]+]], !noundef [[META2]]
; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
; CHECK-MODIFY-CFG: .cont:
; CHECK-MODIFY-CFG-NEXT: [[LOAD_PTR:%.*]] = phi ptr [ [[LOAD_PTR_THEN_VAL]], [[DOTTHEN]] ], [ undef, [[TMP0:%.*]] ]
More information about the llvm-commits
mailing list