codegen for walkRight

This commit is contained in:
Folkert 2020-09-06 22:03:46 +02:00
parent 5d0ec57461
commit 25cd0a2c02
7 changed files with 188 additions and 15 deletions

View file

@ -642,10 +642,96 @@ pub fn list_len<'ctx>(
}
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
pub fn list_walk_right<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
#[allow(clippy::too_many_arguments)]
pub fn list_walk_right<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
list: BasicValueEnum<'ctx>,
list_layout: &Layout<'a>,
func: BasicValueEnum<'ctx>,
func_layout: &Layout<'a>,
default: BasicValueEnum<'ctx>,
default_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let builder = env.builder;
BasicValueEnum::IntValue(ctx.i64_type().const_int(0 as u64, false))
let list_wrapper = list.into_struct_value();
let len = list_len(env.builder, list_wrapper);
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
builder.build_store(accum_alloca, default);
let then_block = ctx.append_basic_block(parent, "then");
let cont_block = ctx.append_basic_block(parent, "branchcont");
let condition = builder.build_int_compare(
IntPredicate::UGT,
len,
ctx.i64_type().const_zero(),
"list_non_empty",
);
builder.build_conditional_branch(condition, then_block, cont_block);
builder.position_at_end(then_block);
match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, _)) => {
let elem_layout = match list_layout {
Layout::Builtin(Builtin::List(_, layout)) => layout,
_ => unreachable!("can only fold over a list"),
};
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
let elem_ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
// load current accumulator
let current = builder.build_load(accum_alloca, "retrieve_accum");
let call_site_value =
builder.build_call(func_ptr, &[elem, current], "#walk_right_func");
// set the calling convention explicitly for this call
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
let new_current = call_site_value
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
.into_int_value();
builder.build_store(accum_alloca, new_current);
};
incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg { ptr: list_ptr, len },
"#index",
None,
walk_right_loop,
);
}
_ => {
unreachable!(
"Invalid function basic value enum or layout for List.keepIf : {:?}",
(func, func_layout)
);
}
}
builder.build_unconditional_branch(cont_block);
builder.position_at_end(cont_block);
builder.build_load(accum_alloca, "load_final_acum")
}
/// List.keepIf : List elem, (elem -> Bool) -> List elem