Merge remote-tracking branch 'origin/trunk' into specialize-lowlevel

This commit is contained in:
Folkert 2021-05-24 15:05:14 +02:00
commit fbb711b2ca
70 changed files with 3513 additions and 126 deletions

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod link;

View file

@ -727,6 +727,42 @@ pub fn listAppend(list: RocList, alignment: usize, element: Opaque, element_widt
return output;
}
pub fn listDrop(
list: RocList,
alignment: usize,
element_width: usize,
drop_count: usize,
dec: Dec,
) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
const keep_count = size - drop_count;
var i: usize = 0;
const iterations = std.math.min(drop_count, size);
while (i < iterations) : (i += 1) {
const element = source_ptr + i * element_width;
dec(element);
}
if (drop_count >= size) {
return RocList.empty();
}
const output = RocList.allocate(std.heap.c_allocator, alignment, keep_count, element_width);
const target_ptr = output.bytes orelse unreachable;
@memcpy(target_ptr, source_ptr + drop_count * element_width, keep_count * element_width);
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width);
return output;
} else {
return RocList.empty();
}
}
pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C) RocList {
const allocator = std.heap.c_allocator;
const IntWidth = utils.IntWidth;

View file

@ -25,6 +25,7 @@ comptime {
exportListFn(list.listReverse, "reverse");
exportListFn(list.listSortWith, "sort_with");
exportListFn(list.listConcat, "concat");
exportListFn(list.listDrop, "drop");
}
// Dict Module

View file

@ -76,6 +76,7 @@ pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
pub const LIST_APPEND: &str = "roc_builtins.list.append";
pub const LIST_DROP: &str = "roc_builtins.list.drop";
pub const LIST_SINGLE: &str = "roc_builtins.list.single";
pub const LIST_JOIN: &str = "roc_builtins.list.join";
pub const LIST_RANGE: &str = "roc_builtins.list.range";

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod bitcode;

View file

@ -849,6 +849,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(list_type(flex(TVAR1))),
);
// drop : List elem, Nat -> List elem
add_top_level_function_type!(
Symbol::LIST_DROP,
vec![list_type(flex(TVAR1)), nat_type()],
Box::new(list_type(flex(TVAR1))),
);
// prepend : List elem, elem -> List elem
add_top_level_function_type!(
Symbol::LIST_PREPEND,

View file

@ -84,6 +84,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_MAP => list_map,
LIST_MAP2 => list_map2,
LIST_MAP3 => list_map3,
LIST_DROP => list_drop,
LIST_MAP_WITH_INDEX => list_map_with_index,
LIST_KEEP_IF => list_keep_if,
LIST_KEEP_OKS => list_keep_oks,
@ -1881,6 +1882,28 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
list_ret_var,
)
}
/// List.drop : List elem, Nat -> List elem
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let index_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::ListDrop,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(index_var, Var(Symbol::ARG_2)),
],
ret_var: list_var,
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1), (index_var, Symbol::ARG_2)],
var_store,
body,
list_var,
)
}
/// List.append : List elem, elem -> List elem
fn list_append(symbol: Symbol, var_store: &mut VarStore) -> Def {

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod annotation;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod builtins;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod annotation;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
// we actually want to compare against the literal float bits

View file

@ -6,9 +6,9 @@ use crate::llvm::build_dict::{
use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map,
list_map2, list_map3, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse,
list_set, list_single, list_sort_with,
list_drop, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len,
list_map, list_map2, list_map3, list_map_with_index, list_prepend, list_range, list_repeat,
list_reverse, list_set, list_single, list_sort_with,
};
use crate::llvm::build_str::{
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
@ -820,7 +820,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
)
}
CallType::LowLevel { op } => {
CallType::LowLevel { op, update_mode: _ } => {
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments)
}
@ -4186,6 +4186,27 @@ fn run_low_level<'a, 'ctx, 'env>(
list_append(env, inplace, original_wrapper, elem, elem_layout)
}
ListDrop => {
// List.drop : List elem, Nat -> List elem
debug_assert_eq!(args.len(), 2);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let original_wrapper = list.into_struct_value();
let count = load_symbol(scope, &args[1]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_drop(
env,
layout_ids,
original_wrapper,
count.into_int_value(),
element_layout,
),
_ => unreachable!("Invalid layout {:?} in List.drop", list_layout),
}
}
ListPrepend => {
// List.prepend : List elem, elem -> List elem
debug_assert_eq!(args.len(), 2);

View file

@ -322,6 +322,28 @@ pub fn list_append<'a, 'ctx, 'env>(
)
}
/// List.drop : List elem, Nat -> List elem
pub fn list_drop<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
original_wrapper: StructValue<'ctx>,
count: IntValue<'ctx>,
element_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, &element_layout);
call_bitcode_fn_returns_list(
env,
&[
pass_list_as_i128(env, original_wrapper.into()),
alignment_intvalue(env, &element_layout),
layout_width(env, &element_layout),
count.into(),
dec_element_fn.as_global_value().as_pointer_value().into(),
],
&bitcode::LIST_DROP,
)
}
/// List.set : List elem, Int, elem -> List elem
pub fn list_set<'a, 'ctx, 'env>(
parent: FunctionValue<'ctx>,

View file

@ -23,7 +23,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
inlinable_string = "0.1"
target-lexicon = "0.10"
libloading = "0.6"
object = { version = "0.22", features = ["write"] }
object = { version = "0.24", features = ["write"] }
[dev-dependencies]
roc_can = { path = "../can" }

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
@ -214,7 +214,7 @@ where
}
}
CallType::LowLevel { op: lowlevel } => {
CallType::LowLevel { op: lowlevel, .. } => {
self.build_run_low_level(sym, lowlevel, arguments, layout)
}
x => Err(format!("the call type, {:?}, is not yet implemented", x)),

View file

@ -13,7 +13,9 @@ use roc_mono::ir::Proc;
use roc_mono::layout::Layout;
use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple};
const VERSION: &str = env!("CARGO_PKG_VERSION");
// This is used by some code below which is currently commented out.
// See that code for more details!
// const VERSION: &str = env!("CARGO_PKG_VERSION");
/// build_module is the high level builder/delegator.
/// It takes the request to build a module and output the object file for the module.
@ -41,6 +43,28 @@ pub fn build_module<'a>(
Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little),
)
}
Triple {
architecture: TargetArch::X86_64,
binary_format: TargetBF::Macho,
..
} => {
let backend: Backend64Bit<
x86_64::X86_64GeneralReg,
x86_64::X86_64FloatReg,
x86_64::X86_64Assembler,
x86_64::X86_64SystemV,
> = Backend::new(env, target)?;
build_object(
env,
procedures,
backend,
Object::new(
BinaryFormat::MachO,
Architecture::X86_64,
Endianness::Little,
),
)
}
Triple {
architecture: TargetArch::Aarch64(_),
binary_format: TargetBF::Elf,
@ -72,12 +96,16 @@ fn build_object<'a, B: Backend<'a>>(
mut output: Object,
) -> Result<Object, String> {
let data_section = output.section_id(StandardSection::Data);
let comment = output.add_section(vec![], b"comment".to_vec(), SectionKind::OtherString);
/*
// Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/rtfeldman/roc/pull/1323
let comment = output.add_section(vec![], b".comment".to_vec(), SectionKind::OtherString);
output.append_section_data(
comment,
format!("\0roc dev backend version {} \0", VERSION).as_bytes(),
1,
);
*/
// Setup layout_ids for procedure calls.
let mut layout_ids = roc_mono::layout::LayoutIds::default();
@ -89,7 +117,7 @@ fn build_object<'a, B: Backend<'a>>(
let section_id = output.add_section(
output.segment_name(StandardSegment::Text).to_vec(),
format!(".text.{}", fn_name).as_bytes().to_vec(),
format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(),
SectionKind::Text,
);
@ -182,7 +210,7 @@ fn build_object<'a, B: Backend<'a>>(
offset: offset + proc_offset,
size: 32,
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::Generic,
encoding: RelocationEncoding::X86Branch,
symbol: sym_id,
addend: -4,
}

View file

@ -10,7 +10,7 @@ extern crate libc;
#[macro_use]
mod helpers;
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
#[cfg(all(test, any(target_os = "linux", target_os = "macos"), any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
mod gen_num {
#[test]
fn i64_values() {
@ -128,9 +128,7 @@ mod gen_num {
af = 31
ag = 32
# This can't be one line because it causes a stack overflow in the frontend :(
tmp = a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q
tmp + r + s + t + u + v + w + x + y + z + aa + ab + ac + ad + ae + af + ag
a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + aa + ab + ac + ad + ae + af + ag
"#
),
528,

View file

@ -52,7 +52,7 @@ const DEFAULT_APP_OUTPUT_PATH: &str = "app";
const ROC_FILE_EXTENSION: &str = "roc";
/// Roc-Config file name
const PKG_CONFIG_FILE_NAME: &str = "Pkg-Config";
const PKG_CONFIG_FILE_NAME: &str = "Package-Config";
/// The . in between module names like Foo.Bar.Baz
const MODULE_SEPARATOR: char = '.';
@ -1894,7 +1894,7 @@ fn update<'a>(
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
// if there is a platform, the Pkg-Config module provides host-exposed,
// if there is a platform, the Package-Config module provides host-exposed,
// otherwise the App module exposes host-exposed
let is_host_exposed = match state.platform_id {
None => module_id == state.root_id,
@ -2060,6 +2060,15 @@ fn update<'a>(
println!("{}", result);
}
if false {
let it = state.procedures.iter().map(|x| x.1);
if let Err(e) = roc_mono::alias_analysis::spec_program(it) {
println!("Error in alias analysis: {:?}", e)
}
}
Proc::insert_refcount_operations(arena, &mut state.procedures);
Proc::optimize_refcount_operations(
@ -2313,7 +2322,7 @@ fn load_pkg_config<'a>(
let chomped = &bytes[..delta];
let header_src = unsafe { std::str::from_utf8_unchecked(chomped) };
// make a Pkg-Config module that ultimately exposes `main` to the host
// make a Package-Config module that ultimately exposes `main` to the host
let pkg_config_module_msg = fabricate_pkg_config_module(
arena,
shorthand,
@ -2552,7 +2561,7 @@ fn parse_header<'a>(
}) => {
match package_or_path {
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
// check whether we can find a Pkg-Config.roc file
// check whether we can find a Package-Config.roc file
let mut pkg_config_roc = pkg_config_dir;
pkg_config_roc.push(package);
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
@ -3800,6 +3809,8 @@ fn make_specializations<'a>(
home,
ident_ids: &mut ident_ids,
ptr_bytes,
update_mode_counter: 0,
call_specialization_counter: 0,
};
// TODO: for now this final specialization pass is sequential,
@ -3861,6 +3872,8 @@ fn build_pending_specializations<'a>(
home,
ident_ids: &mut ident_ids,
ptr_bytes,
update_mode_counter: 0,
call_specialization_counter: 0,
};
// Add modules' decls to Procs

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod docs;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]

View file

@ -39,6 +39,7 @@ pub enum LowLevel {
ListKeepOks,
ListKeepErrs,
ListSortWith,
ListDrop,
DictSize,
DictEmpty,
DictInsert,
@ -124,6 +125,7 @@ impl LowLevel {
| ListGetUnsafe
| ListSet
| ListSetInPlace
| ListDrop
| ListSingle
| ListRepeat
| ListReverse

View file

@ -86,6 +86,10 @@ impl Symbol {
})
}
pub fn as_u64(self) -> u64 {
self.0
}
pub fn fully_qualified(self, interns: &Interns, home: ModuleId) -> InlinableString {
let module_id = self.module_id();
@ -101,6 +105,10 @@ impl Symbol {
.into()
}
}
pub const fn to_ne_bytes(self) -> [u8; 8] {
self.0.to_ne_bytes()
}
}
/// Rather than displaying as this:
@ -930,6 +938,7 @@ define_builtins! {
29 LIST_WALK_UNTIL: "walkUntil"
30 LIST_RANGE: "range"
31 LIST_SORT_WITH: "sortWith"
32 LIST_DROP: "drop"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -15,6 +15,7 @@ roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" }
roc_problem = { path = "../problem" }
ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.6.1", features = ["collections"] }
hashbrown = { version = "0.11.2", features = [ "bumpalo" ] }
ven_ena = { path = "../../vendor/ena" }

View file

@ -0,0 +1,614 @@
use morphic_lib::TypeContext;
use morphic_lib::{
BlockExpr, BlockId, CalleeSpecVar, EntryPointName, ExprContext, FuncDef, FuncDefBuilder,
FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, TypeName, UpdateModeVar,
ValueId,
};
use roc_collections::all::MutMap;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use std::convert::TryFrom;
use crate::ir::{Call, CallType, Expr, Literal, ModifyRc, Proc, Stmt};
use crate::layout::{Builtin, Layout, ListLayout, UnionLayout};
// just using one module for now
const MOD_LIST: ModName = ModName(b"UserApp");
const MOD_APP: ModName = ModName(b"UserApp");
pub fn spec_program<'a, I>(procs: I) -> Result<morphic_lib::Solutions>
where
I: Iterator<Item = &'a Proc<'a>>,
{
let mut main_function = None;
let main_module = {
let mut m = ModDefBuilder::new();
for proc in procs {
let spec = proc_spec(proc)?;
m.add_func(FuncName(&proc.name.to_ne_bytes()), spec)?;
if format!("{:?}", proc.name).contains("mainForHost") {
main_function = Some(proc.name);
}
}
m.build()?
};
let program = {
let mut p = ProgramBuilder::new();
p.add_mod(MOD_APP, main_module)?;
p.add_entry_point(
EntryPointName(b"mainForHost"),
MOD_APP,
FuncName(&main_function.unwrap().to_ne_bytes()),
)?;
p.build()?
};
morphic_lib::solve(program)
}
fn proc_spec(proc: &Proc) -> Result<FuncDef> {
let mut builder = FuncDefBuilder::new();
let mut env = Env::default();
let block = builder.add_block();
// introduce the arguments
let mut argument_layouts = Vec::new();
for (i, (layout, symbol)) in proc.args.iter().enumerate() {
let value_id = builder.add_get_tuple_field(block, builder.get_argument(), i as u32)?;
env.symbols.insert(*symbol, value_id);
argument_layouts.push(*layout);
}
let value_id = stmt_spec(&mut builder, &mut env, block, &proc.ret_layout, &proc.body)?;
let root = BlockExpr(block, value_id);
let arg_type_id = layout_spec(&mut builder, &Layout::Struct(&argument_layouts))?;
let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?;
builder.build(arg_type_id, ret_type_id, root)
}
#[derive(Default)]
struct Env {
symbols: MutMap<Symbol, ValueId>,
join_points: MutMap<crate::ir::JoinPointId, morphic_lib::JoinPointId>,
}
fn stmt_spec(
builder: &mut FuncDefBuilder,
env: &mut Env,
block: BlockId,
layout: &Layout,
stmt: &Stmt,
) -> Result<ValueId> {
use Stmt::*;
match stmt {
Let(symbol, expr, layout, continuation) => {
let value_id = expr_spec(builder, env, block, layout, expr)?;
env.symbols.insert(*symbol, value_id);
let result = stmt_spec(builder, env, block, layout, continuation)?;
env.symbols.remove(symbol);
Ok(result)
}
Invoke {
symbol,
call,
layout: call_layout,
pass,
fail,
} => {
// a call that might throw an exception
let value_id = call_spec(builder, env, block, call_layout, call)?;
let pass_block = builder.add_block();
env.symbols.insert(*symbol, value_id);
let pass_value_id = stmt_spec(builder, env, pass_block, layout, pass)?;
env.symbols.remove(symbol);
let pass_block_expr = BlockExpr(pass_block, pass_value_id);
let fail_block = builder.add_block();
let fail_value_id = stmt_spec(builder, env, fail_block, layout, fail)?;
let fail_block_expr = BlockExpr(fail_block, fail_value_id);
builder.add_choice(block, &[pass_block_expr, fail_block_expr])
}
Switch {
cond_symbol: _,
cond_layout: _,
branches,
default_branch,
ret_layout,
} => {
let mut cases = Vec::with_capacity(branches.len() + 1);
let it = branches
.iter()
.map(|(_, _, body)| body)
.chain(std::iter::once(default_branch.1));
for branch in it {
let block = builder.add_block();
let value_id = stmt_spec(builder, env, block, ret_layout, branch)?;
cases.push(BlockExpr(block, value_id));
}
builder.add_choice(block, &cases)
}
Ret(symbol) => Ok(env.symbols[symbol]),
Refcounting(modify_rc, continuation) => match modify_rc {
ModifyRc::Inc(symbol, _) | ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => {
let result_type = builder.add_tuple_type(&[])?;
let argument = env.symbols[symbol];
// this is how RC is modelled; it recursively touches all heap cells
builder.add_unknown_with(block, &[argument], result_type)?;
stmt_spec(builder, env, block, layout, continuation)
}
},
Join {
id,
parameters,
continuation,
remainder,
} => {
let mut type_ids = Vec::new();
for p in parameters.iter() {
type_ids.push(layout_spec(builder, &p.layout)?);
}
let ret_type_id = layout_spec(builder, layout)?;
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
let (jpid, jp_argument) =
builder.declare_join_point(block, jp_arg_type_id, ret_type_id)?;
let join_body_sub_block = {
env.join_points.insert(*id, jpid);
let jp_body_block = builder.add_block();
// unpack the argument
for (i, p) in parameters.iter().enumerate() {
let value_id =
builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?;
env.symbols.insert(p.symbol, value_id);
}
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?;
BlockExpr(jp_body_block, jp_body_value_id)
};
// NOTE the symbols bound by the join point can shadow the argument symbols of the
// surrounding function, so we don't remove them from the env here
let cont_block = builder.add_block();
let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?;
env.join_points.remove(id);
builder.define_join_point(jpid, join_body_sub_block)?;
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
}
Jump(id, symbols) => {
let ret_type_id = layout_spec(builder, layout)?;
let argument = build_tuple_value(builder, env, block, symbols)?;
let jpid = env.join_points[id];
builder.add_jump(block, jpid, argument, ret_type_id)
}
Rethrow | RuntimeError(_) => {
let type_id = layout_spec(builder, layout)?;
builder.add_terminate(block, type_id)
}
}
}
fn build_tuple_value(
builder: &mut FuncDefBuilder,
env: &Env,
block: BlockId,
symbols: &[Symbol],
) -> Result<ValueId> {
let mut value_ids = Vec::new();
for field in symbols.iter() {
let value_id = match env.symbols.get(field) {
None => panic!(
"Symbol {:?} is not defined in environment {:?}",
field, &env.symbols
),
Some(x) => *x,
};
value_ids.push(value_id);
}
builder.add_make_tuple(block, &value_ids)
}
fn build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result<TypeId> {
let mut field_types = Vec::new();
for field in layouts.iter() {
field_types.push(layout_spec(builder, field)?);
}
builder.add_tuple_type(&field_types)
}
fn call_spec(
builder: &mut FuncDefBuilder,
env: &Env,
block: BlockId,
layout: &Layout,
call: &Call,
) -> Result<ValueId> {
use CallType::*;
match &call.call_type {
ByName {
name: symbol,
full_layout: _,
ret_layout: _,
arg_layouts: _,
specialization_id,
} => {
let array = specialization_id.to_bytes();
let spec_var = CalleeSpecVar(&array);
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
let slice = &symbol.to_ne_bytes();
let name = FuncName(slice);
let module = MOD_APP;
builder.add_call(block, spec_var, module, name, arg_value_id)
}
Foreign {
foreign_symbol: _,
ret_layout,
} => {
let arguments: Vec<_> = call
.arguments
.iter()
.map(|symbol| env.symbols[symbol])
.collect();
let result_type = layout_spec(builder, ret_layout)?;
builder.add_unknown_with(block, &arguments, result_type)
}
LowLevel { op, update_mode } => lowlevel_spec(
builder,
env,
block,
layout,
op,
*update_mode,
call.arguments,
),
HigherOrderLowLevel { .. } => todo!(),
}
}
fn lowlevel_spec(
builder: &mut FuncDefBuilder,
env: &Env,
block: BlockId,
layout: &Layout,
op: &LowLevel,
update_mode: crate::ir::UpdateModeId,
arguments: &[Symbol],
) -> Result<ValueId> {
use LowLevel::*;
let type_id = layout_spec(builder, layout)?;
let mode = update_mode.to_bytes();
let update_mode_var = UpdateModeVar(&mode);
match op {
NumAdd | NumSub => {
// NOTE these numeric operations panic (e.g. on overflow)
let pass_block = {
let block = builder.add_block();
let value = new_num(builder, block)?;
BlockExpr(block, value)
};
let fail_block = {
let block = builder.add_block();
let value = builder.add_terminate(block, type_id)?;
BlockExpr(block, value)
};
let sub_block = {
let block = builder.add_block();
let choice = builder.add_choice(block, &[pass_block, fail_block])?;
BlockExpr(block, choice)
};
builder.add_sub_block(block, sub_block)
}
Eq | NotEq => new_bool(builder, block),
NumLte | NumLt | NumGt | NumGte => new_order(builder, block),
ListLen => {
let list = env.symbols[&arguments[0]];
builder.add_get_tuple_field(block, list, LIST_LEN_INDEX)
}
ListGetUnsafe => {
// NOTE the ListGet lowlevel op is only evaluated if the index is in-bounds
let list = env.symbols[&arguments[0]];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let _unit = builder.add_touch(block, cell)?;
builder.add_bag_get(block, bag)
}
ListSet => {
let list = env.symbols[&arguments[0]];
let to_insert = env.symbols[&arguments[2]];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let _unit = builder.add_update(block, update_mode_var, cell)?;
builder.add_bag_insert(block, bag, to_insert)?;
Ok(list)
}
other => todo!("lowlevel op not implemented: {:?}", other),
}
}
fn build_variant_types(
builder: &mut FuncDefBuilder,
layout: &Layout,
) -> Option<Result<Vec<TypeId>>> {
match layout {
Layout::Union(union_layout) => Some(build_variant_types_help(builder, union_layout)),
_ => None,
}
}
fn build_variant_types_help(
builder: &mut FuncDefBuilder,
union_layout: &UnionLayout,
) -> Result<Vec<TypeId>> {
use UnionLayout::*;
let mut result = Vec::new();
match union_layout {
NonRecursive(tags) => {
for tag in tags.iter() {
result.push(build_tuple_type(builder, tag)?);
}
}
Recursive(_) => todo!(),
NonNullableUnwrapped(_) => todo!(),
NullableWrapped {
nullable_id: _,
other_tags: _,
} => todo!(),
NullableUnwrapped {
nullable_id: _,
other_fields: _,
} => todo!(),
}
Ok(result)
}
fn expr_spec(
builder: &mut FuncDefBuilder,
env: &Env,
block: BlockId,
layout: &Layout,
expr: &Expr,
) -> Result<ValueId> {
use Expr::*;
match expr {
Literal(literal) => literal_spec(builder, block, literal),
Call(call) => call_spec(builder, env, block, layout, call),
Tag {
tag_layout,
tag_name: _,
tag_id,
union_size: _,
arguments,
} => {
let value_id = build_tuple_value(builder, env, block, arguments)?;
let variant_types = build_variant_types(builder, tag_layout).unwrap()?;
builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)
}
Struct(fields) => build_tuple_value(builder, env, block, fields),
AccessAtIndex {
index,
field_layouts: _,
structure,
wrapped,
} => {
use crate::ir::Wrapped;
let value_id = env.symbols[structure];
match wrapped {
Wrapped::EmptyRecord => {
// this is a unit value
builder.add_make_tuple(block, &[])
}
Wrapped::SingleElementRecord => {
todo!("do we unwrap single-element records still?")
}
Wrapped::RecordOrSingleTagUnion => {
builder.add_get_tuple_field(block, value_id, *index as u32)
}
Wrapped::MultiTagUnion => {
builder.add_get_tuple_field(block, value_id, *index as u32)
}
}
}
Array { elem_layout, elems } => {
let type_id = layout_spec(builder, elem_layout)?;
let list = new_list(builder, block, type_id)?;
let mut bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
for symbol in elems.iter() {
let value_id = env.symbols[symbol];
bag = builder.add_bag_insert(block, bag, value_id)?;
}
Ok(bag)
}
EmptyArray => {
use ListLayout::*;
match ListLayout::try_from(layout) {
Ok(EmptyList) => {
// just make up an element type
let type_id = builder.add_tuple_type(&[])?;
new_list(builder, block, type_id)
}
Ok(List(element_layout)) => {
let type_id = layout_spec(builder, element_layout)?;
new_list(builder, block, type_id)
}
Err(()) => unreachable!("empty array does not have a list layout"),
}
}
Reuse { .. } => todo!("currently unused"),
Reset(_) => todo!("currently unused"),
RuntimeErrorFunction(_) => {
let type_id = layout_spec(builder, layout)?;
builder.add_terminate(block, type_id)
}
}
}
fn literal_spec(
builder: &mut FuncDefBuilder,
block: BlockId,
literal: &Literal,
) -> Result<ValueId> {
use Literal::*;
match literal {
Str(_) => new_static_string(builder, block),
Int(_) | Float(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
}
}
fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result<TypeId> {
use Layout::*;
match layout {
Builtin(builtin) => builtin_spec(builder, builtin),
PhantomEmptyStruct => todo!(),
Struct(fields) => build_tuple_type(builder, fields),
Union(union_layout) => {
let variant_types = build_variant_types_help(builder, union_layout)?;
builder.add_union_type(&variant_types)
}
RecursivePointer => todo!(),
FunctionPointer(_, _) => todo!(),
Closure(_, _, _) => todo!(),
Pointer(_) => todo!(),
}
}
fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeId> {
use Builtin::*;
match builtin {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize => builder.add_tuple_type(&[]),
Float128 => todo!(),
Float64 => todo!(),
Float32 => todo!(),
Float16 => todo!(),
Str => todo!(),
Dict(_, _) => todo!(),
Set(_) => todo!(),
List(_, _) => {
// TODO should incorporate the element type into the name
Ok(builder.add_named_type(MOD_LIST, TypeName(b"List")))
}
EmptyStr => todo!(),
EmptyList => todo!(),
EmptyDict => todo!(),
EmptySet => todo!(),
}
}
// const OK_TAG_ID: u8 = 1u8;
// const ERR_TAG_ID: u8 = 0u8;
const LIST_CELL_INDEX: u32 = 0;
const LIST_BAG_INDEX: u32 = 1;
const LIST_LEN_INDEX: u32 = 2;
fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result<ValueId> {
let cell = builder.add_new_heap_cell(block)?;
let bag = builder.add_empty_bag(block, element_type)?;
let length = new_usize(builder, block)?;
builder.add_make_tuple(block, &[cell, bag, length])
}
fn new_usize(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
new_num(builder, block)
}
fn new_static_string(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
let cell = builder.add_new_heap_cell(block)?;
// immediately mutate the cell, so any future updates on this value are invalid
// updating a static string would cause a crash at runtime
let _ = builder.add_update(block, UpdateModeVar(&[]), cell)?;
let length = new_usize(builder, block)?;
builder.add_make_tuple(block, &[cell, length])
}
fn new_order(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
// always generats EQ
let tag_id = 0;
let unit = builder.add_tuple_type(&[])?;
let unit_value = builder.add_make_tuple(block, &[])?;
builder.add_make_union(block, &[unit, unit, unit], tag_id, unit_value)
}
fn new_bool(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
// always generats False
let tag_id = 0;
let unit = builder.add_tuple_type(&[])?;
let unit_value = builder.add_make_tuple(block, &[])?;
builder.add_make_union(block, &[unit, unit], tag_id, unit_value)
}
fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
// we model all our numbers as unit values
builder.add_make_tuple(block, &[])
}

View file

@ -382,7 +382,7 @@ impl<'a> BorrowInfState<'a> {
self.own_args_using_params(arguments, ps);
}
LowLevel { op } => {
LowLevel { op, .. } => {
debug_assert!(!op.is_higher_order());
self.own_var(z);
@ -747,6 +747,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
// TODO when we have lists with capacity (if ever)
// List.append should own its first argument
ListAppend => arena.alloc_slice_copy(&[owned, owned]),
ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]),
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),

View file

@ -1404,8 +1404,12 @@ fn compile_test_help<'a>(
default_branch,
};
let op = LowLevel::Eq;
let test = Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
call_type: crate::ir::CallType::LowLevel {
op,
update_mode: env.next_update_mode_id(),
},
arguments: arena.alloc([lhs, rhs]),
});

View file

@ -441,7 +441,7 @@ impl<'a> Context<'a> {
use crate::ir::CallType::*;
match &call_type {
LowLevel { op } => {
LowLevel { op, .. } => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
@ -932,7 +932,7 @@ impl<'a> Context<'a> {
use crate::ir::CallType;
let stmt = match &call.call_type {
CallType::LowLevel { op } => {
CallType::LowLevel { op, .. } => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
}

View file

@ -777,6 +777,8 @@ pub struct Env<'a, 'i> {
pub home: ModuleId,
pub ident_ids: &'i mut IdentIds,
pub ptr_bytes: u32,
pub update_mode_counter: u64,
pub call_specialization_counter: u64,
}
impl<'a, 'i> Env<'a, 'i> {
@ -788,6 +790,26 @@ impl<'a, 'i> Env<'a, 'i> {
Symbol::new(self.home, ident_id)
}
pub fn next_update_mode_id(&mut self) -> UpdateModeId {
let id = UpdateModeId {
id: self.update_mode_counter,
};
self.update_mode_counter += 1;
id
}
pub fn next_call_specialization_id(&mut self) -> CallSpecId {
let id = CallSpecId {
id: self.call_specialization_counter,
};
self.call_specialization_counter += 1;
id
}
pub fn is_imported_symbol(&self, symbol: Symbol) -> bool {
symbol.module_id() != self.home && !symbol.is_builtin()
}
@ -1038,7 +1060,7 @@ impl<'a> Call<'a> {
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
}
LowLevel { op: lowlevel } => {
LowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
@ -1065,14 +1087,36 @@ impl<'a> Call<'a> {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CallSpecId {
id: u64,
}
impl CallSpecId {
pub fn to_bytes(self) -> [u8; 8] {
self.id.to_ne_bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UpdateModeId {
id: u64,
}
impl UpdateModeId {
pub fn to_bytes(self) -> [u8; 8] {
self.id.to_ne_bytes()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum CallType<'a> {
ByName {
name: Symbol,
full_layout: Layout<'a>,
ret_layout: Layout<'a>,
arg_layouts: &'a [Layout<'a>],
specialization_id: CallSpecId,
},
Foreign {
foreign_symbol: ForeignSymbol,
@ -1080,6 +1124,7 @@ pub enum CallType<'a> {
},
LowLevel {
op: LowLevel,
update_mode: UpdateModeId,
},
HigherOrderLowLevel {
op: LowLevel,
@ -4329,7 +4374,10 @@ pub fn with_hole<'a>(
}
_ => {
let call = self::Call {
call_type: CallType::LowLevel { op },
call_type: CallType::LowLevel {
op,
update_mode: env.next_update_mode_id(),
},
arguments: arg_symbols,
};
@ -4547,8 +4595,10 @@ pub fn from_can<'a>(
let bool_layout = Layout::Builtin(Builtin::Int1);
let cond_symbol = env.unique_symbol();
let op = LowLevel::ExpectTrue;
let call_type = CallType::LowLevel {
op: LowLevel::ExpectTrue,
op,
update_mode: env.next_update_mode_id(),
};
let arguments = env.arena.alloc([cond_symbol]);
let call = self::Call {
@ -5300,11 +5350,13 @@ fn substitute_in_call<'a>(
arg_layouts,
ret_layout,
full_layout,
specialization_id,
} => substitute(subs, *name).map(|new| CallType::ByName {
name: new,
arg_layouts,
ret_layout: *ret_layout,
full_layout: *full_layout,
specialization_id: *specialization_id,
}),
CallType::Foreign { .. } => None,
CallType::LowLevel { .. } => None,
@ -5774,6 +5826,7 @@ fn force_thunk<'a>(
ret_layout: layout,
full_layout,
arg_layouts: &[],
specialization_id: env.next_call_specialization_id(),
},
arguments: &[],
};
@ -6248,6 +6301,7 @@ fn call_by_name_help<'a>(
ret_layout: *ret_layout,
full_layout: function_layout,
arg_layouts: argument_layouts,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -6280,6 +6334,7 @@ fn call_by_name_help<'a>(
ret_layout: *ret_layout,
full_layout: function_layout,
arg_layouts: argument_layouts,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -6332,6 +6387,7 @@ fn call_by_name_help<'a>(
ret_layout: *ret_layout,
full_layout: function_layout,
arg_layouts: argument_layouts,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -6582,6 +6638,7 @@ fn call_specialized_proc<'a>(
ret_layout: function_layout.result,
full_layout: function_layout.full(),
arg_layouts: function_layout.arguments,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -6601,6 +6658,7 @@ fn call_specialized_proc<'a>(
ret_layout: function_layout.result,
full_layout: function_layout.full(),
arg_layouts: function_layout.arguments,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -6624,6 +6682,7 @@ fn call_specialized_proc<'a>(
ret_layout: function_layout.result,
full_layout: function_layout.full(),
arg_layouts: function_layout.arguments,
specialization_id: env.next_call_specialization_id(),
},
arguments: field_symbols,
};
@ -7907,6 +7966,7 @@ fn union_lambda_set_branch_help<'a>(
full_layout,
ret_layout: return_layout,
arg_layouts: argument_layouts,
specialization_id: env.next_call_specialization_id(),
},
arguments: argument_symbols,
}),
@ -8030,6 +8090,7 @@ fn enum_lambda_set_branch<'a>(
full_layout,
ret_layout: return_layout,
arg_layouts: argument_layouts,
specialization_id: env.next_call_specialization_id(),
},
arguments: argument_symbols,
}),

View file

@ -1915,3 +1915,21 @@ impl<'a> LayoutIds<'a> {
LayoutId(answer)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ListLayout<'a> {
EmptyList,
List(&'a Layout<'a>),
}
impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> {
type Error = ();
fn try_from(value: &Layout<'a>) -> Result<Self, Self::Error> {
match value {
Layout::Builtin(Builtin::EmptyList) => Ok(ListLayout::EmptyList),
Layout::Builtin(Builtin::List(_, element)) => Ok(ListLayout::List(element)),
_ => Err(()),
}
}
}

View file

@ -1,7 +1,8 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
pub mod alias_analysis;
pub mod borrow;
pub mod expand_rc;
pub mod inc_dec;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod can;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]

View file

@ -100,6 +100,8 @@ mod test_reporting {
home,
ident_ids: &mut ident_ids,
ptr_bytes: 8,
update_mode_counter: 0,
call_specialization_counter: 0,
};
let _mono_expr = Stmt::new(
&mut mono_env,

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]

View file

@ -147,6 +147,17 @@ fn list_append() {
);
}
#[test]
fn list_drop() {
assert_evals_to!(
"List.drop [1,2,3] 2",
RocList::from_slice(&[3]),
RocList<i64>
);
assert_evals_to!("List.drop [] 1", RocList::from_slice(&[]), RocList<i64>);
assert_evals_to!("List.drop [1,2] 5", RocList::from_slice(&[]), RocList<i64>);
}
#[test]
fn list_append_to_empty_list() {
assert_evals_to!("List.append [] 3", RocList::from_slice(&[3]), RocList<i64>);

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
// we actually want to compare against the literal float bits

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod builtin_aliases;

View file

@ -1,4 +1,4 @@
#![warn(clippy::all, clippy::dbg_macro)]
#![warn(clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]