Skip to content

Commit

Permalink
llvm logic for tag field reuse
Browse files Browse the repository at this point in the history
  • Loading branch information
folkertdev committed Jul 30, 2023
1 parent 302b65a commit e082b5a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 29 deletions.
135 changes: 109 additions & 26 deletions crates/compiler/gen_llvm/src/llvm/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,25 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
tag_id,
reuse,
} => {
let reuse_ptr = reuse.map(|ru| scope.load_symbol(&ru.0.symbol).into_pointer_value());
let reuse = match reuse {
Some((token, mask)) => {
let ptr = scope.load_symbol(&token.symbol).into_pointer_value();

let reuse_tag = ReuseTag {
ptr,
// TODO this should really be token.can_reuse_tag_id,
can_reuse_tag_id: true,
can_reuse_argument_mask: *mask,
};

if reuse_tag.can_reuse_all(arguments.len()) {
return ptr.into();
} else {
Some(reuse_tag)
}
}
None => None,
};

build_tag(
env,
Expand All @@ -1101,7 +1119,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
union_layout,
*tag_id,
arguments,
reuse_ptr,
reuse,
parent,
)
}
Expand Down Expand Up @@ -1612,7 +1630,7 @@ fn build_wrapped_tag<'a, 'ctx>(
arguments: &[Symbol],
tag_field_layouts: &[InLayout<'a>],
tags: &[&[InLayout<'a>]],
reuse_allocation: Option<PointerValue<'ctx>>,
reuse_allocation: Option<ReuseTag<'ctx>>,
parent: FunctionValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
Expand All @@ -1625,7 +1643,7 @@ fn build_wrapped_tag<'a, 'ctx>(
let union_struct_type = struct_type_from_union_layout(env, layout_interner, union_layout);

// Create the struct_type
let raw_data_ptr = allocate_tag(
let (performed_reuse, raw_data_ptr) = allocate_tag(
env,
layout_interner,
parent,
Expand All @@ -1635,6 +1653,11 @@ fn build_wrapped_tag<'a, 'ctx>(
);
let struct_type = env.context.struct_type(&field_types, false);

let reuse_field_mask = reuse_allocation.and_then(|token| match token.can_reuse_argument_mask {
0 => None,
n => Some(n),
});

if union_layout.stores_tag_id_as_data(env.target_info) {
let tag_id_ptr = builder
.new_build_struct_gep(
Expand Down Expand Up @@ -1674,13 +1697,44 @@ fn build_wrapped_tag<'a, 'ctx>(

raw_data_ptr.into()
} else {
struct_pointer_from_fields(
env,
layout_interner,
struct_type,
raw_data_ptr,
field_values.into_iter().enumerate(),
);
if let Some(mask) = reuse_field_mask {
let then_block = env.context.append_basic_block(parent, "reuse_fields");
let else_block = env.context.append_basic_block(parent, "dont_reuse_fields");
let cont_block = env.context.append_basic_block(parent, "cont");

env.builder
.build_conditional_branch(performed_reuse, then_block, else_block);

{
env.builder.position_at_end(then_block);

let it = field_values
.iter()
.copied()
.enumerate()
.filter(|(i, _)| mask & (1 << i) == 0);

struct_pointer_from_fields(env, layout_interner, struct_type, raw_data_ptr, it);

env.builder.build_unconditional_branch(cont_block);
}

{
env.builder.position_at_end(else_block);

let it = field_values.into_iter().enumerate();

struct_pointer_from_fields(env, layout_interner, struct_type, raw_data_ptr, it);

env.builder.build_unconditional_branch(cont_block);
}

env.builder.position_at_end(cont_block);
} else {
// no reuse of fields
let it = field_values.into_iter().enumerate();
struct_pointer_from_fields(env, layout_interner, struct_type, raw_data_ptr, it);
}

tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into()
}
Expand Down Expand Up @@ -1772,7 +1826,7 @@ fn build_tag<'a, 'ctx>(
union_layout: &UnionLayout<'a>,
tag_id: TagIdIntType,
arguments: &[Symbol],
reuse_allocation: Option<PointerValue<'ctx>>,
reuse_allocation: Option<ReuseTag<'ctx>>,
parent: FunctionValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let union_size = union_layout.number_of_tags();
Expand Down Expand Up @@ -1903,7 +1957,7 @@ fn build_tag<'a, 'ctx>(
debug_assert!(union_size == 2);

// Create the struct_type
let data_ptr = allocate_tag(
let (_performed_reuse, data_ptr) = allocate_tag(
env,
layout_interner,
parent,
Expand Down Expand Up @@ -2017,18 +2071,33 @@ pub fn tag_pointer_clear_tag_id<'ctx>(
.build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr")
}

#[derive(Debug, Clone, Copy)]
struct ReuseTag<'ctx> {
ptr: PointerValue<'ctx>,
// is the tag id the same?
can_reuse_tag_id: bool,
// 1 if reuse, 0 if not
can_reuse_argument_mask: u64,
}

impl ReuseTag<'_> {
fn can_reuse_all(&self, n: usize) -> bool {
self.can_reuse_tag_id && self.can_reuse_argument_mask.trailing_ones() == n as u32
}
}

fn allocate_tag<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
parent: FunctionValue<'ctx>,
reuse_allocation: Option<PointerValue<'ctx>>,
reuse_allocation: Option<ReuseTag<'ctx>>,
union_layout: &UnionLayout<'a>,
tags: &[&[InLayout<'a>]],
) -> PointerValue<'ctx> {
) -> (IntValue<'ctx>, PointerValue<'ctx>) {
match reuse_allocation {
Some(ptr) => {
Some(reuse) => {
// check if its a null pointer
let is_null_ptr = env.builder.build_is_null(ptr, "is_null_ptr");
let is_null_ptr = env.builder.build_is_null(reuse.ptr, "is_null_ptr");
let ctx = env.context;
let then_block = ctx.append_basic_block(parent, "then_allocate_fresh");
let else_block = ctx.append_basic_block(parent, "else_reuse");
Expand All @@ -2052,7 +2121,7 @@ fn allocate_tag<'a, 'ctx>(
let reuse_ptr = {
env.builder.position_at_end(else_block);

let cleared = tag_pointer_clear_tag_id(env, ptr);
let cleared = tag_pointer_clear_tag_id(env, reuse.ptr);

env.builder.build_unconditional_branch(cont_block);

Expand All @@ -2061,19 +2130,33 @@ fn allocate_tag<'a, 'ctx>(

{
env.builder.position_at_end(cont_block);
let phi = env.builder.build_phi(raw_ptr.get_type(), "branch");

let phi = env.builder.build_phi(raw_ptr.get_type(), "ptr");
phi.add_incoming(&[(&raw_ptr, then_block), (&reuse_ptr, else_block)]);
let ptr = phi.as_basic_value().into_pointer_value();

let bool_true = env.context.bool_type().const_int(true as u64, false);
let bool_false = env.context.bool_type().const_int(false as u64, false);

phi.as_basic_value().into_pointer_value()
let phi = env.builder.build_phi(env.context.bool_type(), "did_reuse");
phi.add_incoming(&[(&bool_false, then_block), (&bool_true, else_block)]);
let performed_reuse = phi.as_basic_value().into_int_value();

(performed_reuse, ptr)
}
}
None => reserve_with_refcount_union_as_block_of_memory(
env,
layout_interner,
*union_layout,
tags,
),
None => {
let bool_false = env.context.bool_type().const_int(false as u64, false);

let ptr = reserve_with_refcount_union_as_block_of_memory(
env,
layout_interner,
*union_layout,
tags,
);

(bool_false, ptr)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/mono/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1846,7 +1846,7 @@ pub struct HigherOrderLowLevel<'a> {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ReuseToken {
pub symbol: Symbol,
pub update_tag_id: bool,
pub can_reuse_tag_id: bool,
pub update_mode: UpdateModeId,
pub original_symbol: Option<Symbol>,
}
Expand Down
4 changes: 2 additions & 2 deletions crates/compiler/mono/src/reset_reuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
symbol: reuse_symbol,
update_mode: update_mode_ids.next_id(),
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
can_reuse_tag_id: false,
original_symbol: Some(symbol),
};

Expand Down Expand Up @@ -830,7 +830,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode: update_mode_ids.next_id(),
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
can_reuse_tag_id: false,
original_symbol: token.token.original_symbol,
},
inlayout: token.inlayout,
Expand Down

0 comments on commit e082b5a

Please sign in to comment.