Merge branch 'trunk' into builtins-list-intersperse

This commit is contained in:
satotake 2021-11-18 11:16:27 +00:00 committed by GitHub
commit ce8a88416d
319 changed files with 4413 additions and 3654 deletions

View file

@ -248,6 +248,47 @@ fn list_sublist() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_split() {
assert_evals_to!(
r#"
list = List.split [1, 2, 3] 0
list.before
"#,
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
r#"
list = List.split [1, 2, 3] 0
list.others
"#,
RocList::from_slice(&[1, 2, 3]),
RocList<i64>
);
assert_evals_to!(
"List.split [1, 2, 3] 1",
(RocList::from_slice(&[1]), RocList::from_slice(&[2, 3]),),
(RocList<i64>, RocList<i64>,)
);
assert_evals_to!(
"List.split [1, 2, 3] 3",
(RocList::from_slice(&[1, 2, 3]), RocList::from_slice(&[]),),
(RocList<i64>, RocList<i64>,)
);
assert_evals_to!(
"List.split [1, 2, 3] 4",
(RocList::from_slice(&[1, 2, 3]), RocList::from_slice(&[]),),
(RocList<i64>, RocList<i64>,)
);
assert_evals_to!(
"List.split [] 1",
(RocList::from_slice(&[]), RocList::from_slice(&[]),),
(RocList<i64>, RocList<i64>,)
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_drop() {

View file

@ -1214,7 +1214,18 @@ fn tail_call_elimination() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-dev"))]
fn int_negate_dev() {
// Dev backend yet to have `Num.maxInt` or `Num.minInt`.
// TODO Remove this test and add "gen-dev" feature the below
// after implementing the both.
assert_evals_to!("Num.neg 123", -123, i64);
assert_evals_to!("Num.neg -123", 123, i64);
assert_evals_to!("Num.neg 0", 0, i64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_negate() {
assert_evals_to!("Num.neg 123", -123, i64);
assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64);

View file

@ -1,16 +1,22 @@
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
use std::cell::Cell;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use tempfile::{tempdir, TempDir};
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
use crate::helpers::wasm32_test_result::Wasm32TestResult;
use roc_builtins::bitcode;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::{MutMap, MutSet};
use roc_gen_wasm::wasm_module::linking::{WasmObjectSymbol, WASM_SYM_UNDEFINED};
use roc_gen_wasm::wasm_module::sections::{Import, ImportDesc};
use roc_gen_wasm::wasm_module::{
CodeBuilder, Export, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule,
};
use roc_gen_wasm::MEMORY_NAME;
use tempfile::tempdir;
const TEST_WRAPPER_NAME: &str = "test_wrapper";
std::thread_local! {
@ -92,11 +98,6 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
// }
debug_assert_eq!(exposed_to_host.len(), 1);
let main_fn_symbol = loaded.entry_point.symbol;
let main_fn_index = procedures
.keys()
.position(|(s, _)| *s == main_fn_symbol)
.unwrap();
let exposed_to_host = exposed_to_host.keys().copied().collect::<MutSet<_>>();
@ -106,63 +107,61 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
exposed_to_host,
};
let mut wasm_module = roc_gen_wasm::build_module_help(&env, procedures).unwrap();
let (mut wasm_module, main_fn_index) =
roc_gen_wasm::build_module_help(&env, procedures).unwrap();
T::insert_test_wrapper(
arena,
&mut wasm_module,
TEST_WRAPPER_NAME,
main_fn_index as u32,
);
T::insert_test_wrapper(arena, &mut wasm_module, TEST_WRAPPER_NAME, main_fn_index);
// We can either generate the test platform or write an external source file, whatever works
generate_test_platform(&mut wasm_module, arena);
let mut module_bytes = std::vec::Vec::with_capacity(4096);
wasm_module.serialize_mut(&mut module_bytes);
// for debugging (e.g. with wasm2wat or wasm-objdump)
if false {
use std::io::Write;
let mut hash_state = DefaultHasher::new();
src.hash(&mut hash_state);
let src_hash = hash_state.finish();
// Filename contains a hash of the Roc test source code. Helpful when comparing across commits.
let dir = "/tmp/roc/gen_wasm";
std::fs::create_dir_all(dir).unwrap();
let path = format!("{}/test-{:016x}.wasm", dir, src_hash);
// Print out filename (appears just after test name)
println!("dumping file {:?}", path);
match std::fs::File::create(path) {
Err(e) => eprintln!("Problem creating wasm debug file: {:?}", e),
Ok(mut file) => {
file.write_all(&module_bytes).unwrap();
}
}
}
// now, do wasmer stuff
use wasmer::{Instance, Module, Store};
let store = Store::default();
// Keep the final .wasm file for debugging with wasm-objdump or wasm2wat
const DEBUG_WASM_FILE: bool = true;
let wasmer_module = {
let dir = tempdir().unwrap();
let dirpath = dir.path();
let final_wasm_file = dirpath.join("final.wasm");
let app_o_file = dirpath.join("app.o");
let tmp_dir: TempDir; // directory for normal test runs, deleted when dropped
let debug_dir: String; // persistent directory for debugging
let wasm_build_dir: &Path = if DEBUG_WASM_FILE {
// Directory name based on a hash of the Roc source
let mut hash_state = DefaultHasher::new();
src.hash(&mut hash_state);
let src_hash = hash_state.finish();
debug_dir = format!("/tmp/roc/gen_wasm/{:016x}", src_hash);
std::fs::create_dir_all(&debug_dir).unwrap();
println!(
"Debug command:\n\twasm-objdump -sdx {}/final.wasm",
&debug_dir
);
Path::new(&debug_dir)
} else {
tmp_dir = tempdir().unwrap();
tmp_dir.path()
};
let final_wasm_file = wasm_build_dir.join("final.wasm");
let app_o_file = wasm_build_dir.join("app.o");
let libc_a_file = "../gen_wasm/lib/libc.a";
// write the module to a file so the linker can access it
std::fs::write(&app_o_file, &module_bytes).unwrap();
std::process::Command::new("zig")
let _linker_output = std::process::Command::new("zig")
.args(&[
"wasm-ld",
// input files
app_o_file.to_str().unwrap(),
bitcode::BUILTINS_WASM32_OBJ_PATH,
libc_a_file,
// output
"-o",
final_wasm_file.to_str().unwrap(),
@ -182,6 +181,8 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
.output()
.unwrap();
// dbg!(_linker_output);
Module::from_file(&store, &final_wasm_file).unwrap()
};
@ -276,3 +277,123 @@ pub fn identity<T>(value: T) -> T {
pub(crate) use assert_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_wasm_evals_to;
fn wrap_libc_fn<'a>(
module: &mut WasmModule<'a>,
arena: &'a Bump,
roc_name: &'a str,
libc_name: &'a str,
params: &'a [(ValueType, bool)],
ret_type: Option<ValueType>,
) {
let symbol_table = module.linking.symbol_table_mut();
// Type signatures
let mut wrapper_signature = Signature {
param_types: Vec::with_capacity_in(params.len(), arena),
ret_type,
};
let mut libc_signature = Signature {
param_types: Vec::with_capacity_in(params.len(), arena),
ret_type,
};
for (ty, used) in params.iter() {
wrapper_signature.param_types.push(*ty);
if *used {
libc_signature.param_types.push(*ty);
}
}
/*
* Import a function from libc
*/
let libc_signature_index = module.types.insert(libc_signature);
// Import
let import_index = module.import.entries.len() as u32;
module.import.entries.push(Import {
module: "env",
name: libc_name.to_string(),
description: ImportDesc::Func {
signature_index: libc_signature_index,
},
});
// Linker info
let libc_sym_idx = symbol_table.len() as u32;
symbol_table.push(SymInfo::Function(WasmObjectSymbol::Imported {
flags: WASM_SYM_UNDEFINED,
index: import_index,
}));
/*
* Export a wrapper function
*/
// Declaration
let wrapper_sig_index = module.types.insert(wrapper_signature);
module.function.signature_indices.push(wrapper_sig_index);
// Body
let mut code_builder = CodeBuilder::new(arena);
let mut num_libc_args = 0;
for (i, (_, used)) in params.iter().enumerate() {
if *used {
code_builder.get_local(LocalId(i as u32));
num_libc_args += 1;
}
}
code_builder.call(
import_index,
libc_sym_idx,
num_libc_args,
ret_type.is_some(),
);
code_builder.build_fn_header(&[], 0, None);
let wrapper_index = module.code.code_builders.len() as u32;
module.code.code_builders.push(code_builder);
// Export
module.export.entries.push(Export {
name: roc_name.to_string(),
ty: ExportType::Func,
index: wrapper_index,
});
// Linker symbol
symbol_table.push(SymInfo::Function(WasmObjectSymbol::Defined {
flags: 0,
index: wrapper_index,
name: roc_name.to_string(),
}));
}
fn generate_test_platform<'a>(module: &mut WasmModule<'a>, arena: &'a Bump) {
use ValueType::I32;
wrap_libc_fn(
module,
arena,
"roc_alloc",
"malloc",
// only the first argument of roc_alloc is passed to malloc
&[(I32, true), (I32, false)],
Some(I32),
);
wrap_libc_fn(
module,
arena,
"roc_dealloc",
"free",
&[(I32, true), (I32, false)],
None,
);
wrap_libc_fn(
module,
arena,
"roc_realloc",
"realloc",
&[(I32, true), (I32, false), (I32, true), (I32, false)],
Some(I32),
);
}

View file

@ -1,6 +1,6 @@
// Wasm pointers are only 32bit. This effects RocStr.
// These are versions of the str tests assuming 32bit pointers.
#![cfg(not(feature = "gen-dev"))]
#![cfg(feature = "gen-wasm")]
// TODO: We need to make these tests work with the llvm wasm backend.
@ -12,7 +12,7 @@ use crate::helpers::wasm::assert_evals_to;
#[allow(unused_imports)]
use indoc::indoc;
// use roc_std::RocStr;
use roc_std::RocStr;
// #[test]
// fn str_split_bigger_delimiter_small_str() {
@ -287,41 +287,23 @@ fn small_str_zeroed_literal() {
);
}
// TODO: fix linking errors for undefined symbols roc_alloc, roc_dealloc
// #[test]
// fn long_str_literal() {
// assert_evals_to!(
// "\"0123456789 123456789 123456789\"",
// RocStr::from_slice(b"0123456789 123456789 123456789"),
// RocStr
// );
// }
#[test]
fn long_str_literal() {
assert_evals_to!(
"\"0123456789 123456789 123456789\"",
RocStr::from_slice(b"0123456789 123456789 123456789"),
RocStr
);
}
// #[test]
// fn small_str_concat_empty_first_arg() {
// assert_llvm_evals_to!(
// r#"Str.concat "" "JJJJJJJJJJJJJJJ""#,
// [
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0x4a,
// 0b1000_1111
// ],
// [u8; 16]
// );
// }
#[test]
fn small_str_concat_empty_first_arg() {
assert_evals_to!(
r#"Str.concat "" "JJJJJJJ""#,
[0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0b1000_0111],
[u8; 8]
);
}
// #[test]
// fn small_str_concat_empty_second_arg() {