Add lli support for running example test cases #327

Merged
sb10q merged 7 commits from lli-support into master 2023-09-30 09:31:18 +08:00
11 changed files with 167 additions and 124 deletions

View File

@ -9,6 +9,7 @@
in rec {
packages.x86_64-linux = rec {
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
clang-unwrapped = pkgs.runCommandNoCC "clang-unwrapped" {} "mkdir -p $out/bin; ln -s ${pkgs.llvmPackages_14.clang-unwrapped}/bin/clang $out/bin/clang-unwrapped";
nac3artiq = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage rec {
name = "nac3artiq";
@ -18,7 +19,7 @@
lockFile = ./Cargo.lock;
};
passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang packages.x86_64-linux.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ])) ];
checkPhase =
@ -143,7 +144,8 @@
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
llvmPackages_14.clang-unwrapped # IRRT
llvmPackages_14.clang # demo
packages.x86_64-linux.clang-unwrapped # IRRT
pkgs.llvmPackages_14.llvm.out # IRRT
cargo
rustc

View File

@ -29,7 +29,7 @@ fn main() {
"-o",
"-",
];
let output = Command::new("clang")
let output = Command::new("clang-unwrapped")
.args(FLAG)
.output()
.map(|o| {

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
if [ -z "$1" ]; then
echo "Requires at least one argument"
exit 1
fi
demo="${@:-1}"
set -- "${@:1:$(($# - 1))}"
echo -n "Checking $demo... "
./interpret_demo.py "$demo" > interpreted.log
./run_demo.sh "$@" "$demo" > run.log
./run_demo_lli.sh "$@" "$demo" > run_lli.log
diff -Nau interpreted.log run.log
diff -Nau interpreted.log run_lli.log
echo "ok"
rm -f interpreted.log run.log run_lli.log
sb10q marked this conversation as resolved Outdated
Outdated
Review

Just keep them, they can be used for debugging if the test failed.

Just keep them, they can be used for debugging if the test failed.

If the test failed, the files won't be deleted anyways because the script will exit after running diff, so it doesn't matter.

If the test failed, the files won't be deleted anyways because the script will exit after running `diff`, so it doesn't matter.

View File

@ -4,12 +4,8 @@ set -e
count=0
for demo in src/*.py; do
echo -n "checking $demo... "
./interpret_demo.py $demo > interpreted.log
./run_demo.sh "$@" $demo > run.log
diff -Nau interpreted.log run.log
derppening marked this conversation as resolved Outdated
Outdated
Review

Just add a run_demo_lli.sh > run_lli.log and keep this structure, which avoids invoking the interpreter multiple times and overwriting files.

Just add a `run_demo_lli.sh > run_lli.log` and keep this structure, which avoids invoking the interpreter multiple times and overwriting files.
echo "ok"
let "count+=1"
./check_demo.sh "$@" "$demo"
((count += 1))
done
echo "Ran $count demo checks - PASSED"

View File

@ -0,0 +1,89 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if __SIZEOF_POINTER__ == 8
#define usize uint64_t
#elif __SIZEOF_POINTER__ == 4
#define usize uint32_t
#else
#error "Unsupported platform - Platform is not 32-bit or 64-bit"
#endif
void output_int32(const int32_t x) {
printf("%d\n", x);
}
void output_int64(const int64_t x) {
printf("%ld\n", x);
}
void output_uint32(const uint32_t x) {
printf("%d\n", x);
}
void output_uint64(const uint64_t x) {
printf("%ld\n", x);
}
void output_float64(const double x) {
printf("%f\n", x);
}
void output_asciiart(const int32_t x) {
const char* chars = " .,-:;i+hHM$*#@ ";
if (x < 0) {
putchar('\n');
} else {
putchar(chars[x]);
}
}
struct cslice {
const void* data;
usize len;
};
void output_int32_list(struct cslice* slice) {
const int32_t* data = (const int32_t*) slice->data;
putchar('[');
for (usize i = 0; i < slice->len; ++i) {
if (i == slice->len - 1) {
printf("%d", data[i]);
} else {
printf("%d, ", data[i]);
}
}
putchar(']');
putchar('\n');
}
void output_str(struct cslice* slice) {
const char* data = (const char*) slice->data;
for (usize i = 0; i < slice->len; ++i) {
putchar(data[i]);
}
putchar('\n');
}
uint32_t __nac3_personality(uint32_t state, uint32_t exception_object, uint32_t context) {
printf("__nac3_personality(state: %u, exception_object: %u, context: %u\n", state, exception_object, context);
exit(101);
__builtin_unreachable();
}
uint32_t __nac3_raise(uint32_t state, uint32_t exception_object, uint32_t context) {
printf("__nac3_raise(state: %u, exception_object: %u, context: %u\n", state, exception_object, context);
exit(101);
__builtin_unreachable();
}
void __nac3_end_catch(void) {}
extern int32_t run(void);
int main(void) {
run();
}

View File

@ -1,111 +0,0 @@
use std::io;
use std::io::Write;
use std::process::exit;
mod cslice {
// copied from https://github.com/dherman/cslice
use std::marker::PhantomData;
use std::slice;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CSlice<'a, T> {
base: *const T,
len: usize,
marker: PhantomData<&'a ()>,
}
impl<'a, T> AsRef<[T]> for CSlice<'a, T> {
fn as_ref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.base, self.len) }
}
}
}
#[no_mangle]
pub extern "C" fn output_int32(x: i32) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_int64(x: i64) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_uint32(x: u32) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_uint64(x: u64) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_float64(x: f64) {
// debug output to preserve the digits after the decimal points
// to match python `print` function
println!("{:?}", x);
}
#[no_mangle]
pub extern "C" fn output_asciiart(x: i32) {
let chars = " .,-:;i+hHM$*#@ ";
if x < 0 {
println!("");
} else {
print!("{}", chars.chars().nth(x as usize).unwrap());
}
}
#[no_mangle]
pub extern "C" fn output_str(x: &cslice::CSlice<u8>) {
for e in x.as_ref().iter() {
print!("{}", char::from(*e));
}
println!();
}
#[no_mangle]
pub extern "C" fn output_int32_list(x: &cslice::CSlice<i32>) {
print!("[");
let mut it = x.as_ref().iter().peekable();
while let Some(e) = it.next() {
if it.peek().is_none() {
print!("{}", e);
} else {
print!("{}, ", e);
}
}
println!("]");
}
#[no_mangle]
pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _context: u32) -> u32 {
unimplemented!();
}
#[no_mangle]
pub extern "C" fn __nac3_raise(state: u32, exception_object: u32, context: u32) -> u32 {
writeln!(io::stderr(),
"__nac3_raise(state: {:#010x}, exception_object: {:#010x}, context: {:#010x})",
state,
exception_object,
context
).unwrap();
exit(101);
}
#[no_mangle]
pub extern "C" fn __nac3_end_catch() {}
extern "C" {
fn run() -> i32;
}
fn main() {
unsafe {
run();
}
}

View File

@ -48,17 +48,21 @@ def patch(module):
else:
sys.stdout.write(" .,-:;i+hHM$*#@ "[x])
def output_float(x):
print("%f" % x)
def extern(fun):
name = fun.__name__
if name == "output_asciiart":
return output_asciiart
elif name == "output_float64":
return output_float
elif name in {
"output_int32",
"output_int64",
"output_int32_list",
"output_uint32",
"output_uint64",
"output_float64",
"output_str",
}:
return print

View File

@ -14,7 +14,9 @@ else
nac3standalone=../../target/x86_64-unknown-linux-gnu/release/nac3standalone
fi
rm -f *.o
rm -f "*.o" demo
sb10q marked this conversation as resolved
Review

Why? The next commands will overwrite them.

Why? The next commands will overwrite them.
Review

I just found it to be intuitive that if you are removing the object files, then the output executable should also be removed.

I just found it to be intuitive that if you are removing the object files, then the output executable should also be removed.
$nac3standalone "$@"
rustc -o demo demo.rs -Crelocation-model=static -Clink-arg=./module.o
clang -c -std=gnu11 -Wall -Wextra -O3 -o demo.o demo.c
clang -o demo module.o demo.o
./demo

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
if [ -z "$1" ]; then
echo "No argument supplied"
exit 1
fi
if [ -e ../../target/release/nac3standalone ]; then
nac3standalone=../../target/release/nac3standalone
else
# used by Nix builds
nac3standalone=../../target/x86_64-unknown-linux-gnu/release/nac3standalone
fi
rm -f "*.o" "*.bc" demo
$nac3standalone --emit-llvm "$@"
clang -c -std=gnu11 -Wall -Wextra -O3 -emit-llvm -o demo.bc demo.c
lli --extra-module demo.bc --extra-module irrt.bc main.bc
Outdated
Review

I don't think you need to add IRRT here. The output of the compiler (nac3core) is already supposed to inline IRRT.

I don't think you need to add IRRT here. The output of the compiler (nac3core) is already supposed to inline IRRT.

This is necessary, as the nac3standalone program will only generate LLVM bitcode for the module-under-compilation (rather than including the dependencies).

This is necessary, as the `nac3standalone` program will only generate LLVM bitcode for the module-under-compilation (rather than including the dependencies).
Outdated
Review

I would change it so it also emits the already compiled and integrated IRRT then, instead of compiling it redundantly (and with the wrong compiler).

I would change it so it also emits the already compiled and integrated IRRT then, instead of compiling it redundantly (and with the wrong compiler).

View File

@ -14,6 +14,10 @@ def output_uint32(x: uint32):
def output_uint64(x: uint64):
...
@extern
def output_float64(x: float):
...
@extern
def output_int32_list(x: list[int32]):
...
@ -38,6 +42,15 @@ def test_output_uint32():
def test_output_uint64():
output_uint64(uint64(256))
def test_output_float64():
output_float64(0.0)
output_float64(1.0)
output_float64(-1.0)
output_float64(128.0)
output_float64(-128.0)
output_float64(16.25)
output_float64(-16.25)
def test_output_asciiart():
for i in range(17):
output_asciiart(i)
@ -54,6 +67,7 @@ def run() -> int32:
test_output_int64()
test_output_uint32()
test_output_uint64()
test_output_float64()
test_output_asciiart()
test_output_int32_list()
test_output_str_family()

View File

@ -354,7 +354,12 @@ fn main() {
main.link_in_module(other).unwrap();
}
main.link_in_module(load_irrt(&context)).unwrap();
let irrt = load_irrt(&context);
if emit_llvm {
irrt.write_bitcode_to_path(Path::new("irrt.bc"));
}
main.link_in_module(irrt).unwrap();
let mut function_iter = main.get_first_function();
while let Some(func) = function_iter {