Create code gen tests for refcounting

This commit is contained in:
Brian Carroll 2021-12-29 09:45:42 +00:00
parent 746c8c2c8e
commit 4d2e4d454b
4 changed files with 169 additions and 10 deletions

View file

@ -1,8 +1,9 @@
use core::cell::Cell;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use tempfile::{tempdir, TempDir};
use wasmer::Memory;
use wasmer::{Memory, WasmPtr};
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
use crate::helpers::wasm32_test_result::Wasm32TestResult;
@ -36,11 +37,11 @@ fn promote_expr_to_module(src: &str) -> String {
}
#[allow(dead_code)]
pub fn helper_wasm<'a, T: Wasm32TestResult>(
pub fn compile_and_load<'a, T: Wasm32TestResult>(
arena: &'a bumpalo::Bump,
src: &str,
stdlib: &'a roc_builtins::std::StdLib,
_result_type_dummy: PhantomData<T>,
_test_wrapper_type_info: PhantomData<T>,
) -> wasmer::Instance {
use std::path::{Path, PathBuf};
@ -178,10 +179,11 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
//
// It seems that it will not write out an export you didn't explicitly specify,
// even if it's a dependency of another export!
// In our case we always export main and test_wrapper so that's OK.
"--export",
"test_wrapper",
"--export",
"init_refcount_test",
"--export",
"#UserApp_main_1",
];
@ -225,7 +227,7 @@ where
// NOTE the stdlib must be in the arena; just taking a reference will segfault
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
let instance = crate::helpers::wasm::helper_wasm(&arena, src, stdlib, phantom);
let instance = crate::helpers::wasm::compile_and_load(&arena, src, stdlib, phantom);
let memory = instance.exports.get_memory(MEMORY_NAME).unwrap();
@ -256,6 +258,55 @@ where
}
}
#[allow(dead_code)]
pub fn assert_wasm_refcounts_help<T>(
src: &str,
phantom: PhantomData<T>,
num_refcounts: usize,
) -> Result<Vec<u32>, String>
where
T: FromWasm32Memory + Wasm32TestResult,
{
let arena = bumpalo::Bump::new();
// NOTE the stdlib must be in the arena; just taking a reference will segfault
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
let instance = crate::helpers::wasm::compile_and_load(&arena, src, stdlib, phantom);
let memory = instance.exports.get_memory(MEMORY_NAME).unwrap();
let init_refcount_test = instance.exports.get_function("init_refcount_test").unwrap();
let init_result = init_refcount_test.call(&[wasmer::Value::I32(num_refcounts as i32)]);
let refcount_array_addr = match init_result {
Err(e) => return Err(format!("{:?}", e)),
Ok(result) => match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!(),
},
};
// An array of refcount pointers
let refcount_ptr_array: WasmPtr<WasmPtr<i32>, wasmer::Array> =
WasmPtr::new(refcount_array_addr as u32);
let refcount_ptrs: &[Cell<WasmPtr<i32>>] = refcount_ptr_array
.deref(memory, 0, num_refcounts as u32)
.unwrap();
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();
match test_wrapper.call(&[]) {
Err(e) => return Err(format!("{:?}", e)),
Ok(_) => {}
}
let mut refcounts = Vec::with_capacity(num_refcounts);
for i in 0..num_refcounts {
let rc_encoded = refcount_ptrs[i].get().deref(memory).unwrap().get();
let rc = (rc_encoded - i32::MIN + 1) as u32;
refcounts.push(rc);
}
Ok(refcounts)
}
/// Print out hex bytes of the test result, and a few words on either side
/// Can be handy for debugging misalignment issues etc.
pub fn debug_memory_hex(memory: &Memory, address: i32, size: usize) {
@ -330,7 +381,32 @@ pub fn identity<T>(value: T) -> T {
value
}
#[allow(unused_macros)]
macro_rules! assert_refcounts {
($src: expr, $ty: ty, $expected_refcounts: expr) => {
// Same as above, except with an additional transformation argument.
{
let phantom = std::marker::PhantomData;
let num_refcounts = $expected_refcounts.len();
let result = $crate::helpers::wasm::assert_wasm_refcounts_help::<$ty>(
$src,
phantom,
num_refcounts,
);
match result {
Err(msg) => panic!("{:?}", msg),
Ok(actual_refcounts) => {
assert_eq!(&actual_refcounts, $expected_refcounts)
}
}
}
};
}
#[allow(unused_imports)]
pub(crate) use assert_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_wasm_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_refcounts;

View file

@ -1,14 +1,59 @@
#include <stdio.h>
// If any printf is included for compilation, even if unused, test runs take 50% longer
#define DEBUG 0
// Makes test runs take 50% longer, due to linking
#define ENABLE_PRINTF 0
// Globals for refcount testing
size_t **rc_pointers; // array of pointers to refcount values
size_t rc_pointers_len;
size_t rc_pointers_index;
// The rust test passes us the max number of allocations it expects to make,
// and we tell it where we're going to write the refcount pointers.
// It won't actually read that memory until later, when the test is done.
size_t **init_refcount_test(size_t max_allocs)
{
rc_pointers = malloc(max_allocs * sizeof(size_t *));
rc_pointers_len = max_allocs;
rc_pointers_index = 0;
for (size_t i = 0; i < max_allocs; ++i)
rc_pointers[i] = NULL;
return rc_pointers;
}
#if ENABLE_PRINTF
#define ASSERT(x) \
if (!(x)) \
{ \
printf("FAILED: " #x "\n"); \
abort(); \
}
#else
#define ASSERT(x) \
if (!(x)) \
abort();
#endif
//--------------------------
void *roc_alloc(size_t size, unsigned int alignment)
{
void *allocated = malloc(size);
#if DEBUG
if (rc_pointers)
{
ASSERT(alignment >= sizeof(size_t));
ASSERT(rc_pointers_index < rc_pointers_len);
size_t alloc_addr = (size_t)allocated;
size_t rc_addr = alloc_addr + alignment - sizeof(size_t);
size_t *rc_ptr = (size_t *)rc_addr;
rc_pointers[rc_pointers_index] = rc_ptr;
rc_pointers_index++;
}
#if ENABLE_PRINTF
if (!allocated)
{
fprintf(stderr, "roc_alloc failed\n");
@ -27,6 +72,10 @@ void *roc_alloc(size_t size, unsigned int alignment)
void *roc_realloc(void *ptr, size_t new_size, size_t old_size,
unsigned int alignment)
{
#if ENABLE_PRINTF
printf("roc_realloc reallocated %p from %d to %d with alignment %zd\n",
ptr, old_size, new_size, alignment);
#endif
return realloc(ptr, new_size);
}
@ -34,6 +83,9 @@ void *roc_realloc(void *ptr, size_t new_size, size_t old_size,
void roc_dealloc(void *ptr, unsigned int alignment)
{
#if ENABLE_PRINTF
printf("roc_dealloc deallocated %p with alignment %zd\n", ptr, alignment);
#endif
free(ptr);
}
@ -41,12 +93,12 @@ void roc_dealloc(void *ptr, unsigned int alignment)
void roc_panic(void *ptr, unsigned int alignment)
{
#if DEBUG
#if ENABLE_PRINTF
char *msg = (char *)ptr;
fprintf(stderr,
"Application crashed with message\n\n %s\n\nShutting down\n", msg);
#endif
exit(1);
abort();
}
//--------------------------