Skip to content

Commit

Permalink
feat: initial very leaky implementation of heap allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Jan 11, 2020
1 parent 0e75842 commit 26621df
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 98 deletions.
36 changes: 16 additions & 20 deletions crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use hir::{CallableDef, Ty, TypeCtor};
use inkwell::{
attributes::Attribute,
module::{Linkage, Module},
types::{AnyTypeEnum, StructType},
types::StructType,
values::{FunctionValue, IntValue, PointerValue, StructValue, UnnamedAddress},
AddressSpace,
};
Expand Down Expand Up @@ -215,25 +215,21 @@ fn gen_struct_info_array<'a, D: IrDatabase>(
) -> GlobalValue {
let struct_infos: Vec<StructValue> = structs
.map(|(s, _)| {
if let AnyTypeEnum::StructType(_) = db.type_ir(s.ty(db)) {
let name_str = intern_string(&module, &s.name(db).to_string());

let fields = s.fields(db);
let field_types = fields.iter().map(|field| field.ty(db));
let (fields, num_fields) = gen_type_info_array(db, module, types, field_types);

types.struct_info_type.const_named_struct(&[
name_str.into(),
fields.into(),
module
.get_context()
.i16_type()
.const_int(num_fields as u64, false)
.into(),
])
} else {
unreachable!();
}
let name_str = intern_string(&module, &s.name(db).to_string());

let fields = s.fields(db);
let field_types = fields.iter().map(|field| field.ty(db));
let (fields, num_fields) = gen_type_info_array(db, module, types, field_types);

types.struct_info_type.const_named_struct(&[
name_str.into(),
fields.into(),
module
.get_context()
.i16_type()
.const_int(num_fields as u64, false)
.into(),
])
})
.collect();

Expand Down
5 changes: 5 additions & 0 deletions crates/mun_codegen/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


use crate::{code_gen::symbols::TypeInfo, ir::module::ModuleIR, Context};
use inkwell::types::StructType;
use inkwell::{types::AnyTypeEnum, OptimizationLevel};
use mun_target::spec::Target;
use std::sync::Arc;
Expand All @@ -27,6 +28,10 @@ pub trait IrDatabase: hir::HirDatabase {
#[salsa::invoke(crate::ir::ty::ir_query)]
fn type_ir(&self, ty: hir::Ty) -> AnyTypeEnum;

/// Given a struct, return the corresponding IR type.
#[salsa::invoke(crate::ir::ty::struct_ty_query)]
fn struct_ty(&self, s: hir::Struct) -> StructType;

/// Given a `hir::FileId` generate code for the module.
#[salsa::invoke(crate::ir::module::ir_query)]
fn module_ir(&self, file: hir::FileId) -> Arc<ModuleIR>;
Expand Down
47 changes: 21 additions & 26 deletions crates/mun_codegen/src/ir/adt.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
//use crate::ir::module::Types;
use crate::ir::try_convert_any_to_basic;
use crate::IrDatabase;
use inkwell::types::{AnyTypeEnum, BasicTypeEnum, StructType};
use inkwell::values::BasicValueEnum;
use inkwell::types::{BasicTypeEnum, StructType};
use inkwell::values::{BasicValueEnum, StructValue};

pub(super) fn gen_struct_decl(db: &impl IrDatabase, s: hir::Struct) -> StructType {
if let AnyTypeEnum::StructType(struct_type) = db.type_ir(s.ty(db)) {
if struct_type.is_opaque() {
let field_types: Vec<BasicTypeEnum> = s
.fields(db)
.iter()
.map(|field| {
let field_type = field.ty(db);
try_convert_any_to_basic(db.type_ir(field_type))
.expect("could not convert field type")
})
.collect();
let struct_type = db.struct_ty(s);
if struct_type.is_opaque() {
let field_types: Vec<BasicTypeEnum> = s
.fields(db)
.iter()
.map(|field| {
let field_type = field.ty(db);
try_convert_any_to_basic(db.type_ir(field_type))
.expect("could not convert field type")
})
.collect();

struct_type.set_body(&field_types, false);
}
struct_type
} else {
unreachable!()
struct_type.set_body(&field_types, false);
}
struct_type
}

pub(crate) fn gen_named_struct_lit(
/// Constructs a struct literal value of type `s`.
pub(super) fn gen_named_struct_lit(
db: &impl IrDatabase,
ty: hir::Ty,
s: hir::Struct,
values: &[BasicValueEnum],
) -> BasicValueEnum {
if let inkwell::types::AnyTypeEnum::StructType(struct_type) = db.type_ir(ty) {
struct_type.const_named_struct(&values).into()
} else {
unreachable!("at this stage there must be a struct type");
}
) -> StructValue {
let struct_ty = db.struct_ty(s);
struct_ty.const_named_struct(values)
}
76 changes: 64 additions & 12 deletions crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
ir::adt::gen_named_struct_lit, ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic,
IrDatabase,
};
use crate::{ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic, IrDatabase};
use hir::{
ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal,
Name, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor,
Expand All @@ -14,6 +11,7 @@ use inkwell::{
};
use std::{collections::HashMap, mem, sync::Arc};

use crate::ir::adt::gen_named_struct_lit;
use inkwell::basic_block::BasicBlock;
use inkwell::values::PointerValue;

Expand Down Expand Up @@ -174,7 +172,8 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
name,
} => {
let ptr = self.gen_field(expr, *receiver_expr, name);
Some(self.builder.build_load(ptr, &name.to_string()))
let value = self.builder.build_load(ptr, &name.to_string());
Some(value)
}
_ => unimplemented!("unimplemented expr type {:?}", &body[expr]),
}
Expand Down Expand Up @@ -215,36 +214,61 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
self.module.get_context().const_struct(&[], false).into()
}

/// Allocate a struct literal either on the stack or the heap based on the type of the struct.
fn gen_struct_alloc(
&mut self,
hir_struct: hir::Struct,
args: &[BasicValueEnum],
) -> BasicValueEnum {
let struct_lit = gen_named_struct_lit(self.db, hir_struct, &args);
match hir_struct.data(self.db).memory_kind {
hir::StructMemoryKind::Value => struct_lit.into(),
hir::StructMemoryKind::GC => {
// TODO: Root memory in GC
let struct_ir_ty = self.db.struct_ty(hir_struct);
let struct_ptr: PointerValue = self
.builder
.build_malloc(struct_ir_ty, &hir_struct.name(self.db).to_string());
let struct_value = gen_named_struct_lit(self.db, hir_struct, &args);
self.builder.build_store(struct_ptr, struct_value);
struct_ptr.into()
}
}
}

/// Generates IR for a record literal, e.g. `Foo { a: 1.23, b: 4 }`
fn gen_record_lit(
&mut self,
type_expr: ExprId,
fields: &[hir::RecordLitField],
) -> BasicValueEnum {
let struct_ty = self.infer[type_expr].clone();
let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
let fields: Vec<BasicValueEnum> = fields
.iter()
.map(|field| self.gen_expr(field.expr).expect("expected a field value"))
.collect();

gen_named_struct_lit(self.db, struct_ty, &fields)
self.gen_struct_alloc(hir_struct, &fields)
}

/// Generates IR for a named tuple literal, e.g. `Foo(1.23, 4)`
fn gen_named_tuple_lit(&mut self, type_expr: ExprId, args: &[ExprId]) -> BasicValueEnum {
let struct_ty = self.infer[type_expr].clone();
let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
let args: Vec<BasicValueEnum> = args
.iter()
.map(|expr| self.gen_expr(*expr).expect("expected a field value"))
.collect();

gen_named_struct_lit(self.db, struct_ty, &args)
self.gen_struct_alloc(hir_struct, &args)
}

/// Generates IR for a unit struct literal, e.g `Foo`
fn gen_unit_struct_lit(&self, type_expr: ExprId) -> BasicValueEnum {
fn gen_unit_struct_lit(&mut self, type_expr: ExprId) -> BasicValueEnum {
let struct_ty = self.infer[type_expr].clone();
gen_named_struct_lit(self.db, struct_ty, &[])
let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
self.gen_struct_alloc(hir_struct, &[])
}

/// Generates IR for the specified block expression.
Expand Down Expand Up @@ -313,7 +337,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {

/// Generates IR for looking up a certain path expression.
fn gen_path_expr(
&self,
&mut self,
path: &Path,
expr: ExprId,
resolver: &Resolver,
Expand All @@ -339,6 +363,22 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
}
}

/// Given an expression and the type of the expression, optionally dereference the value.
fn opt_deref_value(&mut self, expr: ExprId, value: BasicValueEnum) -> BasicValueEnum {
match &self.infer[expr] {
hir::Ty::Apply(hir::ApplicationTy {
ctor: hir::TypeCtor::Struct(s),
..
}) => match s.data(self.db).memory_kind {
hir::StructMemoryKind::GC => {
self.builder.build_load(value.into_pointer_value(), "deref")
}
hir::StructMemoryKind::Value => value,
},
_ => value,
}
}

/// Generates IR for looking up a certain path expression.
fn gen_path_place_expr(
&self,
Expand Down Expand Up @@ -390,10 +430,12 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
) -> Option<BasicValueEnum> {
let lhs = self
.gen_expr(lhs_expr)
.map(|value| self.opt_deref_value(lhs_expr, value))
.expect("no lhs value")
.into_float_value();
let rhs = self
.gen_expr(rhs_expr)
.map(|value| self.opt_deref_value(rhs_expr, value))
.expect("no rhs value")
.into_float_value();
match op {
Expand Down Expand Up @@ -447,10 +489,12 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
) -> Option<BasicValueEnum> {
let lhs = self
.gen_expr(lhs_expr)
.map(|value| self.opt_deref_value(lhs_expr, value))
.expect("no lhs value")
.into_int_value();
let rhs = self
.gen_expr(rhs_expr)
.map(|value| self.opt_deref_value(lhs_expr, value))
.expect("no rhs value")
.into_int_value();
match op {
Expand Down Expand Up @@ -573,7 +617,10 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
else_branch: Option<ExprId>,
) -> Option<inkwell::values::BasicValueEnum> {
// Generate IR for the condition
let condition_ir = self.gen_expr(condition)?.into_int_value();
let condition_ir = self
.gen_expr(condition)
.map(|value| self.opt_deref_value(condition, value))?
.into_int_value();

// Generate the code blocks to branch to
let context = self.module.get_context();
Expand Down Expand Up @@ -708,7 +755,9 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {

// Generate condition block
self.builder.position_at_end(&cond_block);
let condition_ir = self.gen_expr(condition_expr);
let condition_ir = self
.gen_expr(condition_expr)
.map(|value| self.opt_deref_value(condition_expr, value));
if let Some(condition_ir) = condition_ir {
self.builder.build_conditional_branch(
condition_ir.into_int_value(),
Expand Down Expand Up @@ -777,6 +826,9 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
.into();

let receiver_ptr = self.gen_place_expr(receiver_expr);
let receiver_ptr = self
.opt_deref_value(receiver_expr, receiver_ptr.into())
.into_pointer_value();
unsafe {
self.builder.build_struct_gep(
receiver_ptr,
Expand Down
18 changes: 14 additions & 4 deletions crates/mun_codegen/src/ir/ty.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::try_convert_any_to_basic;
use crate::IrDatabase;
use hir::{ApplicationTy, CallableDef, Ty, TypeCtor};
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum};
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum, StructType};
use inkwell::AddressSpace;

/// Given a mun type, construct an LLVM IR type
pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty) -> AnyTypeEnum {
Expand Down Expand Up @@ -29,12 +30,21 @@ pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty) -> AnyTypeEnum {

AnyTypeEnum::FunctionType(fn_type)
}
TypeCtor::FnDef(CallableDef::Struct(s)) | TypeCtor::Struct(s) => {
let name = s.name(db).to_string();
context.opaque_struct_type(&name).into()
TypeCtor::Struct(s) => {
let struct_ty = db.struct_ty(s);
match s.data(db).memory_kind {
hir::StructMemoryKind::GC => struct_ty.ptr_type(AddressSpace::Generic).into(),
hir::StructMemoryKind::Value => struct_ty.into(),
}
}
_ => unreachable!(),
},
_ => unreachable!("unknown type can not be converted"),
}
}

/// Returns the LLVM IR type of the specified struct
pub fn struct_ty_query(db: &impl IrDatabase, s: hir::Struct) -> StructType {
let name = s.name(db).to_string();
db.context().opaque_struct_type(&name)
}
31 changes: 31 additions & 0 deletions crates/mun_codegen/src/snapshots/test__gc_struct.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
source: crates/mun_codegen/src/test.rs
expression: "struct(gc) Foo { a: int, b: int };\n\nfn foo() {\n let a = Foo { a: 3, b: 4 };\n a.b += 3;\n let b = a;\n}"
---
; ModuleID = 'main.mun'
source_filename = "main.mun"

%Foo = type { i64, i64 }

define void @foo() {
body:
%b4 = alloca %Foo*
%a = alloca %Foo*
%malloccall = tail call i8* @malloc(i32 trunc (i64 mul nuw (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2) to i32))
%Foo = bitcast i8* %malloccall to %Foo*
store %Foo { i64 3, i64 4 }, %Foo* %Foo
store %Foo* %Foo, %Foo** %a
%deref = load %Foo*, %Foo** %a
%Foo.b = getelementptr inbounds %Foo, %Foo* %deref, i32 0, i32 1
%b = load i64, i64* %Foo.b
%add = add i64 %b, 3
%deref1 = load %Foo*, %Foo** %a
%Foo.b2 = getelementptr inbounds %Foo, %Foo* %deref1, i32 0, i32 1
store i64 %add, i64* %Foo.b2
%a3 = load %Foo*, %Foo** %a
store %Foo* %a3, %Foo** %b4
ret void
}

declare noalias i8* @malloc(i32)

Loading

0 comments on commit 26621df

Please sign in to comment.