Skip to content

Commit e9c4ec3

Browse files
committed
CI: test Lean emulator on some ELF files
1 parent 7f08a93 commit e9c4ec3

11 files changed

Lines changed: 148 additions & 69 deletions

File tree

.github/workflows/compile-lean.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,9 @@ jobs:
5151
uses: leanprover/lean-action@v1
5252
with:
5353
lake-package-directory: "build/model/Lean_RV64D/"
54+
55+
- name: Test the Lean emulator
56+
run: |
57+
eval $(opam config env)
58+
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSAIL_MODULES="H;Zca;Zicbom_insts;Zicboz;Zicsr_insts;Zkr;Sscofpmf;postlude" -DPRINT_EFFECTS=true
59+
cmake --build build --target test_lean_emulator

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ add_subdirectory("model")
168168
# Emulator binary.
169169
add_subdirectory("c_emulator")
170170

171+
# Lean emulator
172+
add_subdirectory("lean_emulator")
173+
171174
# Old pre-compiled riscv-tests & first-party tests.
172175
add_subdirectory("test")
173176

handwritten_support/RiscvExtrasExecutable.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def plat_term_read : Unit → SailM String := λ _ => panic "TODO: plat_term_rea
3333
-- Reservations
3434
def load_reservation : Arch.pa → Nat → SailM Unit := λ _ => panic "TODO: load_reservation"
3535
def match_reservation : Arch.pa → Bool := λ _ => panic "TODO: match_reservation"
36-
def cancel_reservation : Unit → SailM Unit := λ _ => panic "TODO: cancel_reservation"
36+
def cancel_reservation : Unit → SailM Unit := λ _ => dbg_trace "TODO: cancel_reservation"; return ()
3737
def valid_reservation : Unit → Bool := λ _ => false
3838

3939
def get_16_random_bits : Unit → SailM (BitVec 16) := λ _ => panic "TODO: get_16_random_bits"
@@ -112,6 +112,6 @@ def riscv_f64roundToInt : BitVec 3 → BitVec 64 → Bool → (BitVec 5 × BitVe
112112

113113
-- Termination of extensionEnabled
114114
instance : SizeOf extension where
115-
sizeOf := extension.toCtorIdx
115+
sizeOf := extension.ctorIdx
116116

117117
macro_rules | `(tactic| decreasing_trivial) => `(tactic| decide)

lean_emulator/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
add_custom_target(test_lean_emulator
2+
COMMAND cmake -P "${CMAKE_CURRENT_SOURCE_DIR}/test_lean_emulator.cmake"
3+
)
4+
5+
# We need to copy the whole `lean_emulator` directory structure in the build
6+
# add_custom_target(copy_lean_emulator
7+
# COMMAND cmake -E copy_directory
8+
# ${CMAKE_CURRENT_SOURCE_DIR}
9+
# ${CMAKE_CURRENT_BINARY_DIR}
10+
# COMMENT "Copying Lean emulator directory to build directory"
11+
# )
12+
13+
add_dependencies(
14+
test_lean_emulator
15+
# copy_lean_emulator
16+
generated_lean_executable_rv64d)

lean_emulator/LeanRiscv.lean

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def initializeRegisters (elf: ELF64File): SailM PUnit :=
246246
writeReg htif_payload_writes (← (undefined_bitvector 4))
247247
writeReg satp (← (undefined_bitvector ((2 ^i 2) *i 8)))
248248

249-
def my_main (elf: ELF64File) :=
249+
def my_main (elf: ELF64File) : SailM Int :=
250250
open LeanRV64DExecutable.Functions in
251251
open Sail in
252252
do
@@ -260,20 +260,32 @@ def my_main (elf: ELF64File) :=
260260
print_bits_effect "htif_tohost = " (← readReg htif_tohost)
261261
loop ()
262262
)
263-
(λ the_exception ↦
263+
(λ the_exception ↦ do
264264
match the_exception with
265265
| .Error_not_implemented s => (pure (print_string "Error: Not implemented: " s))
266266
| .Error_internal_error () => (pure (print "Error: internal error"))
267267
| .Error_reserved_behavior s => (pure (print_string "Error: Reserved behavior: " s))
268+
return 1
268269
)
269270

270271
def runElf64 (elf : ELF64File) : IO UInt32 :=
271272
open Sail in
272273
open LeanRV64DExecutable.Functions in
273-
let mem := initializeMemory MachineBits.B64 elf
274-
let regs := Std.ExtDHashMap.emptyWithCapacity
275-
let initialState := ⟨regs, (), mem, default, default, default⟩
276-
main_of_sail_main initialState $ fun () => do
277-
sail_model_init ()
278-
initializeRegisters elf
279-
my_main elf
274+
do
275+
let mem := initializeMemory MachineBits.B64 elf
276+
let regs := Std.ExtDHashMap.emptyWithCapacity
277+
let initialState := ⟨regs, (), mem, default, default, default⟩
278+
let main := do
279+
sail_model_init ()
280+
initializeRegisters elf
281+
my_main elf
282+
match main.run initialState with
283+
| .ok res s => do
284+
for m in s.sailOutput do
285+
IO.print m
286+
IO.Process.exit $ UInt8.ofInt res
287+
| .error e s => do
288+
for m in s.sailOutput do
289+
IO.print m
290+
IO.eprintln s!"Error while running the sail program!: {e.print}"
291+
IO.Process.exit 1

lean_emulator/lean-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
leanprover/lean4:nightly-2026-01-22
1+
leanprover/lean4:nightly-2026-01-22
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
include(${CMAKE_CURRENT_LIST_DIR}/../test/download_riscv_tests.cmake)
2+
3+
set(TARBALL_NAME "riscv-tests")
4+
set(DOWNLOAD_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TEST_DOWNLOAD_VERSION}")
5+
set(DOWNLOAD_URL "${TEST_DOWNLOAD_URL}/${TEST_DOWNLOAD_VERSION}/${TARBALL_NAME}.tar.gz")
6+
7+
download_riscv_tests(
8+
"${DOWNLOAD_PATH}"
9+
"${TARBALL_NAME}"
10+
"${DOWNLOAD_URL}"
11+
)
12+
13+
# Note: this is a bit slow, so only testing a handful of arbitrarily picked ELF
14+
# files.
15+
set(LEAN_EMULATOR_TEST_ELFS
16+
rv64ui-p-add
17+
rv64ui-p-addi
18+
rv64ui-p-addiw
19+
rv64ui-p-addw
20+
rv64ui-p-and
21+
rv64ui-p-andi
22+
rv64ui-p-auipc
23+
rv64ui-p-beq
24+
rv64ui-p-bge
25+
rv64ui-p-bgeu
26+
rv64ui-p-blt
27+
rv64ui-p-bltu
28+
rv64ui-p-bne
29+
# rv64ui-p-fence_i # does not work at the moment
30+
rv64ui-p-jal
31+
rv64ui-p-jalr
32+
rv64ui-p-lb
33+
rv64ui-p-lbu
34+
rv64ui-p-ld
35+
rv64ui-p-lh
36+
rv64ui-p-lhu
37+
rv64ui-p-lui
38+
rv64ui-p-lw
39+
rv64ui-p-lwu
40+
rv64ui-p-or
41+
rv64ui-p-ori
42+
rv64ui-p-sb
43+
rv64ui-p-sd
44+
rv64ui-p-sh
45+
rv64ui-p-simple
46+
rv64ui-p-sll
47+
rv64ui-p-slli
48+
rv64ui-p-slliw
49+
rv64ui-p-sllw
50+
rv64ui-p-slt
51+
rv64ui-p-slti
52+
rv64ui-p-sltiu
53+
rv64ui-p-sltu
54+
rv64ui-p-sra
55+
rv64ui-p-srai
56+
rv64ui-p-sraiw
57+
rv64ui-p-sraw
58+
rv64ui-p-srl
59+
rv64ui-p-srli
60+
rv64ui-p-srliw
61+
rv64ui-p-srlw
62+
rv64ui-p-sub
63+
rv64ui-p-subw
64+
rv64ui-p-sw
65+
rv64ui-p-xor
66+
rv64ui-p-xori
67+
)
68+
69+
execute_process(
70+
COMMAND lake build lean_riscv_emulator
71+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
72+
)
73+
foreach(file IN LISTS LEAN_EMULATOR_TEST_ELFS)
74+
message(STATUS "Testing Lean emulator on file ${file}")
75+
execute_process(
76+
COMMAND lake exec lean_riscv_emulator -- "${DOWNLOAD_PATH}/riscv-tests/${file}"
77+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
78+
COMMAND_ERROR_IS_FATAL ANY
79+
)
80+
endforeach()

model/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ set(sail_common
2424
--memo-z3-path "${CMAKE_CURRENT_BINARY_DIR}/sail_smt_cache"
2525
)
2626

27+
# Mirroring Sail's PRINT_EFFECTS flag
28+
option(PRINT_EFFECTS "Pass PRINT_EFFECTS to Sail (used in Lean and Rocq backends)" OFF)
29+
if(PRINT_EFFECTS)
30+
list(APPEND sail_common -D PRINT_EFFECTS)
31+
endif()
32+
2733
set(project_file "riscv.sail_project")
2834

2935
# Reconfigure if the project file changes.

model/main/main.sail

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ function main() : unit -> unit = {
1414
init_model("");
1515
print_bits("PC = ", PC);
1616
sail_end_cycle();
17-
loop()
17+
let _ = loop();
18+
return ()
1819
} catch {
1920
Error_not_implemented(s) => print_string("Error: Not implemented: ", s),
2021
Error_internal_error() => print("Error: internal error")

model/postlude/step.sail

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ function try_step(step_no : nat, exit_wait : bool) -> bool = {
268268
}
269269
}
270270

271-
function loop () : unit -> unit = {
271+
function loop () : unit -> int = {
272272
var i : nat = 0;
273273
var step_no : nat = 0;
274274
while not(htif_done) do {
@@ -283,20 +283,21 @@ function loop () : unit -> unit = {
283283
sail_end_cycle()
284284
};
285285

286-
// check htif exit
287-
if htif_done then {
288-
let exit_val = unsigned(htif_exit_code);
289-
if exit_val == 0 then print("SUCCESS")
290-
else print_int("FAILURE: ", exit_val);
291-
} else {
286+
if not(htif_done) then {
292287
// update time
293288
i = i + 1;
294289
if i == plat_insns_per_tick then {
295290
tick_clock();
296291
i = 0;
297292
}
298293
}
299-
}
294+
};
295+
296+
let exit_val = unsigned(htif_exit_code);
297+
if exit_val == 0
298+
then print_endline("SUCCESS")
299+
else print_int("FAILURE: ", exit_val);
300+
return exit_val
300301
}
301302

302303
// Termination measures for loops are not supported by the Lean backend, so they

0 commit comments

Comments
 (0)