mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
Merge pull request #208 from rtfeldman/list-int
Basic List Int code gen in LLVM
This commit is contained in:
commit
aa8f151d34
@ -109,7 +109,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||
|
||||
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
layout.stack_size(cfg),
|
||||
layout.stack_size(cfg.pointer_bytes() as u32),
|
||||
));
|
||||
|
||||
builder.ins().stack_store(val, slot, Offset32::new(0));
|
||||
@ -199,7 +199,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||
// Create a slot
|
||||
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
layout.stack_size(cfg),
|
||||
layout.stack_size(cfg.pointer_bytes() as u32),
|
||||
));
|
||||
|
||||
// Create instructions for storing each field's expression
|
||||
@ -259,7 +259,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||
if elems.is_empty() {
|
||||
panic!("TODO build an empty Array in Crane");
|
||||
} else {
|
||||
let elem_bytes = elem_layout.stack_size(env.cfg) as usize;
|
||||
let elem_bytes = elem_layout.stack_size(env.cfg.pointer_bytes() as u32) as usize;
|
||||
let bytes_len = (elem_bytes * elems.len()) + 1/* TODO drop the +1 when we have structs and this is no longer NUL-terminated. */;
|
||||
let ptr = call_malloc(env, module, builder, bytes_len);
|
||||
let mem_flags = MemFlags::new();
|
||||
|
@ -32,6 +32,7 @@ pub struct Env<'a, 'ctx, 'env> {
|
||||
pub module: &'ctx Module<'ctx>,
|
||||
pub interns: Interns,
|
||||
pub subs: Subs,
|
||||
pub pointer_bytes: u32,
|
||||
}
|
||||
|
||||
pub fn build_expr<'a, 'ctx, 'env>(
|
||||
@ -215,6 +216,45 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||
BasicValueEnum::PointerValue(ptr)
|
||||
}
|
||||
}
|
||||
Array { elem_layout, elems } => {
|
||||
if elems.is_empty() {
|
||||
panic!("TODO build an empty string in LLVM");
|
||||
} else {
|
||||
let elem_bytes = elem_layout.stack_size(env.pointer_bytes) as u64;
|
||||
let bytes_len = elem_bytes * (elems.len() + 1) as u64/* TODO drop the +1 when we have structs and this is no longer a NUL-terminated CString.*/;
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let elem_type = basic_type_from_layout(ctx, elem_layout);
|
||||
let nul_terminator = elem_type.into_int_type().const_zero();
|
||||
let len = ctx.i32_type().const_int(bytes_len, false);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_array_malloc(elem_type, len, "str_ptr")
|
||||
.unwrap();
|
||||
|
||||
// Copy the bytes from the string literal into the array
|
||||
for (index, elem) in elems.iter().enumerate() {
|
||||
let offset = ctx.i32_type().const_int(elem_bytes * index as u64, false);
|
||||
let elem_ptr = unsafe { builder.build_gep(ptr, &[offset], "elem") };
|
||||
|
||||
let val = build_expr(env, &scope, parent, &elem, procs);
|
||||
|
||||
builder.build_store(elem_ptr, val);
|
||||
}
|
||||
|
||||
// Add a NUL terminator at the end.
|
||||
// TODO: Instead of NUL-terminating, return a struct
|
||||
// with the pointer and also the length and capacity.
|
||||
let index = ctx.i32_type().const_int(bytes_len as u64 - 1, false);
|
||||
let elem_ptr = unsafe { builder.build_gep(ptr, &[index], "nul_terminator") };
|
||||
|
||||
builder.build_store(elem_ptr, nul_terminator);
|
||||
|
||||
BasicValueEnum::PointerValue(ptr)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("I don't yet know how to LLVM build {:?}", expr);
|
||||
}
|
||||
@ -549,6 +589,21 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||
|
||||
BasicValueEnum::IntValue(int_val)
|
||||
}
|
||||
Symbol::LIST_GET_UNSAFE => {
|
||||
debug_assert!(args.len() == 2);
|
||||
|
||||
let list_ptr = args[0].into_pointer_value();
|
||||
let elem_index = args[1].into_int_value();
|
||||
|
||||
let builder = env.builder;
|
||||
let elem_bytes = 8; // TODO Look this up instead of hardcoding it!
|
||||
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
||||
let offset = builder.build_int_mul(elem_index, elem_size, "MUL_OFFSET");
|
||||
|
||||
let elem_ptr = unsafe { builder.build_gep(list_ptr, &[offset], "elem") };
|
||||
|
||||
builder.build_load(elem_ptr, "List.get")
|
||||
}
|
||||
_ => {
|
||||
let fn_val = env
|
||||
.module
|
||||
|
@ -4,7 +4,6 @@ use crate::module::symbol::Symbol;
|
||||
use crate::subs::{Content, FlatType, Subs, Variable};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use cranelift_codegen::isa::TargetFrontendConfig;
|
||||
|
||||
/// Types for code gen must be monomorphic. No type variables allowed!
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@ -58,33 +57,25 @@ impl<'a> Layout<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO this will probably need to move to crane:: because
|
||||
/// LLVM gets the answer using different APIs! Also, might be
|
||||
/// nice to rename it to bytes_size, both to include the units
|
||||
/// and also because this is the size on the stack *and* the heap!
|
||||
pub fn stack_size(&self, cfg: TargetFrontendConfig) -> u32 {
|
||||
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
||||
use Layout::*;
|
||||
|
||||
match self {
|
||||
Builtin(builtin) => builtin.stack_size(cfg),
|
||||
Builtin(builtin) => builtin.stack_size(pointer_size),
|
||||
Struct(fields) => {
|
||||
let mut sum = 0;
|
||||
|
||||
for (_, field_layout) in *fields {
|
||||
sum += field_layout.stack_size(cfg);
|
||||
sum += field_layout.stack_size(pointer_size);
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
Pointer(_) | FunctionPointer(_, _) => pointer_size(cfg),
|
||||
Pointer(_) | FunctionPointer(_, _) => pointer_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_size(cfg: TargetFrontendConfig) -> u32 {
|
||||
cfg.pointer_bytes() as u32
|
||||
}
|
||||
|
||||
impl<'a> Builtin<'a> {
|
||||
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||
@ -94,15 +85,15 @@ impl<'a> Builtin<'a> {
|
||||
const MAP_WORDS: u32 = 6;
|
||||
const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value
|
||||
|
||||
pub fn stack_size(&self, cfg: TargetFrontendConfig) -> u32 {
|
||||
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
||||
use Builtin::*;
|
||||
|
||||
match self {
|
||||
Int64 => Builtin::I64_SIZE,
|
||||
Float64 => Builtin::F64_SIZE,
|
||||
Str => Builtin::STR_WORDS * pointer_size(cfg),
|
||||
Map(_, _) => Builtin::MAP_WORDS * pointer_size(cfg),
|
||||
Set(_) => Builtin::SET_WORDS * pointer_size(cfg),
|
||||
Str => Builtin::STR_WORDS * pointer_size,
|
||||
Map(_, _) => Builtin::MAP_WORDS * pointer_size,
|
||||
Set(_) => Builtin::SET_WORDS * pointer_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,13 @@ mod test_gen {
|
||||
.fn_type(&[], false);
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
let execution_engine =
|
||||
module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
let pointer_bytes = execution_engine.get_target_data().get_pointer_byte_size(None);
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let mut env = roc::llvm::build::Env {
|
||||
arena: &arena,
|
||||
@ -213,6 +220,7 @@ mod test_gen {
|
||||
context: &context,
|
||||
interns,
|
||||
module: arena.alloc(module),
|
||||
pointer_bytes
|
||||
};
|
||||
let mut procs = MutMap::default();
|
||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||
@ -282,11 +290,6 @@ mod test_gen {
|
||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
let execution_engine = env
|
||||
.module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn() -> $ty> = execution_engine
|
||||
.get_function(main_fn_name)
|
||||
@ -344,8 +347,8 @@ mod test_gen {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_int_list() {
|
||||
assert_crane_evals_to!("List.getUnsafe [ 12, 9, 6, 3 ] 1", 9, i64, |a| a);
|
||||
fn get_int_list() {
|
||||
assert_evals_to!("List.getUnsafe [ 12, 9, 6, 3 ] 1", 9, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user