Merge pull request #5335 from JTeeuwissen/drop_specialization

Drop specialization
This commit is contained in:
Folkert de Vries 2023-04-30 21:14:06 +02:00 committed by GitHub
commit 5183bf0196
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 2248 additions and 886 deletions

View file

@ -30,18 +30,19 @@ ROC_WORKSPACE_DIR = { value = "", relative = true }
# Debug flags. Keep this up-to-date with compiler/debug_flags/src/lib.rs.
# Set = "1" to turn a debug flag on.
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
ROC_PRINT_UNIFICATIONS = "0"
ROC_PRINT_UNDERIVABLE = "0"
ROC_TRACE_COMPACTION = "0"
ROC_PRINT_UNIFICATIONS_DERIVED = "0"
ROC_PRINT_MISMATCHES = "0"
ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
ROC_CHECK_MONO_IR = "0"
ROC_PRINT_IR_AFTER_SPECIALIZATION = "0"
ROC_PRINT_IR_AFTER_RESET_REUSE = "0"
ROC_PRINT_IR_AFTER_REFCOUNT = "0"
ROC_PRINT_RUNTIME_ERROR_GEN = "0"
ROC_DEBUG_ALIAS_ANALYSIS = "0"
ROC_PRINT_LLVM_FN_VERIFICATION = "0"
ROC_PRINT_LOAD_LOG = "0"
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
ROC_PRINT_UNIFICATIONS = "0"
ROC_PRINT_UNDERIVABLE = "0"
ROC_TRACE_COMPACTION = "0"
ROC_PRINT_UNIFICATIONS_DERIVED = "0"
ROC_PRINT_MISMATCHES = "0"
ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
ROC_CHECK_MONO_IR = "0"
ROC_PRINT_IR_AFTER_SPECIALIZATION = "0"
ROC_PRINT_IR_AFTER_RESET_REUSE = "0"
ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION = "0"
ROC_PRINT_IR_AFTER_REFCOUNT = "0"
ROC_PRINT_RUNTIME_ERROR_GEN = "0"
ROC_DEBUG_ALIAS_ANALYSIS = "0"
ROC_PRINT_LLVM_FN_VERIFICATION = "0"
ROC_PRINT_LOAD_LOG = "0"

View file

@ -974,7 +974,7 @@ test "listConcat: non-unique with unique overlapping" {
var bytes: [*]u8 = @ptrCast([*]u8, nonUnique.bytes);
const ptr_width = @sizeOf(usize);
const refcount_ptr = @ptrCast([*]isize, @alignCast(ptr_width, bytes) - ptr_width);
utils.increfC(&refcount_ptr[0], 1);
utils.increfRcPtrC(&refcount_ptr[0], 1);
defer nonUnique.decref(@sizeOf(u8)); // listConcat will dec the other refcount
var unique = RocList.fromSlice(u8, ([_]u8{ 2, 3, 4 })[0..]);

View file

@ -173,8 +173,11 @@ comptime {
// Utils
comptime {
exportUtilsFn(utils.test_panic, "test_panic");
exportUtilsFn(utils.increfC, "incref");
exportUtilsFn(utils.decrefC, "decref");
exportUtilsFn(utils.increfRcPtrC, "incref_rc_ptr");
exportUtilsFn(utils.decrefRcPtrC, "decref_rc_ptr");
exportUtilsFn(utils.increfDataPtrC, "incref_data_ptr");
exportUtilsFn(utils.decrefDataPtrC, "decref_data_ptr");
exportUtilsFn(utils.isUnique, "is_unique");
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");

View file

@ -162,7 +162,7 @@ pub const RocStr = extern struct {
const ref_ptr = self.getRefcountPtr();
if (ref_ptr != null) {
const isizes: [*]isize = @ptrCast([*]isize, @alignCast(@alignOf(isize), ref_ptr));
utils.increfC(@ptrCast(*isize, isizes - 1), @intCast(isize, n));
utils.increfRcPtrC(@ptrCast(*isize, isizes - 1), @intCast(isize, n));
}
}
}

View file

@ -147,7 +147,7 @@ const Refcount = enum {
const RC_TYPE = Refcount.normal;
pub fn increfC(ptr_to_refcount: *isize, amount: isize) callconv(.C) void {
pub fn increfRcPtrC(ptr_to_refcount: *isize, amount: isize) callconv(.C) void {
if (RC_TYPE == Refcount.none) return;
// Ensure that the refcount is not whole program lifetime.
if (ptr_to_refcount.* != REFCOUNT_MAX_ISIZE) {
@ -165,7 +165,7 @@ pub fn increfC(ptr_to_refcount: *isize, amount: isize) callconv(.C) void {
}
}
pub fn decrefC(
pub fn decrefRcPtrC(
bytes_or_null: ?[*]isize,
alignment: u32,
) callconv(.C) void {
@ -188,6 +188,36 @@ pub fn decrefCheckNullC(
}
}
pub fn decrefDataPtrC(
bytes_or_null: ?[*]isize,
alignment: u32,
) callconv(.C) void {
var bytes = bytes_or_null orelse return;
const ptr = @ptrToInt(bytes);
const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11;
const masked_ptr = ptr & ~tag_mask;
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
return decrefRcPtrC(isizes - 1, alignment);
}
pub fn increfDataPtrC(
bytes_or_null: ?[*]isize,
inc_amount: isize,
) callconv(.C) void {
var bytes = bytes_or_null orelse return;
const ptr = @ptrToInt(bytes);
const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11;
const masked_ptr = ptr & ~tag_mask;
const isizes: *isize = @intToPtr(*isize, masked_ptr - @sizeOf(usize));
return increfRcPtrC(isizes, inc_amount);
}
pub fn decref(
bytes_or_null: ?[*]u8,
data_bytes: usize,
@ -231,6 +261,22 @@ inline fn decref_ptr_to_refcount(
}
}
pub fn isUnique(
bytes_or_null: ?[*]u8,
) callconv(.C) bool {
var bytes = bytes_or_null orelse return true;
const ptr = @ptrToInt(bytes);
const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11;
const masked_ptr = ptr & ~tag_mask;
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
const refcount = (isizes - 1)[0];
return refcount == REFCOUNT_ONE_ISIZE;
}
// We follow roughly the [fbvector](https://github.com/facebook/folly/blob/main/folly/docs/FBVector.md) when it comes to growing a RocList.
// Here is [their growth strategy](https://github.com/facebook/folly/blob/3e0525988fd444201b19b76b390a5927c15cb697/folly/FBVector.h#L1128) for push_back:
//
@ -347,13 +393,13 @@ pub const UpdateMode = enum(u8) {
test "increfC, refcounted data" {
var mock_rc: isize = REFCOUNT_ONE_ISIZE + 17;
var ptr_to_refcount: *isize = &mock_rc;
increfC(ptr_to_refcount, 2);
increfRcPtrC(ptr_to_refcount, 2);
try std.testing.expectEqual(mock_rc, REFCOUNT_ONE_ISIZE + 19);
}
test "increfC, static data" {
var mock_rc: isize = REFCOUNT_MAX_ISIZE;
var ptr_to_refcount: *isize = &mock_rc;
increfC(ptr_to_refcount, 2);
increfRcPtrC(ptr_to_refcount, 2);
try std.testing.expectEqual(mock_rc, REFCOUNT_MAX_ISIZE);
}

View file

@ -376,8 +376,11 @@ pub const DEC_MUL_SATURATED: &str = "roc_builtins.dec.mul_saturated";
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
pub const UTILS_ALLOCATE_WITH_REFCOUNT: &str = "roc_builtins.utils.allocate_with_refcount";
pub const UTILS_INCREF: &str = "roc_builtins.utils.incref";
pub const UTILS_DECREF: &str = "roc_builtins.utils.decref";
pub const UTILS_INCREF_RC_PTR: &str = "roc_builtins.utils.incref_rc_ptr";
pub const UTILS_DECREF_RC_PTR: &str = "roc_builtins.utils.decref_rc_ptr";
pub const UTILS_INCREF_DATA_PTR: &str = "roc_builtins.utils.incref_data_ptr";
pub const UTILS_DECREF_DATA_PTR: &str = "roc_builtins.utils.decref_data_ptr";
pub const UTILS_IS_UNIQUE: &str = "roc_builtins.utils.is_unique";
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
pub const UTILS_EXPECT_FAILED_START_SHARED_BUFFER: &str =

View file

@ -86,8 +86,11 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
LowLevel::Hash => unimplemented!(),
LowLevel::PtrCast => unimplemented!(),
LowLevel::PtrWrite => unimplemented!(),
LowLevel::RefCountInc => unimplemented!(),
LowLevel::RefCountDec => unimplemented!(),
LowLevel::RefCountIncRcPtr => unimplemented!(),
LowLevel::RefCountDecRcPtr=> unimplemented!(),
LowLevel::RefCountIncDataPtr => unimplemented!(),
LowLevel::RefCountDecDataPtr=> unimplemented!(),
LowLevel::RefCountIsUnique => unimplemented!(),
// these are not implemented, not sure why
LowLevel::StrFromInt => unimplemented!(),

View file

@ -133,6 +133,10 @@ flags! {
/// instructions.
ROC_PRINT_IR_AFTER_REFCOUNT
/// Writes a pretty-printed mono IR to stderr after performing dropspecialization.
/// Which inlines drop functions to remove pairs of alloc/dealloc instructions of its children.
ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION
/// Prints debug information during the alias analysis pass.
ROC_DEBUG_ALIAS_ANALYSIS

View file

@ -2083,11 +2083,10 @@ impl<
}
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src);
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
self.storage_manager
.ensure_symbol_on_stack(&mut self.buf, src);
let (offset, _) = self.storage_manager.stack_offset_and_size(src);
ASM::add_reg64_reg64_imm32(&mut self.buf, dst_reg, CC::BASE_PTR_REG, offset);
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, src_reg)
}
fn create_empty_array(&mut self, sym: &Symbol) {

View file

@ -1111,16 +1111,37 @@ trait Backend<'a> {
self.build_ptr_write(*sym, args[0], args[1], element_layout);
}
LowLevel::RefCountDec => self.build_fn_call(
LowLevel::RefCountDecRcPtr => self.build_fn_call(
sym,
bitcode::UTILS_DECREF.to_string(),
bitcode::UTILS_DECREF_RC_PTR.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::RefCountInc => self.build_fn_call(
LowLevel::RefCountIncRcPtr => self.build_fn_call(
sym,
bitcode::UTILS_INCREF.to_string(),
bitcode::UTILS_INCREF_RC_PTR.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::RefCountDecDataPtr => self.build_fn_call(
sym,
bitcode::UTILS_DECREF_DATA_PTR.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::RefCountIncDataPtr => self.build_fn_call(
sym,
bitcode::UTILS_INCREF_DATA_PTR.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::RefCountIsUnique => self.build_fn_call(
sym,
bitcode::UTILS_IS_UNIQUE.to_string(),
args,
arg_layouts,
ret_layout,

View file

@ -3,7 +3,6 @@ use crate::llvm::build::{
allocate_with_refcount_help, cast_basic_basic, Env, RocFunctionCall, Scope,
};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::increment_refcount_layout;
use inkwell::builder::Builder;
use inkwell::types::{BasicType, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
@ -125,7 +124,6 @@ pub(crate) fn list_with_capacity<'a, 'ctx>(
pub(crate) fn list_get_unsafe<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &mut STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>,
element_layout: InLayout<'a>,
elem_index: IntValue<'ctx>,
wrapper_struct: StructValue<'ctx>,
@ -148,17 +146,13 @@ pub(crate) fn list_get_unsafe<'a, 'ctx>(
)
};
let result = load_roc_value(
load_roc_value(
env,
layout_interner,
element_layout,
elem_ptr,
"list_get_load_element",
);
increment_refcount_layout(env, layout_interner, layout_ids, 1, result, element_layout);
result
)
}
/// List.reserve : List elem, Nat -> List elem

View file

@ -25,7 +25,7 @@ use crate::llvm::{
},
build::{
complex_bitcast_check_size, create_entry_block_alloca, function_value_by_func_spec,
load_roc_value, roc_function_call, BuilderExt, RocReturn,
load_roc_value, roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn,
},
build_list::{
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
@ -42,6 +42,7 @@ use crate::llvm::{
LLVM_LOG, LLVM_MUL_WITH_OVERFLOW, LLVM_POW, LLVM_ROUND, LLVM_SIN, LLVM_SQRT,
LLVM_SUB_SATURATED, LLVM_SUB_WITH_OVERFLOW,
},
refcounting::PointerToRefcount,
};
use super::{build::throw_internal_exception, convert::zig_with_overflow_roc_dec};
@ -807,7 +808,6 @@ pub(crate) fn run_low_level<'a, 'ctx>(
list_get_unsafe(
env,
layout_interner,
layout_ids,
list_element_layout!(layout_interner, list_layout),
element_index.into_int_value(),
wrapper_struct.into_struct_value(),
@ -1247,10 +1247,34 @@ pub(crate) fn run_low_level<'a, 'ctx>(
unreachable!("The {:?} operation is turned into mono Expr", op)
}
PtrCast | PtrWrite | RefCountInc | RefCountDec => {
PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr => {
unreachable!("Not used in LLVM backend: {:?}", op);
}
RefCountIsUnique => {
arguments_with_layouts!((data_ptr, data_layout));
let ptr = env.builder.build_pointer_cast(
data_ptr.into_pointer_value(),
env.context.i8_type().ptr_type(AddressSpace::default()),
"cast_to_i8_ptr",
);
let value_ptr = match layout_interner.get(data_layout) {
Layout::Union(union_layout)
if union_layout.stores_tag_id_in_pointer(env.target_info) =>
{
tag_pointer_clear_tag_id(env, ptr)
}
_ => ptr,
};
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
BasicValueEnum::IntValue(refcount_ptr.is_1(env))
}
Unreachable => match RocReturn::from_layout(env, layout_interner, layout) {
RocReturn::Return => {
let basic_type = basic_type_from_layout(env, layout_interner, layout);

View file

@ -212,7 +212,7 @@ fn incref_pointer<'ctx>(
.into(),
amount.into(),
],
roc_builtins::bitcode::UTILS_INCREF,
roc_builtins::bitcode::UTILS_INCREF_RC_PTR,
);
}
@ -230,7 +230,7 @@ fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, al
.into(),
alignment.into(),
],
roc_builtins::bitcode::UTILS_DECREF,
roc_builtins::bitcode::UTILS_DECREF_RC_PTR,
);
}
@ -874,7 +874,14 @@ fn modify_refcount_boxed<'a, 'ctx>(
let basic_type = basic_type_from_layout(env, layout_interner, boxed_layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_box_help(env, layout_interner, mode, inner_layout, function_value);
modify_refcount_box_help(
env,
layout_interner,
layout_ids,
mode,
inner_layout,
function_value,
);
function_value
}
@ -889,6 +896,7 @@ fn modify_refcount_boxed<'a, 'ctx>(
fn modify_refcount_box_help<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &mut STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
inner_layout: InLayout<'a>,
fn_val: FunctionValue<'ctx>,
@ -912,10 +920,49 @@ fn modify_refcount_box_help<'a, 'ctx>(
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, boxed);
let call_mode = mode_to_call_mode(fn_val, mode);
let boxed_layout = layout_interner.insert(Layout::Boxed(inner_layout));
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
// this function returns void
builder.build_return(None);
match mode {
Mode::Inc => {
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
builder.build_return(None);
}
Mode::Dec => {
// if the box is unique, also decrement its inner value
let do_recurse_block = env.context.append_basic_block(fn_val, "do_recurse");
let no_recurse_block = env.context.append_basic_block(fn_val, "no_recurse");
builder.build_conditional_branch(
refcount_ptr.is_1(env),
do_recurse_block,
no_recurse_block,
);
{
env.builder.position_at_end(do_recurse_block);
let inner = load_roc_value(env, layout_interner, inner_layout, boxed, "inner");
modify_refcount_layout(
env,
layout_interner,
layout_ids,
call_mode,
inner,
inner_layout,
);
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
env.builder.build_return(None);
}
{
env.builder.position_at_end(no_recurse_block);
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
env.builder.build_return(None);
}
}
}
}
/// Build an increment or decrement function for a specific layout

View file

@ -1947,8 +1947,15 @@ impl<'a> LowLevelCall<'a> {
self.load_args(backend);
backend.code_builder.i32_eqz();
}
RefCountInc => self.load_args_and_call_zig(backend, bitcode::UTILS_INCREF),
RefCountDec => self.load_args_and_call_zig(backend, bitcode::UTILS_DECREF),
RefCountIncRcPtr => self.load_args_and_call_zig(backend, bitcode::UTILS_INCREF_RC_PTR),
RefCountDecRcPtr => self.load_args_and_call_zig(backend, bitcode::UTILS_DECREF_RC_PTR),
RefCountIncDataPtr => {
self.load_args_and_call_zig(backend, bitcode::UTILS_INCREF_DATA_PTR)
}
RefCountDecDataPtr => {
self.load_args_and_call_zig(backend, bitcode::UTILS_DECREF_DATA_PTR)
}
RefCountIsUnique => self.load_args_and_call_zig(backend, bitcode::UTILS_IS_UNIQUE),
PtrCast => {
let code_builder = &mut backend.code_builder;

View file

@ -19,8 +19,8 @@ use roc_constrain::module::constrain_module;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::{
ROC_CHECK_MONO_IR, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG,
ROC_CHECK_MONO_IR, ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION, ROC_PRINT_IR_AFTER_REFCOUNT,
ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG,
};
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
@ -30,7 +30,6 @@ use roc_module::symbol::{
IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds,
PackageQualified, Symbol,
};
use roc_mono::inc_dec;
use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
ProcLayout, Procs, ProcsBase, UpdateModeIds,
@ -40,6 +39,7 @@ use roc_mono::layout::{
GlobalLayoutInterner, LambdaName, Layout, LayoutCache, LayoutProblem, Niche, STLayoutInterner,
};
use roc_mono::reset_reuse;
use roc_mono::{drop_specialization, inc_dec};
use roc_packaging::cache::RocCacheDir;
use roc_parse::ast::{
self, CommentOrNewline, Defs, Expr, ExtractSpaces, Pattern, Spaced, StrLiteral, TypeAnnotation,
@ -3121,6 +3121,21 @@ fn update<'a>(
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
drop_specialization::specialize_drops(
arena,
&mut layout_interner,
module_id,
ident_ids,
state.target_info,
&mut state.procedures,
);
debug_print_ir!(
state,
&layout_interner,
ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION
);
// This is not safe with the new non-recursive RC updates that we do for tag unions
//
// Proc::optimize_refcount_operations(

View file

@ -116,8 +116,11 @@ pub enum LowLevel {
Hash,
PtrCast,
PtrWrite,
RefCountInc,
RefCountDec,
RefCountIncRcPtr,
RefCountDecRcPtr,
RefCountIncDataPtr,
RefCountDecDataPtr,
RefCountIsUnique,
BoxExpr,
UnboxExpr,
Unreachable,
@ -155,6 +158,7 @@ impl LowLevel {
/// Some wrapper functions can just be replaced by lowlevels in the backend for performance.
/// For example, Num.add should be an instruction, not a function call.
/// Variant names are chosen to help explain what to do when adding new lowlevels
#[derive(PartialEq, Eq)]
pub enum LowLevelWrapperType {
/// This wrapper function contains no logic and we can remove it in code gen
CanBeReplacedBy(LowLevel),
@ -221,8 +225,11 @@ macro_rules! map_symbol_to_lowlevel {
LowLevel::Hash => unimplemented!(),
LowLevel::PtrCast => unimplemented!(),
LowLevel::PtrWrite => unimplemented!(),
LowLevel::RefCountInc => unimplemented!(),
LowLevel::RefCountDec => unimplemented!(),
LowLevel::RefCountIncRcPtr => unimplemented!(),
LowLevel::RefCountDecRcPtr=> unimplemented!(),
LowLevel::RefCountIncDataPtr => unimplemented!(),
LowLevel::RefCountDecDataPtr=> unimplemented!(),
LowLevel::RefCountIsUnique => unimplemented!(),
// these are not implemented, not sure why
LowLevel::StrFromInt => unimplemented!(),

View file

@ -1031,7 +1031,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
unreachable!("These lowlevel operations are turned into mono Expr's")
}
PtrCast | PtrWrite | RefCountInc | RefCountDec => {
PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", op);
}
}

View file

@ -642,6 +642,7 @@ fn eq_boxed<'a>(
}
/// List equality
/// TODO, ListGetUnsafe no longer increments the refcount, so we can use it here.
/// We can't use `ListGetUnsafe` because it increments the refcount, and we don't want that.
/// Another way to dereference a heap pointer is to use `Expr::UnionAtIndex`.
/// To achieve this we use `PtrCast` to cast the element pointer to a "Box" layout.

View file

@ -1,4 +1,5 @@
use bumpalo::collections::vec::Vec;
use bumpalo::collections::CollectIn;
use bumpalo::Bump;
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
@ -40,6 +41,10 @@ impl HelperOp {
fn is_decref(&self) -> bool {
matches!(self, Self::DecRef(_))
}
fn is_dec(&self) -> bool {
matches!(self, Self::Dec)
}
}
#[derive(Debug)]
@ -80,7 +85,6 @@ pub struct CodeGenHelp<'a> {
home: ModuleId,
target_info: TargetInfo,
layout_isize: InLayout<'a>,
union_refcount: UnionLayout<'a>,
specializations: Vec<'a, Specialization<'a>>,
debug_recursion_depth: usize,
}
@ -89,15 +93,11 @@ impl<'a> CodeGenHelp<'a> {
pub fn new(arena: &'a Bump, target_info: TargetInfo, home: ModuleId) -> Self {
let layout_isize = Layout::isize(target_info);
// Refcount is a boxed isize. TODO: use the new Box layout when dev backends support it
let union_refcount = UnionLayout::NonNullableUnwrapped(arena.alloc([layout_isize]));
CodeGenHelp {
arena,
home,
target_info,
layout_isize,
union_refcount,
specializations: Vec::with_capacity_in(16, arena),
debug_recursion_depth: 0,
}
@ -538,46 +538,44 @@ impl<'a> CodeGenHelp<'a> {
fn union_tail_recursion_fields(
&self,
layout_interner: &STLayoutInterner<'a>,
union_in_layout: InLayout<'a>,
union: UnionLayout<'a>,
) -> (bool, Vec<'a, Option<usize>>) {
) -> Option<Vec<'a, Option<usize>>> {
use UnionLayout::*;
match union {
NonRecursive(_) => (false, bumpalo::vec![in self.arena]),
NonRecursive(_) => None,
Recursive(tags) => self.union_tail_recursion_fields_help(layout_interner, tags),
Recursive(tags) => self.union_tail_recursion_fields_help(union_in_layout, tags),
NonNullableUnwrapped(field_layouts) => {
self.union_tail_recursion_fields_help(layout_interner, &[field_layouts])
self.union_tail_recursion_fields_help(union_in_layout, &[field_layouts])
}
NullableWrapped {
other_tags: tags, ..
} => self.union_tail_recursion_fields_help(layout_interner, tags),
} => self.union_tail_recursion_fields_help(union_in_layout, tags),
NullableUnwrapped { other_fields, .. } => {
self.union_tail_recursion_fields_help(layout_interner, &[other_fields])
self.union_tail_recursion_fields_help(union_in_layout, &[other_fields])
}
}
}
fn union_tail_recursion_fields_help(
&self,
layout_interner: &STLayoutInterner<'a>,
in_layout: InLayout<'a>,
tags: &[&'a [InLayout<'a>]],
) -> (bool, Vec<'a, Option<usize>>) {
let mut can_use_tailrec = false;
let mut tailrec_indices = Vec::with_capacity_in(tags.len(), self.arena);
) -> Option<Vec<'a, Option<usize>>> {
let tailrec_indices = tags
.iter()
.map(|fields| fields.iter().position(|f| *f == in_layout))
.collect_in::<Vec<_>>(self.arena);
for fields in tags.iter() {
let found_index = fields
.iter()
.position(|f| matches!(layout_interner.get(*f), Layout::RecursivePointer(_)));
tailrec_indices.push(found_index);
can_use_tailrec |= found_index.is_some();
if tailrec_indices.iter().any(|i| i.is_some()) {
None
} else {
Some(tailrec_indices)
}
(can_use_tailrec, tailrec_indices)
}
}

View file

@ -186,6 +186,51 @@ pub fn refcount_generic<'a>(
}
}
fn if_unique<'a>(
root: &mut CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
value: Symbol,
when_unique: impl FnOnce(JoinPointId) -> Stmt<'a>,
when_done: Stmt<'a>,
) -> Stmt<'a> {
// joinpoint f =
// <when_done>
// in
// if is_unique <value> then
// <when_unique>(f)
// else
// jump f
let joinpoint = root.create_symbol(ident_ids, "is_unique_joinpoint");
let joinpoint = JoinPointId(joinpoint);
let is_unique = root.create_symbol(ident_ids, "is_unique");
let mut stmt = Stmt::if_then_else(
root.arena,
is_unique,
Layout::UNIT,
when_unique(joinpoint),
root.arena.alloc(Stmt::Jump(joinpoint, &[])),
);
stmt = Stmt::Join {
id: joinpoint,
parameters: &[],
body: root.arena.alloc(when_done),
remainder: root.arena.alloc(stmt),
};
let_lowlevel(
root.arena,
root.layout_isize,
is_unique,
LowLevel::RefCountIsUnique,
&[value],
root.arena.alloc(stmt),
)
}
pub fn refcount_reset_proc_body<'a>(
root: &mut CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
@ -291,24 +336,15 @@ pub fn refcount_reset_proc_body<'a>(
.unwrap();
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
// Zero
let zero = root.create_symbol(ident_ids, "zero");
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
// Null pointer with union layout
let null = root.create_symbol(ident_ids, "null");
let null_stmt =
|next| let_lowlevel(root.arena, root.layout_isize, null, PtrCast, &[zero], next);
let null_stmt = |next| Stmt::Let(null, Expr::NullPointer, layout, next);
decrement_stmt(root.arena.alloc(
//
zero_stmt(root.arena.alloc(
null_stmt(root.arena.alloc(
//
null_stmt(root.arena.alloc(
//
Stmt::Ret(null),
)),
Stmt::Ret(null),
)),
))
};
@ -348,12 +384,8 @@ pub fn refcount_reset_proc_body<'a>(
);
// Refcount value
let rc_expr = Expr::UnionAtIndex {
structure: rc_ptr,
tag_id: 0,
union_layout: root.union_refcount,
index: 0,
};
let rc_expr = Expr::ExprUnbox { symbol: rc_ptr };
let rc_stmt = Stmt::Let(
rc,
rc_expr,
@ -361,6 +393,9 @@ pub fn refcount_reset_proc_body<'a>(
root.arena.alloc(refcount_1_stmt),
);
// a Box never masks bits
let mask_lower_bits = false;
// Refcount pointer
let rc_ptr_stmt = {
rc_ptr_from_data_ptr_help(
@ -368,7 +403,7 @@ pub fn refcount_reset_proc_body<'a>(
ident_ids,
structure,
rc_ptr,
union_layout.stores_tag_id_in_pointer(root.target_info),
mask_lower_bits,
root.arena.alloc(rc_stmt),
addr,
recursion_ptr,
@ -403,39 +438,7 @@ pub fn refcount_resetref_proc_body<'a>(
let recursion_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
// Reset structure is unique. Return a pointer to the allocation.
let then_stmt = {
let alignment = root.create_symbol(ident_ids, "alignment");
let alignment_int = layout_interner
.get(layout)
.allocation_alignment_bytes(layout_interner, root.target_info);
let alignment_expr = Expr::Literal(Literal::Int((alignment_int as i128).to_ne_bytes()));
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
let alloc_addr_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::NumSubWrap,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: root.arena.alloc([addr, alignment]),
});
Stmt::Let(
alignment,
alignment_expr,
root.layout_isize,
root.arena.alloc(
//
Stmt::Let(
alloc_addr,
alloc_addr_expr,
root.layout_isize,
root.arena.alloc(
//
Stmt::Ret(alloc_addr),
),
),
),
)
};
let then_stmt = Stmt::Ret(addr);
// Reset structure is not unique. Decrement it and return a NULL pointer.
let else_stmt = {
@ -451,35 +454,26 @@ pub fn refcount_resetref_proc_body<'a>(
.unwrap();
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
// Zero
let zero = root.create_symbol(ident_ids, "zero");
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
// Null pointer with union layout
let null = root.create_symbol(ident_ids, "null");
let null_stmt =
|next| let_lowlevel(root.arena, root.layout_isize, null, PtrCast, &[zero], next);
let null_stmt = |next| Stmt::Let(null, Expr::NullPointer, layout, next);
decrement_stmt(root.arena.alloc(
//
zero_stmt(root.arena.alloc(
null_stmt(root.arena.alloc(
//
null_stmt(root.arena.alloc(
//
Stmt::Ret(null),
)),
Stmt::Ret(null),
)),
))
};
let if_stmt = Stmt::Switch {
cond_symbol: is_unique,
cond_layout: LAYOUT_BOOL,
branches: root.arena.alloc([(1, BranchInfo::None, then_stmt)]),
default_branch: (BranchInfo::None, root.arena.alloc(else_stmt)),
ret_layout: layout,
};
let if_stmt = Stmt::if_then_else(
root.arena,
is_unique,
layout,
then_stmt,
root.arena.alloc(else_stmt),
);
// Uniqueness test
let is_unique_stmt = {
@ -508,12 +502,8 @@ pub fn refcount_resetref_proc_body<'a>(
);
// Refcount value
let rc_expr = Expr::UnionAtIndex {
structure: rc_ptr,
tag_id: 0,
union_layout: root.union_refcount,
index: 0,
};
let rc_expr = Expr::ExprUnbox { symbol: rc_ptr };
let rc_stmt = Stmt::Let(
rc,
rc_expr,
@ -521,6 +511,9 @@ pub fn refcount_resetref_proc_body<'a>(
root.arena.alloc(refcount_1_stmt),
);
// a Box never masks bits
let mask_lower_bits = false;
// Refcount pointer
let rc_ptr_stmt = {
rc_ptr_from_data_ptr_help(
@ -528,7 +521,7 @@ pub fn refcount_resetref_proc_body<'a>(
ident_ids,
structure,
rc_ptr,
union_layout.stores_tag_id_in_pointer(root.target_info),
mask_lower_bits,
root.arena.alloc(rc_stmt),
addr,
recursion_ptr,
@ -561,33 +554,7 @@ fn refcount_args<'a>(root: &CodeGenHelp<'a>, ctx: &Context<'a>, structure: Symbo
}
}
// Subtract a constant from a pointer to find the refcount
// Also does some type casting, so that we have different Symbols and Layouts
// for the 'pointer' and 'integer' versions of the address.
// This helps to avoid issues with the backends Symbol->Layout mapping.
pub fn rc_ptr_from_data_ptr<'a>(
root: &CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
structure: Symbol,
rc_ptr_sym: Symbol,
mask_lower_bits: bool,
following: &'a Stmt<'a>,
recursive_layout: InLayout<'a>,
) -> Stmt<'a> {
let addr_sym = root.create_symbol(ident_ids, "addr");
rc_ptr_from_data_ptr_help(
root,
ident_ids,
structure,
rc_ptr_sym,
mask_lower_bits,
following,
addr_sym,
recursive_layout,
)
}
pub fn rc_ptr_from_data_ptr_help<'a>(
fn rc_ptr_from_data_ptr_help<'a>(
root: &CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
structure: Symbol,
@ -700,7 +667,7 @@ fn modify_refcount<'a>(
root: &CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
rc_ptr: Symbol,
data_ptr: Symbol,
alignment: u32,
following: &'a Stmt<'a>,
) -> Stmt<'a> {
@ -710,10 +677,10 @@ fn modify_refcount<'a>(
HelperOp::Inc => {
let zig_call_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::RefCountInc,
op: LowLevel::RefCountIncDataPtr,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: root.arena.alloc([rc_ptr, Symbol::ARG_2]),
arguments: root.arena.alloc([data_ptr, Symbol::ARG_2]),
});
Stmt::Let(zig_call_result, zig_call_expr, LAYOUT_UNIT, following)
}
@ -726,10 +693,10 @@ fn modify_refcount<'a>(
let zig_call_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::RefCountDec,
op: LowLevel::RefCountDecDataPtr,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: root.arena.alloc([rc_ptr, alignment_sym]),
arguments: root.arena.alloc([data_ptr, alignment_sym]),
});
let zig_call_stmt = Stmt::Let(zig_call_result, zig_call_expr, LAYOUT_UNIT, following);
@ -782,7 +749,7 @@ fn refcount_str<'a>(
let is_big_str_stmt = |next| Stmt::Let(is_big_str, is_big_str_expr, LAYOUT_BOOL, next);
// Get the pointer to the string elements
let elements = root.create_symbol(ident_ids, "elements");
let elements = root.create_symbol(ident_ids, "characters");
let elements_expr = Expr::StructAtIndex {
index: 0,
field_layouts,
@ -791,7 +758,6 @@ fn refcount_str<'a>(
let elements_stmt = |next| Stmt::Let(elements, elements_expr, layout_isize, next);
// A pointer to the refcount value itself
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = root.target_info.ptr_width() as u32;
let ret_unit_stmt = rc_return_stmt(root, ident_ids, ctx);
@ -799,38 +765,21 @@ fn refcount_str<'a>(
root,
ident_ids,
ctx,
rc_ptr,
elements,
alignment,
root.arena.alloc(ret_unit_stmt),
);
// Generate an `if` to skip small strings but modify big strings
let then_branch = elements_stmt(root.arena.alloc(
//
rc_ptr_from_data_ptr(
root,
ident_ids,
elements,
rc_ptr,
false,
root.arena.alloc(
//
mod_rc_stmt,
),
Layout::OPAQUE_PTR,
),
));
let then_branch = elements_stmt(root.arena.alloc(mod_rc_stmt));
let if_stmt = Stmt::Switch {
cond_symbol: is_big_str,
cond_layout: LAYOUT_BOOL,
branches: root.arena.alloc([(1, BranchInfo::None, then_branch)]),
default_branch: (
BranchInfo::None,
root.arena.alloc(rc_return_stmt(root, ident_ids, ctx)),
),
ret_layout: LAYOUT_UNIT,
};
let if_stmt = Stmt::if_then_else(
root.arena,
is_big_str,
Layout::UNIT,
then_branch,
root.arena.alloc(rc_return_stmt(root, ident_ids, ctx)),
);
// Combine the statements in sequence
last_word_stmt(root.arena.alloc(
@ -896,7 +845,6 @@ fn refcount_list<'a>(
// (elements first, to avoid use-after-free for Dec)
//
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = Ord::max(
root.target_info.ptr_width() as u32,
layout_interner.alignment_bytes(elem_layout),
@ -907,23 +855,13 @@ fn refcount_list<'a>(
root,
ident_ids,
ctx,
rc_ptr,
elements,
alignment,
arena.alloc(ret_stmt),
);
let get_rc_and_modify_list = rc_ptr_from_data_ptr(
root,
ident_ids,
elements,
rc_ptr,
false,
arena.alloc(modify_list),
Layout::OPAQUE_PTR,
);
let modify_elems_and_list =
if layout_interner.get(elem_layout).is_refcounted() && !ctx.op.is_decref() {
if layout_interner.get(elem_layout).is_refcounted() && ctx.op.is_dec() {
refcount_list_elems(
root,
ident_ids,
@ -934,10 +872,10 @@ fn refcount_list<'a>(
box_layout,
len,
elements,
get_rc_and_modify_list,
modify_list,
)
} else {
get_rc_and_modify_list
modify_list
};
//
@ -952,15 +890,13 @@ fn refcount_list<'a>(
)),
);
let if_stmt = Stmt::Switch {
cond_symbol: is_empty,
cond_layout: LAYOUT_BOOL,
branches: root
.arena
.alloc([(1, BranchInfo::None, rc_return_stmt(root, ident_ids, ctx))]),
default_branch: (BranchInfo::None, non_empty_branch),
ret_layout: LAYOUT_UNIT,
};
let if_stmt = Stmt::if_then_else(
root.arena,
is_empty,
Layout::UNIT,
rc_return_stmt(root, ident_ids, ctx),
non_empty_branch,
);
len_stmt(arena.alloc(
//
@ -1179,7 +1115,7 @@ fn refcount_union<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: InLayout<'a>,
union_in_layout: InLayout<'a>,
union: UnionLayout<'a>,
structure: Symbol,
) -> Stmt<'a> {
@ -1202,8 +1138,8 @@ fn refcount_union<'a>(
),
Recursive(tags) => {
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
let tailrec_idx = root.union_tail_recursion_fields(union_in_layout, union);
if let (Some(tail_idx), true) = (tailrec_idx, ctx.op.is_dec()) {
refcount_union_tailrec(
root,
ident_ids,
@ -1216,7 +1152,6 @@ fn refcount_union<'a>(
structure,
)
} else {
let recursive_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
refcount_union_rec(
root,
ident_ids,
@ -1225,7 +1160,6 @@ fn refcount_union<'a>(
union,
tags,
None,
recursive_ptr,
structure,
)
}
@ -1237,7 +1171,6 @@ fn refcount_union<'a>(
// a direct RecursionPointer is only possible if there's at least one non-recursive variant.
// This nesting makes it harder to do tail recursion, so we just don't.
let tags = root.arena.alloc([field_layouts]);
let recursive_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
refcount_union_rec(
root,
ident_ids,
@ -1246,7 +1179,6 @@ fn refcount_union<'a>(
union,
tags,
None,
recursive_ptr,
structure,
)
}
@ -1256,8 +1188,8 @@ fn refcount_union<'a>(
nullable_id,
} => {
let null_id = Some(nullable_id);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
let tailrec_idx = root.union_tail_recursion_fields(union_in_layout, union);
if let (Some(tail_idx), true) = (tailrec_idx, ctx.op.is_dec()) {
refcount_union_tailrec(
root,
ident_ids,
@ -1270,7 +1202,6 @@ fn refcount_union<'a>(
structure,
)
} else {
let recursive_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
refcount_union_rec(
root,
ident_ids,
@ -1279,7 +1210,6 @@ fn refcount_union<'a>(
union,
tags,
null_id,
recursive_ptr,
structure,
)
}
@ -1291,8 +1221,8 @@ fn refcount_union<'a>(
} => {
let null_id = Some(nullable_id as TagIdIntType);
let tags = root.arena.alloc([other_fields]);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
let tailrec_idx = root.union_tail_recursion_fields(union_in_layout, union);
if let (Some(tail_idx), true) = (tailrec_idx, ctx.op.is_dec()) {
refcount_union_tailrec(
root,
ident_ids,
@ -1305,7 +1235,6 @@ fn refcount_union<'a>(
structure,
)
} else {
let recursive_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
refcount_union_rec(
root,
ident_ids,
@ -1314,7 +1243,6 @@ fn refcount_union<'a>(
union,
tags,
null_id,
recursive_ptr,
structure,
)
}
@ -1391,17 +1319,12 @@ fn refcount_union_contents<'a>(
if let Some(id) = null_id {
let ret = rc_return_stmt(root, ident_ids, ctx);
tag_branches.push((id as u64, BranchInfo::None, ret));
}
let mut tag_id: TagIdIntType = 0;
for field_layouts in tag_layouts.iter() {
match null_id {
Some(id) if id == tag_id => {
tag_id += 1;
}
_ => {}
}
};
for (field_layouts, tag_id) in tag_layouts
.iter()
.zip((0..).filter(|tag_id| !matches!(null_id, Some(id) if tag_id == &id)))
{
// After refcounting the fields, jump to modify the union itself
// (Order is important, to avoid use-after-free for Dec)
let following = Stmt::Jump(jp_contents_modified, &[]);
@ -1426,8 +1349,6 @@ fn refcount_union_contents<'a>(
);
tag_branches.push((tag_id as u64, BranchInfo::None, fields_stmt));
tag_id += 1;
}
let default_stmt: Stmt<'a> = tag_branches.pop().unwrap().2;
@ -1440,11 +1361,39 @@ fn refcount_union_contents<'a>(
ret_layout: LAYOUT_UNIT,
};
Stmt::Join {
id: jp_contents_modified,
parameters: &[],
body: root.arena.alloc(next_stmt),
remainder: root.arena.alloc(tag_id_switch),
if let UnionLayout::NonRecursive(_) = union_layout {
Stmt::Join {
id: jp_contents_modified,
parameters: &[],
body: root.arena.alloc(next_stmt),
remainder: root.arena.alloc(tag_id_switch),
}
} else {
let is_unique = root.create_symbol(ident_ids, "is_unique");
let switch_with_unique_check = Stmt::if_then_else(
root.arena,
is_unique,
Layout::UNIT,
tag_id_switch,
root.arena.alloc(Stmt::Jump(jp_contents_modified, &[])),
);
let switch_with_unique_check_and_let = let_lowlevel(
root.arena,
Layout::BOOL,
is_unique,
LowLevel::RefCountIsUnique,
&[structure],
root.arena.alloc(switch_with_unique_check),
);
Stmt::Join {
id: jp_contents_modified,
parameters: &[],
body: root.arena.alloc(next_stmt),
remainder: root.arena.alloc(switch_with_unique_check_and_let),
}
}
}
@ -1456,7 +1405,6 @@ fn refcount_union_rec<'a>(
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [InLayout<'a>]],
null_id: Option<TagIdIntType>,
recursion_ptr: InLayout<'a>,
structure: Symbol,
) -> Stmt<'a> {
let tag_id_layout = union_layout.tag_id_layout();
@ -1475,34 +1423,21 @@ fn refcount_union_rec<'a>(
};
let rc_structure_stmt = {
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = Layout::Union(union_layout)
.allocation_alignment_bytes(layout_interner, root.target_info);
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
let modify_structure_stmt = modify_refcount(
modify_refcount(
root,
ident_ids,
ctx,
rc_ptr,
structure,
alignment,
root.arena.alloc(ret_stmt),
);
rc_ptr_from_data_ptr(
root,
ident_ids,
structure,
rc_ptr,
union_layout.stores_tag_id_in_pointer(root.target_info),
root.arena.alloc(modify_structure_stmt),
recursion_ptr,
)
};
let rc_contents_then_structure = if ctx.op.is_decref() {
rc_structure_stmt
} else {
let rc_contents_then_structure = if ctx.op.is_dec() {
refcount_union_contents(
root,
ident_ids,
@ -1516,6 +1451,8 @@ fn refcount_union_rec<'a>(
tag_id_layout,
rc_structure_stmt,
)
} else {
rc_structure_stmt
};
if ctx.op.is_decref() && null_id.is_none() {
@ -1544,7 +1481,6 @@ fn refcount_union_tailrec<'a>(
let current = root.create_symbol(ident_ids, "current");
let next_ptr = root.create_symbol(ident_ids, "next_ptr");
let layout = layout_interner.insert(Layout::Union(union_layout));
let recursion_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
let tag_id_layout = union_layout.tag_id_layout();
@ -1565,7 +1501,6 @@ fn refcount_union_tailrec<'a>(
// In the control flow, this comes *after* refcounting the fields
// It receives a `next` parameter to pass through to the outer joinpoint
let rc_structure_stmt = {
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let next_addr = root.create_symbol(ident_ids, "next_addr");
let exit_stmt = rc_return_stmt(root, ident_ids, ctx);
@ -1590,23 +1525,13 @@ fn refcount_union_tailrec<'a>(
};
let alignment = layout_interner.allocation_alignment_bytes(layout);
let modify_structure_stmt = modify_refcount(
modify_refcount(
root,
ident_ids,
ctx,
rc_ptr,
current,
alignment,
root.arena.alloc(loop_or_exit_based_on_next_addr),
);
rc_ptr_from_data_ptr(
root,
ident_ids,
current,
rc_ptr,
union_layout.stores_tag_id_in_pointer(root.target_info),
root.arena.alloc(modify_structure_stmt),
recursion_ptr,
)
};
@ -1620,15 +1545,11 @@ fn refcount_union_tailrec<'a>(
tag_branches.push((id as u64, BranchInfo::None, ret));
}
let mut tag_id: TagIdIntType = 0;
for (field_layouts, opt_tailrec_index) in tag_layouts.iter().zip(tailrec_indices) {
match null_id {
Some(id) if id == tag_id => {
tag_id += 1;
}
_ => {}
}
for ((field_layouts, opt_tailrec_index), tag_id) in tag_layouts
.iter()
.zip(tailrec_indices)
.zip((0..).filter(|tag_id| !matches!(null_id, Some(id) if tag_id == &id)))
{
// After refcounting the fields, jump to modify the union itself.
// The loop param is a pointer to the next union. It gets passed through two jumps.
let (non_tailrec_fields, jump_to_modify_union) =
@ -1655,20 +1576,12 @@ fn refcount_union_tailrec<'a>(
(filtered.into_bump_slice(), tail_stmt.unwrap())
} else {
let zero = root.create_symbol(ident_ids, "zero");
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
let null = root.create_symbol(ident_ids, "null");
let null_stmt =
|next| let_lowlevel(root.arena, layout, null, PtrCast, &[zero], next);
let null_stmt = |next| Stmt::Let(null, Expr::NullPointer, layout, next);
let tail_stmt = zero_stmt(root.arena.alloc(
let tail_stmt = null_stmt(root.arena.alloc(
//
null_stmt(root.arena.alloc(
//
Stmt::Jump(jp_modify_union, root.arena.alloc([null])),
)),
Stmt::Jump(jp_modify_union, root.arena.alloc([null])),
));
let field_layouts = field_layouts
@ -1694,8 +1607,6 @@ fn refcount_union_tailrec<'a>(
);
tag_branches.push((tag_id as u64, BranchInfo::None, fields_stmt));
tag_id += 1;
}
let default_stmt: Stmt<'a> = tag_branches.pop().unwrap().2;
@ -1708,6 +1619,36 @@ fn refcount_union_tailrec<'a>(
ret_layout: LAYOUT_UNIT,
};
let is_unique = root.create_symbol(ident_ids, "is_unique");
let null_pointer = root.create_symbol(ident_ids, "null_pointer");
let jump_with_null_ptr = Stmt::Let(
null_pointer,
Expr::NullPointer,
layout_interner.insert(Layout::Union(union_layout)),
root.arena.alloc(Stmt::Jump(
jp_modify_union,
root.arena.alloc([null_pointer]),
)),
);
let switch_with_unique_check = Stmt::if_then_else(
root.arena,
is_unique,
Layout::UNIT,
tag_id_switch,
root.arena.alloc(jump_with_null_ptr),
);
let switch_with_unique_check_and_let = let_lowlevel(
root.arena,
Layout::BOOL,
is_unique,
LowLevel::RefCountIsUnique,
&[current],
root.arena.alloc(switch_with_unique_check),
);
let jp_param = Param {
symbol: next_ptr,
ownership: Ownership::Borrowed,
@ -1718,7 +1659,7 @@ fn refcount_union_tailrec<'a>(
id: jp_modify_union,
parameters: root.arena.alloc([jp_param]),
body: root.arena.alloc(rc_structure_stmt),
remainder: root.arena.alloc(tag_id_switch),
remainder: root.arena.alloc(switch_with_unique_check_and_let),
}
};
@ -1804,29 +1745,19 @@ fn refcount_boxed<'a>(
// We're defining statements in reverse, so define outer first
//
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = layout_interner.allocation_alignment_bytes(layout);
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
let modify_outer = modify_refcount(
root,
ident_ids,
ctx,
rc_ptr,
outer,
alignment,
arena.alloc(ret_stmt),
);
let get_rc_and_modify_outer = rc_ptr_from_data_ptr(
root,
ident_ids,
outer,
rc_ptr,
false,
arena.alloc(modify_outer),
Layout::OPAQUE_PTR,
);
if layout_interner.is_refcounted(inner_layout) && !ctx.op.is_decref() {
// decrement the inner value if the operation is a decrement and the box itself is unique
if layout_interner.is_refcounted(inner_layout) && ctx.op.is_dec() {
let inner = root.create_symbol(ident_ids, "inner");
let inner_expr = Expr::ExprUnbox { symbol: outer };
@ -1842,18 +1773,26 @@ fn refcount_boxed<'a>(
)
.unwrap();
Stmt::Let(
inner,
inner_expr,
inner_layout,
arena.alloc(Stmt::Let(
mod_inner_unit,
mod_inner_expr,
LAYOUT_UNIT,
arena.alloc(get_rc_and_modify_outer),
)),
if_unique(
root,
ident_ids,
outer,
|id| {
Stmt::Let(
inner,
inner_expr,
inner_layout,
arena.alloc(Stmt::Let(
mod_inner_unit,
mod_inner_expr,
LAYOUT_UNIT,
arena.alloc(Stmt::Jump(id, &[])),
)),
)
},
modify_outer,
)
} else {
get_rc_and_modify_outer
modify_outer
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ use std::{collections::HashMap, hash::BuildHasherDefault};
use bumpalo::collections::{CollectIn, Vec};
use bumpalo::Bump;
use roc_collections::{all::WyHash, MutMap, MutSet};
use roc_module::low_level::LowLevel;
use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol};
use crate::{
@ -70,8 +71,8 @@ Implemented as two sets for efficiency.
*/
#[derive(Clone, Default)]
struct SymbolRcTypes {
owned: MutSet<Symbol>,
borrowed: MutSet<Symbol>,
reference_counted: MutSet<Symbol>,
not_reference_counted: MutSet<Symbol>,
}
impl SymbolRcTypes {
@ -81,10 +82,10 @@ impl SymbolRcTypes {
fn insert(&mut self, symbol: Symbol, var_rc_type: VarRcType) {
match var_rc_type {
VarRcType::ReferenceCounted => {
self.owned.insert(symbol);
self.reference_counted.insert(symbol);
}
VarRcType::NotReferenceCounted => {
self.borrowed.insert(symbol);
self.not_reference_counted.insert(symbol);
}
}
}
@ -93,10 +94,10 @@ impl SymbolRcTypes {
Get the reference count type of a symbol.
*/
fn get(&self, symbol: &Symbol) -> Option<VarRcType> {
if self.owned.contains(symbol) {
debug_assert!(!self.borrowed.contains(symbol));
if self.reference_counted.contains(symbol) {
debug_assert!(!self.not_reference_counted.contains(symbol));
Some(VarRcType::ReferenceCounted)
} else if self.borrowed.contains(symbol) {
} else if self.not_reference_counted.contains(symbol) {
Some(VarRcType::NotReferenceCounted)
} else {
None
@ -177,15 +178,13 @@ impl<'a, 'i> SymbolRcTypesEnv<'a, 'i> {
.map(|(_branch, info, stmt)| (info, stmt))
.chain([(&default_branch.0, default_branch.1)])
{
match info {
BranchInfo::None => (),
BranchInfo::Constructor {
scrutinee,
layout,
tag_id: _,
} => {
self.insert_symbol_layout_rc_type(scrutinee, layout);
}
if let BranchInfo::Constructor {
scrutinee,
layout,
tag_id: _,
} = info
{
self.insert_symbol_layout_rc_type(scrutinee, layout);
}
self.insert_symbols_rc_type_stmt(stmt);
@ -860,7 +859,7 @@ fn insert_refcount_operations_binding<'a>(
stmt: &'a Stmt<'a>,
) -> &'a Stmt<'a> {
macro_rules! dec_borrowed {
($symbols:expr,$stmt:expr) => {
($symbols:expr, $stmt:expr) => {
// Insert decrement operations for borrowed symbols if they are currently owned.
consume_and_insert_dec_stmts(
arena,
@ -894,170 +893,7 @@ fn insert_refcount_operations_binding<'a>(
// Literals, empty arrays, and runtime errors are not (and have nothing) reference counted.
new_let!(stmt)
}
Expr::Call(Call {
arguments,
call_type,
}) => match call_type {
// A by name call refers to a normal function call.
// Normal functions take all their parameters as owned, so we can mark them all as such.
CallType::ByName { name, .. } => {
// Lowlevels are wrapped in another function in order to add type signatures which help with inference.
// But the reference counting algorithm inserts reference counting operations in the wrapper function.
// But in a later stage, calls to the wrapper function were replaced by calls to the lowlevel function.
// Effectively removing the inserted reference counting operations.
// Thus to prevent that, we inline the operations here already.
if let LowLevelWrapperType::CanBeReplacedBy(op) =
LowLevelWrapperType::from_symbol(name.name())
{
let borrow_signature = lowlevel_borrow_signature(arena, op);
let arguments_with_borrow_signature = arguments
.iter()
.copied()
.zip(borrow_signature.iter().copied());
let owned_arguments = arguments_with_borrow_signature
.clone()
.filter_map(|(symbol, ownership)| ownership.is_owned().then_some(symbol));
let borrowed_arguments =
arguments_with_borrow_signature.filter_map(|(symbol, ownership)| {
ownership.is_borrowed().then_some(symbol)
});
let new_stmt = dec_borrowed!(borrowed_arguments, stmt);
let new_let = new_let!(new_stmt);
inc_owned!(owned_arguments, new_let)
} else {
let new_let = new_let!(stmt);
inc_owned!(arguments.iter().copied(), new_let)
}
}
CallType::Foreign { .. } => {
// Foreign functions should be responsible for their own memory management.
// But previously they were assumed to be called with borrowed parameters, so we do the same now.
let new_stmt = dec_borrowed!(arguments.iter().copied(), stmt);
new_let!(new_stmt)
}
// Doesn't include higher order
CallType::LowLevel {
op: operator,
update_mode: _,
} => {
let borrow_signature = lowlevel_borrow_signature(arena, *operator);
let arguments_with_borrow_signature = arguments
.iter()
.copied()
.zip(borrow_signature.iter().copied());
let owned_arguments = arguments_with_borrow_signature
.clone()
.filter_map(|(symbol, ownership)| ownership.is_owned().then_some(symbol));
let borrowed_arguments = arguments_with_borrow_signature
.filter_map(|(symbol, ownership)| ownership.is_borrowed().then_some(symbol));
let new_stmt = dec_borrowed!(borrowed_arguments, stmt);
let new_let = new_let!(new_stmt);
inc_owned!(owned_arguments, new_let)
}
CallType::HigherOrder(HigherOrderLowLevel {
op: operator,
closure_env_layout: _,
/// update mode of the higher order lowlevel itself
update_mode: _,
passed_function,
}) => {
// Functions always take their arguments as owned.
// (Except lowlevels, but those are wrapped in functions that take their arguments as owned and perform rc.)
// This should always be true, not sure where this could be set to false.
debug_assert!(passed_function.owns_captured_environment);
// define macro that inserts a decref statement for a symbol amount of symbols
macro_rules! decref_lists {
($stmt:expr, $symbol:expr) => {
arena.alloc(Stmt::Refcounting(ModifyRc::DecRef($symbol), $stmt))
};
($stmt:expr, $symbol:expr, $($symbols:expr),+) => {{
decref_lists!(decref_lists!($stmt, $symbol), $($symbols),+)
}};
}
match operator {
HigherOrder::ListMap { xs } => {
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs].into_iter(), new_let)
} else {
panic!("ListMap should have 3 arguments");
}
}
HigherOrder::ListMap2 { xs, ys } => {
if let [_xs_symbol, _ys_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys].into_iter(), new_let)
} else {
panic!("ListMap2 should have 4 arguments");
}
}
HigherOrder::ListMap3 { xs, ys, zs } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs].into_iter(), new_let)
} else {
panic!("ListMap3 should have 5 arguments");
}
}
HigherOrder::ListMap4 { xs, ys, zs, ws } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _ws_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs, *ws);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs, *ws].into_iter(), new_let)
} else {
panic!("ListMap4 should have 6 arguments");
}
}
HigherOrder::ListSortWith { xs } => {
// TODO if non-unique, elements have been consumed, must still consume the list itself
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_let = new_let!(new_stmt);
inc_owned!([*xs].into_iter(), new_let)
} else {
panic!("ListSortWith should have 3 arguments");
}
}
}
}
},
Expr::Tag { arguments, .. } | Expr::Struct(arguments) => {
let new_let = new_let!(stmt);
@ -1068,13 +904,13 @@ fn insert_refcount_operations_binding<'a>(
inc_owned!([*symbol], new_let)
}
Expr::GetTagId { structure, .. }
| Expr::StructAtIndex { structure, .. }
| Expr::UnionAtIndex { structure, .. }
| Expr::ExprUnbox { symbol: structure } => {
// All structures are alive at this point and don't have to be copied in order to take an index out/get tag id/copy values to the stack.
// But we do want to make sure to decrement this item if it is the last reference.
let new_stmt = dec_borrowed!([*structure], stmt);
// Add an increment operation for the binding if it is reference counted and if the expression creates a new reference to a value.
@ -1114,6 +950,164 @@ fn insert_refcount_operations_binding<'a>(
new_let
)
}
Expr::Call(Call {
arguments,
call_type,
}) => {
match call_type.clone().replace_lowlevel_wrapper() {
// A by name call refers to a normal function call.
// Normal functions take all their parameters as owned, so we can mark them all as such.
CallType::ByName { .. } => {
let new_let = new_let!(stmt);
inc_owned!(arguments.iter().copied(), new_let)
}
CallType::Foreign { .. } => {
// Foreign functions should be responsible for their own memory management.
// But previously they were assumed to be called with borrowed parameters, so we do the same now.
let new_stmt = dec_borrowed!(arguments.iter().copied(), stmt);
new_let!(new_stmt)
}
// Doesn't include higher order
CallType::LowLevel {
op: operator,
update_mode: _,
} => match operator {
// List get unsafe is a special case, because it returns a reference to the list element.
// This means that we have to increment the reference count of this element.
LowLevel::ListGetUnsafe => {
let structure = match arguments {
[structure, _index] => *structure,
_ => unreachable!("List get should have two arguments"),
};
let new_stmt = dec_borrowed!([structure], stmt);
let newer_stmt = if matches!(
environment.get_symbol_rc_type(binding),
VarRcType::ReferenceCounted
) {
insert_inc_stmt(arena, *binding, 1, new_stmt)
} else {
new_stmt
};
new_let!(newer_stmt)
}
// Otherwise, perform regular reference counting using the lowlevel borrow signature.
_ => {
let borrow_signature = lowlevel_borrow_signature(arena, operator);
let arguments_with_borrow_signature = arguments
.iter()
.copied()
.zip(borrow_signature.iter().copied());
let owned_arguments = arguments_with_borrow_signature.clone().filter_map(
|(symbol, ownership)| ownership.is_owned().then_some(symbol),
);
let borrowed_arguments =
arguments_with_borrow_signature.filter_map(|(symbol, ownership)| {
ownership.is_borrowed().then_some(symbol)
});
let new_stmt = dec_borrowed!(borrowed_arguments, stmt);
let new_let = new_let!(new_stmt);
inc_owned!(owned_arguments, new_let)
}
},
CallType::HigherOrder(HigherOrderLowLevel {
op: operator,
closure_env_layout: _,
/// update mode of the higher order lowlevel itself
update_mode: _,
passed_function,
}) => {
// Functions always take their arguments as owned.
// (Except lowlevels, but those are wrapped in functions that take their arguments as owned and perform rc.)
// This should always be true, not sure where this could be set to false.
debug_assert!(passed_function.owns_captured_environment);
// define macro that inserts a decref statement for a symbol amount of symbols
macro_rules! decref_lists {
($stmt:expr, $symbol:expr) => {
arena.alloc(Stmt::Refcounting(ModifyRc::DecRef($symbol), $stmt))
};
($stmt:expr, $symbol:expr, $($symbols:expr),+) => {{
decref_lists!(decref_lists!($stmt, $symbol), $($symbols),+)
}};
}
match operator {
HigherOrder::ListMap { xs } => {
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs].into_iter(), new_let)
} else {
panic!("ListMap should have 3 arguments");
}
}
HigherOrder::ListMap2 { xs, ys } => {
if let [_xs_symbol, _ys_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys].into_iter(), new_let)
} else {
panic!("ListMap2 should have 4 arguments");
}
}
HigherOrder::ListMap3 { xs, ys, zs } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs].into_iter(), new_let)
} else {
panic!("ListMap3 should have 5 arguments");
}
}
HigherOrder::ListMap4 { xs, ys, zs, ws } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _ws_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs, *ws);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs, *ws].into_iter(), new_let)
} else {
panic!("ListMap4 should have 6 arguments");
}
}
HigherOrder::ListSortWith { xs } => {
// TODO if non-unique, elements have been consumed, must still consume the list itself
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_let = new_let!(new_stmt);
inc_owned!([*xs].into_iter(), new_let)
} else {
panic!("ListSortWith should have 3 arguments");
}
}
}
}
}
}
Expr::Reuse { .. } | Expr::Reset { .. } | Expr::ResetRef { .. } => {
unreachable!("Reset(ref) and reuse should not exist at this point")
}

View file

@ -17,15 +17,15 @@ use roc_collections::VecMap;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::{
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
ROC_PRINT_RUNTIME_ERROR_GEN,
ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION, ROC_PRINT_IR_AFTER_REFCOUNT,
ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_RUNTIME_ERROR_GEN,
};
use roc_derive::SharedDerivedModule;
use roc_error_macros::{internal_error, todo_abilities};
use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapshot};
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_problem::can::{RuntimeError, ShadowKind};
use roc_region::all::{Loc, Region};
@ -57,6 +57,9 @@ pub fn pretty_print_ir_symbols() -> bool {
dbg_do!(ROC_PRINT_IR_AFTER_REFCOUNT, {
return true;
});
dbg_do!(ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION, {
return true;
});
false
}
@ -1595,6 +1598,10 @@ pub enum BranchInfo<'a> {
layout: InLayout<'a>,
tag_id: TagIdIntType,
},
List {
scrutinee: Symbol,
len: u64,
},
}
impl<'a> BranchInfo<'a> {
@ -1783,6 +1790,24 @@ pub enum CallType<'a> {
HigherOrder(&'a HigherOrderLowLevel<'a>),
}
impl<'a> CallType<'a> {
/**
Replace calls to wrappers of lowlevel functions with the lowlevel function itself
*/
pub fn replace_lowlevel_wrapper(self) -> Self {
match self {
CallType::ByName { name, .. } => match LowLevelWrapperType::from_symbol(name.name()) {
LowLevelWrapperType::CanBeReplacedBy(lowlevel) => CallType::LowLevel {
op: lowlevel,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
LowLevelWrapperType::NotALowLevelWrapper => self,
},
_ => self,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PassedFunction<'a> {
/// name of the top-level function that is passed as an argument

View file

@ -1699,7 +1699,7 @@ fn test_to_comparison<'a>(
(
stores,
(lhs_symbol, Comparator::Eq, rhs_symbol),
Some(ConstructorKnown::OnlyPass {
Some(ConstructorKnown::OneTag {
scrutinee: path_symbol,
layout: *cond_layout,
tag_id,
@ -1855,7 +1855,7 @@ fn compile_test<'a>(
) -> Stmt<'a> {
compile_test_help(
env,
ConstructorKnown::Neither,
ConstructorKnown::None,
ret_layout,
stores,
lhs,
@ -1885,7 +1885,7 @@ fn compile_test_help<'a>(
let (pass_info, fail_info) = {
use ConstructorKnown::*;
match branch_info {
Both {
BothTags {
scrutinee,
layout,
pass,
@ -1905,7 +1905,7 @@ fn compile_test_help<'a>(
(pass_info, fail_info)
}
OnlyPass {
OneTag {
scrutinee,
layout,
tag_id,
@ -1919,7 +1919,13 @@ fn compile_test_help<'a>(
(pass_info, BranchInfo::None)
}
Neither => (BranchInfo::None, BranchInfo::None),
ListLen { scrutinee, len } => {
let pass_info = BranchInfo::List { scrutinee, len };
(pass_info, BranchInfo::None)
}
None => (BranchInfo::None, BranchInfo::None),
}
};
@ -1981,18 +1987,22 @@ fn compile_tests<'a>(
#[derive(Debug)]
enum ConstructorKnown<'a> {
Both {
BothTags {
scrutinee: Symbol,
layout: InLayout<'a>,
pass: TagIdIntType,
fail: TagIdIntType,
},
OnlyPass {
OneTag {
scrutinee: Symbol,
layout: InLayout<'a>,
tag_id: TagIdIntType,
},
Neither,
ListLen {
scrutinee: Symbol,
len: u64,
},
None,
}
impl<'a> ConstructorKnown<'a> {
@ -2006,23 +2016,30 @@ impl<'a> ConstructorKnown<'a> {
Test::IsCtor { tag_id, union, .. } if path.is_empty() => {
if union.alternatives.len() == 2 {
// excluded middle: we also know the tag_id in the fail branch
ConstructorKnown::Both {
ConstructorKnown::BothTags {
layout: cond_layout,
scrutinee: cond_symbol,
pass: *tag_id,
fail: (*tag_id == 0) as _,
}
} else {
ConstructorKnown::OnlyPass {
ConstructorKnown::OneTag {
layout: cond_layout,
scrutinee: cond_symbol,
tag_id: *tag_id,
}
}
}
_ => ConstructorKnown::Neither,
Test::IsListLen {
bound: ListLenBound::Exact,
len,
} if path.is_empty() => ConstructorKnown::ListLen {
scrutinee: cond_symbol,
len: *len,
},
_ => ConstructorKnown::None,
},
_ => ConstructorKnown::Neither,
_ => ConstructorKnown::None,
}
}
}
@ -2255,18 +2272,31 @@ fn decide_to_branching<'a>(
};
// branch info is only useful for refcounted values
let branch_info = if let Test::IsCtor { tag_id, union, .. } = test {
tag_id_sum -= tag_id as i64;
union_size = union.alternatives.len() as i64;
let branch_info = match test {
Test::IsCtor { tag_id, union, .. } => {
tag_id_sum -= tag_id as i64;
union_size = union.alternatives.len() as i64;
BranchInfo::Constructor {
scrutinee: inner_cond_symbol,
layout: inner_cond_layout,
tag_id,
BranchInfo::Constructor {
scrutinee: inner_cond_symbol,
layout: inner_cond_layout,
tag_id,
}
}
Test::IsListLen {
bound: ListLenBound::Exact,
len,
} => {
tag_id_sum = -1;
BranchInfo::List {
scrutinee: inner_cond_symbol,
len,
}
}
_ => {
tag_id_sum = -1;
BranchInfo::None
}
} else {
tag_id_sum = -1;
BranchInfo::None
};
branches.push((tag, branch_info, branch));

View file

@ -11,6 +11,7 @@
pub mod borrow;
pub mod code_gen_help;
pub mod drop_specialization;
pub mod inc_dec;
pub mod ir;
pub mod layout;

View file

@ -3296,7 +3296,6 @@ fn box_num() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
#[ignore = "triggers some UB somewhere in at least the llvm and dev backends"]
fn box_str() {
assert_evals_to!(
"Box.box \"short\"",

View file

@ -96,7 +96,7 @@ fn list_str_inc() {
),
RocList<RocList<RocStr>>,
&[
Live(6), // s
Live(3), // s
Live(2), // list
Live(1) // result
]
@ -228,8 +228,8 @@ fn union_recursive_inc() {
),
(Pointer, Pointer),
&[
Live(4), // s
Live(4), // sym
Live(1), // s
Live(2), // x
Live(2), // e
]
);
@ -294,7 +294,7 @@ fn refcount_different_rosetrees_inc() {
),
(Pointer, Pointer),
&[
Live(2), // s
Live(1), // s
Live(3), // i1
Live(2), // s1
Live(1), // [i1, i1]
@ -364,10 +364,10 @@ fn union_linked_list_inc() {
),
(Pointer, Pointer),
&[
Live(6), // s
Live(2), // Cons
Live(2), // Cons
Live(2), // Cons
Live(3), // s
Live(1), // inner-most Cons
Live(1), // middle Cons
Live(2), // linked
]
);
}
@ -468,7 +468,7 @@ fn boxed_str_inc() {
),
(Pointer, Pointer),
&[
Live(2), // s
Live(1), // s
Live(2), // b
]
);

View file

@ -7,6 +7,7 @@ procedure List.2 (List.96, List.97):
let List.504 : Int1 = CallByName Num.22 List.97 List.508;
if List.504 then
let List.506 : Str = CallByName List.66 List.96 List.97;
inc List.506;
dec List.96;
let List.505 : [C {}, C Str] = TagId(1) List.506;
ret List.505;
@ -37,8 +38,6 @@ procedure List.9 (List.287):
let List.500 : Int1 = lowlevel Eq List.498 List.499;
if List.500 then
let List.288 : Str = UnionAtIndex (Id 1) (Index 0) List.494;
inc List.288;
dec List.494;
let List.495 : [C {}, C Str] = TagId(1) List.288;
ret List.495;
else
@ -58,8 +57,6 @@ procedure Result.5 (Result.12, Result.13):
if Result.41 then
dec Result.13;
let Result.14 : Str = UnionAtIndex (Id 1) (Index 0) Result.12;
inc Result.14;
dec Result.12;
ret Result.14;
else
dec Result.12;
@ -81,26 +78,42 @@ procedure Test.2 (Test.6):
let Test.31 : Int1 = lowlevel Eq Test.29 Test.30;
if Test.31 then
let Test.7 : [<r>C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6;
inc Test.7;
dec Test.6;
let Test.8 : Str = CallByName Test.2 Test.7;
let Test.18 : Int1 = CallByName Bool.1;
if Test.18 then
ret Test.8;
joinpoint #Derived_gen.2:
let Test.8 : Str = CallByName Test.2 Test.7;
let Test.18 : Int1 = CallByName Bool.1;
if Test.18 then
ret Test.8;
else
dec Test.8;
let Test.17 : Str = "foo";
ret Test.17;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.3 then
decref Test.6;
jump #Derived_gen.2;
else
dec Test.8;
let Test.17 : Str = "foo";
ret Test.17;
inc Test.7;
decref Test.6;
jump #Derived_gen.2;
else
let Test.9 : List [<r>C List *self, C *self] = UnionAtIndex (Id 0) (Index 0) Test.6;
inc Test.9;
dec Test.6;
let Test.24 : {} = Struct {};
let Test.23 : List Str = CallByName List.5 Test.9 Test.24;
let Test.21 : [C {}, C Str] = CallByName List.9 Test.23;
let Test.22 : Str = "foo";
let Test.20 : Str = CallByName Result.5 Test.21 Test.22;
ret Test.20;
joinpoint #Derived_gen.4:
let Test.24 : {} = Struct {};
let Test.23 : List Str = CallByName List.5 Test.9 Test.24;
let Test.21 : [C {}, C Str] = CallByName List.9 Test.23;
let Test.22 : Str = "foo";
let Test.20 : Str = CallByName Result.5 Test.21 Test.22;
ret Test.20;
in
let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.5 then
decref Test.6;
jump #Derived_gen.4;
else
inc Test.9;
decref Test.6;
jump #Derived_gen.4;
procedure Test.0 ():
let Test.32 : List [<r>C List *self, C *self] = Array [];

View file

@ -58,33 +58,41 @@ procedure Test.11 (Test.53, Test.54):
joinpoint Test.27 Test.12 #Attr.12:
let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12;
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12;
inc Test.7;
dec #Attr.12;
joinpoint Test.31 Test.29:
let Test.30 : U8 = GetTagId Test.7;
switch Test.30:
joinpoint #Derived_gen.5:
joinpoint Test.31 Test.29:
let Test.30 : U8 = GetTagId Test.7;
switch Test.30:
case 0:
dec Test.7;
let Test.28 : Str = CallByName Test.2 Test.29;
ret Test.28;
case 1:
let Test.28 : Str = CallByName Test.9 Test.29 Test.7;
ret Test.28;
default:
jump Test.27 Test.29 Test.7;
in
switch Test.8:
case 0:
dec Test.7;
let Test.28 : Str = CallByName Test.2 Test.29;
ret Test.28;
case 1:
let Test.28 : Str = CallByName Test.9 Test.29 Test.7;
ret Test.28;
let Test.32 : Str = CallByName Test.3 Test.12;
jump Test.31 Test.32;
default:
jump Test.27 Test.29 Test.7;
let Test.32 : Str = CallByName Test.4 Test.12;
jump Test.31 Test.32;
in
switch Test.8:
case 0:
let Test.32 : Str = CallByName Test.3 Test.12;
jump Test.31 Test.32;
default:
let Test.32 : Str = CallByName Test.4 Test.12;
jump Test.31 Test.32;
let #Derived_gen.6 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.6 then
decref #Attr.12;
jump #Derived_gen.5;
else
inc Test.7;
decref #Attr.12;
jump #Derived_gen.5;
in
jump Test.27 Test.53 Test.54;
@ -117,34 +125,42 @@ procedure Test.6 (Test.7, Test.8, Test.5):
procedure Test.9 (Test.10, #Attr.12):
let Test.8 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
inc Test.7;
dec #Attr.12;
let Test.37 : U8 = GetTagId Test.7;
joinpoint Test.38 Test.36:
switch Test.8:
joinpoint #Derived_gen.3:
let Test.37 : U8 = GetTagId Test.7;
joinpoint Test.38 Test.36:
switch Test.8:
case 0:
let Test.35 : Str = CallByName Test.3 Test.36;
ret Test.35;
default:
let Test.35 : Str = CallByName Test.4 Test.36;
ret Test.35;
in
switch Test.37:
case 0:
let Test.35 : Str = CallByName Test.3 Test.36;
ret Test.35;
dec Test.7;
let Test.39 : Str = CallByName Test.2 Test.10;
jump Test.38 Test.39;
case 1:
let Test.39 : Str = CallByName Test.9 Test.10 Test.7;
jump Test.38 Test.39;
default:
let Test.35 : Str = CallByName Test.4 Test.36;
ret Test.35;
let Test.39 : Str = CallByName Test.11 Test.10 Test.7;
jump Test.38 Test.39;
in
switch Test.37:
case 0:
dec Test.7;
let Test.39 : Str = CallByName Test.2 Test.10;
jump Test.38 Test.39;
case 1:
let Test.39 : Str = CallByName Test.9 Test.10 Test.7;
jump Test.38 Test.39;
default:
let Test.39 : Str = CallByName Test.11 Test.10 Test.7;
jump Test.38 Test.39;
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.4 then
decref #Attr.12;
jump #Derived_gen.3;
else
inc Test.7;
decref #Attr.12;
jump #Derived_gen.3;
procedure Test.0 ():
let Test.41 : Int1 = false;

View file

@ -4,7 +4,6 @@ procedure Test.1 (Test.2):
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
if Test.12 then
let Test.3 : U64 = UnionAtIndex (Id 1) (Index 0) Test.2;
dec Test.2;
ret Test.3;
else
dec Test.2;

View file

@ -16,7 +16,12 @@ procedure Dict.34 ():
procedure Dict.4 (Dict.504):
let Dict.85 : U64 = StructAtIndex 3 Dict.504;
dec Dict.504;
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.504;
dec #Derived_gen.2;
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.504;
dec #Derived_gen.1;
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.504;
dec #Derived_gen.0;
ret Dict.85;
procedure List.11 (List.115, List.116):

View file

@ -89,8 +89,6 @@ procedure Json.118 (Json.119, Json.486, Json.117):
let Json.495 : {} = Struct {};
let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495;
let Json.123 : List U8 = StructAtIndex 0 Json.493;
inc Json.123;
dec Json.493;
let Json.492 : I64 = 125i64;
let Json.491 : U8 = CallByName Num.127 Json.492;
let Json.490 : List U8 = CallByName List.4 Json.123 Json.491;
@ -105,8 +103,6 @@ procedure Json.118 (Json.119, Json.486, Json.117):
let Json.535 : {} = Struct {};
let Json.533 : {List U8, U64} = CallByName List.18 Json.117 Json.534 Json.535;
let Json.123 : List U8 = StructAtIndex 0 Json.533;
inc Json.123;
dec Json.533;
let Json.532 : I64 = 125i64;
let Json.531 : U8 = CallByName Num.127 Json.532;
let Json.530 : List U8 = CallByName List.4 Json.123 Json.531;
@ -114,14 +110,9 @@ procedure Json.118 (Json.119, Json.486, Json.117):
procedure Json.120 (Json.488, Json.489):
let Json.126 : Str = StructAtIndex 0 Json.489;
inc Json.126;
let Json.127 : Str = StructAtIndex 1 Json.489;
inc Json.127;
dec Json.489;
let Json.124 : List U8 = StructAtIndex 0 Json.488;
inc Json.124;
let Json.125 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.516 : I64 = 34i64;
let Json.515 : U8 = CallByName Num.127 Json.516;
let Json.513 : List U8 = CallByName List.4 Json.124 Json.515;
@ -153,14 +144,9 @@ procedure Json.120 (Json.488, Json.489):
procedure Json.120 (Json.488, Json.489):
let Json.126 : Str = StructAtIndex 0 Json.489;
inc Json.126;
let Json.127 : Str = StructAtIndex 1 Json.489;
inc Json.127;
dec Json.489;
let Json.124 : List U8 = StructAtIndex 0 Json.488;
inc Json.124;
let Json.125 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.556 : I64 = 34i64;
let Json.555 : U8 = CallByName Num.127 Json.556;
let Json.553 : List U8 = CallByName List.4 Json.124 Json.555;
@ -261,6 +247,7 @@ procedure List.80 (List.547, List.548, List.549, List.550, List.551):
let List.524 : Int1 = CallByName Num.22 List.436 List.437;
if List.524 then
let List.531 : {Str, Str} = CallByName List.66 List.433 List.436;
inc List.531;
let List.525 : {List U8, U64} = CallByName List.139 List.434 List.531 List.435;
let List.528 : U64 = 1i64;
let List.527 : U64 = CallByName Num.19 List.436 List.528;
@ -276,6 +263,7 @@ procedure List.80 (List.621, List.622, List.623, List.624, List.625):
let List.597 : Int1 = CallByName Num.22 List.436 List.437;
if List.597 then
let List.604 : {Str, Str} = CallByName List.66 List.433 List.436;
inc List.604;
let List.598 : {List U8, U64} = CallByName List.139 List.434 List.604 List.435;
let List.601 : U64 = 1i64;
let List.600 : U64 = CallByName Num.19 List.436 List.601;
@ -333,14 +321,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.28;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -355,8 +342,6 @@ procedure Test.0 ():
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;

View file

@ -62,8 +62,6 @@ procedure Json.118 (Json.119, Json.486, Json.117):
let Json.495 : {} = Struct {};
let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495;
let Json.123 : List U8 = StructAtIndex 0 Json.493;
inc Json.123;
dec Json.493;
let Json.492 : I64 = 125i64;
let Json.491 : U8 = CallByName Num.127 Json.492;
let Json.490 : List U8 = CallByName List.4 Json.123 Json.491;
@ -71,14 +69,9 @@ procedure Json.118 (Json.119, Json.486, Json.117):
procedure Json.120 (Json.488, Json.489):
let Json.126 : Str = StructAtIndex 0 Json.489;
inc Json.126;
let Json.127 : Str = StructAtIndex 1 Json.489;
inc Json.127;
dec Json.489;
let Json.124 : List U8 = StructAtIndex 0 Json.488;
inc Json.124;
let Json.125 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.516 : I64 = 34i64;
let Json.515 : U8 = CallByName Num.127 Json.516;
let Json.513 : List U8 = CallByName List.4 Json.124 Json.515;
@ -159,6 +152,7 @@ procedure List.80 (List.554, List.555, List.556, List.557, List.558):
let List.530 : Int1 = CallByName Num.22 List.436 List.437;
if List.530 then
let List.537 : {Str, Str} = CallByName List.66 List.433 List.436;
inc List.537;
let List.531 : {List U8, U64} = CallByName List.139 List.434 List.537 List.435;
let List.534 : U64 = 1i64;
let List.533 : U64 = CallByName Num.19 List.436 List.534;
@ -210,14 +204,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.14 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.14;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -232,8 +225,6 @@ procedure Test.0 ():
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;

View file

@ -10,8 +10,7 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1):
let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.11, #Derived_gen.12};
let #Derived_gen.8 : Str = "b";
let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1;
inc #Derived_gen.10;
dec #Derived.1;
dec #Derived_gen.13;
let #Derived_gen.9 : Str = CallByName Json.18 #Derived_gen.10;
let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9};
let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7];
@ -70,8 +69,6 @@ procedure Json.118 (Json.119, Json.486, Json.117):
let Json.495 : {} = Struct {};
let Json.493 : {List U8, U64} = CallByName List.18 Json.117 Json.494 Json.495;
let Json.123 : List U8 = StructAtIndex 0 Json.493;
inc Json.123;
dec Json.493;
let Json.492 : I64 = 125i64;
let Json.491 : U8 = CallByName Num.127 Json.492;
let Json.490 : List U8 = CallByName List.4 Json.123 Json.491;
@ -79,14 +76,9 @@ procedure Json.118 (Json.119, Json.486, Json.117):
procedure Json.120 (Json.488, Json.489):
let Json.126 : Str = StructAtIndex 0 Json.489;
inc Json.126;
let Json.127 : Str = StructAtIndex 1 Json.489;
inc Json.127;
dec Json.489;
let Json.124 : List U8 = StructAtIndex 0 Json.488;
inc Json.124;
let Json.125 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.516 : I64 = 34i64;
let Json.515 : U8 = CallByName Num.127 Json.516;
let Json.513 : List U8 = CallByName List.4 Json.124 Json.515;
@ -167,6 +159,7 @@ procedure List.80 (List.554, List.555, List.556, List.557, List.558):
let List.530 : Int1 = CallByName Num.22 List.436 List.437;
if List.530 then
let List.537 : {Str, Str} = CallByName List.66 List.433 List.436;
inc List.537;
let List.531 : {List U8, U64} = CallByName List.139 List.434 List.537 List.435;
let List.534 : U64 = 1i64;
let List.533 : U64 = CallByName Num.19 List.436 List.534;
@ -218,14 +211,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.18 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.18;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -242,8 +234,6 @@ procedure Test.0 ():
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;

View file

@ -71,14 +71,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.0 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.0;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -93,8 +92,6 @@ procedure Test.0 ():
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;

View file

@ -57,10 +57,7 @@ procedure Json.102 (Json.103, Json.527, Json.101):
procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.143 : List Str = StructAtIndex 1 #Attr.12;
inc Json.143;
let Json.142 : Str = StructAtIndex 0 #Attr.12;
inc Json.142;
dec #Attr.12;
let Json.524 : I64 = 123i64;
let Json.523 : U8 = CallByName Num.127 Json.524;
let Json.520 : List U8 = CallByName List.4 Json.145 Json.523;
@ -83,8 +80,6 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.497 : {} = Struct {};
let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497;
let Json.149 : List U8 = StructAtIndex 0 Json.495;
inc Json.149;
dec Json.495;
let Json.494 : I64 = 93i64;
let Json.493 : U8 = CallByName Num.127 Json.494;
let Json.490 : List U8 = CallByName List.4 Json.149 Json.493;
@ -95,9 +90,7 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
procedure Json.146 (Json.488, Json.152):
let Json.150 : List U8 = StructAtIndex 0 Json.488;
inc Json.150;
let Json.151 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.507 : {} = Struct {};
let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507;
joinpoint Json.502 Json.154:
@ -168,6 +161,7 @@ procedure List.80 (List.560, List.561, List.562, List.563, List.564):
let List.536 : Int1 = CallByName Num.22 List.436 List.437;
if List.536 then
let List.543 : Str = CallByName List.66 List.433 List.436;
inc List.543;
let List.537 : {List U8, U64} = CallByName List.139 List.434 List.543 List.435;
let List.540 : U64 = 1i64;
let List.539 : U64 = CallByName Num.19 List.436 List.540;
@ -219,14 +213,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.14 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.14;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -241,8 +234,6 @@ procedure Test.0 ():
let Test.9 : Int1 = lowlevel Eq Test.7 Test.8;
if Test.9 then
let Test.4 : Str = UnionAtIndex (Id 1) (Index 0) Test.2;
inc Test.4;
dec Test.2;
ret Test.4;
else
dec Test.2;

View file

@ -8,10 +8,7 @@ procedure #Derived.4 (#Derived.5, #Derived.6, #Derived.1):
ret #Derived_gen.3;
in
let #Derived.2 : Str = StructAtIndex 0 #Derived.1;
inc #Derived.2;
let #Derived.3 : Str = StructAtIndex 1 #Derived.1;
inc #Derived.3;
dec #Derived.1;
let #Derived_gen.7 : Str = "A";
let #Derived_gen.9 : Str = CallByName Json.18 #Derived.2;
let #Derived_gen.10 : Str = CallByName Json.18 #Derived.3;
@ -63,10 +60,7 @@ procedure Json.102 (Json.103, Json.527, Json.101):
procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.143 : List Str = StructAtIndex 1 #Attr.12;
inc Json.143;
let Json.142 : Str = StructAtIndex 0 #Attr.12;
inc Json.142;
dec #Attr.12;
let Json.524 : I64 = 123i64;
let Json.523 : U8 = CallByName Num.127 Json.524;
let Json.520 : List U8 = CallByName List.4 Json.145 Json.523;
@ -89,8 +83,6 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.497 : {} = Struct {};
let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497;
let Json.149 : List U8 = StructAtIndex 0 Json.495;
inc Json.149;
dec Json.495;
let Json.494 : I64 = 93i64;
let Json.493 : U8 = CallByName Num.127 Json.494;
let Json.490 : List U8 = CallByName List.4 Json.149 Json.493;
@ -101,9 +93,7 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
procedure Json.146 (Json.488, Json.152):
let Json.150 : List U8 = StructAtIndex 0 Json.488;
inc Json.150;
let Json.151 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.507 : {} = Struct {};
let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507;
joinpoint Json.502 Json.154:
@ -174,6 +164,7 @@ procedure List.80 (List.560, List.561, List.562, List.563, List.564):
let List.536 : Int1 = CallByName Num.22 List.436 List.437;
if List.536 then
let List.543 : Str = CallByName List.66 List.433 List.436;
inc List.543;
let List.537 : {List U8, U64} = CallByName List.139 List.434 List.543 List.435;
let List.540 : U64 = 1i64;
let List.539 : U64 = CallByName Num.19 List.436 List.540;
@ -225,14 +216,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.15 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.15;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -249,8 +239,6 @@ procedure Test.0 ():
let Test.9 : Int1 = lowlevel Eq Test.7 Test.8;
if Test.9 then
let Test.4 : Str = UnionAtIndex (Id 1) (Index 0) Test.2;
inc Test.4;
dec Test.2;
ret Test.4;
else
dec Test.2;

View file

@ -6,11 +6,7 @@ procedure Test.0 ():
let Test.10 : Int1 = lowlevel Eq Test.8 Test.9;
if Test.10 then
let Test.3 : Str = UnionAtIndex (Id 1) (Index 0) Test.11;
inc Test.3;
dec Test.11;
ret Test.3;
else
let Test.4 : Str = UnionAtIndex (Id 0) (Index 0) Test.11;
inc Test.4;
dec Test.11;
ret Test.4;

View file

@ -13,9 +13,14 @@ procedure Test.2 (Test.19):
ret Test.14;
else
let Test.5 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.7;
inc Test.5;
dec Test.7;
jump Test.13 Test.5;
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.7;
if #Derived_gen.1 then
decref Test.7;
jump Test.13 Test.5;
else
inc Test.5;
decref Test.7;
jump Test.13 Test.5;
in
jump Test.13 Test.19;

View file

@ -41,10 +41,7 @@ procedure Decode.26 (Decode.105, Decode.106):
procedure Decode.27 (Decode.107, Decode.108):
let Decode.122 : {List U8, [C {}, C Str]} = CallByName Decode.26 Decode.107 Decode.108;
let Decode.110 : List U8 = StructAtIndex 0 Decode.122;
inc Decode.110;
let Decode.109 : [C {}, C Str] = StructAtIndex 1 Decode.122;
inc Decode.109;
dec Decode.122;
inc Decode.110;
let Decode.125 : Int1 = CallByName List.1 Decode.110;
if Decode.125 then
@ -54,8 +51,6 @@ procedure Decode.27 (Decode.107, Decode.108):
let Decode.131 : Int1 = lowlevel Eq Decode.129 Decode.130;
if Decode.131 then
let Decode.111 : Str = UnionAtIndex (Id 1) (Index 0) Decode.109;
inc Decode.111;
dec Decode.109;
let Decode.126 : [C [C List U8, C ], C Str] = TagId(1) Decode.111;
ret Decode.126;
else
@ -72,10 +67,7 @@ procedure Decode.27 (Decode.107, Decode.108):
procedure Json.160 (Json.570, Json.571):
joinpoint Json.508 Json.505 Json.159:
let Json.162 : List U8 = StructAtIndex 0 Json.505;
inc Json.162;
let Json.161 : List U8 = StructAtIndex 1 Json.505;
inc Json.161;
dec Json.505;
joinpoint Json.546:
let Json.543 : {List U8, List U8} = Struct {Json.162, Json.161};
ret Json.543;
@ -199,10 +191,7 @@ procedure Json.42 (Json.298):
inc Json.298;
let Json.563 : {List U8, List U8} = CallByName List.52 Json.298 Json.564;
let Json.299 : List U8 = StructAtIndex 0 Json.563;
inc Json.299;
let Json.301 : List U8 = StructAtIndex 1 Json.563;
inc Json.301;
dec Json.563;
let Json.562 : U8 = 34i64;
let Json.561 : List U8 = Array [Json.562];
let Json.491 : Int1 = CallByName Bool.11 Json.299 Json.561;
@ -213,23 +202,18 @@ procedure Json.42 (Json.298):
let Json.504 : {} = Struct {};
let Json.503 : {List U8, List U8} = CallByName Json.24 Json.301 Json.504;
let Json.304 : List U8 = StructAtIndex 0 Json.503;
inc Json.304;
let Json.303 : List U8 = StructAtIndex 1 Json.503;
inc Json.303;
dec Json.503;
let Json.492 : [C {U64, U8}, C Str] = CallByName Str.9 Json.303;
let Json.500 : U8 = 1i64;
let Json.501 : U8 = GetTagId Json.492;
let Json.502 : Int1 = lowlevel Eq Json.500 Json.501;
if Json.502 then
let Json.307 : Str = UnionAtIndex (Id 1) (Index 0) Json.492;
inc Json.307;
dec Json.492;
let Json.496 : U64 = 1i64;
let Json.495 : {List U8, List U8} = CallByName List.52 Json.304 Json.496;
let Json.309 : List U8 = StructAtIndex 1 Json.495;
inc Json.309;
dec Json.495;
let #Derived_gen.0 : List U8 = StructAtIndex 0 Json.495;
dec #Derived_gen.0;
let Json.494 : [C {}, C Str] = TagId(1) Json.307;
let Json.493 : {List U8, [C {}, C Str]} = Struct {Json.309, Json.494};
ret Json.493;
@ -358,14 +342,13 @@ procedure Str.9 (Str.79):
let Str.302 : Int1 = StructAtIndex 2 Str.80;
if Str.302 then
let Str.304 : Str = StructAtIndex 1 Str.80;
inc Str.304;
dec Str.80;
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
ret Str.303;
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.1 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.1;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;

View file

@ -45,10 +45,7 @@ procedure Decode.26 (Decode.105, Decode.106):
procedure Json.160 (Json.570, Json.571):
joinpoint Json.508 Json.505 Json.159:
let Json.162 : List U8 = StructAtIndex 0 Json.505;
inc Json.162;
let Json.161 : List U8 = StructAtIndex 1 Json.505;
inc Json.161;
dec Json.505;
joinpoint Json.546:
let Json.543 : {List U8, List U8} = Struct {Json.162, Json.161};
ret Json.543;
@ -172,10 +169,7 @@ procedure Json.42 (Json.298):
inc Json.298;
let Json.563 : {List U8, List U8} = CallByName List.52 Json.298 Json.564;
let Json.299 : List U8 = StructAtIndex 0 Json.563;
inc Json.299;
let Json.301 : List U8 = StructAtIndex 1 Json.563;
inc Json.301;
dec Json.563;
let Json.562 : U8 = 34i64;
let Json.561 : List U8 = Array [Json.562];
let Json.491 : Int1 = CallByName Bool.11 Json.299 Json.561;
@ -186,23 +180,18 @@ procedure Json.42 (Json.298):
let Json.504 : {} = Struct {};
let Json.503 : {List U8, List U8} = CallByName Json.24 Json.301 Json.504;
let Json.304 : List U8 = StructAtIndex 0 Json.503;
inc Json.304;
let Json.303 : List U8 = StructAtIndex 1 Json.503;
inc Json.303;
dec Json.503;
let Json.492 : [C {U64, U8}, C Str] = CallByName Str.9 Json.303;
let Json.500 : U8 = 1i64;
let Json.501 : U8 = GetTagId Json.492;
let Json.502 : Int1 = lowlevel Eq Json.500 Json.501;
if Json.502 then
let Json.307 : Str = UnionAtIndex (Id 1) (Index 0) Json.492;
inc Json.307;
dec Json.492;
let Json.496 : U64 = 1i64;
let Json.495 : {List U8, List U8} = CallByName List.52 Json.304 Json.496;
let Json.309 : List U8 = StructAtIndex 1 Json.495;
inc Json.309;
dec Json.495;
let #Derived_gen.0 : List U8 = StructAtIndex 0 Json.495;
dec #Derived_gen.0;
let Json.494 : [C {}, C Str] = TagId(1) Json.307;
let Json.493 : {List U8, [C {}, C Str]} = Struct {Json.309, Json.494};
ret Json.493;
@ -351,14 +340,13 @@ procedure Str.9 (Str.79):
let Str.316 : Int1 = StructAtIndex 2 Str.80;
if Str.316 then
let Str.318 : Str = StructAtIndex 1 Str.80;
inc Str.318;
dec Str.80;
let Str.317 : [C {U64, U8}, C Str] = TagId(1) Str.318;
ret Str.317;
else
let Str.314 : U8 = StructAtIndex 3 Str.80;
let Str.315 : U64 = StructAtIndex 0 Str.80;
dec Str.80;
let #Derived_gen.1 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.1;
let Str.313 : {U64, U8} = Struct {Str.315, Str.314};
let Str.312 : [C {U64, U8}, C Str] = TagId(0) Str.313;
ret Str.312;
@ -369,17 +357,12 @@ procedure Test.0 ():
let Test.36 : {} = CallByName Json.2;
let Test.34 : {List U8, [C {}, C Str]} = CallByName Decode.26 Test.35 Test.36;
let Test.2 : List U8 = StructAtIndex 0 Test.34;
inc Test.2;
let Test.1 : [C {}, C Str] = StructAtIndex 1 Test.34;
inc Test.1;
dec Test.34;
let Test.31 : U8 = 1i64;
let Test.32 : U8 = GetTagId Test.1;
let Test.33 : Int1 = lowlevel Eq Test.31 Test.32;
if Test.33 then
let Test.3 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.3;
dec Test.1;
let Test.19 : [C {}, C I64] = CallByName Str.27 Test.3;
let Test.25 : U8 = 1i64;
let Test.26 : U8 = GetTagId Test.19;

View file

@ -12,20 +12,16 @@ procedure Test.2 (Test.7):
procedure Test.6 (Test.17, #Attr.12):
let Test.5 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
dec #Attr.12;
let Test.31 : Str = "";
ret Test.31;
procedure Test.6 (Test.17, #Attr.12):
let Test.5 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
dec #Attr.12;
let Test.19 : Str = "";
ret Test.19;
procedure Test.8 (Test.23, #Attr.12):
let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12;
inc Test.7;
dec #Attr.12;
ret Test.7;
procedure Test.0 ():

View file

@ -19,6 +19,7 @@ procedure List.80 (List.517, List.518, List.519, List.520, List.521):
let List.502 : Int1 = CallByName Num.22 List.436 List.437;
if List.502 then
let List.509 : [<rnu>C *self, <null>] = CallByName List.66 List.433 List.436;
inc List.509;
let List.503 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.139 List.434 List.509 List.435;
let List.506 : U64 = 1i64;
let List.505 : U64 = CallByName Num.19 List.436 List.506;

View file

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

View file

@ -1,8 +1,6 @@
procedure List.3 (List.104, List.105, List.106):
let List.497 : {List I64, I64} = CallByName List.64 List.104 List.105 List.106;
let List.496 : List I64 = StructAtIndex 0 List.497;
inc List.496;
dec List.497;
ret List.496;
procedure List.6 (#Attr.2):

View file

@ -3,6 +3,7 @@ procedure List.2 (List.96, List.97):
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
if List.496 then
let List.498 : Str = CallByName List.66 List.96 List.97;
inc List.498;
dec List.96;
let List.497 : [C {}, C Str] = TagId(1) List.498;
ret List.497;
@ -66,8 +67,6 @@ procedure Test.0 ():
let Test.11 : Int1 = lowlevel Eq Test.9 Test.10;
if Test.11 then
let Test.5 : Str = UnionAtIndex (Id 1) (Index 0) Test.6;
inc Test.5;
dec Test.6;
ret Test.5;
else
dec Test.6;

View file

@ -3,6 +3,7 @@ procedure List.2 (List.96, List.97):
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
if List.496 then
let List.498 : Str = CallByName List.66 List.96 List.97;
inc List.498;
dec List.96;
let List.497 : [C {}, C Str] = TagId(1) List.498;
ret List.497;
@ -62,8 +63,6 @@ procedure Test.0 ():
let Test.11 : Int1 = lowlevel Eq Test.9 Test.10;
if Test.11 then
let Test.5 : Str = UnionAtIndex (Id 1) (Index 0) Test.6;
inc Test.5;
dec Test.6;
ret Test.5;
else
dec Test.6;

View file

@ -1,8 +1,6 @@
procedure List.3 (List.104, List.105, List.106):
let List.495 : {List I64, I64} = CallByName List.64 List.104 List.105 List.106;
let List.494 : List I64 = StructAtIndex 0 List.495;
inc List.494;
dec List.495;
ret List.494;
procedure List.6 (#Attr.2):

View file

@ -4,13 +4,9 @@ procedure Test.2 (Test.4):
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
if Test.12 then
let Test.5 : Str = UnionAtIndex (Id 0) (Index 0) Test.4;
inc Test.5;
dec Test.4;
ret Test.5;
else
let Test.6 : Str = UnionAtIndex (Id 1) (Index 0) Test.4;
inc Test.6;
dec Test.4;
ret Test.6;
procedure Test.0 ():

View file

@ -17,21 +17,17 @@ procedure Test.0 ():
let Test.19 : {I64, Str} = CallByName Test.1;
let Test.5 : {{I64, Str}, {}} = Struct {Test.19, Test.18};
let Test.14 : {I64, Str} = StructAtIndex 0 Test.5;
inc Test.14;
let Test.15 : I64 = StructAtIndex 0 Test.14;
dec Test.14;
let Test.16 : I64 = 42i64;
let Test.17 : Int1 = lowlevel Eq Test.16 Test.15;
if Test.17 then
let Test.13 : {I64, Str} = StructAtIndex 0 Test.5;
inc Test.13;
dec Test.5;
let Test.7 : {I64, Str} = CallByName Test.1;
let Test.6 : Int1 = CallByName Bool.11 Test.7 Test.13;
dec Test.13;
dec Test.7;
ret Test.6;
else
dec Test.5;
dec Test.14;
let Test.11 : Int1 = CallByName Bool.1;
ret Test.11;

View file

@ -21,9 +21,11 @@ procedure Test.0 ():
let Test.6 : {I64, Str} = CallByName Test.1;
let Test.5 : Int1 = CallByName Bool.11 Test.6 Test.4;
dec Test.6;
dec Test.4;
let #Derived_gen.0 : Str = StructAtIndex 1 Test.4;
dec #Derived_gen.0;
ret Test.5;
else
dec Test.4;
let #Derived_gen.1 : Str = StructAtIndex 1 Test.4;
dec #Derived_gen.1;
let Test.10 : Int1 = CallByName Bool.1;
ret Test.10;

View file

@ -8,19 +8,26 @@ procedure Test.0 ():
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
if Test.18 then
let Test.12 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.2;
inc Test.12;
dec Test.2;
let Test.13 : U8 = 0i64;
let Test.14 : U8 = GetTagId Test.12;
dec Test.12;
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
if Test.15 then
let Test.8 : I64 = 1i64;
ret Test.8;
joinpoint #Derived_gen.2:
let Test.13 : U8 = 0i64;
let Test.14 : U8 = GetTagId Test.12;
dec Test.12;
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
if Test.15 then
let Test.8 : I64 = 1i64;
ret Test.8;
else
let Test.9 : I64 = 0i64;
ret Test.9;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.2;
if #Derived_gen.3 then
decref Test.2;
jump #Derived_gen.2;
else
let Test.9 : I64 = 0i64;
ret Test.9;
inc Test.12;
decref Test.2;
jump #Derived_gen.2;
else
dec Test.2;
let Test.10 : I64 = 0i64;
ret Test.10;

View file

@ -20,8 +20,6 @@ procedure Test.1 (Test.24, Test.25, Test.26):
let Test.21 : {I64, List I64} = Struct {Test.22, Test.23};
let Test.5 : I64 = StructAtIndex 0 Test.21;
let Test.6 : List I64 = StructAtIndex 1 Test.21;
inc Test.6;
dec Test.21;
let Test.20 : I64 = 1i64;
let Test.19 : I64 = CallByName Num.20 Test.5 Test.20;
let Test.16 : List I64 = CallByName Test.1 Test.6 Test.3 Test.19;

View file

@ -15,8 +15,6 @@ procedure List.2 (List.96, List.97):
procedure List.3 (List.104, List.105, List.106):
let List.503 : {List I64, I64} = CallByName List.64 List.104 List.105 List.106;
let List.502 : List I64 = StructAtIndex 0 List.503;
inc List.502;
dec List.503;
ret List.502;
procedure List.6 (#Attr.2):

View file

@ -26,21 +26,29 @@ procedure Test.3 (Test.7):
procedure Test.6 (Test.16, #Attr.12):
let Test.5 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
dec #Attr.12;
let Test.19 : {} = Struct {};
let Test.22 : Str = "foobar";
let Test.20 : [<rnu><null>, C {}] = CallByName Test.8 Test.22 Test.5;
let Test.21 : U8 = GetTagId Test.20;
switch Test.21:
case 0:
let Test.18 : Str = CallByName Test.6 Test.19 Test.20;
ret Test.18;
default:
dec Test.20;
let Test.18 : Str = CallByName Test.11 Test.19;
ret Test.18;
joinpoint #Derived_gen.0:
let Test.19 : {} = Struct {};
let Test.22 : Str = "foobar";
let Test.20 : [<rnu><null>, C {}] = CallByName Test.8 Test.22 Test.5;
let Test.21 : U8 = GetTagId Test.20;
switch Test.21:
case 0:
let Test.18 : Str = CallByName Test.6 Test.19 Test.20;
ret Test.18;
default:
dec Test.20;
let Test.18 : Str = CallByName Test.11 Test.19;
ret Test.18;
in
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.1 then
decref #Attr.12;
jump #Derived_gen.0;
else
decref #Attr.12;
jump #Derived_gen.0;
procedure Test.8 (Test.9, Test.7):
let Test.24 : [<rnu><null>, C {}] = CallByName Test.10 Test.9;

View file

@ -39,19 +39,27 @@ procedure Test.4 (Test.28, Test.29):
joinpoint Test.15 Test.5 #Attr.12:
let Test.2 : U8 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
let Test.3 : [<rnu><null>, C *self U8] = UnionAtIndex (Id 0) (Index 0) #Attr.12;
inc Test.3;
dec #Attr.12;
let Test.17 : U8 = CallByName Num.21 Test.2 Test.5;
let Test.18 : U8 = GetTagId Test.3;
switch Test.18:
case 0:
jump Test.15 Test.17 Test.3;
default:
dec Test.3;
let Test.16 : U8 = CallByName Test.6 Test.17;
ret Test.16;
joinpoint #Derived_gen.1:
let Test.17 : U8 = CallByName Num.21 Test.2 Test.5;
let Test.18 : U8 = GetTagId Test.3;
switch Test.18:
case 0:
jump Test.15 Test.17 Test.3;
default:
dec Test.3;
let Test.16 : U8 = CallByName Test.6 Test.17;
ret Test.16;
in
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.2 then
decref #Attr.12;
jump #Derived_gen.1;
else
inc Test.3;
decref #Attr.12;
jump #Derived_gen.1;
in
jump Test.15 Test.28 Test.29;

View file

@ -8,8 +8,13 @@ procedure Str.3 (#Attr.2, #Attr.3):
procedure Test.11 (Test.29, #Attr.12):
let Test.10 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
dec #Attr.12;
ret Test.10;
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.2 then
decref #Attr.12;
ret Test.10;
else
decref #Attr.12;
ret Test.10;
procedure Test.11 (Test.29, Test.10):
ret Test.10;
@ -18,20 +23,28 @@ procedure Test.14 (Test.62, Test.63):
joinpoint Test.37 Test.36 #Attr.12:
let Test.12 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.13 : I64 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
dec #Attr.12;
let Test.43 : {} = Struct {};
let Test.42 : {} = CallByName Test.11 Test.43 Test.12;
let Test.38 : [<r>C {}, C I64 {}] = CallByName Test.9 Test.42 Test.13;
let Test.40 : {} = Struct {};
let Test.41 : U8 = GetTagId Test.38;
switch Test.41:
case 0:
let Test.39 : {} = CallByName Test.11 Test.40 Test.38;
ret Test.39;
default:
jump Test.37 Test.40 Test.38;
joinpoint #Derived_gen.0:
let Test.43 : {} = Struct {};
let Test.42 : {} = CallByName Test.11 Test.43 Test.12;
let Test.38 : [<r>C {}, C I64 {}] = CallByName Test.9 Test.42 Test.13;
let Test.40 : {} = Struct {};
let Test.41 : U8 = GetTagId Test.38;
switch Test.41:
case 0:
let Test.39 : {} = CallByName Test.11 Test.40 Test.38;
ret Test.39;
default:
jump Test.37 Test.40 Test.38;
in
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.1 then
decref #Attr.12;
jump #Derived_gen.0;
else
decref #Attr.12;
jump #Derived_gen.0;
in
jump Test.37 Test.62 Test.63;

View file

@ -15,8 +15,6 @@ procedure List.2 (List.96, List.97):
procedure List.3 (List.104, List.105, List.106):
let List.503 : {List I64, I64} = CallByName List.64 List.104 List.105 List.106;
let List.502 : List I64 = StructAtIndex 0 List.503;
inc List.502;
dec List.503;
ret List.502;
procedure List.6 (#Attr.2):

View file

@ -46,10 +46,7 @@ procedure Json.102 (Json.103, Json.530, Json.101):
procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.143 : List Str = StructAtIndex 1 #Attr.12;
inc Json.143;
let Json.142 : Str = StructAtIndex 0 #Attr.12;
inc Json.142;
dec #Attr.12;
let Json.524 : I64 = 123i64;
let Json.523 : U8 = CallByName Num.127 Json.524;
let Json.520 : List U8 = CallByName List.4 Json.145 Json.523;
@ -72,8 +69,6 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.497 : {} = Struct {};
let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497;
let Json.149 : List U8 = StructAtIndex 0 Json.495;
inc Json.149;
dec Json.495;
let Json.494 : I64 = 93i64;
let Json.493 : U8 = CallByName Num.127 Json.494;
let Json.490 : List U8 = CallByName List.4 Json.149 Json.493;
@ -84,9 +79,7 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
procedure Json.146 (Json.488, Json.152):
let Json.150 : List U8 = StructAtIndex 0 Json.488;
inc Json.150;
let Json.151 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.507 : {} = Struct {};
let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507;
joinpoint Json.502 Json.154:
@ -153,6 +146,7 @@ procedure List.80 (List.558, List.559, List.560, List.561, List.562):
let List.534 : Int1 = CallByName Num.22 List.436 List.437;
if List.534 then
let List.541 : Str = CallByName List.66 List.433 List.436;
inc List.541;
let List.535 : {List U8, U64} = CallByName List.139 List.434 List.541 List.435;
let List.538 : U64 = 1i64;
let List.537 : U64 = CallByName Num.19 List.436 List.538;
@ -212,8 +206,8 @@ procedure Test.5 (Test.6, Test.7, Test.4):
if Test.25 then
let Test.26 : Str = "A";
let Test.29 : Str = StructAtIndex 0 Test.4;
inc Test.29;
dec Test.4;
let #Derived_gen.0 : Str = StructAtIndex 1 Test.4;
dec #Derived_gen.0;
let Test.28 : Str = CallByName Json.18 Test.29;
let Test.27 : List Str = Array [Test.28];
let Test.19 : {Str, List Str} = CallByName Json.22 Test.26 Test.27;
@ -221,8 +215,8 @@ procedure Test.5 (Test.6, Test.7, Test.4):
else
let Test.21 : Str = "B";
let Test.24 : Str = StructAtIndex 1 Test.4;
inc Test.24;
dec Test.4;
let #Derived_gen.1 : Str = StructAtIndex 0 Test.4;
dec #Derived_gen.1;
let Test.23 : Str = CallByName Json.18 Test.24;
let Test.22 : List Str = Array [Test.23];
let Test.19 : {Str, List Str} = CallByName Json.22 Test.21 Test.22;

View file

@ -54,14 +54,13 @@ procedure Test.43 (Test.44, Test.42):
let Test.75 : Int1 = CallByName Bool.2;
if Test.75 then
let Test.77 : Str = StructAtIndex 0 Test.42;
inc Test.77;
dec Test.42;
let Test.76 : Int1 = CallByName Test.16 Test.77;
let Test.61 : Int1 = CallByName Test.14 Test.76;
jump Test.62 Test.61;
else
let Test.69 : U8 = StructAtIndex 1 Test.42;
dec Test.42;
let #Derived_gen.0 : Str = StructAtIndex 0 Test.42;
dec #Derived_gen.0;
let Test.63 : Int1 = CallByName Test.15 Test.69;
let Test.61 : Int1 = CallByName Test.14 Test.63;
jump Test.62 Test.61;

View file

@ -87,10 +87,7 @@ procedure Json.1 ():
procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.143 : List [C {}, C {}] = StructAtIndex 1 #Attr.12;
inc Json.143;
let Json.142 : Str = StructAtIndex 0 #Attr.12;
inc Json.142;
dec #Attr.12;
let Json.524 : I64 = 123i64;
let Json.523 : U8 = CallByName Num.127 Json.524;
let Json.520 : List U8 = CallByName List.4 Json.145 Json.523;
@ -113,8 +110,6 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.497 : {} = Struct {};
let Json.495 : {List U8, U64} = CallByName List.18 Json.143 Json.496 Json.497;
let Json.149 : List U8 = StructAtIndex 0 Json.495;
inc Json.149;
dec Json.495;
let Json.494 : I64 = 93i64;
let Json.493 : U8 = CallByName Num.127 Json.494;
let Json.490 : List U8 = CallByName List.4 Json.149 Json.493;
@ -125,10 +120,7 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.143 : List [] = StructAtIndex 1 #Attr.12;
inc Json.143;
let Json.142 : Str = StructAtIndex 0 #Attr.12;
inc Json.142;
dec #Attr.12;
let Json.574 : I64 = 123i64;
let Json.573 : U8 = CallByName Num.127 Json.574;
let Json.570 : List U8 = CallByName List.4 Json.145 Json.573;
@ -151,8 +143,6 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
let Json.547 : {} = Struct {};
let Json.545 : {List U8, U64} = CallByName List.18 Json.143 Json.546 Json.547;
let Json.149 : List U8 = StructAtIndex 0 Json.545;
inc Json.149;
dec Json.545;
let Json.544 : I64 = 93i64;
let Json.543 : U8 = CallByName Num.127 Json.544;
let Json.540 : List U8 = CallByName List.4 Json.149 Json.543;
@ -163,9 +153,7 @@ procedure Json.144 (Json.145, Json.486, #Attr.12):
procedure Json.146 (Json.488, Json.152):
let Json.150 : List U8 = StructAtIndex 0 Json.488;
inc Json.150;
let Json.151 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.507 : {} = Struct {};
let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.507;
joinpoint Json.502 Json.154:
@ -186,9 +174,7 @@ procedure Json.146 (Json.488, Json.152):
procedure Json.146 (Json.488, Json.152):
let Json.150 : List U8 = StructAtIndex 0 Json.488;
inc Json.150;
let Json.151 : U64 = StructAtIndex 1 Json.488;
dec Json.488;
let Json.557 : {} = Struct {};
let Json.153 : List U8 = CallByName Encode.24 Json.150 Json.152 Json.557;
joinpoint Json.552 Json.154:

View file

@ -26,7 +26,7 @@ procedure Test.0 ():
let Test.3 : Str = "http://www.example.com";
let Test.4 : {Str, Str} = CallByName Dep.0;
let Test.2 : Str = StructAtIndex 0 Test.4;
inc Test.2;
dec Test.4;
let #Derived_gen.0 : Str = StructAtIndex 1 Test.4;
dec #Derived_gen.0;
let Test.1 : {Str, Str} = Struct {Test.2, Test.3};
ret Test.1;