[llvm] [llubi] Implements common library functions (PR #190147)
Zhige Chen via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 2 23:51:15 PDT 2026
https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/190147
>From d97a702403a3102adc2321baf0e1c7098e7a3152 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 18:40:05 +0800
Subject: [PATCH 1/5] [llubi] Implements common library functions
---
llvm/test/tools/llubi/lib_abort.ll | 30 ++
llvm/test/tools/llubi/lib_cxx_memory.ll | 20 ++
llvm/test/tools/llubi/lib_double_free.ll | 21 ++
llvm/test/tools/llubi/lib_exit.ll | 30 ++
llvm/test/tools/llubi/lib_io.ll | 36 +++
llvm/test/tools/llubi/lib_memory.ll | 32 ++
llvm/test/tools/llubi/lib_printf_format.ll | 58 ++++
llvm/test/tools/llubi/lib_terminate.ll | 30 ++
llvm/test/tools/llubi/lib_uninit_string.ll | 18 ++
llvm/tools/llubi/lib/CMakeLists.txt | 1 +
llvm/tools/llubi/lib/Context.h | 41 ++-
llvm/tools/llubi/lib/ExecutorBase.cpp | 16 +-
llvm/tools/llubi/lib/ExecutorBase.h | 14 +-
llvm/tools/llubi/lib/Interpreter.cpp | 89 ++++--
llvm/tools/llubi/lib/Library.cpp | 348 +++++++++++++++++++++
llvm/tools/llubi/lib/Library.h | 55 ++++
llvm/tools/llubi/llubi.cpp | 42 ++-
17 files changed, 835 insertions(+), 46 deletions(-)
create mode 100644 llvm/test/tools/llubi/lib_abort.ll
create mode 100644 llvm/test/tools/llubi/lib_cxx_memory.ll
create mode 100644 llvm/test/tools/llubi/lib_double_free.ll
create mode 100644 llvm/test/tools/llubi/lib_exit.ll
create mode 100644 llvm/test/tools/llubi/lib_io.ll
create mode 100644 llvm/test/tools/llubi/lib_memory.ll
create mode 100644 llvm/test/tools/llubi/lib_printf_format.ll
create mode 100644 llvm/test/tools/llubi/lib_terminate.ll
create mode 100644 llvm/test/tools/llubi/lib_uninit_string.ll
create mode 100644 llvm/tools/llubi/lib/Library.cpp
create mode 100644 llvm/tools/llubi/lib/Library.h
diff --git a/llvm/test/tools/llubi/lib_abort.ll b/llvm/test/tools/llubi/lib_abort.ll
new file mode 100644
index 0000000000000..84327fdc99c5d
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_abort.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare void @abort() noreturn
+declare i32 @puts(ptr)
+
+define i32 @main() {
+entry:
+ %before = alloca [7 x i8]
+ store [7 x i8] c"Before\00", ptr %before
+
+ %after = alloca [6 x i8]
+ store [6 x i8] c"After\00", ptr %after
+
+ %0 = call i32 @puts(ptr %before)
+
+ call void @abort()
+
+ %1 = call i32 @puts(ptr %after)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %before = alloca [7 x i8], align 1 => ptr 0x8 [before]
+; CHECK-NEXT: store [7 x i8] c"Before\00", ptr %before, align 1
+; CHECK-NEXT: %after = alloca [6 x i8], align 1 => ptr 0xF [after]
+; CHECK-NEXT: store [6 x i8] c"After\00", ptr %after, align 1
+; CHECK-NEXT: %0 = call i32 @puts(ptr %before) => i32 1
+; CHECK-NEXT: Program aborted.
+; CHECK-NEXT: Before
diff --git a/llvm/test/tools/llubi/lib_cxx_memory.ll b/llvm/test/tools/llubi/lib_cxx_memory.ll
new file mode 100644
index 0000000000000..fd8e8aca84a3e
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_cxx_memory.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare ptr @_Znwm(i64) ; new(unsigned long)
+declare void @_ZdlPv(ptr) ; delete(void*)
+
+define i32 @main() {
+entry:
+ %ptr = call ptr @_Znwm(i64 8)
+ store i64 42, ptr %ptr
+
+ call void @_ZdlPv(ptr %ptr)
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %ptr = call ptr @_Znwm(i64 8) => ptr 0x10 [ptr]
+; CHECK-NEXT: store i64 42, ptr %ptr, align 4
+; CHECK-NEXT: call void @_ZdlPv(ptr %ptr)
+; CHECK-NEXT: ret i32 0
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/lib_double_free.ll b/llvm/test/tools/llubi/lib_double_free.ll
new file mode 100644
index 0000000000000..2441d69f6628a
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_double_free.ll
@@ -0,0 +1,21 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare ptr @malloc(i64)
+declare void @free(ptr)
+
+define i32 @main() {
+entry:
+ %ptr = call ptr @malloc(i64 4)
+
+ call void @free(ptr %ptr)
+
+ call void @free(ptr %ptr)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %ptr = call ptr @malloc(i64 4) => ptr 0x10 [ptr]
+; CHECK-NEXT: call void @free(ptr %ptr)
+; CHECK-NEXT: Immediate UB detected: freeing an invalid, unallocated, or already freed pointer.
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/lib_exit.ll b/llvm/test/tools/llubi/lib_exit.ll
new file mode 100644
index 0000000000000..d6a7037c50043
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_exit.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare void @exit(i32) noreturn
+declare i32 @puts(ptr)
+
+define i32 @main() {
+entry:
+ %before = alloca [7 x i8]
+ store [7 x i8] c"Before\00", ptr %before
+
+ %after = alloca [6 x i8]
+ store [6 x i8] c"After\00", ptr %after
+
+ %0 = call i32 @puts(ptr %before)
+
+ call void @exit(i32 42)
+
+ %1 = call i32 @puts(ptr %after)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %before = alloca [7 x i8], align 1 => ptr 0x8 [before]
+; CHECK-NEXT: store [7 x i8] c"Before\00", ptr %before, align 1
+; CHECK-NEXT: %after = alloca [6 x i8], align 1 => ptr 0xF [after]
+; CHECK-NEXT: store [6 x i8] c"After\00", ptr %after, align 1
+; CHECK-NEXT: %0 = call i32 @puts(ptr %before) => i32 1
+; CHECK-NEXT: Program exited with code 42
+; CHECK-NEXT: Before
diff --git a/llvm/test/tools/llubi/lib_io.ll b/llvm/test/tools/llubi/lib_io.ll
new file mode 100644
index 0000000000000..5b5c861f5d237
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_io.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare i32 @printf(ptr, ...)
+declare i32 @puts(ptr)
+
+define i32 @main() {
+entry:
+ %puts.str = alloca [13 x i8]
+ store [13 x i8] c"Hello, puts!\00", ptr %puts.str
+
+ %0 = call i32 @puts(ptr %puts.str)
+
+ %fmt.str = alloca [18 x i8]
+ store [18 x i8] c"Int: %d, Str: %s\0A\00", ptr %fmt.str
+
+ %arg.str = alloca [5 x i8]
+ store [5 x i8] c"test\00", ptr %arg.str
+
+ %1 = call i32 (ptr, ...) @printf(ptr %fmt.str, i32 42, ptr %arg.str)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %puts.str = alloca [13 x i8], align 1 => ptr 0x8 [puts.str]
+; CHECK-NEXT: store [13 x i8] c"Hello, puts!\00", ptr %puts.str, align 1
+; CHECK-NEXT: %0 = call i32 @puts(ptr %puts.str) => i32 1
+; CHECK-NEXT: %fmt.str = alloca [18 x i8], align 1 => ptr 0x15 [fmt.str]
+; CHECK-NEXT: store [18 x i8] c"Int: %d, Str: %s\0A\00", ptr %fmt.str, align 1
+; CHECK-NEXT: %arg.str = alloca [5 x i8], align 1 => ptr 0x27 [arg.str]
+; CHECK-NEXT: store [5 x i8] c"test\00", ptr %arg.str, align 1
+; CHECK-NEXT: %1 = call i32 (ptr, ...) @printf(ptr %fmt.str, i32 42, ptr %arg.str) => i32 19
+; CHECK-NEXT: ret i32 0
+; CHECK-NEXT: Exiting function: main
+; CHECK-NEXT: Hello, puts!
+; CHECK-NEXT: Int: 42, Str: test
diff --git a/llvm/test/tools/llubi/lib_memory.ll b/llvm/test/tools/llubi/lib_memory.ll
new file mode 100644
index 0000000000000..4677841059a1b
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_memory.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare ptr @malloc(i64)
+declare ptr @calloc(i64, i64)
+declare void @free(ptr)
+
+define i32 @main() {
+entry:
+ %ptr1 = call ptr @malloc(i64 4)
+ store i32 100, ptr %ptr1
+
+ %ptr2 = call ptr @calloc(i64 1, i64 4)
+
+ %val1 = load i32, ptr %ptr1
+ %val2 = load i32, ptr %ptr2
+
+ call void @free(ptr %ptr1)
+ call void @free(ptr %ptr2)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %ptr1 = call ptr @malloc(i64 4) => ptr 0x10 [ptr1]
+; CHECK-NEXT: store i32 100, ptr %ptr1, align 4
+; CHECK-NEXT: %ptr2 = call ptr @calloc(i64 1, i64 4) => ptr 0x20 [ptr2]
+; CHECK-NEXT: %val1 = load i32, ptr %ptr1, align 4 => i32 100
+; CHECK-NEXT: %val2 = load i32, ptr %ptr2, align 4 => i32 0
+; CHECK-NEXT: call void @free(ptr %ptr1)
+; CHECK-NEXT: call void @free(ptr %ptr2)
+; CHECK-NEXT: ret i32 0
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/lib_printf_format.ll b/llvm/test/tools/llubi/lib_printf_format.ll
new file mode 100644
index 0000000000000..24cc5f2bd2b40
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_printf_format.ll
@@ -0,0 +1,58 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare i32 @printf(ptr, ...)
+
+define i32 @main() {
+entry:
+ %fmt_int = alloca [36 x i8]
+ store [36 x i8] c"Ints: %d, %i, %u, %o, %x, %X, %05d\0A\00", ptr %fmt_int
+
+ %fmt_len = alloca [35 x i8]
+ store [35 x i8] c"Lengths: %ld, %lld, %hd, %hhu, %c\0A\00", ptr %fmt_len
+
+ %fmt_str_ptr = alloca [18 x i8]
+ store [18 x i8] c"Str: %s, Ptr: %p\0A\00", ptr %fmt_str_ptr
+
+ %fmt_pct = alloca [15 x i8]
+ store [15 x i8] c"Percent: %d%%\0A\00", ptr %fmt_pct
+
+ %dummy_str = alloca [6 x i8]
+ store [6 x i8] c"llubi\00", ptr %dummy_str
+
+ %fmt_float = alloca [20 x i8]
+ store [20 x i8] c"Floats: %f, %e, %g\0A\00", ptr %fmt_float
+
+ call i32 (ptr, ...) @printf(ptr %fmt_int, i32 42, i32 -42, i32 255, i32 255, i32 255, i32 255, i32 42)
+ call i32 (ptr, ...) @printf(ptr %fmt_len, i64 123456789, i64 987654321, i32 100, i32 50, i32 65)
+ call i32 (ptr, ...) @printf(ptr %fmt_str_ptr, ptr %dummy_str, ptr %dummy_str)
+ call i32 (ptr, ...) @printf(ptr %fmt_pct, i32 100)
+ call i32 (ptr, ...) @printf(ptr %fmt_float, double 3.14159, double 3.14159, double 3.14159)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %fmt_int = alloca [36 x i8], align 1 => ptr 0x8 [fmt_int]
+; CHECK-NEXT: store [36 x i8] c"Ints: %d, %i, %u, %o, %x, %X, %05d\0A\00", ptr %fmt_int, align 1
+; CHECK-NEXT: %fmt_len = alloca [35 x i8], align 1 => ptr 0x2C [fmt_len]
+; CHECK-NEXT: store [35 x i8] c"Lengths: %ld, %lld, %hd, %hhu, %c\0A\00", ptr %fmt_len, align 1
+; CHECK-NEXT: %fmt_str_ptr = alloca [18 x i8], align 1 => ptr 0x4F [fmt_str_ptr]
+; CHECK-NEXT: store [18 x i8] c"Str: %s, Ptr: %p\0A\00", ptr %fmt_str_ptr, align 1
+; CHECK-NEXT: %fmt_pct = alloca [15 x i8], align 1 => ptr 0x61 [fmt_pct]
+; CHECK-NEXT: store [15 x i8] c"Percent: %d%%\0A\00", ptr %fmt_pct, align 1
+; CHECK-NEXT: %dummy_str = alloca [6 x i8], align 1 => ptr 0x70 [dummy_str]
+; CHECK-NEXT: store [6 x i8] c"llubi\00", ptr %dummy_str, align 1
+; CHECK-NEXT: %fmt_float = alloca [20 x i8], align 1 => ptr 0x76 [fmt_float]
+; CHECK-NEXT: store [20 x i8] c"Floats: %f, %e, %g\0A\00", ptr %fmt_float, align 1
+; CHECK-NEXT: %0 = call i32 (ptr, ...) @printf(ptr %fmt_int, i32 42, i32 -42, i32 255, i32 255, i32 255, i32 255, i32 42) => i32 39
+; CHECK-NEXT: %1 = call i32 (ptr, ...) @printf(ptr %fmt_len, i64 123456789, i64 987654321, i32 100, i32 50, i32 65) => i32 42
+; CHECK-NEXT: %2 = call i32 (ptr, ...) @printf(ptr %fmt_str_ptr, ptr %dummy_str, ptr %dummy_str) => i32 22
+; CHECK-NEXT: %3 = call i32 (ptr, ...) @printf(ptr %fmt_pct, i32 100) => i32 14
+; CHECK-NEXT: %4 = call i32 (ptr, ...) @printf(ptr %fmt_float, double 3.141590e+00, double 3.141590e+00, double 3.141590e+00) => i32 40
+; CHECK-NEXT: ret i32 0
+; CHECK-NEXT: Exiting function: main
+; CHECK-NEXT: Ints: 42, -42, 255, 377, ff, FF, 00042
+; CHECK-NEXT: Lengths: 123456789, 987654321, 100, 50, A
+; CHECK-NEXT: Str: llubi, Ptr: 0x70
+; CHECK-NEXT: Percent: 100%
+; CHECK-NEXT: Floats: 3.141590, 3.141590e+00, 3.14159
diff --git a/llvm/test/tools/llubi/lib_terminate.ll b/llvm/test/tools/llubi/lib_terminate.ll
new file mode 100644
index 0000000000000..6d7821584e1a4
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_terminate.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare void @_ZSt9terminatev()
+declare i32 @puts(ptr)
+
+define i32 @main() {
+entry:
+ %before = alloca [7 x i8]
+ store [7 x i8] c"Before\00", ptr %before
+
+ %after = alloca [6 x i8]
+ store [6 x i8] c"After\00", ptr %after
+
+ %0 = call i32 @puts(ptr %before)
+
+ call void @_ZSt9terminatev()
+
+ %1 = call i32 @puts(ptr %after)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %before = alloca [7 x i8], align 1 => ptr 0x8 [before]
+; CHECK-NEXT: store [7 x i8] c"Before\00", ptr %before, align 1
+; CHECK-NEXT: %after = alloca [6 x i8], align 1 => ptr 0xF [after]
+; CHECK-NEXT: store [6 x i8] c"After\00", ptr %after, align 1
+; CHECK-NEXT: %0 = call i32 @puts(ptr %before) => i32 1
+; CHECK-NEXT: Program terminated.
+; CHECK-NEXT: Before
diff --git a/llvm/test/tools/llubi/lib_uninit_string.ll b/llvm/test/tools/llubi/lib_uninit_string.ll
new file mode 100644
index 0000000000000..7274cfdb63363
--- /dev/null
+++ b/llvm/test/tools/llubi/lib_uninit_string.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+declare ptr @malloc(i64)
+declare i32 @puts(ptr)
+
+define i32 @main() {
+entry:
+ %ptr = call ptr @malloc(i64 10)
+
+ %1 = call i32 @puts(ptr %ptr)
+
+ ret i32 0
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %ptr = call ptr @malloc(i64 10) => ptr 0x10 [ptr]
+; CHECK-NEXT: Immediate UB detected: Read uninitialized or poison memory while parsing C-string.
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/tools/llubi/lib/CMakeLists.txt b/llvm/tools/llubi/lib/CMakeLists.txt
index b3c7b60cac50e..1e587834f9dbb 100644
--- a/llvm/tools/llubi/lib/CMakeLists.txt
+++ b/llvm/tools/llubi/lib/CMakeLists.txt
@@ -9,5 +9,6 @@ add_llvm_library(LLVMUBAwareInterpreter
Context.cpp
ExecutorBase.cpp
Interpreter.cpp
+ Library.cpp
Value.cpp
)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index d1960b270d9bd..06aff25f8e46d 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -47,6 +47,34 @@ enum class UndefValueBehavior {
Zero, // All uses of the undef value yield zero.
};
+struct ProgramExitInfo {
+ enum class ProgramExitKind {
+ Invalid,
+ // Program exited via a normal return
+ Returned,
+ // Program exited with an interpreter error (UB/Unsupported
+ // instruction/etc.)
+ Failed,
+ // Program exited via a call to exit()
+ Exited,
+ // Program exited via a call to abort()
+ Aborted,
+ // Program exited via a call to terminate()
+ Terminated,
+ };
+
+ ProgramExitKind Kind = ProgramExitKind::Invalid;
+ uint64_t ExitCode = 0;
+
+ explicit operator bool() const { return Kind != ProgramExitKind::Invalid; }
+
+ bool isExitedByLibcall() const {
+ return Kind == ProgramExitKind::Exited ||
+ Kind == ProgramExitKind::Aborted ||
+ Kind == ProgramExitKind::Terminated;
+ }
+};
+
class MemoryObject : public RefCountedBase<MemoryObject> {
uint64_t Address;
uint64_t Size;
@@ -110,6 +138,7 @@ class EventHandler {
virtual bool onFunctionExit(Function &F, const AnyValue &RetVal) {
return true;
}
+ virtual bool onProgramExit(const ProgramExitInfo &ExitInfo) { return true; }
virtual bool onPrint(StringRef Msg) {
outs() << Msg;
return true;
@@ -257,13 +286,15 @@ class Context {
/// initialization).
bool initGlobalValues();
/// Execute the function \p F with arguments \p Args, and store the return
- /// value in \p RetVal if the function is not void.
- /// Returns true if the function executed successfully. False indicates an
- /// error occurred during execution.
+ /// value in \p RetVal if the function is not void. The exit information is
+ /// store in \p ExitInfo.
+ /// Returns true if the function executed successfully without calls to
+ /// exit()/abort()/terminate(). False indicates an error occurred during
+ /// execution.
bool runFunction(Function &F, ArrayRef<AnyValue> Args, AnyValue &RetVal,
- EventHandler &Handler);
+ EventHandler &Handler, ProgramExitInfo &ExitInfo);
};
} // namespace llvm::ubi
-#endif
+#endif
\ No newline at end of file
diff --git a/llvm/tools/llubi/lib/ExecutorBase.cpp b/llvm/tools/llubi/lib/ExecutorBase.cpp
index ec66e831908c5..d546c80e17aad 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.cpp
+++ b/llvm/tools/llubi/lib/ExecutorBase.cpp
@@ -124,4 +124,18 @@ void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
/*IsStore=*/true))
Ctx.store(*MO, *Offset, Val, ValTy);
}
-} // namespace llvm::ubi
+
+void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
+uint64_t ExitCode) {
+ if (Kind == ProgramExitInfo::ProgramExitKind::Invalid)
+ llvm_unreachable("Invalid program exit kind");
+ Status = false;
+ ExitInfo.Kind = Kind;
+ ExitInfo.ExitCode = ExitCode;
+ Handler.onProgramExit(ExitInfo);
+}
+
+bool ExecutorBase::getExecutionStatus() const { return Status; }
+
+ProgramExitInfo ExecutorBase::getExitInfo() const { return ExitInfo; }
+} // namespace llvm::ubi
\ No newline at end of file
diff --git a/llvm/tools/llubi/lib/ExecutorBase.h b/llvm/tools/llubi/lib/ExecutorBase.h
index 0f80c6a329058..e400fb95d02f7 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.h
+++ b/llvm/tools/llubi/lib/ExecutorBase.h
@@ -71,10 +71,14 @@ class ExecutorBase {
protected:
Context &Ctx;
EventHandler &Handler;
+ Frame *CurrentFrame = nullptr;
+ ProgramExitInfo ExitInfo;
+
+private:
// Used to indicate whether the interpreter should continue execution.
bool Status;
- Frame *CurrentFrame = nullptr;
+protected:
ExecutorBase(Context &C, EventHandler &H)
: Ctx(C), Handler(H), Status(true) {}
~ExecutorBase() = default;
@@ -93,8 +97,14 @@ class ExecutorBase {
AnyValue load(const AnyValue &Ptr, Align Alignment, Type *ValTy);
void store(const AnyValue &Ptr, Align Alignment, const AnyValue &Val,
Type *ValTy);
+
+ void requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
+ uint64_t ExitCode = 0);
+
+ bool getExecutionStatus() const;
+ ProgramExitInfo getExitInfo() const;
};
} // namespace llvm::ubi
-#endif // LLVM_TOOLS_LLUBI_EXECUTORBASE_H
+#endif // LLVM_TOOLS_LLUBI_EXECUTORBASE_H
\ No newline at end of file
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index e5d15be805e07..c72ca1d0842b4 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -12,6 +12,7 @@
#include "Context.h"
#include "ExecutorBase.h"
+#include "Library.h"
#include "Value.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/InlineAsm.h"
@@ -76,8 +77,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void setResult(Instruction &I, AnyValue V) {
- if (Status)
- Status &= Handler.onInstructionExecuted(I, V);
+ if (getExecutionStatus())
+ if (!Handler.onInstructionExecuted(I, V))
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
CurrentFrame->ValueMap.insert_or_assign(&I, std::move(V));
}
@@ -142,7 +144,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
void jumpTo(Instruction &Terminator, BasicBlock *DestBB) {
if (!Handler.onBBJump(Terminator, *DestBB)) {
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
return;
}
BasicBlock *From = CurrentFrame->BB;
@@ -266,23 +268,26 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (auto *RV = RI.getReturnValue())
CurrentFrame->RetVal = getValue(RV);
CurrentFrame->State = FrameState::Exit;
- Status &= Handler.onInstructionExecuted(RI, None);
+ if (getExecutionStatus())
+ if (!Handler.onInstructionExecuted(RI, None))
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
}
- void visitUncondBrInst(UncondBrInst &BI) { jumpTo(BI, BI.getSuccessor()); }
-
- void visitCondBrInst(CondBrInst &BI) {
- switch (getValue(BI.getCondition()).asBoolean()) {
- case BooleanKind::True:
- jumpTo(BI, BI.getSuccessor(0));
- return;
- case BooleanKind::False:
- jumpTo(BI, BI.getSuccessor(1));
- return;
- case BooleanKind::Poison:
- reportImmediateUB("Branch on poison condition.");
- return;
+ void visitBranchInst(BranchInst &BI) {
+ if (BI.isConditional()) {
+ switch (getValue(BI.getCondition()).asBoolean()) {
+ case BooleanKind::True:
+ jumpTo(BI, BI.getSuccessor(0));
+ return;
+ case BooleanKind::False:
+ jumpTo(BI, BI.getSuccessor(1));
+ return;
+ case BooleanKind::Poison:
+ reportImmediateUB("Branch on poison condition.");
+ return;
+ }
}
+ jumpTo(BI, BI.getSuccessor(0));
}
void visitSwitchInst(SwitchInst &SI) {
@@ -311,7 +316,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
Handler.onUnrecognizedInstruction(CI);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
}
void visitIndirectBrInst(IndirectBrInst &IBI) {
@@ -379,7 +384,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
default:
Handler.onUnrecognizedInstruction(CB);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
return AnyValue();
}
}
@@ -390,12 +395,26 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (CB.isNoBuiltin() ||
!CurrentFrame->TLI.getLibFunc(*ResolvedCallee, LF)) {
Handler.onUnrecognizedInstruction(CB);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
return AnyValue();
}
+ Library Lib(Ctx, Handler, DL, static_cast<ExecutorBase &>(*this));
+
+ SmallVector<AnyValue, 8> Args;
+ for (const auto &Arg : CB.args()) {
+ Args.push_back(getValue(Arg));
+ }
+
+ if (auto LibCallRes =
+ Lib.executeLibcall(LF, CB.getName(), CB.getType(), Args))
+ return *LibCallRes;
+
+ if (ExitInfo)
+ return AnyValue();
+
Handler.onUnrecognizedInstruction(CB);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
return AnyValue();
}
@@ -420,7 +439,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (isa<InlineAsm>(CalledOperand)) {
Handler.onUnrecognizedInstruction(CB);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
return;
}
@@ -873,13 +892,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// TODO: track volatile stores
// TODO: handle metadata
store(Ptr, SI.getAlign(), Val, SI.getValueOperand()->getType());
- if (Status)
- Status &= Handler.onInstructionExecuted(SI, AnyValue());
+ if (getExecutionStatus())
+ if (!Handler.onInstructionExecuted(SI, AnyValue()))
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
}
void visitInstruction(Instruction &I) {
Handler.onUnrecognizedInstruction(I);
- Status = false;
+ requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
}
void visitExtractValueInst(ExtractValueInst &EVI) {
@@ -969,7 +989,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
bool runMainLoop() {
uint32_t MaxSteps = Ctx.getMaxSteps();
uint32_t Steps = 0;
- while (Status && !CallStack.empty()) {
+ while (getExecutionStatus() && !CallStack.empty()) {
Frame &Top = CallStack.back();
CurrentFrame = &Top;
if (Top.State == FrameState::Entry) {
@@ -982,7 +1002,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
Top.State = FrameState::Running;
// Interpreter loop inside a function
- while (Status) {
+ while (getExecutionStatus()) {
assert(Top.State == FrameState::Running &&
"Expected to be in running state.");
if (MaxSteps != 0 && Steps >= MaxSteps) {
@@ -993,7 +1013,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
Instruction &I = *Top.PC;
visit(&I);
- if (!Status)
+ if (!getExecutionStatus())
break;
// A function call or return has occurred.
@@ -1007,7 +1027,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
++Top.PC;
}
- if (!Status)
+ if (!getExecutionStatus())
break;
if (Top.State == FrameState::Exit) {
@@ -1023,14 +1043,17 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
"Expected to enter a callee.");
}
}
- return Status;
+ return getExecutionStatus();
}
};
bool Context::runFunction(Function &F, ArrayRef<AnyValue> Args,
- AnyValue &RetVal, EventHandler &Handler) {
+ AnyValue &RetVal, EventHandler &Handler,
+ ProgramExitInfo &ExitInfo) {
InstExecutor Executor(*this, Handler, F, Args, RetVal);
- return Executor.runMainLoop();
+ bool Result = Executor.runMainLoop();
+ ExitInfo = Executor.getExitInfo();
+ return Result;
}
-} // namespace llvm::ubi
+} // namespace llvm::ubi
\ No newline at end of file
diff --git a/llvm/tools/llubi/lib/Library.cpp b/llvm/tools/llubi/lib/Library.cpp
new file mode 100644
index 0000000000000..8f79d14671250
--- /dev/null
+++ b/llvm/tools/llubi/lib/Library.cpp
@@ -0,0 +1,348 @@
+//===- Library.cpp - Library calls for llubi ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements common libcalls for llubi.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Library.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/IR/InstrTypes.h"
+
+namespace llvm::ubi {
+
+static uint64_t getMaxAlignT(const DataLayout &DL) {
+ return DL.getPointerABIAlignment(0).value() >= 8 ? 16 : 8;
+}
+
+Library::Library(Context &Ctx, EventHandler &Handler, const DataLayout &DL,
+ ExecutorBase &Executor)
+ : Ctx(Ctx), Handler(Handler), DL(DL), Executor(Executor) {}
+
+std::optional<std::string> Library::readStringFromMemory(const Pointer &Ptr) {
+ auto *MO = Ptr.getMemoryObject();
+ if (!MO) {
+ Executor.reportImmediateUB(
+ "Invalid memory access via a pointer with nullary "
+ "provenance.");
+ return std::nullopt;
+ }
+
+ std::string Result;
+ const uint64_t Address = Ptr.address().getZExtValue();
+ uint64_t Offset = 0;
+
+ while (true) {
+ auto ValidOffset = Executor.verifyMemAccess(
+ *MO, APInt(DL.getPointerSizeInBits(0), Address + Offset), 1, Align(1),
+ false);
+ if (!ValidOffset) {
+ return std::nullopt;
+ }
+
+ Byte B = (*MO)[*ValidOffset];
+ if (B.ConcreteMask != 0xFF) {
+ Executor.reportImmediateUB("Read uninitialized or poison memory while "
+ "parsing C-string.");
+ return std::nullopt;
+ }
+
+ if (B.Value == 0) {
+ break;
+ }
+
+ Result.push_back(static_cast<char>(B.Value));
+ ++Offset;
+ }
+
+ return Result;
+}
+
+AnyValue Library::executeMalloc(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &SizeVal = Args[0];
+ if (SizeVal.isPoison()) {
+ Executor.reportImmediateUB("malloc() called with a poison size.");
+ return AnyValue::poison();
+ }
+
+ const uint64_t AllocSize = SizeVal.asInteger().getZExtValue();
+ const uint64_t MaxAlign = getMaxAlignT(DL);
+
+ const auto Obj =
+ Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Uninitialized);
+
+ if (!Obj)
+ return AnyValue::getNullValue(Ctx, Type);
+
+ return Ctx.deriveFromMemoryObject(Obj);
+}
+
+AnyValue Library::executeCalloc(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &CountVal = Args[0];
+ const auto &SizeVal = Args[1];
+
+ if (CountVal.isPoison()) {
+ Executor.reportImmediateUB("calloc() called with a poison count.");
+ return AnyValue::poison();
+ }
+ if (SizeVal.isPoison()) {
+ Executor.reportImmediateUB("calloc() called with a poison size.");
+ return AnyValue::poison();
+ }
+
+ const uint64_t Count = CountVal.asInteger().getZExtValue();
+ const uint64_t Size = SizeVal.asInteger().getZExtValue();
+
+ bool Overflow;
+ const uint64_t AllocSize = SaturatingMultiply(Count, Size, &Overflow);
+ if (Overflow) {
+ return AnyValue::getNullValue(Ctx, Type);
+ }
+
+ const uint64_t MaxAlign = getMaxAlignT(DL);
+
+ // TODO: Figure out how to name the allocation
+ const auto Obj =
+ Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Zeroed);
+
+ if (!Obj) {
+ return AnyValue::getNullValue(Ctx, Type);
+ }
+
+ return Ctx.deriveFromMemoryObject(Obj);
+}
+
+AnyValue Library::executeFree(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &PtrVal = Args[0];
+ if (PtrVal.isPoison()) {
+ Executor.reportImmediateUB("free() called with a poison pointer.");
+ return AnyValue::poison();
+ }
+
+ auto &Ptr = PtrVal.asPointer();
+ if (Ptr.address().isZero()) {
+ // no-op when free is called with a null pointer.
+ return AnyValue();
+ }
+
+ if (!Ctx.free(Ptr.address().getZExtValue())) {
+ Executor.reportImmediateUB(
+ "freeing an invalid, unallocated, or already freed pointer.");
+ return AnyValue::poison();
+ }
+
+ return AnyValue();
+}
+
+AnyValue Library::executePuts(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &PtrVal = Args[0];
+ if (PtrVal.isPoison()) {
+ Executor.reportImmediateUB("puts called with a poison pointer.");
+ return AnyValue::poison();
+ }
+
+ const auto StrOpt = readStringFromMemory(PtrVal.asPointer());
+ if (!StrOpt) {
+ return AnyValue::poison();
+ }
+
+ Handler.onPrint(*StrOpt + "\n");
+ return AnyValue(APInt(32, 1));
+}
+
+AnyValue Library::executePrintf(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &FormatPtrVal = Args[0];
+ if (FormatPtrVal.isPoison()) {
+ Executor.reportImmediateUB(
+ "printf called with a poison format string pointer.");
+ return AnyValue::poison();
+ }
+
+ const auto FormatStrOpt = readStringFromMemory(FormatPtrVal.asPointer());
+ if (!FormatStrOpt) {
+ return AnyValue::poison();
+ }
+
+ const std::string FormatStr = *FormatStrOpt;
+ std::string Output;
+ unsigned ArgIndex = 1; // Start from 1 since 0 is the format string.
+
+ for (size_t i = 0; i < FormatStr.size();) {
+ if (FormatStr[i] != '%') {
+ Output.push_back(FormatStr[i++]);
+ continue;
+ }
+
+ const size_t Start = i++;
+ if (i < FormatStr.size() && FormatStr[i] == '%') {
+ Output.push_back('%');
+ ++i;
+ continue;
+ }
+
+ while (i < FormatStr.size() && strchr("-= #0123456789", FormatStr[i])) {
+ ++i;
+ }
+
+ while (i < FormatStr.size() && strchr("hljzt", FormatStr[i])) {
+ ++i;
+ }
+
+ if (i >= FormatStr.size()) {
+ Executor.reportImmediateUB(
+ "Invalid format string in printf: missing conversion "
+ "specifier.");
+ return AnyValue::poison();
+ }
+
+ char Specifier = FormatStr[i++];
+ std::string CleanChunk = FormatStr.substr(Start, i - Start - 1);
+ CleanChunk.erase(std::remove_if(CleanChunk.begin(), CleanChunk.end(),
+ [](char c) { return strchr("hljzt", c); }),
+ CleanChunk.end());
+
+ if (ArgIndex >= Args.size()) {
+ Executor.reportImmediateUB(
+ "Not enough arguments provided for the format string.");
+ return AnyValue::poison();
+ }
+
+ const auto &Arg = Args[ArgIndex++];
+ if (Arg.isPoison()) {
+ Executor.reportImmediateUB("Poison argument passed to printf.");
+ return AnyValue::poison();
+ }
+
+ char Buf[1024];
+ switch (Specifier) {
+ case 'd':
+ case 'i': {
+ std::string HostFmt = CleanChunk + "ll" + Specifier;
+ snprintf(Buf, sizeof(Buf), HostFmt.c_str(),
+ static_cast<long long>(Arg.asInteger().getSExtValue()));
+ Output += Buf;
+ break;
+ }
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'c': {
+ std::string HostFmt = CleanChunk + "ll" + Specifier;
+ snprintf(Buf, sizeof(Buf), HostFmt.c_str(),
+ static_cast<unsigned long long>(Arg.asInteger().getZExtValue()));
+ Output += Buf;
+ break;
+ }
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G': {
+ std::string HostFmt = CleanChunk + Specifier;
+ snprintf(Buf, sizeof(Buf), HostFmt.c_str(),
+ Arg.asFloat().convertToDouble());
+ Output += Buf;
+ break;
+ }
+ case 'p': {
+ std::string HostFmt = CleanChunk + "llx";
+ snprintf(Buf, sizeof(Buf), HostFmt.c_str(),
+ static_cast<unsigned long long>(
+ Arg.asPointer().address().getZExtValue()));
+ Output += "0x";
+ Output += Buf;
+ break;
+ }
+ case 's': {
+ auto StrOpt = readStringFromMemory(Arg.asPointer());
+ if (!StrOpt)
+ return AnyValue::poison();
+ std::string HostFmt = CleanChunk + "s";
+ snprintf(Buf, sizeof(Buf), HostFmt.c_str(), StrOpt->c_str());
+ Output += Buf;
+ break;
+ }
+ default:
+ Executor.reportImmediateUB("Unknown format specifier in printf.");
+ return AnyValue::poison();
+ }
+ }
+
+ Handler.onPrint(Output);
+ return AnyValue(APInt(32, Output.size()));
+}
+
+AnyValue Library::executeExit(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ const auto &RetCodeVal = Args[0];
+
+ if (RetCodeVal.isPoison()) {
+ Executor.reportImmediateUB("exit() called with a poison exit code.");
+ return AnyValue::poison();
+ }
+
+ Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Exited,
+ RetCodeVal.asInteger().getZExtValue());
+ return AnyValue();
+}
+
+AnyValue Library::executeAbort(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Aborted);
+ return AnyValue();
+}
+
+AnyValue Library::executeTerminate(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args) {
+ Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Terminated);
+ return AnyValue();
+}
+
+std::optional<AnyValue> Library::executeLibcall(LibFunc LF, StringRef Name,
+ Type *Type,
+ ArrayRef<AnyValue> Args) {
+ switch (LF) {
+ case LibFunc_malloc:
+ case LibFunc_Znwm:
+ case LibFunc_Znam:
+ return executeMalloc(Name, Type, Args);
+
+ case LibFunc_calloc:
+ return executeCalloc(Name, Type, Args);
+
+ case LibFunc_free:
+ case LibFunc_ZdaPv:
+ case LibFunc_ZdlPv:
+ return executeFree(Name, Type, Args);
+
+ case LibFunc_puts:
+ return executePuts(Name, Type, Args);
+
+ case LibFunc_printf:
+ return executePrintf(Name, Type, Args);
+
+ case LibFunc_exit:
+ return executeExit(Name, Type, Args);
+
+ case LibFunc_abort:
+ return executeAbort(Name, Type, Args);
+
+ case LibFunc_terminate:
+ return executeTerminate(Name, Type, Args);
+
+ default:
+ return std::nullopt;
+ }
+}
+} // namespace llvm::ubi
\ No newline at end of file
diff --git a/llvm/tools/llubi/lib/Library.h b/llvm/tools/llubi/lib/Library.h
new file mode 100644
index 0000000000000..765c5f56616b0
--- /dev/null
+++ b/llvm/tools/llubi/lib/Library.h
@@ -0,0 +1,55 @@
+//===--- Library.h - Library calls for llubi ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements common libcalls for llubi.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLUBI_LIBRARY_H
+#define LLVM_TOOLS_LLUBI_LIBRARY_H
+
+#include "Context.h"
+#include "ExecutorBase.h"
+#include "Value.h"
+#include <optional>
+#include <string>
+
+namespace llvm::ubi {
+
+class Library {
+ Context &Ctx;
+ EventHandler &Handler;
+ const DataLayout &DL;
+ ExecutorBase &Executor;
+
+ std::optional<std::string> readStringFromMemory(const Pointer &Ptr);
+
+ AnyValue executeMalloc(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executeCalloc(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executeFree(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executePuts(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executePrintf(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executeExit(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executeAbort(StringRef Name, Type *Type, ArrayRef<AnyValue> Args);
+ AnyValue executeTerminate(StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args);
+
+public:
+ Library(Context &Ctx, EventHandler &Handler, const DataLayout &DL,
+ ExecutorBase &Executor);
+
+ /// Simulates a libcall. Returns std::nullopt if an unsupported LibFunc is
+ /// passed. Note that the caller is responsible for ensuring the types and
+ /// number of the arguments are correct.
+ std::optional<AnyValue> executeLibcall(LibFunc LF, StringRef Name, Type *Type,
+ ArrayRef<AnyValue> Args);
+};
+
+} // namespace llvm::ubi
+
+#endif // LLVM_TOOLS_LLUBI_LIBRARY_H
\ No newline at end of file
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index de76a7e64c27b..929489dab23b4 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -131,6 +131,26 @@ class VerboseEventHandler : public ubi::EventHandler {
return true;
}
+ bool onProgramExit(const ubi::ProgramExitInfo &Info) override {
+ switch (Info.Kind) {
+ case ubi::ProgramExitInfo::ProgramExitKind::Returned:
+ return true;
+ case ubi::ProgramExitInfo::ProgramExitKind::Failed:
+ return true;
+ case ubi::ProgramExitInfo::ProgramExitKind::Exited:
+ errs() << "Program exited with code " << Info.ExitCode << '\n';
+ return true;
+ case ubi::ProgramExitInfo::ProgramExitKind::Aborted:
+ errs() << "Program aborted.\n";
+ return true;
+ case ubi::ProgramExitInfo::ProgramExitKind::Terminated:
+ errs() << "Program terminated.\n";
+ return true;
+ default:
+ llvm_unreachable("Unknown ProgramExitKind");
+ }
+ }
+
void onUnrecognizedInstruction(Instruction &I) override {
errs() << "Unrecognized instruction: " << I << '\n';
}
@@ -240,11 +260,23 @@ int main(int argc, char **argv) {
ubi::EventHandler NoopHandler;
VerboseEventHandler VerboseHandler;
ubi::AnyValue RetVal;
+ ubi::ProgramExitInfo ExitInfo;
if (!Ctx.runFunction(*EntryFn, Args, RetVal,
- Verbose ? VerboseHandler : NoopHandler)) {
- WithColor::error() << "Execution of function '" << EntryFunc
- << "' failed.\n";
- return 1;
+ Verbose ? VerboseHandler : NoopHandler, ExitInfo)) {
+ if (!ExitInfo.isExitedByLibcall()) {
+ WithColor::error() << "Execution of function '" << EntryFunc
+ << "' failed.\n";
+ return 1;
+ }
+ switch (ExitInfo.Kind) {
+ case ubi::ProgramExitInfo::ProgramExitKind::Exited:
+ return static_cast<int>(ExitInfo.ExitCode & 0xFF);
+ case ubi::ProgramExitInfo::ProgramExitKind::Aborted:
+ case ubi::ProgramExitInfo::ProgramExitKind::Terminated:
+ return 1;
+ default:
+ llvm_unreachable("Unexpected returned kind for ProgramExited status");
+ }
}
// If the function returns an integer, return that as the exit code.
@@ -260,4 +292,4 @@ int main(int argc, char **argv) {
std::min(Result.getBitWidth(), 8U), 0);
}
return 0;
-}
+}
\ No newline at end of file
>From cc0625c7d0592d6f144cc1de44340204089739c5 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 18:45:01 +0800
Subject: [PATCH 2/5] [llubi] Small format fix to ExecutorBase.cpp
---
llvm/tools/llubi/lib/ExecutorBase.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/ExecutorBase.cpp b/llvm/tools/llubi/lib/ExecutorBase.cpp
index d546c80e17aad..8b86d7037926d 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.cpp
+++ b/llvm/tools/llubi/lib/ExecutorBase.cpp
@@ -126,7 +126,7 @@ void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
}
void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
-uint64_t ExitCode) {
+ uint64_t ExitCode) {
if (Kind == ProgramExitInfo::ProgramExitKind::Invalid)
llvm_unreachable("Invalid program exit kind");
Status = false;
>From bb355c303b8cf08d4491bc0509b97de90e993bfa Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Fri, 3 Apr 2026 14:32:26 +0800
Subject: [PATCH 3/5] [llubi] Format fixes
---
llvm/tools/llubi/lib/Context.h | 2 +-
llvm/tools/llubi/lib/ExecutorBase.cpp | 2 +-
llvm/tools/llubi/lib/ExecutorBase.h | 2 +-
llvm/tools/llubi/lib/Interpreter.cpp | 29 +++++++++++++--------------
llvm/tools/llubi/lib/Library.cpp | 2 +-
llvm/tools/llubi/lib/Library.h | 2 +-
llvm/tools/llubi/llubi.cpp | 2 +-
7 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 06aff25f8e46d..0b848bb548c2d 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -297,4 +297,4 @@ class Context {
} // namespace llvm::ubi
-#endif
\ No newline at end of file
+#endif
diff --git a/llvm/tools/llubi/lib/ExecutorBase.cpp b/llvm/tools/llubi/lib/ExecutorBase.cpp
index 8b86d7037926d..e340ac8c1e1f4 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.cpp
+++ b/llvm/tools/llubi/lib/ExecutorBase.cpp
@@ -138,4 +138,4 @@ void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
bool ExecutorBase::getExecutionStatus() const { return Status; }
ProgramExitInfo ExecutorBase::getExitInfo() const { return ExitInfo; }
-} // namespace llvm::ubi
\ No newline at end of file
+} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/ExecutorBase.h b/llvm/tools/llubi/lib/ExecutorBase.h
index e400fb95d02f7..b5db5cd6fea44 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.h
+++ b/llvm/tools/llubi/lib/ExecutorBase.h
@@ -107,4 +107,4 @@ class ExecutorBase {
} // namespace llvm::ubi
-#endif // LLVM_TOOLS_LLUBI_EXECUTORBASE_H
\ No newline at end of file
+#endif // LLVM_TOOLS_LLUBI_EXECUTORBASE_H
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index c72ca1d0842b4..9b4ea25982904 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -273,21 +273,20 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
requestProgramExit(ProgramExitInfo::ProgramExitKind::Failed);
}
- void visitBranchInst(BranchInst &BI) {
- if (BI.isConditional()) {
- switch (getValue(BI.getCondition()).asBoolean()) {
- case BooleanKind::True:
- jumpTo(BI, BI.getSuccessor(0));
- return;
- case BooleanKind::False:
- jumpTo(BI, BI.getSuccessor(1));
- return;
- case BooleanKind::Poison:
- reportImmediateUB("Branch on poison condition.");
- return;
- }
+ void visitUncondBrInst(UncondBrInst &BI) { jumpTo(BI, BI.getSuccessor()); }
+
+ void visitCondBrInst(CondBrInst &BI) {
+ switch (getValue(BI.getCondition()).asBoolean()) {
+ case BooleanKind::True:
+ jumpTo(BI, BI.getSuccessor(0));
+ return;
+ case BooleanKind::False:
+ jumpTo(BI, BI.getSuccessor(1));
+ return;
+ case BooleanKind::Poison:
+ reportImmediateUB("Branch on poison condition.");
+ return;
}
- jumpTo(BI, BI.getSuccessor(0));
}
void visitSwitchInst(SwitchInst &SI) {
@@ -1056,4 +1055,4 @@ bool Context::runFunction(Function &F, ArrayRef<AnyValue> Args,
return Result;
}
-} // namespace llvm::ubi
\ No newline at end of file
+} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/Library.cpp b/llvm/tools/llubi/lib/Library.cpp
index 8f79d14671250..5e68563dfdd13 100644
--- a/llvm/tools/llubi/lib/Library.cpp
+++ b/llvm/tools/llubi/lib/Library.cpp
@@ -345,4 +345,4 @@ std::optional<AnyValue> Library::executeLibcall(LibFunc LF, StringRef Name,
return std::nullopt;
}
}
-} // namespace llvm::ubi
\ No newline at end of file
+} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/Library.h b/llvm/tools/llubi/lib/Library.h
index 765c5f56616b0..c4589c60f500a 100644
--- a/llvm/tools/llubi/lib/Library.h
+++ b/llvm/tools/llubi/lib/Library.h
@@ -52,4 +52,4 @@ class Library {
} // namespace llvm::ubi
-#endif // LLVM_TOOLS_LLUBI_LIBRARY_H
\ No newline at end of file
+#endif // LLVM_TOOLS_LLUBI_LIBRARY_H
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 929489dab23b4..88c5fe1cdc2e4 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -292,4 +292,4 @@ int main(int argc, char **argv) {
std::min(Result.getBitWidth(), 8U), 0);
}
return 0;
-}
\ No newline at end of file
+}
>From 2202107236064321b9fddac18d7186dcf6324123 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Fri, 3 Apr 2026 14:38:05 +0800
Subject: [PATCH 4/5] [llubi] Small fixes to libcalls
---
llvm/tools/llubi/lib/Interpreter.cpp | 6 +--
llvm/tools/llubi/lib/Library.cpp | 60 ++++++++++++----------------
llvm/tools/llubi/lib/Library.h | 2 +-
3 files changed, 29 insertions(+), 39 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 9b4ea25982904..1c90ceb02b3b4 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -69,6 +69,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
const DataLayout &DL;
std::list<Frame> CallStack;
AnyValue None;
+ Library Lib;
const AnyValue &getValue(Value *V) {
if (auto *C = dyn_cast<Constant>(V))
@@ -259,7 +260,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
- : ExecutorBase(C, H), DL(Ctx.getDataLayout()) {
+ : ExecutorBase(C, H), DL(Ctx.getDataLayout()),
+ Lib(Ctx, Handler, DL, static_cast<ExecutorBase &>(*this)) {
CallStack.emplace_back(F, /*CallSite=*/nullptr, /*LastFrame=*/nullptr, Args,
RetVal, Ctx.getTLIImpl());
}
@@ -398,8 +400,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue();
}
- Library Lib(Ctx, Handler, DL, static_cast<ExecutorBase &>(*this));
-
SmallVector<AnyValue, 8> Args;
for (const auto &Arg : CB.args()) {
Args.push_back(getValue(Arg));
diff --git a/llvm/tools/llubi/lib/Library.cpp b/llvm/tools/llubi/lib/Library.cpp
index 5e68563dfdd13..db778ac8022ab 100644
--- a/llvm/tools/llubi/lib/Library.cpp
+++ b/llvm/tools/llubi/lib/Library.cpp
@@ -16,7 +16,7 @@
namespace llvm::ubi {
-static uint64_t getMaxAlignT(const DataLayout &DL) {
+static uint64_t getMaxAlign(const DataLayout &DL) {
return DL.getPointerABIAlignment(0).value() >= 8 ? 16 : 8;
}
@@ -41,9 +41,8 @@ std::optional<std::string> Library::readStringFromMemory(const Pointer &Ptr) {
auto ValidOffset = Executor.verifyMemAccess(
*MO, APInt(DL.getPointerSizeInBits(0), Address + Offset), 1, Align(1),
false);
- if (!ValidOffset) {
+ if (!ValidOffset)
return std::nullopt;
- }
Byte B = (*MO)[*ValidOffset];
if (B.ConcreteMask != 0xFF) {
@@ -52,9 +51,8 @@ std::optional<std::string> Library::readStringFromMemory(const Pointer &Ptr) {
return std::nullopt;
}
- if (B.Value == 0) {
+ if (B.Value == 0)
break;
- }
Result.push_back(static_cast<char>(B.Value));
++Offset;
@@ -72,7 +70,7 @@ AnyValue Library::executeMalloc(StringRef Name, Type *Type,
}
const uint64_t AllocSize = SizeVal.asInteger().getZExtValue();
- const uint64_t MaxAlign = getMaxAlignT(DL);
+ const uint64_t MaxAlign = getMaxAlign(DL);
const auto Obj =
Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Uninitialized);
@@ -102,19 +100,16 @@ AnyValue Library::executeCalloc(StringRef Name, Type *Type,
bool Overflow;
const uint64_t AllocSize = SaturatingMultiply(Count, Size, &Overflow);
- if (Overflow) {
+ if (Overflow)
return AnyValue::getNullValue(Ctx, Type);
- }
- const uint64_t MaxAlign = getMaxAlignT(DL);
+ const uint64_t MaxAlign = getMaxAlign(DL);
- // TODO: Figure out how to name the allocation
const auto Obj =
Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Zeroed);
- if (!Obj) {
+ if (!Obj)
return AnyValue::getNullValue(Ctx, Type);
- }
return Ctx.deriveFromMemoryObject(Obj);
}
@@ -128,10 +123,9 @@ AnyValue Library::executeFree(StringRef Name, Type *Type,
}
auto &Ptr = PtrVal.asPointer();
- if (Ptr.address().isZero()) {
- // no-op when free is called with a null pointer.
+ // no-op when free is called with a null pointer.
+ if (Ptr.address().isZero())
return AnyValue();
- }
if (!Ctx.free(Ptr.address().getZExtValue())) {
Executor.reportImmediateUB(
@@ -151,9 +145,8 @@ AnyValue Library::executePuts(StringRef Name, Type *Type,
}
const auto StrOpt = readStringFromMemory(PtrVal.asPointer());
- if (!StrOpt) {
+ if (!StrOpt)
return AnyValue::poison();
- }
Handler.onPrint(*StrOpt + "\n");
return AnyValue(APInt(32, 1));
@@ -169,44 +162,41 @@ AnyValue Library::executePrintf(StringRef Name, Type *Type,
}
const auto FormatStrOpt = readStringFromMemory(FormatPtrVal.asPointer());
- if (!FormatStrOpt) {
+ if (!FormatStrOpt)
return AnyValue::poison();
- }
- const std::string FormatStr = *FormatStrOpt;
+ const std::string &FormatStr = *FormatStrOpt;
std::string Output;
unsigned ArgIndex = 1; // Start from 1 since 0 is the format string.
- for (size_t i = 0; i < FormatStr.size();) {
- if (FormatStr[i] != '%') {
- Output.push_back(FormatStr[i++]);
+ for (unsigned I = 0; I < FormatStr.size(); ) {
+ if (FormatStr[I] != '%') {
+ Output.push_back(FormatStr[I++]);
continue;
}
- const size_t Start = i++;
- if (i < FormatStr.size() && FormatStr[i] == '%') {
+ const size_t Start = I++;
+ if (I < FormatStr.size() && FormatStr[I] == '%') {
Output.push_back('%');
- ++i;
+ ++I;
continue;
}
- while (i < FormatStr.size() && strchr("-= #0123456789", FormatStr[i])) {
- ++i;
- }
+ while (I < FormatStr.size() && strchr("-= #0123456789", FormatStr[I]))
+ ++I;
- while (i < FormatStr.size() && strchr("hljzt", FormatStr[i])) {
- ++i;
- }
+ while (I < FormatStr.size() && strchr("hljzt", FormatStr[I]))
+ ++I;
- if (i >= FormatStr.size()) {
+ if (I >= FormatStr.size()) {
Executor.reportImmediateUB(
"Invalid format string in printf: missing conversion "
"specifier.");
return AnyValue::poison();
}
- char Specifier = FormatStr[i++];
- std::string CleanChunk = FormatStr.substr(Start, i - Start - 1);
+ char Specifier = FormatStr[I++];
+ std::string CleanChunk = FormatStr.substr(Start, I - Start - 1);
CleanChunk.erase(std::remove_if(CleanChunk.begin(), CleanChunk.end(),
[](char c) { return strchr("hljzt", c); }),
CleanChunk.end());
diff --git a/llvm/tools/llubi/lib/Library.h b/llvm/tools/llubi/lib/Library.h
index c4589c60f500a..3ff880324b5a7 100644
--- a/llvm/tools/llubi/lib/Library.h
+++ b/llvm/tools/llubi/lib/Library.h
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements common libcalls for llubi.
+// This file declares common libcalls for llubi.
//
//===----------------------------------------------------------------------===//
>From bea3e3e7a4b73a1bde01e1bcadbbd992af201e7a Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Fri, 3 Apr 2026 14:50:53 +0800
Subject: [PATCH 5/5] [llubi] Small fixes to libcalls
---
llvm/tools/llubi/lib/Interpreter.cpp | 12 ++---
llvm/tools/llubi/lib/Library.cpp | 74 +++++++++++-----------------
2 files changed, 33 insertions(+), 53 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 1c90ceb02b3b4..24e1e2cdb0136 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -390,7 +390,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
}
- AnyValue callLibFunc(CallBase &CB, Function *ResolvedCallee) {
+ AnyValue callLibFunc(CallBase &CB, Function *ResolvedCallee,
+ ArrayRef<AnyValue> CalleeArgs) {
LibFunc LF;
// Respect nobuiltin attributes on call site.
if (CB.isNoBuiltin() ||
@@ -400,13 +401,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue();
}
- SmallVector<AnyValue, 8> Args;
- for (const auto &Arg : CB.args()) {
- Args.push_back(getValue(Arg));
- }
-
if (auto LibCallRes =
- Lib.executeLibcall(LF, CB.getName(), CB.getType(), Args))
+ Lib.executeLibcall(LF, CB.getName(), CB.getType(), CalleeArgs))
return *LibCallRes;
if (ExitInfo)
@@ -469,7 +465,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
returnFromCallee();
return;
} else if (Callee->isDeclaration()) {
- CurrentFrame->CalleeRetVal = callLibFunc(CB, Callee);
+ CurrentFrame->CalleeRetVal = callLibFunc(CB, Callee, CalleeArgs);
returnFromCallee();
return;
} else {
diff --git a/llvm/tools/llubi/lib/Library.cpp b/llvm/tools/llubi/lib/Library.cpp
index db778ac8022ab..5011d01f9335d 100644
--- a/llvm/tools/llubi/lib/Library.cpp
+++ b/llvm/tools/llubi/lib/Library.cpp
@@ -64,10 +64,6 @@ std::optional<std::string> Library::readStringFromMemory(const Pointer &Ptr) {
AnyValue Library::executeMalloc(StringRef Name, Type *Type,
ArrayRef<AnyValue> Args) {
const auto &SizeVal = Args[0];
- if (SizeVal.isPoison()) {
- Executor.reportImmediateUB("malloc() called with a poison size.");
- return AnyValue::poison();
- }
const uint64_t AllocSize = SizeVal.asInteger().getZExtValue();
const uint64_t MaxAlign = getMaxAlign(DL);
@@ -86,15 +82,6 @@ AnyValue Library::executeCalloc(StringRef Name, Type *Type,
const auto &CountVal = Args[0];
const auto &SizeVal = Args[1];
- if (CountVal.isPoison()) {
- Executor.reportImmediateUB("calloc() called with a poison count.");
- return AnyValue::poison();
- }
- if (SizeVal.isPoison()) {
- Executor.reportImmediateUB("calloc() called with a poison size.");
- return AnyValue::poison();
- }
-
const uint64_t Count = CountVal.asInteger().getZExtValue();
const uint64_t Size = SizeVal.asInteger().getZExtValue();
@@ -114,13 +101,10 @@ AnyValue Library::executeCalloc(StringRef Name, Type *Type,
return Ctx.deriveFromMemoryObject(Obj);
}
-AnyValue Library::executeFree(StringRef Name, Type *Type,
+AnyValue Library::executeFree([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
ArrayRef<AnyValue> Args) {
const auto &PtrVal = Args[0];
- if (PtrVal.isPoison()) {
- Executor.reportImmediateUB("free() called with a poison pointer.");
- return AnyValue::poison();
- }
auto &Ptr = PtrVal.asPointer();
// no-op when free is called with a null pointer.
@@ -136,13 +120,10 @@ AnyValue Library::executeFree(StringRef Name, Type *Type,
return AnyValue();
}
-AnyValue Library::executePuts(StringRef Name, Type *Type,
+AnyValue Library::executePuts([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
ArrayRef<AnyValue> Args) {
const auto &PtrVal = Args[0];
- if (PtrVal.isPoison()) {
- Executor.reportImmediateUB("puts called with a poison pointer.");
- return AnyValue::poison();
- }
const auto StrOpt = readStringFromMemory(PtrVal.asPointer());
if (!StrOpt)
@@ -152,14 +133,10 @@ AnyValue Library::executePuts(StringRef Name, Type *Type,
return AnyValue(APInt(32, 1));
}
-AnyValue Library::executePrintf(StringRef Name, Type *Type,
+AnyValue Library::executePrintf([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
ArrayRef<AnyValue> Args) {
const auto &FormatPtrVal = Args[0];
- if (FormatPtrVal.isPoison()) {
- Executor.reportImmediateUB(
- "printf called with a poison format string pointer.");
- return AnyValue::poison();
- }
const auto FormatStrOpt = readStringFromMemory(FormatPtrVal.asPointer());
if (!FormatStrOpt)
@@ -169,7 +146,7 @@ AnyValue Library::executePrintf(StringRef Name, Type *Type,
std::string Output;
unsigned ArgIndex = 1; // Start from 1 since 0 is the format string.
- for (unsigned I = 0; I < FormatStr.size(); ) {
+ for (unsigned I = 0; I < FormatStr.size();) {
if (FormatStr[I] != '%') {
Output.push_back(FormatStr[I++]);
continue;
@@ -182,10 +159,11 @@ AnyValue Library::executePrintf(StringRef Name, Type *Type,
continue;
}
- while (I < FormatStr.size() && strchr("-= #0123456789", FormatStr[I]))
+ while (I < FormatStr.size() &&
+ StringRef("-= #0123456789").contains(FormatStr[I]))
++I;
- while (I < FormatStr.size() && strchr("hljzt", FormatStr[I]))
+ while (I < FormatStr.size() && StringRef("hljzt").contains(FormatStr[I]))
++I;
if (I >= FormatStr.size()) {
@@ -197,9 +175,10 @@ AnyValue Library::executePrintf(StringRef Name, Type *Type,
char Specifier = FormatStr[I++];
std::string CleanChunk = FormatStr.substr(Start, I - Start - 1);
- CleanChunk.erase(std::remove_if(CleanChunk.begin(), CleanChunk.end(),
- [](char c) { return strchr("hljzt", c); }),
- CleanChunk.end());
+ CleanChunk.erase(
+ std::remove_if(CleanChunk.begin(), CleanChunk.end(),
+ [](char c) { return StringRef("hljzt").contains(c); }),
+ CleanChunk.end());
if (ArgIndex >= Args.size()) {
Executor.reportImmediateUB(
@@ -273,28 +252,26 @@ AnyValue Library::executePrintf(StringRef Name, Type *Type,
return AnyValue(APInt(32, Output.size()));
}
-AnyValue Library::executeExit(StringRef Name, Type *Type,
+AnyValue Library::executeExit([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
ArrayRef<AnyValue> Args) {
const auto &RetCodeVal = Args[0];
- if (RetCodeVal.isPoison()) {
- Executor.reportImmediateUB("exit() called with a poison exit code.");
- return AnyValue::poison();
- }
-
Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Exited,
RetCodeVal.asInteger().getZExtValue());
return AnyValue();
}
-AnyValue Library::executeAbort(StringRef Name, Type *Type,
- ArrayRef<AnyValue> Args) {
+AnyValue Library::executeAbort([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
+ [[maybe_unused]] ArrayRef<AnyValue> Args) {
Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Aborted);
return AnyValue();
}
-AnyValue Library::executeTerminate(StringRef Name, Type *Type,
- ArrayRef<AnyValue> Args) {
+AnyValue Library::executeTerminate([[maybe_unused]] StringRef Name,
+ [[maybe_unused]] Type *Type,
+ [[maybe_unused]] ArrayRef<AnyValue> Args) {
Executor.requestProgramExit(ProgramExitInfo::ProgramExitKind::Terminated);
return AnyValue();
}
@@ -302,6 +279,13 @@ AnyValue Library::executeTerminate(StringRef Name, Type *Type,
std::optional<AnyValue> Library::executeLibcall(LibFunc LF, StringRef Name,
Type *Type,
ArrayRef<AnyValue> Args) {
+ for (const AnyValue &Arg : Args) {
+ if (Arg.isPoison()) {
+ Executor.reportImmediateUB("Poison argument passed to a library call.");
+ return AnyValue::poison();
+ }
+ }
+
switch (LF) {
case LibFunc_malloc:
case LibFunc_Znwm:
More information about the llvm-commits
mailing list