mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
inc/dec for recursive unions; first pass
This commit is contained in:
parent
75ceb9e843
commit
f02d907f17
4 changed files with 617 additions and 33 deletions
|
@ -22,7 +22,7 @@ use inkwell::values::BasicValueEnum::{self, *};
|
||||||
use inkwell::values::{BasicValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use inkwell::{IntPredicate, OptimizationLevel};
|
use inkwell::{IntPredicate, OptimizationLevel};
|
||||||
use roc_collections::all::{ImMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::ir::JoinPointId;
|
use roc_mono::ir::JoinPointId;
|
||||||
|
@ -79,6 +79,11 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RcFunctions<'ctx> {
|
||||||
|
inc: FunctionValue<'ctx>,
|
||||||
|
dec: FunctionValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Env<'a, 'ctx, 'env> {
|
pub struct Env<'a, 'ctx, 'env> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub context: &'ctx Context,
|
pub context: &'ctx Context,
|
||||||
|
@ -88,6 +93,7 @@ pub struct Env<'a, 'ctx, 'env> {
|
||||||
pub ptr_bytes: u32,
|
pub ptr_bytes: u32,
|
||||||
pub leak: bool,
|
pub leak: bool,
|
||||||
pub exposed_to_host: MutSet<Symbol>,
|
pub exposed_to_host: MutSet<Symbol>,
|
||||||
|
pub rc_functions: MutMap<Layout<'a>, RcFunctions<'ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||||
|
@ -358,7 +364,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
// If this is an external-facing function, use the C calling convention.
|
// If this is an external-facing function, use the C calling convention.
|
||||||
call.set_call_convention(C_CALL_CONV);
|
call.set_call_convention(C_CALL_CONV);
|
||||||
} else {
|
} else {
|
||||||
// If it's an internal-only function, use the fast calling conention.
|
// If it's an internal-only function, use the fast calling convention.
|
||||||
call.set_call_convention(FAST_CALL_CONV);
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,6 +577,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Tag { .. } => unreachable!("tags should have a union layout"),
|
Tag { .. } => unreachable!("tags should have a union layout"),
|
||||||
|
|
||||||
|
Reset(_) => todo!(),
|
||||||
|
Reuse { .. } => todo!(),
|
||||||
|
|
||||||
AccessAtIndex {
|
AccessAtIndex {
|
||||||
index,
|
index,
|
||||||
structure,
|
structure,
|
||||||
|
@ -684,6 +694,8 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||||
let len_type = env.ptr_int();
|
let len_type = env.ptr_int();
|
||||||
// bytes per element
|
// bytes per element
|
||||||
let bytes_len = len_type.const_int(value_bytes, false);
|
let bytes_len = len_type.const_int(value_bytes, false);
|
||||||
|
|
||||||
|
// TODO fix offset
|
||||||
let offset = (env.ptr_bytes as u64).max(value_bytes);
|
let offset = (env.ptr_bytes as u64).max(value_bytes);
|
||||||
|
|
||||||
let ptr = {
|
let ptr = {
|
||||||
|
@ -1022,6 +1034,11 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
||||||
let layout = layout.clone();
|
let layout = layout.clone();
|
||||||
|
|
||||||
|
if layout.contains_refcounted() {
|
||||||
|
increment_refcount_layout(env, parent, layout_ids, value, &layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
|
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
|
||||||
increment_refcount_list(env, parent, value.into_struct_value());
|
increment_refcount_list(env, parent, value.into_struct_value());
|
||||||
|
@ -1029,13 +1046,16 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
_ => build_exp_stmt(env, layout_ids, scope, parent, cont),
|
_ => build_exp_stmt(env, layout_ids, scope, parent, cont),
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
||||||
}
|
}
|
||||||
Dec(symbol, cont) => {
|
Dec(symbol, cont) => {
|
||||||
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
||||||
let layout = layout.clone();
|
let layout = layout.clone();
|
||||||
|
|
||||||
if layout.contains_refcounted() {
|
if layout.contains_refcounted() {
|
||||||
decrement_refcount_layout(env, parent, value, &layout);
|
decrement_refcount_layout(env, parent, layout_ids, value, &layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
||||||
|
@ -1058,31 +1078,57 @@ fn refcount_is_one_comparison<'ctx>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn list_get_refcount_ptr<'a, 'ctx, 'env>(
|
fn list_get_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
list_wrapper: StructValue<'ctx>,
|
list_wrapper: StructValue<'ctx>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let builder = env.builder;
|
|
||||||
let ctx = env.context;
|
|
||||||
|
|
||||||
// pointer to usize
|
|
||||||
let ptr_bytes = env.ptr_bytes;
|
|
||||||
let int_type = ptr_int(ctx, ptr_bytes);
|
|
||||||
|
|
||||||
// fetch the pointer to the array data, as an integer
|
// fetch the pointer to the array data, as an integer
|
||||||
let ptr_as_int = builder
|
let ptr_as_int = env
|
||||||
|
.builder
|
||||||
.build_extract_value(list_wrapper, Builtin::WRAPPER_PTR, "read_list_ptr")
|
.build_extract_value(list_wrapper, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
// subtract ptr_size, to access the refcount
|
get_refcount_ptr_help(env, layout, ptr_as_int)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
ptr: PointerValue<'ctx>,
|
||||||
|
) -> PointerValue<'ctx> {
|
||||||
|
let ptr_as_int =
|
||||||
|
cast_basic_basic(env.builder, ptr.into(), env.context.i64_type().into()).into_int_value();
|
||||||
|
|
||||||
|
get_refcount_ptr_help(env, layout, ptr_as_int)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_refcount_ptr_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
ptr_as_int: IntValue<'ctx>,
|
||||||
|
) -> PointerValue<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
let value_bytes = layout.stack_size(env.ptr_bytes) as u64;
|
||||||
|
let offset = match dbg!(layout) {
|
||||||
|
Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64,
|
||||||
|
_ => (env.ptr_bytes as u64).max(value_bytes),
|
||||||
|
};
|
||||||
|
|
||||||
|
// subtract offset, to access the refcount
|
||||||
let refcount_ptr = builder.build_int_sub(
|
let refcount_ptr = builder.build_int_sub(
|
||||||
ptr_as_int,
|
ptr_as_int,
|
||||||
ctx.i64_type().const_int(env.ptr_bytes as u64, false),
|
ctx.i64_type().const_int(offset, false),
|
||||||
"make_refcount_ptr",
|
"make_refcount_ptr",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// pointer to usize
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
let int_type = ptr_int(ctx, ptr_bytes);
|
||||||
|
|
||||||
builder.build_int_to_ptr(
|
builder.build_int_to_ptr(
|
||||||
refcount_ptr,
|
refcount_ptr,
|
||||||
int_type.ptr_type(AddressSpace::Generic),
|
int_type.ptr_type(AddressSpace::Generic),
|
||||||
|
@ -1093,13 +1139,14 @@ fn list_get_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
use Layout::*;
|
use Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
Builtin(builtin) => decrement_refcount_builtin(env, parent, value, builtin),
|
Builtin(builtin) => decrement_refcount_builtin(env, parent, value, layout, builtin),
|
||||||
Struct(layouts) => {
|
Struct(layouts) => {
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
|
||||||
|
@ -1110,14 +1157,12 @@ fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
decrement_refcount_layout(env, parent, field_ptr, field_layout)
|
decrement_refcount_layout(env, parent, layout_ids, field_ptr, field_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RecursiveUnion(_) => {
|
|
||||||
println!("TODO implement decrement layout of recursive tag union");
|
|
||||||
}
|
|
||||||
RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"),
|
RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"),
|
||||||
|
|
||||||
Union(tags) => {
|
Union(tags) => {
|
||||||
debug_assert!(!tags.is_empty());
|
debug_assert!(!tags.is_empty());
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
@ -1145,7 +1190,7 @@ fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
decrement_refcount_layout(env, parent, field_ptr, field_layout)
|
decrement_refcount_layout(env, parent, layout_ids, field_ptr, field_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1161,6 +1206,10 @@ fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env.builder.position_at_end(merge_block);
|
env.builder.position_at_end(merge_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursiveUnion(tags) => {
|
||||||
|
build_dec_union(env, layout_ids, tags, value);
|
||||||
|
}
|
||||||
|
|
||||||
FunctionPointer(_, _) | Pointer(_) => {}
|
FunctionPointer(_, _) | Pointer(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1170,6 +1219,7 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
builtin: &Builtin<'a>,
|
builtin: &Builtin<'a>,
|
||||||
) {
|
) {
|
||||||
use Builtin::*;
|
use Builtin::*;
|
||||||
|
@ -1180,7 +1230,7 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
// TODO decrement all values
|
// TODO decrement all values
|
||||||
}
|
}
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
decrement_refcount_list(env, parent, wrapper_struct);
|
decrement_refcount_list(env, parent, layout, wrapper_struct);
|
||||||
}
|
}
|
||||||
List(MemoryMode::Unique, _element_layout) => {
|
List(MemoryMode::Unique, _element_layout) => {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -1189,16 +1239,71 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
if element_layout.contains_refcounted() {
|
if element_layout.contains_refcounted() {
|
||||||
// TODO decrement all values
|
// TODO decrement all values
|
||||||
}
|
}
|
||||||
let wrapper_struct = value.into_struct_value();
|
todo!();
|
||||||
decrement_refcount_list(env, parent, wrapper_struct);
|
|
||||||
}
|
}
|
||||||
Map(key_layout, value_layout) => {
|
Map(key_layout, value_layout) => {
|
||||||
if key_layout.contains_refcounted() || value_layout.contains_refcounted() {
|
if key_layout.contains_refcounted() || value_layout.contains_refcounted() {
|
||||||
// TODO decrement all values
|
// TODO decrement all values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increment_refcount_layout<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
use Layout::*;
|
||||||
|
|
||||||
|
match layout {
|
||||||
|
Builtin(builtin) => increment_refcount_builtin(env, parent, value, layout, builtin),
|
||||||
|
|
||||||
|
RecursiveUnion(tags) => {
|
||||||
|
build_inc_union(env, layout_ids, tags, value);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn increment_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
builtin: &Builtin<'a>,
|
||||||
|
) {
|
||||||
|
use Builtin::*;
|
||||||
|
|
||||||
|
match builtin {
|
||||||
|
List(MemoryMode::Refcounted, element_layout) => {
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
decrement_refcount_list(env, parent, wrapper_struct);
|
increment_refcount_list(env, parent, layout, wrapper_struct);
|
||||||
|
}
|
||||||
|
List(MemoryMode::Unique, _element_layout) => {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
Set(element_layout) => {
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
Map(key_layout, value_layout) => {
|
||||||
|
if key_layout.contains_refcounted() || value_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -1207,6 +1312,7 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
fn increment_refcount_list<'a, 'ctx, 'env>(
|
fn increment_refcount_list<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
@ -1229,7 +1335,7 @@ fn increment_refcount_list<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(increment_block);
|
builder.position_at_end(increment_block);
|
||||||
|
|
||||||
let refcount_ptr = list_get_refcount_ptr(env, original_wrapper);
|
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
||||||
|
|
||||||
let refcount = env
|
let refcount = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1253,6 +1359,7 @@ fn increment_refcount_list<'a, 'ctx, 'env>(
|
||||||
fn decrement_refcount_list<'a, 'ctx, 'env>(
|
fn decrement_refcount_list<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
@ -1282,7 +1389,7 @@ fn decrement_refcount_list<'a, 'ctx, 'env>(
|
||||||
let then_block = ctx.append_basic_block(parent, "then");
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
let else_block = ctx.append_basic_block(parent, "else");
|
let else_block = ctx.append_basic_block(parent, "else");
|
||||||
|
|
||||||
let refcount_ptr = list_get_refcount_ptr(env, original_wrapper);
|
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
||||||
|
|
||||||
let refcount = env
|
let refcount = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1326,6 +1433,65 @@ fn decrement_refcount_list<'a, 'ctx, 'env>(
|
||||||
builder.position_at_end(cont_block);
|
builder.position_at_end(cont_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
field_ptr: PointerValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
// the block we'll always jump to when we're done
|
||||||
|
let cont_block = ctx.append_basic_block(parent, "after_decrement_block");
|
||||||
|
|
||||||
|
let refcount_ptr = get_refcount_ptr(env, layout, field_ptr);
|
||||||
|
|
||||||
|
let refcount = env
|
||||||
|
.builder
|
||||||
|
.build_load(refcount_ptr, "get_refcount")
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
|
||||||
|
|
||||||
|
// build blocks
|
||||||
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
|
let else_block = ctx.append_basic_block(parent, "else");
|
||||||
|
|
||||||
|
// TODO what would be most optimial for the branch predictor
|
||||||
|
//
|
||||||
|
// are most refcounts 1 most of the time? or not?
|
||||||
|
builder.build_conditional_branch(comparison, then_block, else_block);
|
||||||
|
|
||||||
|
// build then block
|
||||||
|
{
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
if !env.leak {
|
||||||
|
builder.build_free(refcount_ptr);
|
||||||
|
}
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build else block
|
||||||
|
{
|
||||||
|
builder.position_at_end(else_block);
|
||||||
|
// our refcount 0 is actually usize::MAX, so decrementing the refcount means incrementing this value.
|
||||||
|
let decremented = env.builder.build_int_add(
|
||||||
|
ctx.i64_type().const_int(1 as u64, false),
|
||||||
|
refcount,
|
||||||
|
"decremented_refcount",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mutate the new array in-place to change the element.
|
||||||
|
builder.build_store(refcount_ptr, decremented);
|
||||||
|
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit merge block
|
||||||
|
builder.position_at_end(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_symbol<'a, 'ctx, 'env>(
|
pub fn load_symbol<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
|
@ -2004,7 +2170,8 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
let ret_type =
|
let ret_type =
|
||||||
basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
||||||
|
|
||||||
let refcount_ptr = list_get_refcount_ptr(env, list_symbol.into_struct_value());
|
let refcount_ptr =
|
||||||
|
list_get_refcount_ptr(env, list_layout, list_symbol.into_struct_value());
|
||||||
|
|
||||||
let refcount = env
|
let refcount = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -2403,3 +2570,385 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_dec_header<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
fn_name: String,
|
||||||
|
) -> FunctionValue<'ctx> {
|
||||||
|
let arena = env.arena;
|
||||||
|
let context = &env.context;
|
||||||
|
|
||||||
|
let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes);
|
||||||
|
|
||||||
|
let fn_type = context.void_type().fn_type(&[arg_type], false);
|
||||||
|
|
||||||
|
let fn_val = env
|
||||||
|
.module
|
||||||
|
.add_function(fn_name.as_str(), fn_type, Some(Linkage::Private));
|
||||||
|
|
||||||
|
// If it's an internal-only function, it should use the fast calling convention.
|
||||||
|
fn_val.set_call_conventions(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
fn_val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_dec_union<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
) {
|
||||||
|
let layout = Layout::Union(fields);
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let symbol = Symbol::DEC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_inc_dec_header(env, &layout, fn_name);
|
||||||
|
|
||||||
|
build_dec_union_help(env, layout_ids, fields, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
let call = env
|
||||||
|
.builder
|
||||||
|
.build_call(function, &[value], "decrement_union");
|
||||||
|
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
tags: &[&[Layout<'a>]],
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
debug_assert!(!tags.is_empty());
|
||||||
|
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let context = &env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = context.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
|
// Add args to scope
|
||||||
|
let arg_symbol = Symbol::ARG_1;
|
||||||
|
let layout = Layout::Union(tags);
|
||||||
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
|
let alloca = create_entry_block_alloca(
|
||||||
|
env,
|
||||||
|
fn_val,
|
||||||
|
arg_val.get_type(),
|
||||||
|
arg_symbol.ident_string(&env.interns),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_store(alloca, arg_val);
|
||||||
|
|
||||||
|
scope.insert(arg_symbol, (layout.clone(), alloca));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let layout = Layout::RecursiveUnion(tags);
|
||||||
|
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let wrapper_struct = arg_val.into_struct_value();
|
||||||
|
|
||||||
|
// let tag_id_u8 = cast_basic_basic(env.builder, tag_id.into(), env.context.i8_type().into());
|
||||||
|
|
||||||
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||||
|
|
||||||
|
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||||
|
let block = env.context.append_basic_block(parent, "tag_id_decrement");
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
|
let wrapper_type = basic_type_from_layout(
|
||||||
|
env.arena,
|
||||||
|
env.context,
|
||||||
|
&Layout::Struct(field_layouts),
|
||||||
|
env.ptr_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let wrapper_struct =
|
||||||
|
cast_struct_struct(env.builder, wrapper_struct, wrapper_type.into_struct_type());
|
||||||
|
|
||||||
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if let Layout::RecursivePointer = field_layout {
|
||||||
|
// a *i64 pointer to the recursive data
|
||||||
|
// we need to cast this pointer to the appropriate type
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// recursively decrement
|
||||||
|
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||||
|
let recursive_field_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
field_ptr,
|
||||||
|
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
let recursive_field = env
|
||||||
|
.builder
|
||||||
|
.build_load(recursive_field_ptr, "load_recursive_field");
|
||||||
|
|
||||||
|
// recursively decrement the field
|
||||||
|
let call =
|
||||||
|
env.builder
|
||||||
|
.build_call(fn_val, &[recursive_field], "recursive_tag_decrement");
|
||||||
|
|
||||||
|
// Because it's an internal-only function, use the fast calling convention.
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
// first decrement the pointer itself
|
||||||
|
// This is so the recursive decrement may optimize into a tail call
|
||||||
|
decrement_refcount_ptr(env, parent, &layout, field_ptr.into_pointer_value());
|
||||||
|
} else if field_layout.contains_refcounted() {
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
decrement_refcount_layout(env, parent, layout_ids, field_ptr, field_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_unconditional_branch(merge_block);
|
||||||
|
|
||||||
|
cases.push((
|
||||||
|
env.context.i64_type().const_int(tag_id as u64, false),
|
||||||
|
block,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
cases.reverse();
|
||||||
|
|
||||||
|
let (_, default_block) = cases.pop().unwrap();
|
||||||
|
|
||||||
|
env.builder.position_at_end(before_block);
|
||||||
|
|
||||||
|
// read the tag_id
|
||||||
|
let current_tag_id = {
|
||||||
|
// the first element of the wrapping struct is an array of i64
|
||||||
|
let first_array = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, 0, "read_tag_id")
|
||||||
|
.unwrap()
|
||||||
|
.into_array_value();
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_extract_value(first_array, 0, "read_tag_id_2")
|
||||||
|
.unwrap()
|
||||||
|
.into_int_value()
|
||||||
|
};
|
||||||
|
|
||||||
|
// switch on it
|
||||||
|
env.builder
|
||||||
|
.build_switch(current_tag_id, default_block, &cases);
|
||||||
|
|
||||||
|
env.builder.position_at_end(merge_block);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_union<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
) {
|
||||||
|
let layout = Layout::Union(fields);
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let symbol = Symbol::INC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_inc_dec_header(env, &layout, fn_name);
|
||||||
|
|
||||||
|
build_inc_union_help(env, layout_ids, fields, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
let call = env
|
||||||
|
.builder
|
||||||
|
.build_call(function, &[value], "increment_union");
|
||||||
|
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_union_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
tags: &[&[Layout<'a>]],
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
debug_assert!(!tags.is_empty());
|
||||||
|
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let context = &env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = context.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
|
// Add args to scope
|
||||||
|
let arg_symbol = Symbol::ARG_1;
|
||||||
|
let layout = Layout::Union(tags);
|
||||||
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
|
let alloca = create_entry_block_alloca(
|
||||||
|
env,
|
||||||
|
fn_val,
|
||||||
|
arg_val.get_type(),
|
||||||
|
arg_symbol.ident_string(&env.interns),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_store(alloca, arg_val);
|
||||||
|
|
||||||
|
scope.insert(arg_symbol, (layout.clone(), alloca));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let layout = Layout::RecursiveUnion(tags);
|
||||||
|
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let wrapper_struct = arg_val.into_struct_value();
|
||||||
|
|
||||||
|
// read the tag_id
|
||||||
|
let tag_id = {
|
||||||
|
// the first element of the wrapping struct is an array of i64
|
||||||
|
let first_array = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, 0, "read_tag_id")
|
||||||
|
.unwrap()
|
||||||
|
.into_array_value();
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_extract_value(first_array, 0, "read_tag_id_2")
|
||||||
|
.unwrap()
|
||||||
|
.into_int_value()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tag_id_u8 = cast_basic_basic(env.builder, tag_id.into(), env.context.i8_type().into());
|
||||||
|
|
||||||
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||||
|
|
||||||
|
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||||
|
let block = env.context.append_basic_block(parent, "tag_id_decrement");
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
|
let wrapper_type = basic_type_from_layout(
|
||||||
|
env.arena,
|
||||||
|
env.context,
|
||||||
|
&Layout::Struct(field_layouts),
|
||||||
|
env.ptr_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let wrapper_struct =
|
||||||
|
cast_struct_struct(env.builder, wrapper_struct, wrapper_type.into_struct_type());
|
||||||
|
|
||||||
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if let Layout::RecursivePointer = field_layout {
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
|
.unwrap()
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
// recursively decrement
|
||||||
|
let recursive_field_ptr_as_int =
|
||||||
|
env.builder.build_load(field_ptr, "recursive_decrement");
|
||||||
|
|
||||||
|
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||||
|
let recursive_field_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
recursive_field_ptr_as_int,
|
||||||
|
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
let recursive_field = env
|
||||||
|
.builder
|
||||||
|
.build_load(recursive_field_ptr, "load_recursive_field");
|
||||||
|
|
||||||
|
// first decrement the pointer itself
|
||||||
|
// This is so the recursive decrement may optimize into a tail call
|
||||||
|
//decrement_refcount_ptr(env, parent, wrapper_struct, field_ptr);
|
||||||
|
|
||||||
|
// recursively decrement the field
|
||||||
|
let call =
|
||||||
|
env.builder
|
||||||
|
.build_call(fn_val, &[recursive_field], "recursive_tag_increment");
|
||||||
|
|
||||||
|
// Because it's an internal-only function, use the fast calling convention.
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
} else if field_layout.contains_refcounted() {
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "increment_struct_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
decrement_refcount_layout(env, parent, layout_ids, field_ptr, field_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_unconditional_branch(merge_block);
|
||||||
|
|
||||||
|
cases.push((env.context.i8_type().const_int(tag_id as u64, false), block));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, default_block) = cases.pop().unwrap();
|
||||||
|
|
||||||
|
env.builder.position_at_end(before_block);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_switch(tag_id_u8.into_int_value(), default_block, &cases);
|
||||||
|
|
||||||
|
env.builder.position_at_end(merge_block);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
|
@ -533,7 +533,36 @@ mod gen_primitives {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
i64
|
i64,
|
||||||
|
|x| x,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linked_list_len_1() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||||
|
|
||||||
|
one : LinkedList Int
|
||||||
|
one = Cons 1 Nil
|
||||||
|
|
||||||
|
length : LinkedList a -> Int
|
||||||
|
length = \list ->
|
||||||
|
when list is
|
||||||
|
Nil -> 0
|
||||||
|
Cons _ rest -> 1 + length rest
|
||||||
|
|
||||||
|
|
||||||
|
length one
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
i64,
|
||||||
|
|x| x,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +587,9 @@ mod gen_primitives {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
3,
|
3,
|
||||||
i64
|
i64,
|
||||||
|
|x| x,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
|
|
||||||
pub fn helper_without_uniqueness<'a>(
|
pub fn helper_without_uniqueness<'a>(
|
||||||
|
@ -90,6 +90,7 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: leak,
|
leak: leak,
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
|
rc_functions: MutMap::default(),
|
||||||
};
|
};
|
||||||
let mut procs = roc_mono::ir::Procs::default();
|
let mut procs = roc_mono::ir::Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
@ -184,7 +185,7 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
env.module.print_to_stderr();
|
||||||
|
|
||||||
if main_fn.verify(true) {
|
if main_fn.verify(true) {
|
||||||
function_pass.run_on(&main_fn);
|
function_pass.run_on(&main_fn);
|
||||||
|
@ -284,6 +285,7 @@ pub fn helper_with_uniqueness<'a>(
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: leak,
|
leak: leak,
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
|
rc_functions: MutMap::default(),
|
||||||
};
|
};
|
||||||
let mut procs = roc_mono::ir::Procs::default();
|
let mut procs = roc_mono::ir::Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
@ -475,7 +477,7 @@ macro_rules! assert_evals_to {
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
|
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assert_opt_evals_to!($src, $expected, $ty, (|val| val));
|
// assert_opt_evals_to!($src, $expected, $ty, (|val| val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
|
@ -493,7 +495,7 @@ macro_rules! assert_evals_to {
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
// assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -598,6 +598,8 @@ define_builtins! {
|
||||||
7 ARG_6: "#arg6"
|
7 ARG_6: "#arg6"
|
||||||
8 ARG_7: "#arg7"
|
8 ARG_7: "#arg7"
|
||||||
9 ARG_8: "#arg8"
|
9 ARG_8: "#arg8"
|
||||||
|
10 INC: "#inc" // internal function that increments the refcount
|
||||||
|
11 DEC: "#dec" // internal function that increments the refcount
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue