Merge branch 'trunk' into thread-morphic

This commit is contained in:
Richard Feldman 2021-06-02 21:45:13 -04:00 committed by GitHub
commit 60ae15c7ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 61 deletions

View file

@ -8,6 +8,7 @@ use crate::llvm::convert::{
basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int, basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int,
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::module::Linkage; use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum}; use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum};
@ -94,6 +95,14 @@ impl<'ctx> PointerToRefcount<'ctx> {
Self::from_ptr_to_data(env, data_ptr) Self::from_ptr_to_data(env, data_ptr)
} }
pub fn is_1<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> {
let current = self.get_refcount(env);
let one = refcount_1(env.context, env.ptr_bytes);
env.builder
.build_int_compare(IntPredicate::EQ, current, one, "is_one")
}
pub fn get_refcount<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> { pub fn get_refcount<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> {
env.builder env.builder
.build_load(self.value, "get_refcount") .build_load(self.value, "get_refcount")
@ -724,7 +733,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
Layout::RecursivePointer => match when_recursive { Layout::RecursivePointer => match when_recursive {
WhenRecursive::Unreachable => { WhenRecursive::Unreachable => {
unreachable!("recursion pointers should never be hashed directly") unreachable!("recursion pointers cannot be in/decremented directly")
} }
WhenRecursive::Loop(union_layout) => { WhenRecursive::Loop(union_layout) => {
let layout = Layout::Union(*union_layout); let layout = Layout::Union(*union_layout);
@ -1249,7 +1258,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
mode: Mode, mode: Mode,
when_recursive: &WhenRecursive<'a>, when_recursive: &WhenRecursive<'a>,
tags: &[&[Layout<'a>]], tags: &'a [&'a [roc_mono::layout::Layout<'a>]],
fn_val: FunctionValue<'ctx>, fn_val: FunctionValue<'ctx>,
is_nullable: bool, is_nullable: bool,
) { ) {
@ -1258,8 +1267,6 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
let context = &env.context; let context = &env.context;
let builder = env.builder; let builder = env.builder;
let pick = |a, b| if let Mode::Inc = mode { a } else { b };
// Add a basic block for the entry point // Add a basic block for the entry point
let entry = context.append_basic_block(fn_val, "entry"); let entry = context.append_basic_block(fn_val, "entry");
@ -1281,6 +1288,92 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
debug_assert!(arg_val.is_pointer_value()); debug_assert!(arg_val.is_pointer_value());
let value_ptr = arg_val.into_pointer_value(); let value_ptr = arg_val.into_pointer_value();
// to increment/decrement the cons-cell itself
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
let call_mode = mode_to_call_mode(fn_val, mode);
let should_recurse_block = env.context.append_basic_block(parent, "should_recurse");
let ctx = env.context;
if is_nullable {
let is_null = env.builder.build_is_null(value_ptr, "is_null");
let then_block = ctx.append_basic_block(parent, "then");
env.builder
.build_conditional_branch(is_null, then_block, should_recurse_block);
{
env.builder.position_at_end(then_block);
env.builder.build_return(None);
}
} else {
env.builder.build_unconditional_branch(should_recurse_block);
}
env.builder.position_at_end(should_recurse_block);
match mode {
Mode::Inc => {
// inc is cheap; we never recurse
refcount_ptr.modify(call_mode, &layout, env);
env.builder.build_return(None);
}
Mode::Dec => {
let do_recurse_block = env.context.append_basic_block(parent, "do_recurse");
let no_recurse_block = env.context.append_basic_block(parent, "no_recurse");
builder.build_conditional_branch(
refcount_ptr.is_1(env),
do_recurse_block,
no_recurse_block,
);
{
env.builder.position_at_end(no_recurse_block);
refcount_ptr.modify(call_mode, &layout, env);
env.builder.build_return(None);
}
{
env.builder.position_at_end(do_recurse_block);
build_rec_union_recursive_decrement(
env,
layout_ids,
when_recursive,
parent,
fn_val,
layout,
tags,
value_ptr,
refcount_ptr,
do_recurse_block,
)
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: &WhenRecursive<'a>,
parent: FunctionValue<'ctx>,
decrement_fn: FunctionValue<'ctx>,
layout: Layout<'a>,
tags: &[&[Layout<'a>]],
value_ptr: PointerValue<'ctx>,
refcount_ptr: PointerToRefcount<'ctx>,
match_block: BasicBlock<'ctx>,
) {
let mode = Mode::Dec;
let call_mode = mode_to_call_mode(decrement_fn, mode);
let builder = env.builder;
// branches that are not/don't contain anything refcounted // branches that are not/don't contain anything refcounted
// if there is only one branch, we don't need to switch // if there is only one branch, we don't need to switch
let switch_needed: bool = (|| { let switch_needed: bool = (|| {
@ -1296,28 +1389,6 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
false false
})(); })();
// to increment/decrement the cons-cell itself
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
let call_mode = mode_to_call_mode(fn_val, mode);
let ctx = env.context;
let cont_block = ctx.append_basic_block(parent, "cont");
if is_nullable {
let is_null = env.builder.build_is_null(value_ptr, "is_null");
let then_block = ctx.append_basic_block(parent, "then");
env.builder
.build_conditional_branch(is_null, then_block, cont_block);
{
env.builder.position_at_end(then_block);
env.builder.build_return(None);
}
} else {
env.builder.build_unconditional_branch(cont_block);
}
// next, make a jump table for all possible values of the tag_id // next, make a jump table for all possible values of the tag_id
let mut cases = Vec::with_capacity_in(tags.len(), env.arena); let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
@ -1330,9 +1401,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
continue; continue;
} }
let block = env let block = env.context.append_basic_block(parent, "tag_id_decrement");
.context
.append_basic_block(parent, pick("tag_id_increment", "tag_id_decrement"));
env.builder.position_at_end(block); env.builder.position_at_end(block);
@ -1382,10 +1451,9 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer") .build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
.unwrap(); .unwrap();
let field = env.builder.build_load( let field = env
elem_pointer, .builder
pick("increment_struct_field", "decrement_struct_field"), .build_load(elem_pointer, "decrement_struct_field");
);
deferred_nonrec.push((field, field_layout)); deferred_nonrec.push((field, field_layout));
} }
@ -1404,7 +1472,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
env, env,
parent, parent,
layout_ids, layout_ids,
mode.to_call_mode(fn_val), mode.to_call_mode(decrement_fn),
when_recursive, when_recursive,
field, field,
field_layout, field_layout,
@ -1413,7 +1481,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
for ptr in deferred_rec { for ptr in deferred_rec {
// recursively decrement the field // recursively decrement the field
let call = call_help(env, fn_val, mode.to_call_mode(fn_val), ptr); let call = call_help(env, decrement_fn, mode.to_call_mode(decrement_fn), ptr);
call.set_tail_call(true); call.set_tail_call(true);
} }
@ -1426,9 +1494,9 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
)); ));
} }
cases.reverse(); env.builder.position_at_end(match_block);
env.builder.position_at_end(cont_block); cases.reverse();
if cases.len() == 1 && !switch_needed { if cases.len() == 1 && !switch_needed {
// there is only one tag in total; we don't need a switch // there is only one tag in total; we don't need a switch
@ -1441,9 +1509,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
// read the tag_id // read the tag_id
let current_tag_id = rec_union_read_tag(env, value_ptr); let current_tag_id = rec_union_read_tag(env, value_ptr);
let merge_block = env let merge_block = env.context.append_basic_block(parent, "decrement_merge");
.context
.append_basic_block(parent, pick("increment_merge", "decrement_merge"));
// switch on it // switch on it
env.builder env.builder

View file

@ -2025,7 +2025,7 @@ fn update<'a>(
} }
MadeSpecializations { MadeSpecializations {
module_id, module_id,
mut ident_ids, ident_ids,
subs, subs,
procedures, procedures,
external_specializations_requested, external_specializations_requested,
@ -2048,6 +2048,8 @@ fn update<'a>(
&& state.dependencies.solved_all() && state.dependencies.solved_all()
&& state.goal_phase == Phase::MakeSpecializations && state.goal_phase == Phase::MakeSpecializations
{ {
Proc::insert_refcount_operations(arena, &mut state.procedures);
// display the mono IR of the module, for debug purposes // display the mono IR of the module, for debug purposes
if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS { if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS {
let procs_string = state let procs_string = state
@ -2061,14 +2063,14 @@ fn update<'a>(
println!("{}", result); println!("{}", result);
} }
Proc::insert_refcount_operations(arena, &mut state.procedures); // This is not safe with the new non-recursive RC updates that we do for tag unions
//
Proc::optimize_refcount_operations( // Proc::optimize_refcount_operations(
arena, // arena,
module_id, // module_id,
&mut ident_ids, // &mut ident_ids,
&mut state.procedures, // &mut state.procedures,
); // );
state.constrained_ident_ids.insert(module_id, ident_ids); state.constrained_ident_ids.insert(module_id, ident_ids);

View file

@ -1113,7 +1113,6 @@ fn layout_from_flat_type<'a>(
// That means none of the optimizations for enums or single tag tag unions apply // That means none of the optimizations for enums or single tag tag unions apply
let rec_var = subs.get_root_key_without_compacting(rec_var); let rec_var = subs.get_root_key_without_compacting(rec_var);
env.insert_seen(rec_var);
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
// VERY IMPORTANT: sort the tags // VERY IMPORTANT: sort the tags
@ -1131,6 +1130,7 @@ fn layout_from_flat_type<'a>(
} }
} }
env.insert_seen(rec_var);
for (index, (_name, variables)) in tags_vec.into_iter().enumerate() { for (index, (_name, variables)) in tags_vec.into_iter().enumerate() {
if matches!(nullable, Some(i) if i == index as i64) { if matches!(nullable, Some(i) if i == index as i64) {
// don't add the // don't add the
@ -1163,6 +1163,7 @@ fn layout_from_flat_type<'a>(
tag_layouts.push(tag_layout.into_bump_slice()); tag_layouts.push(tag_layout.into_bump_slice());
} }
env.remove_seen(rec_var);
let union_layout = if let Some(tag_id) = nullable { let union_layout = if let Some(tag_id) = nullable {
match tag_layouts.into_bump_slice() { match tag_layouts.into_bump_slice() {
@ -1186,8 +1187,6 @@ fn layout_from_flat_type<'a>(
UnionLayout::Recursive(tag_layouts.into_bump_slice()) UnionLayout::Recursive(tag_layouts.into_bump_slice())
}; };
env.remove_seen(rec_var);
Ok(Layout::Union(union_layout)) Ok(Layout::Union(union_layout))
} }
EmptyTagUnion => { EmptyTagUnion => {
@ -1422,10 +1421,6 @@ pub fn union_sorted_tags_help<'a>(
seen: MutSet::default(), seen: MutSet::default(),
}; };
if let Some(rec_var) = opt_rec_var {
env.insert_seen(rec_var);
}
match tags_vec.len() { match tags_vec.len() {
0 => { 0 => {
// trying to instantiate a type with no values // trying to instantiate a type with no values

View file

@ -6,5 +6,6 @@ procedure Test.0 ():
let Test.5 = 3.14f64; let Test.5 = 3.14f64;
let Test.3 = Struct {Test.4, Test.5}; let Test.3 = Struct {Test.4, Test.5};
let Test.1 = Index 0 Test.3; let Test.1 = Index 0 Test.3;
decref Test.3; inc Test.1;
dec Test.3;
ret Test.1; ret Test.1;

View file

@ -12,10 +12,11 @@ procedure Test.0 ():
let Test.17 = lowlevel Eq Test.15 Test.16; let Test.17 = lowlevel Eq Test.15 Test.16;
if Test.17 then if Test.17 then
let Test.11 = Index 1 Test.2; let Test.11 = Index 1 Test.2;
inc Test.11;
dec Test.2;
let Test.12 = 0i64; let Test.12 = 0i64;
let Test.13 = Index 0 Test.11; let Test.13 = Index 0 Test.11;
dec Test.11; dec Test.11;
decref Test.2;
let Test.14 = lowlevel Eq Test.12 Test.13; let Test.14 = lowlevel Eq Test.12 Test.13;
if Test.14 then if Test.14 then
let Test.7 = 1i64; let Test.7 = 1i64;
@ -24,5 +25,6 @@ procedure Test.0 ():
let Test.9 = 0i64; let Test.9 = 0i64;
ret Test.9; ret Test.9;
else else
dec Test.2;
let Test.10 = 0i64; let Test.10 = 0i64;
ret Test.10; ret Test.10;

View file

@ -1,5 +1,5 @@
The editor is a work in progress, only a limited subset of Roc expressions are currently supported. New features are added every week! The editor is a work in progress, only a limited subset of Roc expressions are currently supported.
Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete and refactoring. Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete and refactoring.
@ -28,4 +28,4 @@ We thank the following open source projects in particular for inspiring us when
- [learn-wgpu](https://github.com/sotrh/learn-wgpu) - [learn-wgpu](https://github.com/sotrh/learn-wgpu)
- [rgx](https://github.com/cloudhead/rgx) - [rgx](https://github.com/cloudhead/rgx)
- [elm-editor](https://github.com/jxxcarlson/elm-editor) - [elm-editor](https://github.com/jxxcarlson/elm-editor)
- [iced](https://github.com/hecrj/iced) - [iced](https://github.com/hecrj/iced)

View file

@ -41,7 +41,11 @@ Nice collection of research on innovative editors, [link](https://futureofcoding
* [Hest](https://ivanish.ca/hest-time-travel/) tool for making highly interactive simulations. * [Hest](https://ivanish.ca/hest-time-travel/) tool for making highly interactive simulations.
* Say you have a failing test that used to work, it would be very valuable to see all code that was changed that was used only by that test. * Say you have a failing test that used to work, it would be very valuable to see all code that was changed that was used only by that test.
e.g. you have a test `calculate_sum_test` that only uses the function `add`, when the test fails you should be able to see a diff showing only what changed for the function `add`. It would also be great to have a diff of [expression values](https://homepages.cwi.nl/~storm/livelit/images/bret.png) Bret Victor style. An ambitious project would be to suggest or automatically try fixes based on these diffs. e.g. you have a test `calculate_sum_test` that only uses the function `add`, when the test fails you should be able to see a diff showing only what changed for the function `add`. It would also be great to have a diff of [expression values](https://homepages.cwi.nl/~storm/livelit/images/bret.png) Bret Victor style. An ambitious project would be to suggest or automatically try fixes based on these diffs.
* I think it could be possible to create a minimal reproduction of a program / block of code / code used by a single test. So for a failing unit test I would expect it to extract imports, the platform, types and functions that are necessary to run only that unit test and put them in a standalone roc project. This would be useful for sharing bugs with library+application authors and colleagues, for profiling or debugging with all "clutter" removed. * I think it could be possible to create a minimal reproduction of a program / block of code / code used by a single test. So for a failing unit test I would expect it to extract imports, the platform, types and functions that are necessary to run only that unit test and put them in a standalone roc project. This would be useful for sharing bugs with library+application authors and colleagues, for profiling or debugging with all "clutter" removed.
### Cool regular editors
* [Helix](https://github.com/helix-editor/helix) modal (terminal, for now) editor in rust. Good UX.
### Structured Editing ### Structured Editing