[llvm] [llubi] Add support for llvm.speculative.load. (PR #226839)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 27 14:46:31 PDT 2026


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

Implement the direct form of llvm.speculative.load, where the number of
accessible bytes N is passed as an i64. Only the N accessible bytes are
read from memory and they must be in bounds of the underlying object;
all other bytes are poison. With from_end, the accessible bytes are the
last N bytes of the loaded value. It is UB if N is poison or exceeds the
size of the loaded type.

Support for the oracle form will be added as follow-up.

>From fac4e8c4708fd9a8e5076e94c09363ec35fe44b5 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 27 Sep 2026 15:13:56 +0000
Subject: [PATCH 1/2] [llubi] Precommit speculative load tests

---
 .../test/tools/llubi/intr_speculative_load.ll | 53 +++++++++++++
 .../tools/llubi/intr_speculative_load_ub.ll   | 76 +++++++++++++++++++
 2 files changed, 129 insertions(+)
 create mode 100644 llvm/test/tools/llubi/intr_speculative_load.ll
 create mode 100644 llvm/test/tools/llubi/intr_speculative_load_ub.ll

diff --git a/llvm/test/tools/llubi/intr_speculative_load.ll b/llvm/test/tools/llubi/intr_speculative_load.ll
new file mode 100644
index 0000000000000..221200b7c3e64
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_speculative_load.ll
@@ -0,0 +1,53 @@
+; RUN: not llubi --verbose --entry-function=first_bytes < %s 2>&1 | FileCheck %s --check-prefix=FIRST
+; RUN: not llubi --verbose --entry-function=last_bytes < %s 2>&1 | FileCheck %s --check-prefix=LAST
+; RUN: not 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
+
+ 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
+}
+
+define void @first_bytes() {
+; FIRST: Entering function: first_bytes
+; FIRST-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 8)
+; FIRST-NEXT: error: Execution of function 'first_bytes' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 8)
+  ret void
+}
+
+define void @last_bytes() {
+; LAST: Entering function: last_bytes
+; LAST-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, i64 8)
+; LAST-NEXT: error: Execution of function 'last_bytes' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, i64 8)
+  ret void
+}
+
+define void @past_end() {
+; PAST-END: Entering function: past_end
+; PAST-END-NEXT:   %p = getelementptr i32, ptr @a, i64 4 => ptr 0x20 [@a + 16]
+; PAST-END-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, i64 8)
+; PAST-END-NEXT: error: Execution of function 'past_end' failed.
+  %p = getelementptr i32, ptr @a, i64 4
+  %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_ub.ll b/llvm/test/tools/llubi/intr_speculative_load_ub.ll
new file mode 100644
index 0000000000000..c6c163adb4d3d
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_speculative_load_ub.ll
@@ -0,0 +1,76 @@
+; RUN: not llubi --verbose --entry-function=out_of_bounds < %s 2>&1 | FileCheck %s --check-prefix=OOB
+; RUN: not llubi --verbose --entry-function=from_end_out_of_bounds < %s 2>&1 | FileCheck %s --check-prefix=FROM-END-OOB
+; 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
+
+ at 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: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 12)
+; 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
+}
+
+define void @from_end_out_of_bounds() {
+; FROM-END-OOB: Entering function: from_end_out_of_bounds
+; FROM-END-OOB-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr getelementptr (i8, ptr @a, i64 -8), i1 true, i64 12)
+; 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
+}
+
+define void @exceeds_size() {
+; EXCEEDS-SIZE: Entering function: exceeds_size
+; EXCEEDS-SIZE-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 17)
+; EXCEEDS-SIZE-NEXT: error: Execution of function 'exceeds_size' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 17)
+  ret void
+}
+
+define void @poison_num_bytes() {
+; POISON-N: Entering function: poison_num_bytes
+; POISON-N-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 poison)
+; POISON-N-NEXT: error: Execution of function 'poison_num_bytes' failed.
+  %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 poison)
+  ret void
+}
+
+define void @poison_pointer() {
+; POISON-PTR: Entering function: poison_pointer
+; POISON-PTR-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr poison, i1 false, i64 0)
+; POISON-PTR-NEXT: error: Execution of function 'poison_pointer' failed.
+  %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
+}

>From 91857a1e80e6acda39f950dfe921bb27484d3fe5 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 27 Sep 2026 12:24:51 +0100
Subject: [PATCH 2/2] [llubi] Add support for llvm.speculative.load.

Implement the direct form of llvm.speculative.load, where the number of
accessible bytes N is passed as an i64. Only the N accessible bytes are
read from memory and they must be in bounds of the underlying object;
all other bytes are poison. With from_end, the accessible bytes are the
last N bytes of the loaded value. It is UB if N is poison or exceeds the
size of the loaded type.

Support for the oracle form will be added as follow-up.
---
 .../test/tools/llubi/intr_speculative_load.ll | 21 ++++----
 .../tools/llubi/intr_speculative_load_ub.ll   | 20 ++++++--
 llvm/tools/llubi/lib/Interpreter.cpp          | 48 +++++++++++++++++++
 3 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/llvm/test/tools/llubi/intr_speculative_load.ll b/llvm/test/tools/llubi/intr_speculative_load.ll
index 221200b7c3e64..8e927f5b49038 100644
--- a/llvm/test/tools/llubi/intr_speculative_load.ll
+++ b/llvm/test/tools/llubi/intr_speculative_load.ll
@@ -1,6 +1,6 @@
-; RUN: not llubi --verbose --entry-function=first_bytes < %s 2>&1 | FileCheck %s --check-prefix=FIRST
-; RUN: not llubi --verbose --entry-function=last_bytes < %s 2>&1 | FileCheck %s --check-prefix=LAST
-; RUN: not llubi --verbose --entry-function=past_end < %s 2>&1 | FileCheck %s --check-prefix=PAST-END
+; 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]
@@ -16,16 +16,18 @@ define i64 @oracle(ptr %p, ptr %end) memory(none) nounwind nosync willreturn {
 
 define void @first_bytes() {
 ; FIRST: Entering function: first_bytes
-; FIRST-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 8)
-; FIRST-NEXT: error: Execution of function 'first_bytes' failed.
+; 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 }
+; FIRST-NEXT:   ret void
+; FIRST-NEXT: Exiting function: first_bytes
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 8)
   ret void
 }
 
 define void @last_bytes() {
 ; LAST: Entering function: last_bytes
-; LAST-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, i64 8)
-; LAST-NEXT: error: Execution of function 'last_bytes' failed.
+; LAST-NEXT:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, i64 8) => { poison, poison, i32 2, i32 3 }
+; LAST-NEXT:   ret void
+; LAST-NEXT: Exiting function: last_bytes
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 true, i64 8)
   ret void
 }
@@ -33,8 +35,9 @@ define void @last_bytes() {
 define void @past_end() {
 ; PAST-END: Entering function: past_end
 ; PAST-END-NEXT:   %p = getelementptr i32, ptr @a, i64 4 => ptr 0x20 [@a + 16]
-; PAST-END-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, i64 8)
-; PAST-END-NEXT: error: Execution of function 'past_end' failed.
+; PAST-END-NEXT:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, i64 8) => { i32 4, i32 5, poison, poison }
+; PAST-END-NEXT:   ret void
+; PAST-END-NEXT: Exiting function: past_end
   %p = getelementptr i32, ptr @a, i64 4
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr %p, i1 false, i64 8)
   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 c6c163adb4d3d..e08e1e410183d 100644
--- a/llvm/test/tools/llubi/intr_speculative_load_ub.ll
+++ b/llvm/test/tools/llubi/intr_speculative_load_ub.ll
@@ -21,7 +21,9 @@ declare i64 @oracle_decl(i64) memory(none) nounwind nosync willreturn
 
 define void @out_of_bounds() {
 ; OOB: Entering function: out_of_bounds
-; OOB-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 12)
+; 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: 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
@@ -29,7 +31,9 @@ define void @out_of_bounds() {
 
 define void @from_end_out_of_bounds() {
 ; FROM-END-OOB: Entering function: from_end_out_of_bounds
-; FROM-END-OOB-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr getelementptr (i8, ptr @a, i64 -8), i1 true, i64 12)
+; 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: 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
@@ -37,7 +41,9 @@ define void @from_end_out_of_bounds() {
 
 define void @exceeds_size() {
 ; EXCEEDS-SIZE: Entering function: exceeds_size
-; EXCEEDS-SIZE-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 17)
+; EXCEEDS-SIZE-NEXT: Stacktrace:
+; EXCEEDS-SIZE-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 17) at @exceeds_size <stdin>:{{[0-9]+}}
+; EXCEEDS-SIZE-NEXT: Immediate UB detected: llvm.speculative.load number of accessible bytes 17 exceeds the loaded size 16.
 ; EXCEEDS-SIZE-NEXT: error: Execution of function 'exceeds_size' failed.
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 17)
   ret void
@@ -45,7 +51,9 @@ define void @exceeds_size() {
 
 define void @poison_num_bytes() {
 ; POISON-N: Entering function: poison_num_bytes
-; POISON-N-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 poison)
+; POISON-N-NEXT: Stacktrace:
+; POISON-N-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 poison) at @poison_num_bytes <stdin>:{{[0-9]+}}
+; POISON-N-NEXT: Immediate UB detected: llvm.speculative.load with poison number of accessible bytes.
 ; POISON-N-NEXT: error: Execution of function 'poison_num_bytes' failed.
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr @a, i1 false, i64 poison)
   ret void
@@ -53,7 +61,9 @@ define void @poison_num_bytes() {
 
 define void @poison_pointer() {
 ; POISON-PTR: Entering function: poison_pointer
-; POISON-PTR-NEXT: Unrecognized instruction:   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr poison, i1 false, i64 0)
+; POISON-PTR-NEXT: Stacktrace:
+; POISON-PTR-NEXT: #0   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr poison, i1 false, i64 0) at @poison_pointer <stdin>:{{[0-9]+}}
+; POISON-PTR-NEXT: Immediate UB detected: llvm.speculative.load with poison pointer.
 ; POISON-PTR-NEXT: error: Execution of function 'poison_pointer' failed.
   %r = call <4 x i32> (ptr, i1, ...) @llvm.speculative.load.v4i32.p0(ptr poison, i1 false, i64 0)
   ret void
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 0c7e74f52f689..3e4b250f4c85c 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -919,6 +919,46 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     return AnyValue();
   }
 
+  AnyValue callSpeculativeLoadIntrinsic(CallBase &CB, const AnyValue &Ptr,
+                                        const AnyValue &NumBytes) {
+    Type *RetTy = CB.getType();
+    if (Ptr.isPoison()) {
+      reportImmediateUB() << "llvm.speculative.load with poison pointer.";
+      return AnyValue();
+    }
+    if (NumBytes.isPoison()) {
+      reportImmediateUB()
+          << "llvm.speculative.load with poison number of accessible bytes.";
+      return AnyValue();
+    }
+
+    const uint64_t Size = Ctx.getEffectiveTypeStoreSize(RetTy);
+    const APInt &NumBytesInt = NumBytes.asInteger();
+    if (NumBytesInt.ugt(Size)) {
+      reportImmediateUB() << "llvm.speculative.load number of accessible bytes "
+                          << NumBytesInt.getZExtValue()
+                          << " exceeds the loaded size " << Size << ".";
+      return AnyValue();
+    }
+
+    // Only the accessible bytes are read from memory and must be in bounds of
+    // the underlying object. All other bytes are poison.
+    const uint64_t N = NumBytesInt.getZExtValue();
+    SmallVector<Byte> Bytes(Size, Byte::poison());
+    if (N != 0) {
+      const bool FromEnd = cast<ConstantInt>(CB.getArgOperand(1))->isOne();
+      const uint64_t Start = FromEnd ? Size - N : 0;
+      const Pointer &PtrVal = Ptr.asPointer();
+      auto [MO, Offset] =
+          verifyMemAccess(PtrVal.getWithNewAddr(PtrVal.address() + Start), N,
+                          Align(1), /*IsStore=*/false);
+      if (!MO)
+        return AnyValue();
+      copy(MO->getBytes().slice(Offset, N), Bytes.begin() + Start);
+    }
+    return Ctx.fromBytes(Bytes, RetTy);
+  }
+
 public:
   InstExecutor(Context &C, EventHandler &H, Function &F,
                ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -1743,6 +1783,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     case Intrinsic::memset:
     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.
       return AnyValue();



More information about the llvm-commits mailing list