Merge branch 'trunk' into store-dec-as-str

This commit is contained in:
Richard Feldman 2021-08-26 21:24:56 -04:00 committed by GitHub
commit ffeaa1ac08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 555 additions and 142 deletions

4
Cargo.lock generated
View File

@ -3918,9 +3918,9 @@ dependencies = [
[[package]]
name = "target-lexicon"
version = "0.10.0"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff"
[[package]]
name = "tempfile"

View File

@ -67,7 +67,7 @@ libc = "0.2"
libloading = "0.6"
inkwell = { path = "../vendor/inkwell", optional = true }
target-lexicon = "0.10"
target-lexicon = "0.12.2"
tempfile = "3.1.0"
[dev-dependencies]

View File

@ -222,7 +222,8 @@ fn jit_to_ast_help<'a>(
let tags_map: roc_collections::all::MutMap<_, _> =
tags_vec.iter().cloned().collect();
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
let union_variant =
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
let size = layout.stack_size(env.ptr_bytes);
use roc_mono::layout::WrappedVariant::*;
@ -886,7 +887,8 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
.map(|(a, b)| (a.clone(), b.to_vec()))
.collect();
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
let union_variant =
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
match union_variant {
UnionVariant::ByteUnion(tagnames) => {

View File

@ -223,13 +223,13 @@ mod cli_run {
// expected_ending: "",
// use_valgrind: true,
// },
// cli:"cli" => Example {
// filename: "Echo.roc",
// executable_filename: "echo",
// stdin: &["Giovanni\n", "Giorgio\n"],
// expected_ending: "Giovanni Giorgio!\n",
// use_valgrind: true,
// },
cli:"cli" => Example {
filename: "Echo.roc",
executable_filename: "echo",
stdin: &["Giovanni\n", "Giorgio\n"],
expected_ending: "Hi, Giovanni Giorgio!\n",
use_valgrind: true,
},
// custom_malloc:"custom-malloc" => Example {
// filename: "Main.roc",
// executable_filename: "custom-malloc-example",

View File

@ -30,7 +30,7 @@ libloading = "0.6"
tempfile = "3.1.0"
serde_json = "1.0"
inkwell = { path = "../../vendor/inkwell", optional = true }
target-lexicon = "0.10"
target-lexicon = "0.12.2"
[dev-dependencies]
pretty_assertions = "0.5.1"

View File

@ -21,7 +21,7 @@ roc_mono = { path = "../mono" }
im = "14" # im and im-rc should always have the same version!
im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.6.1", features = ["collections"] }
target-lexicon = "0.10"
target-lexicon = "0.12.2"
libloading = "0.6"
object = { version = "0.24", features = ["write"] }

View File

@ -22,7 +22,7 @@ im = "14" # im and im-rc should always have the same version!
im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.6.1", features = ["collections"] }
inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.10"
target-lexicon = "0.12.2"
[dev-dependencies]
roc_can = { path = "../can" }

View File

@ -3935,6 +3935,8 @@ pub fn get_call_conventions(cc: target_lexicon::CallingConvention) -> u32 {
SystemV => C_CALL_CONV,
WasmBasicCAbi => C_CALL_CONV,
WindowsFastcall => C_CALL_CONV,
AppleAarch64 => C_CALL_CONV,
_ => C_CALL_CONV,
}
}
@ -5174,89 +5176,171 @@ fn run_low_level<'a, 'ctx, 'env>(
}
}
fn build_foreign_symbol_return_result<'a, 'ctx, 'env>(
/// A type that is valid according to the C ABI
///
/// As an example, structs that fit inside an integer type should
/// (this does not currently happen here) be coerced to that integer type.
fn to_cc_type<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &mut Scope<'a, 'ctx>,
foreign: &roc_module::ident::ForeignSymbol,
arguments: &[Symbol],
return_type: BasicTypeEnum<'ctx>,
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
for arg in arguments.iter() {
let (value, layout) = load_symbol_and_layout(scope, arg);
arg_vals.push(value);
let arg_type = basic_type_from_layout(env, layout);
debug_assert_eq!(arg_type, value.get_type());
arg_types.push(arg_type);
layout: &Layout<'a>,
) -> BasicTypeEnum<'ctx> {
match layout {
Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin),
_ => {
// TODO this is almost certainly incorrect for bigger structs
basic_type_from_layout(env, layout)
}
}
}
let function_type = return_type.fn_type(&arg_types, false);
let function = get_foreign_symbol(env, foreign.clone(), function_type);
(function, arg_vals.into_bump_slice())
}
fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>(
fn to_cc_type_builtin<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &mut Scope<'a, 'ctx>,
foreign: &roc_module::ident::ForeignSymbol,
arguments: &[Symbol],
return_pointer: PointerValue<'ctx>,
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
arg_vals.push(return_pointer.into());
arg_types.push(return_pointer.get_type().into());
for arg in arguments.iter() {
let (value, layout) = load_symbol_and_layout(scope, arg);
arg_vals.push(value);
let arg_type = basic_type_from_layout(env, layout);
debug_assert_eq!(arg_type, value.get_type());
arg_types.push(arg_type);
builtin: &Builtin<'a>,
) -> BasicTypeEnum<'ctx> {
match builtin {
Builtin::Int128
| Builtin::Int64
| Builtin::Int32
| Builtin::Int16
| Builtin::Int8
| Builtin::Int1
| Builtin::Usize
| Builtin::Decimal
| Builtin::Float128
| Builtin::Float64
| Builtin::Float32
| Builtin::Float16 => basic_type_from_builtin(env, builtin),
Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => {
env.str_list_c_abi().into()
}
Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::EmptyDict | Builtin::EmptySet => {
// TODO verify this is what actually happens
basic_type_from_builtin(env, builtin)
}
}
}
let function_type = env.context.void_type().fn_type(&arg_types, false);
let function = get_foreign_symbol(env, foreign.clone(), function_type);
(function, arg_vals.into_bump_slice())
enum CCReturn {
/// Return as normal
Return,
/// require an extra argument, a pointer
/// where the result is written into
/// returns void
ByPointer,
/// The return type is zero-sized
Void,
}
/// According to the C ABI, how should we return a value with the given layout?
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
let return_size = layout.stack_size(env.ptr_bytes);
let pass_result_by_pointer = return_size > 2 * env.ptr_bytes;
if return_size == 0 {
CCReturn::Void
} else if pass_result_by_pointer {
CCReturn::ByPointer
} else {
CCReturn::Return
}
}
#[allow(clippy::too_many_arguments)]
fn build_foreign_symbol<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &mut Scope<'a, 'ctx>,
foreign: &roc_module::ident::ForeignSymbol,
arguments: &[Symbol],
argument_symbols: &[Symbol],
ret_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let ret_type = basic_type_from_layout(env, ret_layout);
let return_pointer = env.builder.build_alloca(ret_type, "return_value");
let builder = env.builder;
let context = env.context;
// crude approximation of the C calling convention
let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes;
// Here we build two functions:
//
// - an C_CALL_CONV extern that will be provided by the host, e.g. `roc_fx_putLine`
// This is just a type signature that we make available to the linker,
// and can use in the wrapper
// - a FAST_CALL_CONV wrapper that we make here, e.g. `roc_fx_putLine_fastcc_wrapper`
let (function, arguments) = if pass_result_by_pointer {
build_foreign_symbol_write_result_into_ptr(env, scope, foreign, arguments, return_pointer)
} else {
build_foreign_symbol_return_result(env, scope, foreign, arguments, ret_type)
let return_type = basic_type_from_layout(env, ret_layout);
let cc_return = to_cc_return(env, ret_layout);
let mut cc_argument_types = Vec::with_capacity_in(argument_symbols.len() + 1, env.arena);
let mut fastcc_argument_types = Vec::with_capacity_in(argument_symbols.len(), env.arena);
let mut arguments = Vec::with_capacity_in(argument_symbols.len(), env.arena);
for symbol in argument_symbols {
let (value, layout) = load_symbol_and_layout(scope, symbol);
cc_argument_types.push(to_cc_type(env, layout));
let basic_type = basic_type_from_layout(env, layout);
fastcc_argument_types.push(basic_type);
arguments.push(value);
}
let cc_type = match cc_return {
CCReturn::Void => env.context.void_type().fn_type(&cc_argument_types, false),
CCReturn::ByPointer => {
cc_argument_types.push(return_type.ptr_type(AddressSpace::Generic).into());
env.context.void_type().fn_type(&cc_argument_types, false)
}
CCReturn::Return => return_type.fn_type(&cc_argument_types, false),
};
let call = env.builder.build_call(function, arguments, "tmp");
let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type);
// this is a foreign function, use c calling convention
let fastcc_type = return_type.fn_type(&fastcc_argument_types, false);
let fastcc_function = add_func(
env.module,
&format!("{}_fastcc_wrapper", foreign.as_str()),
fastcc_type,
Linkage::Private,
FAST_CALL_CONV,
);
let old = builder.get_insert_block().unwrap();
let entry = context.append_basic_block(fastcc_function, "entry");
{
builder.position_at_end(entry);
let return_pointer = env.builder.build_alloca(return_type, "return_value");
let fastcc_parameters = fastcc_function.get_params();
let mut cc_arguments = Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena);
for (param, cc_type) in fastcc_parameters.into_iter().zip(cc_argument_types.iter()) {
if param.get_type() == *cc_type {
cc_arguments.push(param);
} else {
let as_cc_type = complex_bitcast(env.builder, param, *cc_type, "to_cc_type");
cc_arguments.push(as_cc_type);
}
}
if let CCReturn::ByPointer = cc_return {
cc_arguments.push(return_pointer.into());
}
let call = env.builder.build_call(cc_function, &cc_arguments, "tmp");
call.set_call_convention(C_CALL_CONV);
call.try_as_basic_value();
let return_value = match cc_return {
CCReturn::Return => call.try_as_basic_value().left().unwrap(),
if pass_result_by_pointer {
env.builder.build_load(return_pointer, "read_result")
} else {
call.try_as_basic_value().left().unwrap()
CCReturn::ByPointer => env.builder.build_load(return_pointer, "read_result"),
CCReturn::Void => return_type.const_zero(),
};
builder.build_return(Some(&return_value));
}
builder.position_at_end(old);
let call = env.builder.build_call(fastcc_function, &arguments, "tmp");
call.set_call_convention(FAST_CALL_CONV);
return call.try_as_basic_value().left().unwrap();
}
fn throw_on_overflow<'a, 'ctx, 'env>(

View File

@ -832,6 +832,7 @@ struct State<'a> {
pub exposed_types: SubsByModule,
pub output_path: Option<&'a str>,
pub platform_path: PlatformPath<'a>,
pub ptr_bytes: u32,
pub headers_parsed: MutSet<ModuleId>,
@ -1467,6 +1468,7 @@ where
let mut state = State {
root_id,
ptr_bytes,
platform_data: None,
goal_phase,
stdlib,
@ -1978,7 +1980,10 @@ fn update<'a>(
);
if state.goal_phase > Phase::SolveTypes {
let layout_cache = state.layout_caches.pop().unwrap_or_default();
let layout_cache = state
.layout_caches
.pop()
.unwrap_or_else(|| LayoutCache::new(state.ptr_bytes));
let typechecked = TypeCheckedModule {
module_id,

View File

@ -3124,7 +3124,8 @@ pub fn with_hole<'a>(
mut fields,
..
} => {
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
let sorted_fields =
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
@ -3459,7 +3460,8 @@ pub fn with_hole<'a>(
loc_expr,
..
} => {
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
let sorted_fields =
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
let mut index = None;
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
@ -3601,7 +3603,8 @@ pub fn with_hole<'a>(
// This has the benefit that we don't need to do anything special for reference
// counting
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
let sorted_fields =
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
@ -4205,7 +4208,8 @@ fn convert_tag_union<'a>(
arena: &'a Bump,
) -> Stmt<'a> {
use crate::layout::UnionVariant::*;
let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs);
let res_variant =
crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.ptr_bytes);
let variant = match res_variant {
Ok(cached) => cached,
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
@ -4541,7 +4545,7 @@ fn sorted_field_symbols<'a>(
}
};
let alignment = layout.alignment_bytes(8);
let alignment = layout.alignment_bytes(env.ptr_bytes);
let symbol = possible_reuse_symbol(env, procs, &arg.value);
field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol))));
@ -6978,7 +6982,8 @@ fn from_can_pattern_help<'a>(
use crate::exhaustive::Union;
use crate::layout::UnionVariant::*;
let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs);
let res_variant =
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes);
let variant = match res_variant {
Ok(cached) => cached,
@ -7397,7 +7402,8 @@ fn from_can_pattern_help<'a>(
..
} => {
// sorted fields based on the type
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
let sorted_fields =
crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes);
// sorted fields based on the destruct
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);

View File

@ -138,7 +138,8 @@ impl<'a> RawFunctionLayout<'a> {
let fn_args = fn_args.into_bump_slice();
let ret = arena.alloc(ret);
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
Ok(Self::Function(fn_args, lambda_set, ret))
}
@ -516,6 +517,7 @@ impl<'a> LambdaSet<'a> {
arena: &'a Bump,
subs: &Subs,
closure_var: Variable,
ptr_bytes: u32,
) -> Result<Self, LayoutProblem> {
let mut tags = std::vec::Vec::new();
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
@ -529,6 +531,7 @@ impl<'a> LambdaSet<'a> {
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes,
};
for (tag_name, variables) in tags.iter() {
@ -545,7 +548,8 @@ impl<'a> LambdaSet<'a> {
}
}
let representation = arena.alloc(Self::make_representation(arena, subs, tags));
let representation =
arena.alloc(Self::make_representation(arena, subs, tags, ptr_bytes));
Ok(LambdaSet {
set: set.into_bump_slice(),
@ -568,9 +572,10 @@ impl<'a> LambdaSet<'a> {
arena: &'a Bump,
subs: &Subs,
tags: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
ptr_bytes: u32,
) -> Layout<'a> {
// otherwise, this is a closure with a payload
let variant = union_sorted_tags_help(arena, tags, None, subs);
let variant = union_sorted_tags_help(arena, tags, None, subs, ptr_bytes);
use UnionVariant::*;
match variant {
@ -648,6 +653,7 @@ pub enum Builtin<'a> {
}
pub struct Env<'a, 'b> {
ptr_bytes: u32,
arena: &'a Bump,
seen: Vec<'a, Variable>,
subs: &'b Subs,
@ -972,8 +978,9 @@ impl<'a> Layout<'a> {
/// e.g. `identity : a -> a` could be specialized to `Bool -> Bool` or `Str -> Str`.
/// Therefore in general it's invalid to store a map from variables to layouts
/// But if we're careful when to invalidate certain keys, we still get some benefit
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct LayoutCache<'a> {
ptr_bytes: u32,
_marker: std::marker::PhantomData<&'a u8>,
}
@ -985,6 +992,13 @@ pub enum CachedLayout<'a> {
}
impl<'a> LayoutCache<'a> {
pub fn new(ptr_bytes: u32) -> Self {
Self {
ptr_bytes,
_marker: Default::default(),
}
}
pub fn from_var(
&mut self,
arena: &'a Bump,
@ -998,6 +1012,7 @@ impl<'a> LayoutCache<'a> {
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes: self.ptr_bytes,
};
Layout::from_var(&mut env, var)
@ -1016,6 +1031,7 @@ impl<'a> LayoutCache<'a> {
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes: self.ptr_bytes,
};
RawFunctionLayout::from_var(&mut env, var)
@ -1182,6 +1198,7 @@ fn layout_from_flat_type<'a>(
let arena = env.arena;
let subs = env.subs;
let ptr_bytes = env.ptr_bytes;
match flat_type {
Apply(symbol, args) => {
@ -1273,7 +1290,7 @@ fn layout_from_flat_type<'a>(
}
}
Func(_, closure_var, _) => {
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
Ok(lambda_set.runtime_representation())
}
@ -1299,8 +1316,6 @@ fn layout_from_flat_type<'a>(
let mut pairs = Vec::from_iter_in(pairs_it, arena);
pairs.sort_by(|(label1, layout1), (label2, layout2)| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -1320,14 +1335,14 @@ fn layout_from_flat_type<'a>(
TagUnion(tags, ext_var) => {
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
Ok(layout_from_tag_union(arena, tags, subs))
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
}
FunctionOrTagUnion(tag_name, _, ext_var) => {
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
let tags = UnionTags::from_tag_name_index(tag_name);
Ok(layout_from_tag_union(arena, tags, subs))
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
}
RecursiveTagUnion(rec_var, tags, ext_var) => {
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
@ -1377,8 +1392,6 @@ fn layout_from_flat_type<'a>(
}
tag_layout.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -1425,11 +1438,13 @@ pub fn sort_record_fields<'a>(
arena: &'a Bump,
var: Variable,
subs: &Subs,
ptr_bytes: u32,
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes,
};
let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var);
@ -1445,6 +1460,8 @@ fn sort_record_fields_help<'a>(
env: &mut Env<'a, '_>,
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
let ptr_bytes = env.ptr_bytes;
// Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena);
@ -1468,8 +1485,6 @@ fn sort_record_fields_help<'a>(
|(label1, _, res_layout1), (label2, _, res_layout2)| match res_layout1 {
Ok(layout1) | Err(layout1) => match res_layout2 {
Ok(layout2) | Err(layout2) => {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -1605,6 +1620,7 @@ pub fn union_sorted_tags<'a>(
arena: &'a Bump,
var: Variable,
subs: &Subs,
ptr_bytes: u32,
) -> Result<UnionVariant<'a>, LayoutProblem> {
let var =
if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) {
@ -1617,7 +1633,7 @@ pub fn union_sorted_tags<'a>(
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => {
let opt_rec_var = get_recursion_var(subs, var);
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs)
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, ptr_bytes)
}
Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous),
Err(other) => panic!("invalid content in tag union variable: {:?}", other),
@ -1651,6 +1667,7 @@ fn union_sorted_tags_help_new<'a>(
mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>,
opt_rec_var: Option<Variable>,
subs: &Subs,
ptr_bytes: u32,
) -> UnionVariant<'a> {
// sort up front; make sure the ordering stays intact!
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
@ -1659,6 +1676,7 @@ fn union_sorted_tags_help_new<'a>(
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes,
};
match tags_vec.len() {
@ -1708,8 +1726,6 @@ fn union_sorted_tags_help_new<'a>(
}
layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -1793,8 +1809,6 @@ fn union_sorted_tags_help_new<'a>(
}
arg_layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -1867,6 +1881,7 @@ pub fn union_sorted_tags_help<'a>(
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
subs: &Subs,
ptr_bytes: u32,
) -> UnionVariant<'a> {
// sort up front; make sure the ordering stays intact!
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
@ -1875,6 +1890,7 @@ pub fn union_sorted_tags_help<'a>(
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes,
};
match tags_vec.len() {
@ -1921,8 +1937,6 @@ pub fn union_sorted_tags_help<'a>(
}
layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -2005,8 +2019,6 @@ pub fn union_sorted_tags_help<'a>(
}
arg_layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
@ -2091,7 +2103,12 @@ fn cheap_sort_tags<'a, 'b>(
tags_vec
}
fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
fn layout_from_newtype<'a>(
arena: &'a Bump,
tags: UnionTags,
subs: &Subs,
ptr_bytes: u32,
) -> Layout<'a> {
debug_assert!(tags.is_newtype_wrapper(subs));
let slice_index = tags.variables().into_iter().next().unwrap();
@ -2109,6 +2126,7 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
arena,
subs,
seen: Vec::new_in(arena),
ptr_bytes,
};
match Layout::from_var(&mut env, var) {
@ -2128,11 +2146,16 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
}
}
fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
fn layout_from_tag_union<'a>(
arena: &'a Bump,
tags: UnionTags,
subs: &Subs,
ptr_bytes: u32,
) -> Layout<'a> {
use UnionVariant::*;
if tags.is_newtype_wrapper(subs) {
return layout_from_newtype(arena, tags, subs);
return layout_from_newtype(arena, tags, subs, ptr_bytes);
}
let tags_vec = cheap_sort_tags(arena, tags, subs);
@ -2148,7 +2171,7 @@ fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> L
}
_ => {
let opt_rec_var = None;
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs);
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, ptr_bytes);
match variant {
Never => Layout::Union(UnionLayout::NonRecursive(&[])),

View File

@ -92,14 +92,15 @@ mod test_reporting {
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
let mut layout_cache = LayoutCache::default();
let ptr_bytes = 8;
let mut layout_cache = LayoutCache::new(ptr_bytes);
let mut mono_env = roc_mono::ir::Env {
arena: &arena,
subs: &mut subs,
problems: &mut mono_problems,
home,
ident_ids: &mut ident_ids,
ptr_bytes: 8,
ptr_bytes,
update_mode_counter: 0,
// call_specialization_counter=0 is reserved
call_specialization_counter: 1,

View File

@ -30,7 +30,7 @@ either = "1.6.1"
indoc = "0.3.3"
libc = "0.2"
inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.10"
target-lexicon = "0.12.2"
libloading = "0.6"
[dev-dependencies]

View File

@ -28,7 +28,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
either = "1.6.1"
indoc = "0.3.3"
libc = "0.2"
target-lexicon = "0.10"
target-lexicon = "0.12.2"
libloading = "0.6"
[dev-dependencies]

1
examples/.gitignore vendored
View File

@ -4,4 +4,3 @@ app
libhost.a
roc_app.ll
roc_app.bc
effect-example

View File

@ -29,23 +29,33 @@ extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_1_Fx_size() i64;
extern fn roc__mainForHost_1_Fx_result_size() i64;
extern fn malloc(size: usize) callconv(.C) ?*c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
const Align = extern struct { a: usize, b: usize };
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
_ = alignment;
return malloc(size);
}
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
_ = old_size;
_ = alignment;
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
}
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
_ = alignment;
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
}
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer();
const msg = @ptrCast([*:0]const u8, c_ptr);
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
@ -54,12 +64,9 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
const Unit = extern struct {};
pub export fn main() u8 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
pub fn main() u8 {
const size = @intCast(usize, roc__mainForHost_size());
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
defer {
@ -71,17 +78,17 @@ pub export fn main() u8 {
roc__mainForHost_1_exposed(output);
const elements = @ptrCast([*]u64, @alignCast(8, output));
var flag = elements[0];
const flag = @ptrCast(*u64, @alignCast(@alignOf(u64), output)).*;
if (flag == 0) {
// all is well
const closure_data_pointer = @ptrCast([*]u8, output[8..size]);
const closure_data_pointer = @ptrCast([*]u8, output[@sizeOf(u64)..size]);
call_the_closure(closure_data_pointer);
} else {
const msg = @intToPtr([*:0]const u8, elements[1]);
const ptr = @ptrCast(*u32, output + @sizeOf(u64));
const msg = @intToPtr([*:0]const u8, ptr.*);
const stderr = std.io.getStdErr().writer();
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
return 0;
@ -92,6 +99,7 @@ pub export fn main() u8 {
const delta = to_seconds(ts2) - to_seconds(ts1);
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
return 0;
@ -103,7 +111,7 @@ fn to_seconds(tms: std.os.timespec) f64 {
fn call_the_closure(closure_data_pointer: [*]u8) void {
const size = roc__mainForHost_1_Fx_result_size();
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable;
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
defer {
@ -135,7 +143,7 @@ pub export fn roc_fx_putInt(int: i64) i64 {
return 0;
}
pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
export fn roc_fx_putLine(rocPath: str.RocStr) callconv(.C) void {
const stdout = std.io.getStdOut().writer();
for (rocPath.asSlice()) |char| {
@ -143,8 +151,6 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
}
stdout.print("\n", .{}) catch unreachable;
return 0;
}
const GetInt = extern struct {

1
examples/cli/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
echo

18
examples/cli/Echo.roc Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env roc
app "echo"
packages { base: "platform" }
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
provides [ main ] to base
main : Task {} *
main =
{} <- await (Stdout.line "What's your first name?")
firstName <- await Stdin.line
{} <- await (Stdout.line "What's your last name?")
lastName <- await Stdin.line
Stdout.line "Hi, \(firstName) \(lastName)!"

BIN
examples/cli/cli-example Executable file

Binary file not shown.

BIN
examples/cli/hello-world Executable file

Binary file not shown.

21
examples/cli/platform/Cargo.lock generated Normal file
View File

@ -0,0 +1,21 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "host"
version = "0.1.0"
dependencies = [
"libc",
"roc_std",
]
[[package]]
name = "libc"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "roc_std"
version = "0.1.0"

View File

@ -0,0 +1,15 @@
[package]
name = "host"
version = "0.1.0"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
edition = "2018"
[lib]
crate-type = ["staticlib"]
[dependencies]
roc_std = { path = "../../../roc_std" }
libc = "0.2"
[workspace]

View File

@ -0,0 +1,14 @@
platform examples/cli
requires {}{ main : Task {} [] } # TODO FIXME
exposes []
packages {}
imports [ Task.{ Task } ]
provides [ mainForHost ]
effects fx.Effect
{
putLine : Str -> Effect {},
getLine : Effect Str
}
mainForHost : Task {} [] as Fx
mainForHost = main

View File

@ -0,0 +1,6 @@
interface Stdin
exposes [ line ]
imports [ fx.Effect, Task ]
line : Task.Task Str *
line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice

View File

@ -0,0 +1,9 @@
interface Stdout
exposes [ line ]
imports [ fx.Effect, Task.{ Task } ]
# line : Str -> Task.Task {} *
# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
line : Str -> Task {} *
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})

View File

@ -0,0 +1,44 @@
interface Task
exposes [ Task, succeed, fail, await, map, onFail, attempt ]
imports [ fx.Effect ]
Task ok err : Effect.Effect (Result ok err)
succeed : val -> Task val *
succeed = \val ->
Effect.always (Ok val)
fail : err -> Task * err
fail = \val ->
Effect.always (Err val)
attempt : Task a b, (Result a b -> Task c d) -> Task c d
attempt = \effect, transform ->
Effect.after effect \result ->
when result is
Ok ok -> transform (Ok ok)
Err err -> transform (Err err)
await : Task a err, (a -> Task b err) -> Task b err
await = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> transform a
Err err -> Task.fail err
onFail : Task ok a, (a -> Task ok b) -> Task ok b
onFail = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> Task.succeed a
Err err -> transform err
map : Task a err, (a -> b) -> Task b err
map = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> Task.succeed (transform a)
Err err -> Task.fail err

View File

@ -0,0 +1,7 @@
#include <stdio.h>
extern int rust_main();
int main() {
return rust_main();
}

View File

@ -0,0 +1,139 @@
#![allow(non_snake_case)]
use core::alloc::Layout;
use core::ffi::c_void;
use core::mem::MaybeUninit;
use libc;
use roc_std::{RocCallResult, RocStr};
use std::ffi::CStr;
use std::os::raw::c_char;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed"]
fn roc_main(output: *mut u8) -> ();
#[link_name = "roc__mainForHost_size"]
fn roc_main_size() -> i64;
#[link_name = "roc__mainForHost_1_Fx_caller"]
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> ();
#[allow(dead_code)]
#[link_name = "roc__mainForHost_1_Fx_size"]
fn size_Fx() -> i64;
#[link_name = "roc__mainForHost_1_Fx_result_size"]
fn size_Fx_result() -> i64;
}
#[no_mangle]
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
libc::malloc(size)
}
#[no_mangle]
pub unsafe fn roc_realloc(
c_ptr: *mut c_void,
new_size: usize,
_old_size: usize,
_alignment: u32,
) -> *mut c_void {
libc::realloc(c_ptr, new_size)
}
#[no_mangle]
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
libc::free(c_ptr)
}
#[no_mangle]
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub fn rust_main() -> isize {
let size = unsafe { roc_main_size() } as usize;
let layout = Layout::array::<u8>(size).unwrap();
unsafe {
// TODO allocate on the stack if it's under a certain size
let buffer = std::alloc::alloc(layout);
roc_main(buffer);
let output = buffer as *mut RocCallResult<()>;
match (&*output).into() {
Ok(()) => {
let closure_data_ptr = buffer.offset(8);
let result = call_the_closure(closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
}
Err(msg) => {
std::alloc::dealloc(buffer, layout);
panic!("Roc failed with message: {}", msg);
}
}
};
// Exit code
0
}
unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
let size = size_Fx_result() as usize;
let layout = Layout::array::<u8>(size).unwrap();
let buffer = std::alloc::alloc(layout) as *mut u8;
call_Fx(
// This flags pointer will never get dereferenced
MaybeUninit::uninit().as_ptr(),
closure_data_ptr as *const u8,
buffer as *mut u8,
);
let output = &*(buffer as *mut RocCallResult<()>);
match output.into() {
Ok(_) => {
std::alloc::dealloc(buffer, layout);
0
}
Err(e) => panic!("failed with {}", e),
}
}
#[no_mangle]
pub fn roc_fx_getLine() -> RocStr {
use std::io::{self, BufRead};
let stdin = io::stdin();
let line1 = stdin.lock().lines().next().unwrap().unwrap();
RocStr::from_slice(line1.as_bytes())
}
#[no_mangle]
pub fn roc_fx_putLine(line: RocStr) -> () {
let bytes = line.as_slice();
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
println!("{}", string);
// don't mess with the refcount!
core::mem::forget(line);
()
}

View File

@ -19,23 +19,32 @@ comptime {
}
}
extern fn malloc(size: usize) callconv(.C) ?*c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
const Align = extern struct { a: usize, b: usize };
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
_ = alignment;
return malloc(size);
}
export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignment: u32) callconv(.C) ?*c_void {
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
_ = old_size;
_ = alignment;
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
}
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
_ = alignment;
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
}
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer();
const msg = @ptrCast([*:0]const u8, c_ptr);
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
@ -47,11 +56,11 @@ const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed(*RocCallResult) void;
const RocCallResult = extern struct { flag: usize, content: RocStr };
const RocCallResult = extern struct { flag: u64, content: RocStr };
const Unit = extern struct {};
pub export fn main() i32 {
pub fn main() u8 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();

View File

@ -584,6 +584,10 @@ impl RocStr {
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8);
// write the refcount
let refcount_ptr = raw_ptr as *mut isize;
*(refcount_ptr.offset(-1)) = isize::MIN;
{
// NOTE: using a memcpy here causes weird issues
let target_ptr = raw_ptr as *mut u8;