mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
fix repl
This commit is contained in:
parent
14da3906da
commit
9a0a13646f
7 changed files with 527 additions and 297 deletions
|
@ -1,7 +1,8 @@
|
|||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::execution_engine::{ExecutionEngine, JitFunction};
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -50,20 +51,6 @@ pub unsafe fn jit_to_ast<'a>(
|
|||
jit_to_ast_help(&env, execution_engine, main_fn_name, layout, content)
|
||||
}
|
||||
|
||||
macro_rules! jit_map {
|
||||
($execution_engine: expr, $main_fn_name: expr, $ty: ty, $transform: expr) => {{
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn() -> $ty> = $execution_engine
|
||||
.get_function($main_fn_name)
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
$transform(main.call())
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
fn jit_to_ast_help<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
execution_engine: ExecutionEngine,
|
||||
|
@ -72,32 +59,30 @@ fn jit_to_ast_help<'a>(
|
|||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
jit_map!(execution_engine, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
i64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Float64) => {
|
||||
jit_map!(execution_engine, main_fn_name, f64, |num| num_to_ast(
|
||||
env,
|
||||
f64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => jit_map!(
|
||||
Layout::Builtin(Builtin::Int64) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
i64,
|
||||
|num| num_to_ast(env, i64_to_ast(env.arena, num), content)
|
||||
),
|
||||
Layout::Builtin(Builtin::Float64) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
f64,
|
||||
|num| num_to_ast(env, f64_to_ast(env.arena, num), content)
|
||||
),
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
&'static str,
|
||||
|string: &'static str| { str_slice_to_ast(env.arena, env.arena.alloc(string)) }
|
||||
),
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
jit_map!(execution_engine, main_fn_name, &'static str, |_| {
|
||||
run_jit_function!(execution_engine, main_fn_name, &'static str, |_| {
|
||||
Expr::List(Vec::new_in(env.arena))
|
||||
})
|
||||
}
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => jit_map!(
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
(*const libc::c_void, usize),
|
||||
|
@ -118,18 +103,26 @@ fn jit_to_ast_help<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let fields = [Layout::Builtin(Builtin::Int64), layout.clone()];
|
||||
let layout = Layout::Struct(&fields);
|
||||
|
||||
match env.ptr_bytes {
|
||||
// 64-bit target (8-byte pointers, 16-byte structs)
|
||||
8 => match layout.stack_size(env.ptr_bytes) {
|
||||
8 => {
|
||||
// just one eightbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 8],
|
||||
|bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
16 => {
|
||||
// two eightbytes, returned as-is
|
||||
jit_map!(
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 16],
|
||||
|
@ -138,14 +131,17 @@ fn jit_to_ast_help<'a>(
|
|||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
larger_size => {
|
||||
// anything more than 2 eightbytes
|
||||
// the return "value" is a pointer to the result
|
||||
jit_map!(
|
||||
run_jit_function_dynamic_type!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
*const u8,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| {
|
||||
dbg!(bytes);
|
||||
ptr_to_ast(bytes as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -157,24 +153,37 @@ fn jit_to_ast_help<'a>(
|
|||
match layout.stack_size(env.ptr_bytes) {
|
||||
4 => {
|
||||
// just one fourbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 4],
|
||||
|bytes: [u8; 4]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
8 => {
|
||||
// just one fourbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
jit_map!(
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
*const u8,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
[u8; 8],
|
||||
|bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
run_jit_function_dynamic_type!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| {
|
||||
dbg!(bytes);
|
||||
ptr_to_ast(bytes as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -288,6 +297,7 @@ fn struct_to_ast<'a>(
|
|||
field_layouts: &[Layout<'a>],
|
||||
fields: &MutMap<Lowercase, RecordField<Variable>>,
|
||||
) -> Expr<'a> {
|
||||
dbg!(&ptr);
|
||||
let arena = env.arena;
|
||||
let subs = env.subs;
|
||||
let mut output = Vec::with_capacity_in(field_layouts.len(), &arena);
|
||||
|
|
|
@ -15,6 +15,14 @@ mod repl_eval {
|
|||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
fn expect_failure(input: &str, expected: &str) {
|
||||
let out = helpers::repl_eval(input);
|
||||
|
||||
assert_eq!(&out.stderr, "");
|
||||
assert_eq!(&out.stdout, expected);
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_0() {
|
||||
expect_success("0", "0 : Num *");
|
||||
|
@ -241,6 +249,15 @@ mod repl_eval {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn four_element_record() {
|
||||
// if this tests turns out to fail on 32-bit platforms, look at jit_to_ast_help
|
||||
expect_success(
|
||||
"{ a: 1, b: 2, c: 3, d: 4 }",
|
||||
"{ a: 1, b: 2, c: 3, d: 4 } : { a : Num *, b : Num *, c : Num *, d : Num * }",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn multiline_string() {
|
||||
// // If a string contains newlines, format it as a multiline string in the output
|
||||
|
|
|
@ -13,3 +13,5 @@
|
|||
|
||||
pub mod layout_id;
|
||||
pub mod llvm;
|
||||
|
||||
pub mod run_roc;
|
||||
|
|
|
@ -238,7 +238,10 @@ enum PassVia {
|
|||
|
||||
impl PassVia {
|
||||
fn from_layout(ptr_bytes: u32, layout: &Layout<'_>) -> Self {
|
||||
if layout.stack_size(ptr_bytes) > 16 {
|
||||
let stack_size = layout.stack_size(ptr_bytes);
|
||||
let eightbyte = 8;
|
||||
|
||||
if stack_size > 2 * eightbyte {
|
||||
PassVia::Memory
|
||||
} else {
|
||||
PassVia::Register
|
||||
|
@ -246,43 +249,20 @@ impl PassVia {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn make_main_function<'a, 'ctx, 'env>(
|
||||
/// entry point to roc code; uses the fastcc calling convention
|
||||
pub fn build_roc_main<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
main_body: &roc_mono::ir::Stmt<'a>,
|
||||
) -> (&'static str, &'a FunctionValue<'ctx>) {
|
||||
) -> &'a FunctionValue<'ctx> {
|
||||
use inkwell::types::BasicType;
|
||||
use PassVia::*;
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
let arena = env.arena;
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let personality_func = module.add_function(
|
||||
"__gxx_personality_v0",
|
||||
context.i64_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
let cxa_begin_catch = module.add_function(
|
||||
"__cxa_begin_catch",
|
||||
u8_ptr.fn_type(&[u8_ptr.into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_begin_catch.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let cxa_end_catch = module.add_function(
|
||||
"__cxa_end_catch",
|
||||
context.void_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_end_catch.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let return_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes);
|
||||
let roc_main_fn_name = "$Test.roc_main";
|
||||
|
||||
|
@ -294,9 +274,7 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
.module
|
||||
.add_function(roc_main_fn_name, roc_main_fn_type, None);
|
||||
|
||||
// roc_main_fn.set_personality_function(personality_func);
|
||||
|
||||
// our exposed main function adheres to the C calling convention
|
||||
// internal function, use fast calling convention
|
||||
roc_main_fn.set_call_conventions(FAST_CALL_CONV);
|
||||
|
||||
// Add main's body
|
||||
|
@ -313,6 +291,26 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
main_body,
|
||||
);
|
||||
|
||||
env.arena.alloc(roc_main_fn)
|
||||
}
|
||||
|
||||
pub fn make_main_function<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
main_body: &roc_mono::ir::Stmt<'a>,
|
||||
) -> (&'static str, &'a FunctionValue<'ctx>) {
|
||||
use inkwell::types::BasicType;
|
||||
use PassVia::*;
|
||||
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
// internal main function
|
||||
let roc_main_fn = *build_roc_main(env, layout_ids, layout, main_body);
|
||||
|
||||
// build the C calling convention wrapper
|
||||
|
||||
let main_fn_name = "$Test.main";
|
||||
|
@ -332,11 +330,7 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
};
|
||||
|
||||
// Add main to the module.
|
||||
|
||||
// Add main to the module.
|
||||
let main_fn = env.module.add_function("$Test.main", main_fn_type, None);
|
||||
|
||||
// main_fn.set_personality_function(personality_func);
|
||||
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
|
||||
|
||||
// our exposed main function adheres to the C calling convention
|
||||
main_fn.set_call_conventions(C_CALL_CONV);
|
||||
|
@ -352,31 +346,28 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
let result_alloca = builder.build_alloca(main_return_type, "result");
|
||||
|
||||
// invoke instead of call, so that we can catch any exeptions thrown in Roc code
|
||||
let call_result = {
|
||||
let call = builder.build_invoke(roc_main_fn, &[], then_block, catch_block, "call_roc_main");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let call_result = call.try_as_basic_value().left().unwrap();
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
};
|
||||
|
||||
// exception handling
|
||||
{
|
||||
builder.position_at_end(catch_block);
|
||||
|
||||
let landing_pad_type = context.struct_type(
|
||||
&[
|
||||
context.i8_type().ptr_type(AddressSpace::Generic).into(),
|
||||
context.i32_type().into(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
let landing_pad_type = {
|
||||
let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into();
|
||||
let selector_value = context.i32_type().into();
|
||||
|
||||
context.struct_type(&[exception_ptr, selector_value], false)
|
||||
};
|
||||
|
||||
let info = builder
|
||||
.build_landing_pad(
|
||||
.build_catch_all_landing_pad(
|
||||
&landing_pad_type,
|
||||
&BasicValueEnum::IntValue(context.i8_type().const_zero()),
|
||||
context
|
||||
.i8_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.const_zero(),
|
||||
context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
"main_landing_pad",
|
||||
)
|
||||
.into_struct_value();
|
||||
|
@ -385,60 +376,62 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
.build_extract_value(info, 0, "exception_ptr")
|
||||
.unwrap();
|
||||
|
||||
let puts = module.add_function(
|
||||
"puts",
|
||||
context.i32_type().fn_type(&[u8_ptr.into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
/*
|
||||
let v = module.get_global("error_msg").unwrap().as_pointer_value();
|
||||
let vv = builder.build_bitcast(v, u8_ptr, "name");
|
||||
*/
|
||||
let call = builder.build_call(cxa_begin_catch, &[exception_ptr], "call");
|
||||
let thrown = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
let v42 = context.i64_type().const_int(42, false);
|
||||
let thrown = cxa_begin_catch(env, exception_ptr);
|
||||
|
||||
let error_msg = {
|
||||
let exception_type = u8_ptr;
|
||||
let ptr = builder.build_bitcast(
|
||||
thrown,
|
||||
exception_type.ptr_type(AddressSpace::Generic),
|
||||
"cast",
|
||||
);
|
||||
let val = builder.build_load(ptr.into_pointer_value(), "name");
|
||||
// builder.build_call(puts, &[val], "call");
|
||||
|
||||
builder.build_load(ptr.into_pointer_value(), "error_msg")
|
||||
};
|
||||
|
||||
let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false);
|
||||
|
||||
let return_value = {
|
||||
let v1 = return_type.const_zero();
|
||||
|
||||
// flag is non-zero, indicating failure
|
||||
let flag = context.i64_type().const_int(1, false);
|
||||
|
||||
let v2 = builder
|
||||
.build_insert_value(v1, context.i64_type().const_int(1, false), 0, "set_error")
|
||||
.build_insert_value(v1, flag, 0, "set_error")
|
||||
.unwrap();
|
||||
|
||||
let v3 = builder
|
||||
.build_insert_value(v2, val, 1, "set_exception")
|
||||
.build_insert_value(v2, error_msg, 1, "set_exception")
|
||||
.unwrap();
|
||||
|
||||
v3
|
||||
};
|
||||
|
||||
let ptr = builder.build_bitcast(
|
||||
// bitcast result alloca so we can store our concrete type { flag, error_msg } in there
|
||||
let result_alloca_bitcast = builder
|
||||
.build_bitcast(
|
||||
result_alloca,
|
||||
return_type.ptr_type(AddressSpace::Generic),
|
||||
"name",
|
||||
);
|
||||
builder.build_store(ptr.into_pointer_value(), return_value);
|
||||
"result_alloca_bitcast",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
builder.build_call(cxa_end_catch, &[], "call");
|
||||
// store our return value
|
||||
builder.build_store(result_alloca_bitcast, return_value);
|
||||
|
||||
cxa_end_catch(env);
|
||||
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
let actual_return_type = basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
let return_type = context.struct_type(&[context.i64_type().into(), actual_return_type], false);
|
||||
let actual_return_type =
|
||||
basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
let return_type =
|
||||
context.struct_type(&[context.i64_type().into(), actual_return_type], false);
|
||||
|
||||
let return_value = {
|
||||
let v1 = return_type.const_zero();
|
||||
|
@ -461,7 +454,9 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
builder.build_store(ptr.into_pointer_value(), return_value);
|
||||
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(cont_block);
|
||||
|
||||
let result = builder.build_load(result_alloca, "result");
|
||||
|
@ -469,8 +464,6 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
match register_or_memory {
|
||||
Memory => {
|
||||
// write the result into the supplied pointer
|
||||
// this is a void function, therefore return None
|
||||
|
||||
let ptr_return_type = main_return_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let ptr_as_int = main_fn.get_first_param().unwrap();
|
||||
|
@ -479,6 +472,7 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
|
||||
builder.build_store(ptr.into_pointer_value(), result);
|
||||
|
||||
// this is a void function, therefore return None
|
||||
builder.build_return(None);
|
||||
}
|
||||
Register => {
|
||||
|
@ -487,9 +481,11 @@ pub fn make_main_function<'a, 'ctx, 'env>(
|
|||
builder.build_return(Some(&result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MUST set the personality at the bottom; doing it earlier can cause the personality to be
|
||||
// ignored
|
||||
// MUST set the personality at the very end;
|
||||
// doing it earlier can cause the personality to be ignored
|
||||
let personality_func = get_gxx_personality_v0(env);
|
||||
main_fn.set_personality_function(personality_func);
|
||||
|
||||
(main_fn_name, env.arena.alloc(main_fn))
|
||||
|
@ -1372,100 +1368,12 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
RuntimeError(error_msg) => {
|
||||
let context = env.context;
|
||||
let module = env.module;
|
||||
let builder = env.builder;
|
||||
throw_exception(env, error_msg);
|
||||
|
||||
// define the error message as a global constant
|
||||
let error_msg_global = module.add_global(
|
||||
context.i8_type().array_type(error_msg.len() as u32),
|
||||
None,
|
||||
"error_msg",
|
||||
);
|
||||
|
||||
let i8_type = context.i8_type();
|
||||
let error_msg_bytes = error_msg
|
||||
.chars()
|
||||
.map(|c| i8_type.const_int(c as u64, false))
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
let const_array = i8_type.const_array(&error_msg_bytes);
|
||||
error_msg_global.set_initializer(&const_array);
|
||||
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let void_ptr = u8_ptr.clone();
|
||||
|
||||
// void *__cxa_allocate_exception(size_t thrown_size);
|
||||
let cxa_allocate_exception = module.add_function(
|
||||
"__cxa_allocate_exception",
|
||||
void_ptr.fn_type(&[context.i64_type().into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
// void __cxa_throw (void *thrown_exception, std::type_info *tinfo, void (*dest) (void *) );
|
||||
let cxa_throw = module.add_function(
|
||||
"__cxa_throw",
|
||||
context
|
||||
.void_type()
|
||||
.fn_type(&[u8_ptr.into(), u8_ptr.into(), u8_ptr.into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
let cxa_rethrow = module.add_function(
|
||||
"__cxa_rethrow",
|
||||
context.void_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_rethrow.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let ztii = module.add_global(u8_ptr, Some(AddressSpace::Generic), "_ZTIi");
|
||||
ztii.set_linkage(Linkage::External);
|
||||
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let zero = context.i64_type().const_int(8, false);
|
||||
let call =
|
||||
builder.build_call(cxa_allocate_exception, &[zero.into()], "allocate_exception");
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
let info = {
|
||||
let initial = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
let error_msg_ptr = error_msg_global.as_pointer_value();
|
||||
let error_msg_ptr2 = builder.build_bitcast(error_msg_ptr, u8_ptr, "cast_u8ptr");
|
||||
|
||||
let exception_type = u8_ptr;
|
||||
let exception_value = error_msg_ptr2;
|
||||
|
||||
let temp = builder
|
||||
.build_bitcast(
|
||||
initial,
|
||||
exception_type.ptr_type(AddressSpace::Generic),
|
||||
"name",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
builder.build_store(temp, exception_value);
|
||||
|
||||
initial
|
||||
};
|
||||
|
||||
let arg1 = builder.build_bitcast(ztii.as_pointer_value(), u8_ptr, "cast");
|
||||
let arg2 = context
|
||||
.i8_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.const_zero();
|
||||
|
||||
let call = builder.build_call(cxa_throw, &[info, arg1, arg2.into()], "throw");
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
builder.build_unreachable();
|
||||
|
||||
let zero = context.i64_type().const_zero();
|
||||
|
||||
// builder.build_return(Some(&BasicValueEnum::IntValue(zero)));
|
||||
// unused value (must return a BasicValue)
|
||||
let zero = env.context.i64_type().const_zero();
|
||||
zero.into()
|
||||
}
|
||||
_ => todo!("unsupported expr {:?}", stmt),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2684,3 +2592,272 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn define_global_str<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
message: &str,
|
||||
) -> inkwell::values::GlobalValue<'ctx> {
|
||||
let context = env.context;
|
||||
let module = env.module;
|
||||
|
||||
// hash the name so we don't re-define existing messages
|
||||
let name = {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
message.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
format!("message_{}", hash)
|
||||
};
|
||||
|
||||
match module.get_global(&name) {
|
||||
Some(current) => current,
|
||||
None => {
|
||||
let i8_type = context.i8_type();
|
||||
|
||||
// define the error message as a global constant
|
||||
let message_global =
|
||||
module.add_global(i8_type.array_type(message.len() as u32), None, &name);
|
||||
|
||||
let mut message_bytes = Vec::with_capacity_in(message.len(), env.arena);
|
||||
|
||||
for c in message.chars() {
|
||||
message_bytes.push(i8_type.const_int(c as u64, false));
|
||||
}
|
||||
|
||||
let const_array = i8_type.const_array(&message_bytes);
|
||||
message_global.set_initializer(&const_array);
|
||||
|
||||
message_global
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, message: &str) {
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let info = {
|
||||
// we represend both void and char pointers with `u8*`
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
// allocate an exception (that can hold a pointer to a string)
|
||||
let str_ptr_size = env
|
||||
.context
|
||||
.i64_type()
|
||||
.const_int(env.ptr_bytes as u64, false);
|
||||
let initial = cxa_allocate_exception(env, str_ptr_size);
|
||||
|
||||
// define the error message as a global
|
||||
// (a hash is used such that the same value is not defined repeatedly)
|
||||
let error_msg_global = define_global_str(env, message);
|
||||
|
||||
// cast this to a void pointer
|
||||
let error_msg_ptr =
|
||||
builder.build_bitcast(error_msg_global.as_pointer_value(), u8_ptr, "unused");
|
||||
|
||||
// store this void pointer in the exception
|
||||
let exception_type = u8_ptr;
|
||||
let exception_value = error_msg_ptr;
|
||||
|
||||
let temp = builder
|
||||
.build_bitcast(
|
||||
initial,
|
||||
exception_type.ptr_type(AddressSpace::Generic),
|
||||
"exception_object_str_ptr_ptr",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
builder.build_store(temp, exception_value);
|
||||
|
||||
initial
|
||||
};
|
||||
|
||||
cxa_throw_exception(env, info);
|
||||
|
||||
builder.build_unreachable();
|
||||
}
|
||||
|
||||
fn cxa_allocate_exception<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
exception_size: IntValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let name = "__cxa_allocate_exception";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function = match module.get_function(&name) {
|
||||
Some(gvalue) => gvalue,
|
||||
None => {
|
||||
// void *__cxa_allocate_exception(size_t thrown_size);
|
||||
let cxa_allocate_exception = module.add_function(
|
||||
name,
|
||||
u8_ptr.fn_type(&[context.i64_type().into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_allocate_exception.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
cxa_allocate_exception
|
||||
}
|
||||
};
|
||||
let call = env.builder.build_call(
|
||||
function,
|
||||
&[exception_size.into()],
|
||||
"exception_object_void_ptr",
|
||||
);
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn cxa_throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, info: BasicValueEnum<'ctx>) {
|
||||
let name = "__cxa_throw";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function = match module.get_function(&name) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
// void __cxa_throw (void *thrown_exception, std::type_info *tinfo, void (*dest) (void *) );
|
||||
let cxa_throw = module.add_function(
|
||||
name,
|
||||
context
|
||||
.void_type()
|
||||
.fn_type(&[u8_ptr.into(), u8_ptr.into(), u8_ptr.into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_throw.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
cxa_throw
|
||||
}
|
||||
};
|
||||
|
||||
// global storing the type info of a c++ int (equivalent to `i32` in llvm)
|
||||
// we just need any valid such value, and arbitrarily use this one
|
||||
let ztii = match module.get_global("_ZTIi") {
|
||||
Some(gvalue) => gvalue.as_pointer_value(),
|
||||
None => {
|
||||
let ztii = module.add_global(u8_ptr, Some(AddressSpace::Generic), "_ZTIi");
|
||||
ztii.set_linkage(Linkage::External);
|
||||
|
||||
ztii.as_pointer_value()
|
||||
}
|
||||
};
|
||||
|
||||
let type_info = builder.build_bitcast(ztii, u8_ptr, "cast");
|
||||
let null: BasicValueEnum = u8_ptr.const_zero().into();
|
||||
|
||||
let call = builder.build_call(function, &[info, type_info, null], "throw");
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
||||
let name = "__cxa_rethrow";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
|
||||
let function = match module.get_function(&name) {
|
||||
Some(gvalue) => gvalue,
|
||||
None => {
|
||||
let cxa_rethrow = module.add_function(
|
||||
name,
|
||||
context.void_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_rethrow.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
cxa_rethrow
|
||||
}
|
||||
};
|
||||
let call = env.builder.build_call(function, &[], "never_used");
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
||||
let name = "__cxa_rethrow";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
|
||||
match module.get_function(&name) {
|
||||
Some(gvalue) => gvalue,
|
||||
None => {
|
||||
let personality_func = module.add_function(
|
||||
"__gxx_personality_v0",
|
||||
context.i64_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
personality_func.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
personality_func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cxa_end_catch<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) {
|
||||
let name = "__cxa_end_catch";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
|
||||
let function = match module.get_function(&name) {
|
||||
Some(gvalue) => gvalue,
|
||||
None => {
|
||||
let cxa_end_catch = module.add_function(
|
||||
name,
|
||||
context.void_type().fn_type(&[], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_end_catch.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
cxa_end_catch
|
||||
}
|
||||
};
|
||||
let call = env.builder.build_call(function, &[], "never_used");
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
}
|
||||
|
||||
fn cxa_begin_catch<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
exception_ptr: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let name = "__cxa_begin_catch";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
|
||||
let function = match module.get_function(&name) {
|
||||
Some(gvalue) => gvalue,
|
||||
None => {
|
||||
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let cxa_begin_catch = module.add_function(
|
||||
"__cxa_begin_catch",
|
||||
u8_ptr.fn_type(&[u8_ptr.into()], false),
|
||||
Some(Linkage::External),
|
||||
);
|
||||
cxa_begin_catch.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
cxa_begin_catch
|
||||
}
|
||||
};
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[exception_ptr], "exception_payload_ptr");
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
|
|
@ -820,7 +820,10 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
if True then
|
||||
x + z
|
||||
else
|
||||
y + z
|
||||
"#
|
||||
),
|
||||
3,
|
||||
|
|
|
@ -664,6 +664,19 @@ mod gen_records {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_nested_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
|
||||
"#
|
||||
),
|
||||
(0x0, (6.28, 3.14, 0.1)),
|
||||
(i64, (f64, f64, f64))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn just_to_be_sure() {
|
||||
assert_evals_to!(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use roc_collections::all::MutSet;
|
||||
use roc_types::subs::Subs;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -39,7 +38,11 @@ pub fn helper_without_uniqueness<'a>(
|
|||
src: &str,
|
||||
leak: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
||||
) -> (
|
||||
&'static str,
|
||||
Vec<roc_problem::can::Problem>,
|
||||
inkwell::execution_engine::ExecutionEngine<'a>,
|
||||
) {
|
||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||
|
@ -57,6 +60,8 @@ pub fn helper_without_uniqueness<'a>(
|
|||
problems,
|
||||
..
|
||||
} = can_expr(src);
|
||||
|
||||
// don't panic based on the errors here, so we can test that RuntimeError generates the correct code
|
||||
let errors = problems
|
||||
.into_iter()
|
||||
.filter(|problem| {
|
||||
|
@ -70,8 +75,6 @@ pub fn helper_without_uniqueness<'a>(
|
|||
})
|
||||
.collect::<Vec<roc_problem::can::Problem>>();
|
||||
|
||||
// assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors);
|
||||
|
||||
let subs = Subs::new(var_store.into());
|
||||
let mut unify_problems = Vec::new();
|
||||
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||
|
@ -211,7 +214,7 @@ pub fn helper_without_uniqueness<'a>(
|
|||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
(main_fn_name, execution_engine.clone())
|
||||
(main_fn_name, errors, execution_engine.clone())
|
||||
}
|
||||
|
||||
pub fn helper_with_uniqueness<'a>(
|
||||
|
@ -434,7 +437,7 @@ macro_rules! assert_llvm_evals_to {
|
|||
|
||||
let context = Context::create();
|
||||
|
||||
let (main_fn_name, execution_engine) =
|
||||
let (main_fn_name, errors, execution_engine) =
|
||||
$crate::helpers::eval::helper_without_uniqueness(&arena, $src, $leak, &context);
|
||||
|
||||
unsafe {
|
||||
|
@ -445,7 +448,12 @@ macro_rules! assert_llvm_evals_to {
|
|||
.expect("errored");
|
||||
|
||||
match main.call().into() {
|
||||
Ok(success) => assert_eq!($transform(success), $expected),
|
||||
Ok(success) => {
|
||||
// only if there are no exceptions thrown, check for errors
|
||||
assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors);
|
||||
|
||||
assert_eq!($transform(success), $expected);
|
||||
}
|
||||
Err(error_msg) => panic!("Roc failed with message: {}", error_msg),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue