Merge pull request #208 from rtfeldman/list-int

Basic List Int code gen in LLVM
This commit is contained in:
Richard Feldman 2020-03-01 02:45:46 -05:00 committed by GitHub
commit aa8f151d34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 27 deletions

View File

@ -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();

View File

@ -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

View File

@ -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,
}
}
}

View File

@ -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]