[llvm] [llubi] Support the oracle form of llvm.speculative.load. (PR #227095)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 28 12:51:28 PDT 2026


https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/227095

Add support for llvm.speculative.load with oracle functions.

This is split into 2 steps:
1. enterCall checks if CB is a speculative load in oracle form. If so, adjust Callee to the oracle functions and drop the non-oracle arguments.
2. returnFromCallee also hecks if the current PC is a speculative load with oracle. If so, it calls callSpeculativeLoadIntrinsic before returning.

>From 1c1cf43aac21151c033367ee91545c10b655a1e2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 27 Sep 2026 12:25:48 +0100
Subject: [PATCH] [llubi] Support the oracle form of llvm.speculative.load.

Add support for llvm.speculative.load with oracle functions.

This is split into 2 steps:
1. enterCall checks if CB is a speculative load in oracle form. If so,
   adjust Callee to the oracle functions and drop the non-oracle
   arguments.
2. returnFromCallee also hecks if the current PC is a speculative load
   with oracle. If so, it calls callSpeculativeLoadIntrinsic before
   returning.
---
 .../test/tools/llubi/intr_speculative_load.ll | 22 -----
 .../llubi/intr_speculative_load_oracle.ll     | 70 ++++++++++++++++
 .../llubi/intr_speculative_load_oracle_ub.ll  | 82 +++++++++++++++++++
 .../tools/llubi/intr_speculative_load_ub.ll   | 33 +-------
 llvm/tools/llubi/lib/Interpreter.cpp          | 41 ++++++++--
 5 files changed, 187 insertions(+), 61 deletions(-)
 create mode 100644 llvm/test/tools/llubi/intr_speculative_load_oracle.ll
 create mode 100644 llvm/test/tools/llubi/intr_speculative_load_oracle_ub.ll

diff --git a/llvm/test/tools/llubi/intr_speculative_load.ll b/llvm/test/tools/llubi/intr_speculative_load.ll
index 8e927f5b49038..4b32766e10c3e 100644
--- a/llvm/test/tools/llubi/intr_speculative_load.ll
+++ b/llvm/test/tools/llubi/intr_speculative_load.ll
@@ -1,19 +1,9 @@
 ; RUN: llubi --verbose --entry-function=first_bytes < %s 2>&1 | FileCheck %s --check-prefix=FIRST
 ; RUN: llubi --verbose --entry-function=last_bytes < %s 2>&1 | FileCheck %s --check-prefix=LAST
 ; RUN: llubi --verbose --entry-function=past_end < %s 2>&1 | FileCheck %s --check-prefix=PAST-END
-; RUN: not llubi --verbose --entry-function=oracle_load < %s 2>&1 | FileCheck %s --check-prefix=ORACLE
 
 @a = global [6 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4, i32 5]
 
-; Returns the number of bytes from %p to %end, clamped to 16.
-define i64 @oracle(ptr %p, ptr %end) memory(none) nounwind nosync willreturn {
-  %p.int = ptrtoaddr ptr %p to i64
-  %end.int = ptrtoaddr ptr %end to i64
-  %diff = sub i64 %end.int, %p.int
-  %n = call i64 @llvm.umin.i64(i64 %diff, i64 16)
-  ret i64 %n
-}
-
 define void @first_bytes() {
 ; FIRST: Entering function: first_bytes
 ; FIRST-NEXT:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 8) => { i32 0, i32 1, poison, poison }
@@ -42,15 +32,3 @@ define void @past_end() {
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, i64 8)
   ret void
 }
-
-define void @oracle_load() {
-; ORACLE: Entering function: oracle_load
-; ORACLE-NEXT:   %p = getelementptr i32, ptr @a, i64 4 => ptr 0x20 [@a + 16]
-; ORACLE-NEXT:   %end = getelementptr i32, ptr @a, i64 6 => ptr 0x28 [@a + 24]
-; ORACLE-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle, ptr %p, ptr %end)
-; ORACLE-NEXT: error: Execution of function 'oracle_load' failed.
-  %p = getelementptr i32, ptr @a, i64 4
-  %end = getelementptr i32, ptr @a, i64 6
-  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle, ptr %p, ptr %end)
-  ret void
-}
diff --git a/llvm/test/tools/llubi/intr_speculative_load_oracle.ll b/llvm/test/tools/llubi/intr_speculative_load_oracle.ll
new file mode 100644
index 0000000000000..dc413612491bf
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_speculative_load_oracle.ll
@@ -0,0 +1,70 @@
+; RUN: llubi --verbose --entry-function=oracle_load < %s 2>&1 | FileCheck %s --check-prefix=ORACLE
+; RUN: llubi --verbose --entry-function=nested_oracle_load < %s 2>&1 | FileCheck %s --check-prefix=NESTED
+
+ at a = global [6 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4, i32 5]
+
+; Returns the number of bytes from %p to %end, clamped to 16.
+define i64 @oracle(ptr %p, ptr %end) memory(none) nounwind nosync willreturn {
+  %p.int = ptrtoaddr ptr %p to i64
+  %end.int = ptrtoaddr ptr %end to i64
+  %diff = sub i64 %end.int, %p.int
+  %n = call i64 @llvm.umin.i64(i64 %diff, i64 16)
+  ret i64 %n
+}
+
+; Returns the second element at %p plus 3, loaded via an oracle-form
+; llvm.speculative.load.
+define i64 @oracle_nested(ptr %p) memory(argmem: read) nounwind nosync willreturn {
+  %v = call <2 x i32> (ptr, i1, ...) @llvm.speculative.load.v2i32.p0(ptr %p, i1 false, ptr @oracle_const, i64 8)
+  %e = extractelement <2 x i32> %v, i64 1
+  %z = zext i32 %e to i64
+  %n = add i64 %z, 3
+  ret i64 %n
+}
+
+define i64 @oracle_const(i64 %n) memory(none) nounwind nosync willreturn {
+  ret i64 %n
+}
+
+define void @oracle_load() {
+; ORACLE: Entering function: oracle_load
+; ORACLE-NEXT:   %p = getelementptr i32, ptr @a, i64 4 => ptr 0x20 [@a + 16]
+; ORACLE-NEXT:   %end = getelementptr i32, ptr @a, i64 6 => ptr 0x28 [@a + 24]
+; ORACLE-NEXT: Entering function: oracle
+; ORACLE-NEXT:   ptr %p = ptr 0x20 [@a + 16]
+; ORACLE-NEXT:   ptr %end = ptr 0x28 [@a + 24]
+; ORACLE-NEXT:   %p.int = ptrtoaddr ptr %p to i64 => i64 32
+; ORACLE-NEXT:   %end.int = ptrtoaddr ptr %end to i64 => i64 40
+; ORACLE-NEXT:   %diff = sub i64 %end.int, %p.int => i64 8
+; ORACLE-NEXT:   %n = call i64 @llvm.umin.i64(i64 %diff, i64 16) => i64 8
+; ORACLE-NEXT:   ret i64 %n
+; ORACLE-NEXT: Exiting function: oracle
+; ORACLE-NEXT:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle, ptr %p, ptr %end) => { i32 4, i32 5, poison, poison }
+; ORACLE-NEXT:   ret void
+; ORACLE-NEXT: Exiting function: oracle_load
+  %p = getelementptr i32, ptr @a, i64 4
+  %end = getelementptr i32, ptr @a, i64 6
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle, ptr %p, ptr %end)
+  ret void
+}
+
+define void @nested_oracle_load() {
+; NESTED: Entering function: nested_oracle_load
+; NESTED-NEXT: Entering function: oracle_nested
+; NESTED-NEXT:   ptr %p = ptr 0x10 [@a]
+; NESTED-NEXT: Entering function: oracle_const
+; NESTED-NEXT:   i64 %n = i64 8
+; NESTED-NEXT:   ret i64 %n
+; NESTED-NEXT: Exiting function: oracle_const
+; NESTED-NEXT:   %v = call <2 x i32> (ptr, i1, ...) @llvm.speculative.load.v2i32.p0(ptr %p, i1 false, ptr @oracle_const, i64 8) => { i32 0, i32 1 }
+; NESTED-NEXT:   %e = extractelement <2 x i32> %v, i64 1 => i32 1
+; NESTED-NEXT:   %z = zext i32 %e to i64 => i64 1
+; NESTED-NEXT:   %n = add i64 %z, 3 => i64 4
+; NESTED-NEXT:   ret i64 %n
+; NESTED-NEXT: Exiting function: oracle_nested
+; NESTED-NEXT:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, ptr @oracle_nested, ptr @a) => { poison, poison, poison, i32 3 }
+; NESTED-NEXT:   ret void
+; NESTED-NEXT: Exiting function: nested_oracle_load
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, ptr @oracle_nested, ptr @a)
+  ret void
+}
diff --git a/llvm/test/tools/llubi/intr_speculative_load_oracle_ub.ll b/llvm/test/tools/llubi/intr_speculative_load_oracle_ub.ll
new file mode 100644
index 0000000000000..18eecbd5432ce
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_speculative_load_oracle_ub.ll
@@ -0,0 +1,82 @@
+; RUN: not llubi --verbose --entry-function=oracle_out_of_bounds < %s 2>&1 | FileCheck %s --check-prefix=ORACLE-OOB
+; RUN: not llubi --verbose --entry-function=oracle_poison_noundef_ret < %s 2>&1 | FileCheck %s --check-prefix=NOUNDEF-RET
+; RUN: not llubi --verbose --entry-function=oracle_poison_noundef_arg < %s 2>&1 | FileCheck %s --check-prefix=NOUNDEF-ARG
+; RUN: not llubi --verbose --entry-function=oracle_declaration < %s 2>&1 | FileCheck %s --check-prefix=ORACLE-DECL
+
+ at a = global [6 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4, i32 5]
+
+; Returns one element more than the number of bytes from %p to %end.
+define i64 @oracle_off_by_one(ptr %p, ptr %end) memory(none) nounwind nosync willreturn {
+  %p.int = ptrtoaddr ptr %p to i64
+  %end.int = ptrtoaddr ptr %end to i64
+  %diff = sub i64 %end.int, %p.int
+  %n = add i64 %diff, 4
+  ret i64 %n
+}
+
+define noundef i64 @oracle_noundef_ret(i64 %n) memory(none) nounwind nosync willreturn {
+  ret i64 poison
+}
+
+define i64 @oracle_noundef_arg(i64 noundef %n) memory(none) nounwind nosync willreturn {
+  ret i64 %n
+}
+
+declare i64 @oracle_decl(i64) memory(none) nounwind nosync willreturn
+
+define void @oracle_out_of_bounds() {
+; ORACLE-OOB: Entering function: oracle_out_of_bounds
+; ORACLE-OOB-NEXT:   %p = getelementptr i32, ptr @a, i64 4 => ptr 0x20 [@a + 16]
+; ORACLE-OOB-NEXT:   %end = getelementptr i32, ptr @a, i64 6 => ptr 0x28 [@a + 24]
+; ORACLE-OOB-NEXT: Entering function: oracle_off_by_one
+; ORACLE-OOB-NEXT:   ptr %p = ptr 0x20 [@a + 16]
+; ORACLE-OOB-NEXT:   ptr %end = ptr 0x28 [@a + 24]
+; ORACLE-OOB-NEXT:   %p.int = ptrtoaddr ptr %p to i64 => i64 32
+; ORACLE-OOB-NEXT:   %end.int = ptrtoaddr ptr %end to i64 => i64 40
+; ORACLE-OOB-NEXT:   %diff = sub i64 %end.int, %p.int => i64 8
+; ORACLE-OOB-NEXT:   %n = add i64 %diff, 4 => i64 12
+; ORACLE-OOB-NEXT:   ret i64 %n
+; ORACLE-OOB-NEXT: Exiting function: oracle_off_by_one
+; ORACLE-OOB-NEXT: Stacktrace:
+; ORACLE-OOB-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle_off_by_one, ptr %p, ptr %end) at @oracle_out_of_bounds <stdin>:{{[0-9]+}}
+; ORACLE-OOB-NEXT: Immediate UB detected: Memory access is out of bounds. Accessed size: 12, Address: 0x20, Object base: 0x10, Object size: 24.
+; ORACLE-OOB-NEXT: error: Execution of function 'oracle_out_of_bounds' failed.
+  %p = getelementptr i32, ptr @a, i64 4
+  %end = getelementptr i32, ptr @a, i64 6
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, ptr @oracle_off_by_one, ptr %p, ptr %end)
+  ret void
+}
+
+define void @oracle_poison_noundef_ret() {
+; NOUNDEF-RET: Entering function: oracle_poison_noundef_ret
+; NOUNDEF-RET-NEXT: Entering function: oracle_noundef_ret
+; NOUNDEF-RET-NEXT:   i64 %n = i64 4
+; NOUNDEF-RET-NEXT:   ret i64 poison
+; NOUNDEF-RET-NEXT: Exiting function: oracle_noundef_ret
+; NOUNDEF-RET-NEXT: Stacktrace:
+; NOUNDEF-RET-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_noundef_ret, i64 4) at @oracle_poison_noundef_ret <stdin>:{{[0-9]+}}
+; NOUNDEF-RET-NEXT: Immediate UB detected: The value poison violates noundef attribute.
+; NOUNDEF-RET-NEXT: error: Execution of function 'oracle_poison_noundef_ret' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_noundef_ret, i64 4)
+  ret void
+}
+
+define void @oracle_poison_noundef_arg() {
+; NOUNDEF-ARG: Entering function: oracle_poison_noundef_arg
+; NOUNDEF-ARG-NEXT: Stacktrace:
+; NOUNDEF-ARG-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_noundef_arg, i64 poison) at @oracle_poison_noundef_arg <stdin>:{{[0-9]+}}
+; NOUNDEF-ARG-NEXT: Immediate UB detected: The value poison violates noundef attribute.
+; NOUNDEF-ARG-NEXT: error: Execution of function 'oracle_poison_noundef_arg' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_noundef_arg, i64 poison)
+  ret void
+}
+
+define void @oracle_declaration() {
+; ORACLE-DECL: Entering function: oracle_declaration
+; ORACLE-DECL-NEXT: Stacktrace:
+; ORACLE-DECL-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_decl, i64 4) at @oracle_declaration <stdin>:{{[0-9]+}}
+; ORACLE-DECL-NEXT: Error: Unsupported llvm.speculative.load oracle declaration: oracle_decl.
+; ORACLE-DECL-NEXT: error: Execution of function 'oracle_declaration' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_decl, i64 4)
+  ret void
+}
diff --git a/llvm/test/tools/llubi/intr_speculative_load_ub.ll b/llvm/test/tools/llubi/intr_speculative_load_ub.ll
index e08e1e410183d..11ba81f804e1d 100644
--- a/llvm/test/tools/llubi/intr_speculative_load_ub.ll
+++ b/llvm/test/tools/llubi/intr_speculative_load_ub.ll
@@ -3,27 +3,14 @@
 ; RUN: not llubi --verbose --entry-function=exceeds_size < %s 2>&1 | FileCheck %s --check-prefix=EXCEEDS-SIZE
 ; RUN: not llubi --verbose --entry-function=poison_num_bytes < %s 2>&1 | FileCheck %s --check-prefix=POISON-N
 ; RUN: not llubi --verbose --entry-function=poison_pointer < %s 2>&1 | FileCheck %s --check-prefix=POISON-PTR
-; RUN: not llubi --verbose --entry-function=oracle_out_of_bounds < %s 2>&1 | FileCheck %s --check-prefix=ORACLE-OOB
-; RUN: not llubi --verbose --entry-function=oracle_declaration < %s 2>&1 | FileCheck %s --check-prefix=ORACLE-DECL
 
 @a = global [2 x i32] [i32 0, i32 1]
 
-; Returns one element more than the number of bytes from %p to %end.
-define i64 @oracle_off_by_one(ptr %p, ptr %end) memory(none) nounwind nosync willreturn {
-  %p.int = ptrtoaddr ptr %p to i64
-  %end.int = ptrtoaddr ptr %end to i64
-  %diff = sub i64 %end.int, %p.int
-  %n = add i64 %diff, 4
-  ret i64 %n
-}
-
-declare i64 @oracle_decl(i64) memory(none) nounwind nosync willreturn
-
 define void @out_of_bounds() {
 ; OOB: Entering function: out_of_bounds
 ; OOB-NEXT: Stacktrace:
 ; OOB-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 12) at @out_of_bounds <stdin>:{{[0-9]+}}
-; OOB-NEXT: Immediate UB detected: Memory access is out of bounds. Accessed size: 12, Address: 0xc, Object base: 0xc, Object size: 8.
+; OOB-NEXT: Immediate UB detected: Memory access is out of bounds. Accessed size: 12, Address: 0x8, Object base: 0x8, Object size: 8.
 ; OOB-NEXT: error: Execution of function 'out_of_bounds' failed.
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 12)
   ret void
@@ -33,7 +20,7 @@ define void @from_end_out_of_bounds() {
 ; FROM-END-OOB: Entering function: from_end_out_of_bounds
 ; FROM-END-OOB-NEXT: Stacktrace:
 ; FROM-END-OOB-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr getelementptr (i8, ptr @a, i64 -8), i1 true, i64 12) at @from_end_out_of_bounds <stdin>:{{[0-9]+}}
-; FROM-END-OOB-NEXT: Immediate UB detected: Memory access is out of bounds. Accessed size: 12, Address: 0x8, Object base: 0xc, Object size: 8.
+; FROM-END-OOB-NEXT: Immediate UB detected: Memory access is out of bounds. Accessed size: 12, Address: 0x4, Object base: 0x8, Object size: 8.
 ; FROM-END-OOB-NEXT: error: Execution of function 'from_end_out_of_bounds' failed.
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr getelementptr (i8, ptr @a, i64 -8), i1 true, i64 12)
   ret void
@@ -68,19 +55,3 @@ define void @poison_pointer() {
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr poison, i1 false, i64 0)
   ret void
 }
-
-define void @oracle_out_of_bounds() {
-; ORACLE-OOB: Entering function: oracle_out_of_bounds
-; ORACLE-OOB-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_off_by_one, ptr @a, ptr getelementptr (i8, ptr @a, i64 8))
-; ORACLE-OOB-NEXT: error: Execution of function 'oracle_out_of_bounds' failed.
-  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_off_by_one, ptr @a, ptr getelementptr (i8, ptr @a, i64 8))
-  ret void
-}
-
-define void @oracle_declaration() {
-; ORACLE-DECL: Entering function: oracle_declaration
-; ORACLE-DECL-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_decl, i64 4)
-; ORACLE-DECL-NEXT: error: Execution of function 'oracle_declaration' failed.
-  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, ptr @oracle_decl, i64 4)
-  ret void
-}
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 3e4b250f4c85c..5d5c2d2778e3d 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -919,6 +919,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     return AnyValue();
   }
 
+  /// Returns the oracle function if \p CB is an llvm.speculative.load in
+  /// oracle form, nullptr otherwise.
+  static Function *getSpeculativeLoadOracle(const CallBase &CB) {
+    return CB.getIntrinsicID() == Intrinsic::speculative_load
+               ? dyn_cast<Function>(CB.getArgOperand(2))
+               : nullptr;
+  }
+
   AnyValue callSpeculativeLoadIntrinsic(CallBase &CB, const AnyValue &Ptr,
                                         const AnyValue &NumBytes) {
     Type *RetTy = CB.getType();
@@ -1043,8 +1051,15 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
 
   void returnFromCallee() {
     auto &CB = cast<CallBase>(*CurrentFrame->PC);
-    CurrentFrame->CalleeArgs.clear();
     AnyValue &RetVal = CurrentFrame->CalleeRetVal;
+    if (Function *Oracle = getSpeculativeLoadOracle(CB)) {
+      // RetVal is the oracle's result; use it to complete the load.
+      handleAttributes(Oracle->getReturnType(), RetVal, AttributeSet(),
+                       Oracle->getAttributes().getRetAttrs());
+      RetVal =
+          callSpeculativeLoadIntrinsic(CB, CurrentFrame->CalleeArgs[0], RetVal);
+    }
+    CurrentFrame->CalleeArgs.clear();
     if (Type *RetTy = CB.getType(); !RetTy->isVoidTy()) {
       // Handle attributes on the return value (Attributes from resolved callee
       // should be applied if available).
@@ -1784,12 +1799,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     case Intrinsic::memset_inline:
       return callMemSetIntrinsic(CB, Args);
     case Intrinsic::speculative_load:
-      // TODO: Support the oracle form.
-      if (isa<Function>(CB.getArgOperand(2))) {
-        Handler.onUnrecognizedInstruction(CB);
-        setFailed();
-        return AnyValue();
-      }
       return callSpeculativeLoadIntrinsic(CB, Args[0], Args[2]);
     case Intrinsic::experimental_noalias_scope_decl:
       // FIXME: Not implemented yet. Currently it acts as a noop.
@@ -2212,6 +2221,23 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
 
     CurrentFrame->ResolvedCallee = Callee;
+    ArrayRef<AnyValue> Args = CalleeArgs;
+    // Call the oracle of an llvm.speculative.load with the trailing arguments.
+    // The load is completed in returnFromCallee.
+    if (Function *Oracle = getSpeculativeLoadOracle(CB)) {
+      if (Oracle->isDeclaration()) {
+        reportError()
+            << "Unsupported llvm.speculative.load oracle declaration: "
+            << Oracle->getName() << ".";
+        return;
+      }
+      Args = Args.drop_front(3);
+      for (auto [Arg, ArgVal] :
+           zip_equal(Oracle->args(), MutableArrayRef(CalleeArgs).drop_front(3)))
+        handleAttributes(Arg.getType(), ArgVal, AttributeSet(),
+                         Arg.getAttributes());
+      Callee = Oracle;
+    }
     if (Callee->isIntrinsic()) {
       CurrentFrame->CalleeRetVal = callIntrinsic(CB, CalleeArgs);
       returnFromCallee();
@@ -2228,7 +2254,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       }
       assert(!Callee->empty() && "Expected a defined function.");
       // Suspend the current frame and push the callee frame onto the stack.
-      ArrayRef<AnyValue> Args = CurrentFrame->CalleeArgs;
       AnyValue &RetVal = CurrentFrame->CalleeRetVal;
       CurrentFrame->State = FrameState::Pending;
       CallStack.emplace_back(*Callee, &CB, CurrentFrame, Args, RetVal,



More information about the llvm-commits mailing list