Merge pull request #1432 from rtfeldman/morphic-lowlevel

Morphic lowlevel
This commit is contained in:
Richard Feldman 2021-06-22 23:46:51 -04:00 committed by GitHub
commit 4a5df543a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 44 deletions

View file

@ -1009,7 +1009,25 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
return output; return output;
} }
// input: RocList, pub fn listSetInPlace(
bytes: ?[*]u8,
length: usize,
alignment: u32,
index: usize,
element: Opaque,
element_width: usize,
dec: Dec,
) callconv(.C) ?[*]u8 {
// INVARIANT: bounds checking happens on the roc side
//
// at the time of writing, the function is implemented roughly as
// `if inBounds then LowLevelListGet input index item else input`
// so we don't do a bounds check here. Hence, the list is also non-empty,
// because inserting into an empty list is always out of bounds
return listSetInPlaceHelp(bytes, length, alignment, index, element, element_width, dec);
}
pub fn listSet( pub fn listSet(
bytes: ?[*]u8, bytes: ?[*]u8,
length: usize, length: usize,
@ -1028,23 +1046,34 @@ pub fn listSet(
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes)); const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
if ((ptr - 1)[0] == utils.REFCOUNT_ONE) { if ((ptr - 1)[0] == utils.REFCOUNT_ONE) {
return listSetInPlaceHelp(bytes, length, alignment, index, element, element_width, dec);
// the element we will replace
var element_at_index = (bytes orelse undefined) + (index * element_width);
// decrement its refcount
dec(element_at_index);
// copy in the new element
@memcpy(element_at_index, element orelse undefined, element_width);
return bytes;
} else { } else {
return listSetClone(bytes, length, alignment, index, element, element_width, dec); return listSetImmutable(bytes, length, alignment, index, element, element_width, dec);
} }
} }
inline fn listSetClone( inline fn listSetInPlaceHelp(
bytes: ?[*]u8,
length: usize,
alignment: u32,
index: usize,
element: Opaque,
element_width: usize,
dec: Dec,
) ?[*]u8 {
// the element we will replace
var element_at_index = (bytes orelse undefined) + (index * element_width);
// decrement its refcount
dec(element_at_index);
// copy in the new element
@memcpy(element_at_index, element orelse undefined, element_width);
return bytes;
}
inline fn listSetImmutable(
old_bytes: ?[*]u8, old_bytes: ?[*]u8,
length: usize, length: usize,
alignment: u32, alignment: u32,
@ -1053,8 +1082,6 @@ inline fn listSetClone(
element_width: usize, element_width: usize,
dec: Dec, dec: Dec,
) ?[*]u8 { ) ?[*]u8 {
@setCold(true);
const data_bytes = length * element_width; const data_bytes = length * element_width;
var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); var new_bytes = utils.allocateWithRefcount(data_bytes, alignment);

View file

@ -30,6 +30,7 @@ comptime {
exportListFn(list.listConcat, "concat"); exportListFn(list.listConcat, "concat");
exportListFn(list.listDrop, "drop"); exportListFn(list.listDrop, "drop");
exportListFn(list.listSet, "set"); exportListFn(list.listSet, "set");
exportListFn(list.listSetInPlace, "set_in_place");
exportListFn(list.listSwap, "swap"); exportListFn(list.listSwap, "swap");
} }

View file

@ -65,3 +65,4 @@ pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with"; pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
pub const LIST_CONCAT: &str = "roc_builtins.list.concat"; pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
pub const LIST_SET: &str = "roc_builtins.list.set"; pub const LIST_SET: &str = "roc_builtins.list.set";
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";

View file

@ -42,7 +42,9 @@ use inkwell::values::{
}; };
use inkwell::OptimizationLevel; use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::{CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions}; use morphic_lib::{
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
};
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_collections::all::{ImMap, MutMap, MutSet}; use roc_collections::all::{ImMap, MutMap, MutSet};
use roc_module::ident::TagName; use roc_module::ident::TagName;
@ -826,8 +828,21 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
) )
} }
CallType::LowLevel { op, update_mode: _ } => { CallType::LowLevel { op, update_mode } => {
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments) let bytes = update_mode.to_bytes();
let update_var = UpdateModeVar(&bytes);
let update_mode = func_spec_solutions.update_mode(update_var).ok();
run_low_level(
env,
layout_ids,
scope,
parent,
layout,
*op,
arguments,
update_mode,
)
} }
CallType::HigherOrderLowLevel { CallType::HigherOrderLowLevel {
@ -4190,6 +4205,7 @@ fn run_low_level<'a, 'ctx, 'env>(
layout: &Layout<'a>, layout: &Layout<'a>,
op: LowLevel, op: LowLevel,
args: &[Symbol], args: &[Symbol],
update_mode: Option<UpdateMode>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use LowLevel::*; use LowLevel::*;
@ -4648,27 +4664,6 @@ fn run_low_level<'a, 'ctx, 'env>(
wrapper_struct, wrapper_struct,
) )
} }
ListSetInPlace => {
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let (index, _) = load_symbol_and_layout(scope, &args[1]);
let (element, _) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => {
// no elements, so nothing to remove
empty_list(env)
}
Layout::Builtin(Builtin::List(element_layout)) => list_set(
env,
layout_ids,
list,
index.into_int_value(),
element,
element_layout,
),
_ => unreachable!("invalid dict layout"),
}
}
ListSet => { ListSet => {
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let (index, _) = load_symbol_and_layout(scope, &args[1]); let (index, _) = load_symbol_and_layout(scope, &args[1]);
@ -4686,6 +4681,7 @@ fn run_low_level<'a, 'ctx, 'env>(
index.into_int_value(), index.into_int_value(),
element, element,
element_layout, element_layout,
update_mode.unwrap(),
), ),
_ => unreachable!("invalid dict layout"), _ => unreachable!("invalid dict layout"),
} }

View file

@ -13,6 +13,7 @@ use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, PointerType}; use inkwell::types::{BasicType, BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode;
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_mono::layout::{Builtin, Layout, LayoutIds}; use roc_mono::layout::{Builtin, Layout, LayoutIds};
@ -350,6 +351,7 @@ pub fn list_set<'a, 'ctx, 'env>(
index: IntValue<'ctx>, index: IntValue<'ctx>,
element: BasicValueEnum<'ctx>, element: BasicValueEnum<'ctx>,
element_layout: &'a Layout<'a>, element_layout: &'a Layout<'a>,
update_mode: UpdateMode,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
@ -359,6 +361,11 @@ pub fn list_set<'a, 'ctx, 'env>(
env.context.i8_type().ptr_type(AddressSpace::Generic), env.context.i8_type().ptr_type(AddressSpace::Generic),
); );
let symbol = match update_mode {
UpdateMode::InPlace => bitcode::LIST_SET_IN_PLACE,
UpdateMode::Immutable => bitcode::LIST_SET,
};
let new_bytes = call_bitcode_fn( let new_bytes = call_bitcode_fn(
env, env,
&[ &[
@ -370,7 +377,7 @@ pub fn list_set<'a, 'ctx, 'env>(
layout_width(env, element_layout), layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(),
], ],
&bitcode::LIST_SET, &symbol,
); );
store_list(env, new_bytes.into_pointer_value(), length) store_list(env, new_bytes.into_pointer_value(), length)

View file

@ -18,7 +18,6 @@ pub enum LowLevel {
ListLen, ListLen,
ListGetUnsafe, ListGetUnsafe,
ListSet, ListSet,
ListSetInPlace,
ListSingle, ListSingle,
ListRepeat, ListRepeat,
ListReverse, ListReverse,
@ -125,7 +124,6 @@ impl LowLevel {
| ListLen | ListLen
| ListGetUnsafe | ListGetUnsafe
| ListSet | ListSet
| ListSetInPlace
| ListDrop | ListDrop
| ListSingle | ListSingle
| ListRepeat | ListRepeat

View file

@ -760,7 +760,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
match op { match op {
ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]), ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]),
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListConcat => arena.alloc_slice_copy(&[owned, owned]), ListConcat => arena.alloc_slice_copy(&[owned, owned]),
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),