[llvm] [CodeGen] Discard oracle functions during ISel translation (PR #188017)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 26 08:32:10 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-aarch64
Author: Florian Hahn (fhahn)
<details>
<summary>Changes</summary>
Update IRTranslator.cpp and SelectionDAGISel.cpp to set the linkage of
oracle functions to available_externally, so no code is generated for
them.
Depends on verifier strengthening
https://github.com/llvm/llvm-project/pull/226669 (included in PR)
---
Full diff: https://github.com/llvm/llvm-project/pull/188017.diff
10 Files Affected:
- (modified) llvm/docs/LangRef.md (+2)
- (modified) llvm/include/llvm/IR/IntrinsicInst.h (+7)
- (modified) llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp (+7)
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+7)
- (modified) llvm/lib/IR/IntrinsicInst.cpp (+11)
- (modified) llvm/lib/IR/Verifier.cpp (+11)
- (modified) llvm/test/CodeGen/AArch64/GlobalISel/speculative-load-intrinsic.ll (+14-1)
- (modified) llvm/test/CodeGen/AArch64/speculative-load-intrinsic.ll (+4-2)
- (modified) llvm/test/CodeGen/X86/speculative-load-intrinsic.ll (+4-2)
- (modified) llvm/test/Verifier/speculative-load.ll (+86-8)
``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index ee7068b2125cc..30dda68b859f9 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -24239,6 +24239,8 @@ directly. In the **oracle form**, the third argument must be a direct
reference to a non-variadic function returning `i64` that is `nounwind`,
`nosync` and `willreturn` and may only read memory through its arguments;
the remaining arguments are forwarded to it, and its return value is `N`.
+The oracle function must have local linkage, and it may only be used as the
+oracle argument of '`llvm.speculative.load`' calls.
##### Semantics:
diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 7d0bb92114e84..80c16f29dbae0 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -1832,6 +1832,13 @@ class StructuredGEPInst : public IntrinsicInst {
}
};
+/// Returns true if \p U is the oracle operand of an llvm.speculative.load.
+LLVM_ABI bool isSpeculativeLoadOracleUse(const Use &U);
+
+/// Returns true if \p F is used, and only used, as the oracle operand of
+/// llvm.speculative.load. No code needs to be emitted for such functions.
+LLVM_ABI bool isSpeculativeLoadOracle(const Function &F);
+
} // end namespace llvm
#endif // LLVM_IR_INTRINSICINST_H
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index cbad39bf4016c..d0fe5e6671a6d 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -5115,6 +5115,13 @@ bool IRTranslatorImpl::runOnMachineFunction(
const LibcallLoweringInfo *LibcallInfo, SSPLayoutInfo *StackProtectorInfo) {
MF = &CurMF;
const Function &F = MF->getFunction();
+
+ // Make sure oracle functions are deleted.
+ if (isSpeculativeLoadOracle(F)) {
+ MF->getFunction().setLinkage(GlobalValue::AvailableExternallyLinkage);
+ return false;
+ }
+
ORE = std::make_unique<OptimizationRemarkEmitter>(&F);
CLI = MF->getSubtarget().getCallLowering();
SPInfo = StackProtectorInfo;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 97061800ddba3..fb464c8b2095a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -376,6 +376,13 @@ bool SelectionDAGISelLegacy::runOnMachineFunction(MachineFunction &MF) {
if (MF.getProperties().hasSelected())
return false;
+ // Make sure oracle functions are deleted.
+ Function &F = MF.getFunction();
+ if (isSpeculativeLoadOracle(F)) {
+ F.setLinkage(GlobalValue::AvailableExternallyLinkage);
+ return false;
+ }
+
// Do some sanity-checking on the command-line options.
if (EnableFastISelAbort && !Selector->TM.Options.EnableFastISel)
reportFatalUsageError("-fast-isel-abort > 0 requires -fast-isel");
diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp
index 684aaf1a8f2d3..5d93b41a9397e 100644
--- a/llvm/lib/IR/IntrinsicInst.cpp
+++ b/llvm/lib/IR/IntrinsicInst.cpp
@@ -807,3 +807,14 @@ ConvergenceControlInst::CreateLoop(BasicBlock &BB,
auto *Call = CallInst::Create(Fn, {}, {OB}, "", BB.getFirstInsertionPt());
return cast<ConvergenceControlInst>(Call);
}
+
+bool llvm::isSpeculativeLoadOracleUse(const Use &U) {
+ auto *II = dyn_cast<IntrinsicInst>(U.getUser());
+ return II && II->getIntrinsicID() == Intrinsic::speculative_load &&
+ II->isArgOperand(&U) && II->getArgOperandNo(&U) == 2;
+}
+
+bool llvm::isSpeculativeLoadOracle(const Function &F) {
+ return F.hasLocalLinkage() && !F.use_empty() &&
+ all_of(F.uses(), isSpeculativeLoadOracleUse);
+}
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 2de006bfc032d..c152bfe1547f3 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3494,6 +3494,17 @@ void Verifier::visitFunction(const Function &F) {
PrintDecl);
}
+ // A function used as the oracle of llvm.speculative.load may not be
+ // referenced in any other way.
+ if (isMaterialized && any_of(F.uses(), isSpeculativeLoadOracleUse)) {
+ Check(F.hasLocalLinkage(), "oracle function must have local linkage", &F);
+ for (const Use &U : F.uses())
+ Check(isSpeculativeLoadOracleUse(U),
+ "oracle function may only be used as the oracle operand of "
+ "llvm.speculative.load",
+ &F, U.getUser());
+ }
+
auto *N = F.getSubprogram();
HasDebugInfo = (N != nullptr);
if (!HasDebugInfo)
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/speculative-load-intrinsic.ll b/llvm/test/CodeGen/AArch64/GlobalISel/speculative-load-intrinsic.ll
index 86bd8f49629de..1b601683abffc 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/speculative-load-intrinsic.ll
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/speculative-load-intrinsic.ll
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=aarch64-unknown-linux-gnu -mattr=+sve -global-isel \
-; RUN: -global-isel-abort=1 -aarch64-enable-gisel-sve=1 < %s | FileCheck %s
+; RUN: -global-isel-abort=1 -aarch64-enable-gisel-sve=1 < %s | FileCheck %s --implicit-check-not='{{^}}oracle:'
define <4 x i32> @speculative_load_v4i32(ptr %ptr) {
; CHECK-LABEL: speculative_load_v4i32:
@@ -39,3 +39,16 @@ define <vscale x 4 x i32> @plain_load_nxv4i32(ptr %ptr) {
%r = load <vscale x 4 x i32>, ptr %ptr, align 16
ret <vscale x 4 x i32> %r
}
+
+define internal i64 @oracle(ptr %p, i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+define <4 x i32> @speculative_load_v4i32_oracle(ptr %ptr, i64 %n) {
+; CHECK-LABEL: speculative_load_v4i32_oracle:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldr q0, [x0]
+; CHECK-NEXT: ret
+ %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr align 16 %ptr, i1 false, ptr @oracle, ptr %ptr, i64 %n)
+ ret <4 x i32> %r
+}
diff --git a/llvm/test/CodeGen/AArch64/speculative-load-intrinsic.ll b/llvm/test/CodeGen/AArch64/speculative-load-intrinsic.ll
index ae90f2e3a7fd4..9a111336110a4 100644
--- a/llvm/test/CodeGen/AArch64/speculative-load-intrinsic.ll
+++ b/llvm/test/CodeGen/AArch64/speculative-load-intrinsic.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=aarch64-unknown-linux-gnu -mattr=+sve < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu -mattr=+sve < %s | FileCheck %s --implicit-check-not='{{^}}oracle:'
; Test that @llvm.speculative.load is lowered to a regular load
; in SelectionDAG for fixed vectors, scalable vectors, and bytes.
@@ -77,7 +77,9 @@ define <vscale x 2 x double> @speculative_load_nxv2f64(ptr %ptr) {
; Oracle form tests
-declare i64 @oracle(ptr, i64) memory(argmem: read) nounwind nosync willreturn
+define internal i64 @oracle(ptr %p, i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
define b128 @speculative_load_b128_oracle(ptr %ptr, i64 %n) {
; CHECK-LABEL: speculative_load_b128_oracle:
diff --git a/llvm/test/CodeGen/X86/speculative-load-intrinsic.ll b/llvm/test/CodeGen/X86/speculative-load-intrinsic.ll
index c3fc86dccebe1..e4a7d52e45f96 100644
--- a/llvm/test/CodeGen/X86/speculative-load-intrinsic.ll
+++ b/llvm/test/CodeGen/X86/speculative-load-intrinsic.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 < %s | FileCheck %s
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 < %s | FileCheck %s --implicit-check-not='{{^}}oracle:'
; Test that @llvm.speculative.load is lowered to a regular load
; in SelectionDAG for fixed vectors and bytes.
@@ -67,7 +67,9 @@ define <2 x double> @speculative_load_v2f64(ptr %ptr) {
}
; Oracle form tests
-declare i64 @oracle(ptr, i64) memory(argmem: read) nounwind nosync willreturn
+define internal i64 @oracle(ptr %p, i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
define b128 @speculative_load_b128_oracle(ptr %ptr, i64 %n) {
; CHECK-LABEL: speculative_load_b128_oracle:
diff --git a/llvm/test/Verifier/speculative-load.ll b/llvm/test/Verifier/speculative-load.ll
index d2241ee09037e..f077b861eb8e4 100644
--- a/llvm/test/Verifier/speculative-load.ll
+++ b/llvm/test/Verifier/speculative-load.ll
@@ -13,14 +13,33 @@ declare [4 x i32] @llvm.speculative.load.a4i32.p0(ptr, i1, ...)
declare <4 x b3> @llvm.speculative.load.v4b3.p0(ptr, i1, ...)
declare <3 x ptr> @llvm.speculative.load.v3p0.p0(ptr, i1, ...)
-declare i32 @bad_oracle_ret(ptr, i64) memory(argmem: read) nounwind nosync willreturn
-declare i64 @good_oracle(ptr, i64) memory(argmem: read) nounwind nosync willreturn
-declare i64 @oracle_i32_param(i32) memory(argmem: read) nounwind nosync willreturn
-declare i64 @side_effecting_oracle(ptr, i64)
-declare i64 @throwing_oracle(ptr, i64) memory(argmem: read) nosync willreturn
-declare i64 @syncing_oracle(ptr, i64) memory(argmem: read) nounwind willreturn
-declare i64 @looping_oracle(ptr, i64) memory(argmem: read) nounwind nosync
-declare i64 @variadic_oracle(i64, ...) memory(argmem: read) nounwind nosync willreturn
+define internal i32 @bad_oracle_ret(ptr %p, i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i32 0
+}
+define internal i64 @good_oracle(ptr %p, i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+define internal i64 @oracle_i32_param(i32 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 0
+}
+define internal i64 @side_effecting_oracle(ptr %p, i64 %n) {
+ ret i64 %n
+}
+define internal i64 @throwing_oracle(ptr %p, i64 %n) memory(argmem: read) nosync willreturn {
+ ret i64 %n
+}
+define internal i64 @syncing_oracle(ptr %p, i64 %n) memory(argmem: read) nounwind willreturn {
+ ret i64 %n
+}
+define internal i64 @looping_oracle(ptr %p, i64 %n) memory(argmem: read) nounwind nosync {
+ ret i64 %n
+}
+define internal i64 @variadic_oracle(i64 %n, ...) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+; CHECK: oracle function must have local linkage
+; CHECK-NEXT: ptr @external_oracle
+declare i64 @external_oracle(ptr, i64) memory(argmem: read) nounwind nosync willreturn
define i32 @test_non_byte_non_vector_int(ptr %ptr) {
; CHECK: llvm.speculative.load return type must be a byte type or a vector type
@@ -127,6 +146,11 @@ define b128 @test_non_function_oracle(ptr %ptr, ptr %not_fn) {
ret b128 %res
}
+define b128 @test_oracle_external_linkage(ptr %ptr, i64 %n) {
+ %res = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @external_oracle, ptr %ptr, i64 %n)
+ ret b128 %res
+}
+
define b128 @test_oracle_side_effects(ptr %ptr, i64 %n) {
; CHECK: llvm.speculative.load oracle function must be nounwind, nosync and willreturn, must not have side effects and may only read memory through its arguments
; CHECK-NEXT: call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @side_effecting_oracle, ptr %ptr, i64 %n)
@@ -183,3 +207,57 @@ define <3 x ptr> @test_vector_of_pointers_size_not_pow2(ptr %ptr) {
%res = call <3 x ptr> (ptr, i1, ...) @llvm.speculative.load.v3p0.p0(ptr %ptr, i1 false, i64 24)
ret <3 x ptr> %res
}
+
+; Oracle functions may only be used as the oracle operand of
+; llvm.speculative.load.
+
+ at llvm.used = appending global [1 x ptr] [ptr @oracle_in_used], section "llvm.metadata"
+ at alias = internal alias i64 (i64), ptr @oracle_aliased
+
+; CHECK: oracle function may only be used as the oracle operand of llvm.speculative.load
+; CHECK-NEXT: ptr @oracle_called
+; CHECK-NEXT: %r = call i64 @oracle_called(i64 %n)
+define internal i64 @oracle_called(i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+; CHECK: oracle function may only be used as the oracle operand of llvm.speculative.load
+; CHECK-NEXT: ptr @oracle_stored
+; CHECK-NEXT: store ptr @oracle_stored, ptr %ptr
+define internal i64 @oracle_stored(i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+; CHECK: oracle function may only be used as the oracle operand of llvm.speculative.load
+; CHECK-NEXT: ptr @oracle_in_used
+; CHECK-NEXT: [1 x ptr] [ptr @oracle_in_used]
+define internal i64 @oracle_in_used(i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+; CHECK: oracle function may only be used as the oracle operand of llvm.speculative.load
+; CHECK-NEXT: ptr @oracle_aliased
+; CHECK-NEXT: ptr @alias
+define internal i64 @oracle_aliased(i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+; Operand 2 of an intrinsic other than llvm.speculative.load.
+; CHECK: oracle function may only be used as the oracle operand of llvm.speculative.load
+; CHECK-NEXT: ptr @oracle_other_intrinsic
+; CHECK-NEXT: call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0, ptr @oracle_other_intrinsic)
+define internal i64 @oracle_other_intrinsic(i64 %n) memory(argmem: read) nounwind nosync willreturn {
+ ret i64 %n
+}
+
+define b128 @test_oracle_invalid_uses(ptr %ptr, i64 %n) {
+ %r = call i64 @oracle_called(i64 %n)
+ store ptr @oracle_stored, ptr %ptr
+ call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0, ptr @oracle_other_intrinsic)
+ %a = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @oracle_called, i64 %n)
+ %c = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @oracle_stored, i64 %n)
+ %d = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @oracle_in_used, i64 %n)
+ %e = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @oracle_aliased, i64 %n)
+ %f = call b128 (ptr, i1, ...) @llvm.speculative.load.b128.p0(ptr %ptr, i1 false, ptr @oracle_other_intrinsic, i64 %n)
+ ret b128 %f
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/188017
More information about the llvm-commits
mailing list