forked from M-Labs/nac3
codegen: gep related fixes
we can now compile simple programs that uses tuples and lists
This commit is contained in:
parent
39545c0005
commit
0e2da0d180
|
@ -8,12 +8,28 @@ use crate::{
|
||||||
};
|
};
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::{BasicType, BasicTypeEnum},
|
types::{BasicType, BasicTypeEnum},
|
||||||
values::BasicValueEnum,
|
values::{BasicValueEnum, IntValue, PointerValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
use itertools::{chain, izip, zip, Itertools};
|
use itertools::{chain, izip, zip, Itertools};
|
||||||
use rustpython_parser::ast::{self, Boolop, Constant, Expr, ExprKind, Operator};
|
use rustpython_parser::ast::{self, Boolop, Constant, Expr, ExprKind, Operator};
|
||||||
|
|
||||||
|
fn assert_int_val<'ctx>(val: BasicValueEnum<'ctx>) -> IntValue<'ctx> {
|
||||||
|
if let BasicValueEnum::IntValue(v) = val {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_pointer_val<'ctx>(val: BasicValueEnum<'ctx>) -> PointerValue<'ctx> {
|
||||||
|
if let BasicValueEnum::PointerValue(v) = val {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
fn get_subst_key(&mut self, obj: Option<Type>, fun: &FunSignature) -> String {
|
fn get_subst_key(&mut self, obj: Option<Type>, fun: &FunSignature) -> String {
|
||||||
let mut vars = obj
|
let mut vars = obj
|
||||||
|
@ -63,10 +79,11 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
let zero = self.ctx.i32_type().const_zero();
|
let zero = self.ctx.i32_type().const_zero();
|
||||||
unsafe {
|
unsafe {
|
||||||
for (i, val) in vals.into_iter().enumerate() {
|
for (i, val) in vals.into_iter().enumerate() {
|
||||||
let p = ptr.const_in_bounds_gep(&[
|
let p = self.builder.build_in_bounds_gep(
|
||||||
zero,
|
ptr,
|
||||||
self.ctx.i32_type().const_int(i as u64, false),
|
&[zero, self.ctx.i32_type().const_int(i as u64, false)],
|
||||||
]);
|
"elemptr",
|
||||||
|
);
|
||||||
self.builder.build_store(p, val);
|
self.builder.build_store(p, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,14 +259,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
let ptr = self.var_assignment.get(id).unwrap();
|
let ptr = self.var_assignment.get(id).unwrap();
|
||||||
let primitives = &self.primitives;
|
let primitives = &self.primitives;
|
||||||
// we should only dereference primitive types
|
|
||||||
if [primitives.int32, primitives.int64, primitives.float, primitives.bool]
|
|
||||||
.contains(&self.unifier.get_representative(expr.custom.unwrap()))
|
|
||||||
{
|
|
||||||
self.builder.build_load(*ptr, "load")
|
self.builder.build_load(*ptr, "load")
|
||||||
} else {
|
|
||||||
(*ptr).into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ExprKind::List { elts, .. } => {
|
ExprKind::List { elts, .. } => {
|
||||||
// this shall be optimized later for constant primitive lists...
|
// this shall be optimized later for constant primitive lists...
|
||||||
|
@ -271,23 +281,26 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
);
|
);
|
||||||
let arr_str_ptr = self.builder.build_alloca(arr_ty, "tmparrstr");
|
let arr_str_ptr = self.builder.build_alloca(arr_ty, "tmparrstr");
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let len_ptr =
|
||||||
|
self.builder.build_in_bounds_gep(arr_str_ptr, &[zero, zero], "len_ptr");
|
||||||
self.builder.build_store(
|
self.builder.build_store(
|
||||||
arr_str_ptr.const_in_bounds_gep(&[zero, zero]),
|
len_ptr,
|
||||||
self.ctx.i32_type().const_int(elements.len() as u64, false),
|
self.ctx.i32_type().const_int(elements.len() as u64, false),
|
||||||
);
|
);
|
||||||
self.builder.build_store(
|
let ptr_to_arr = self.builder.build_in_bounds_gep(
|
||||||
arr_str_ptr
|
arr_str_ptr,
|
||||||
.const_in_bounds_gep(&[zero, self.ctx.i32_type().const_int(1, false)]),
|
&[zero, self.ctx.i32_type().const_int(1, false)],
|
||||||
arr_ptr,
|
"ptr_to_arr",
|
||||||
);
|
);
|
||||||
let arr_offset = self.ctx.i32_type().const_int(1, false);
|
self.builder.build_store(ptr_to_arr, arr_ptr);
|
||||||
|
let i32_type = self.ctx.i32_type();
|
||||||
for (i, v) in elements.iter().enumerate() {
|
for (i, v) in elements.iter().enumerate() {
|
||||||
let ptr = self.builder.build_in_bounds_gep(
|
let elem_ptr = self.builder.build_in_bounds_gep(
|
||||||
arr_ptr,
|
arr_ptr,
|
||||||
&[zero, arr_offset, self.ctx.i32_type().const_int(i as u64, false)],
|
&[i32_type.const_int(i as u64, false)],
|
||||||
"arr_element",
|
"elem_ptr",
|
||||||
);
|
);
|
||||||
self.builder.build_store(ptr, *v);
|
self.builder.build_store(elem_ptr, *v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arr_str_ptr.into()
|
arr_str_ptr.into()
|
||||||
|
@ -299,10 +312,11 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
let tuple_ptr = self.builder.build_alloca(tuple_ty, "tuple");
|
let tuple_ptr = self.builder.build_alloca(tuple_ty, "tuple");
|
||||||
for (i, v) in element_val.into_iter().enumerate() {
|
for (i, v) in element_val.into_iter().enumerate() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = tuple_ptr.const_in_bounds_gep(&[
|
let ptr = self.builder.build_in_bounds_gep(
|
||||||
zero,
|
tuple_ptr,
|
||||||
self.ctx.i32_type().const_int(i as u64, false),
|
&[zero, self.ctx.i32_type().const_int(i as u64, false)],
|
||||||
]);
|
"ptr",
|
||||||
|
);
|
||||||
self.builder.build_store(ptr, v);
|
self.builder.build_store(ptr, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,27 +326,19 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
// note that we would handle class methods directly in calls
|
// note that we would handle class methods directly in calls
|
||||||
let index = self.get_attr_index(value.custom.unwrap(), attr);
|
let index = self.get_attr_index(value.custom.unwrap(), attr);
|
||||||
let val = self.gen_expr(value).unwrap();
|
let val = self.gen_expr(value).unwrap();
|
||||||
let ptr = if let BasicValueEnum::PointerValue(v) = val {
|
let ptr = assert_pointer_val(val);
|
||||||
v
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = ptr.const_in_bounds_gep(&[
|
let ptr = self.builder.build_in_bounds_gep(
|
||||||
zero,
|
ptr,
|
||||||
self.ctx.i32_type().const_int(index as u64, false),
|
&[zero, self.ctx.i32_type().const_int(index as u64, false)],
|
||||||
]);
|
"attr",
|
||||||
|
);
|
||||||
self.builder.build_load(ptr, "field")
|
self.builder.build_load(ptr, "field")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::BoolOp { op, values } => {
|
ExprKind::BoolOp { op, values } => {
|
||||||
// requires conditional branches for short-circuiting...
|
// requires conditional branches for short-circuiting...
|
||||||
let left =
|
let left = assert_int_val(self.gen_expr(&values[0]).unwrap());
|
||||||
if let BasicValueEnum::IntValue(left) = self.gen_expr(&values[0]).unwrap() {
|
|
||||||
left
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||||
let a_bb = self.ctx.append_basic_block(current, "a");
|
let a_bb = self.ctx.append_basic_block(current, "a");
|
||||||
let b_bb = self.ctx.append_basic_block(current, "b");
|
let b_bb = self.ctx.append_basic_block(current, "b");
|
||||||
|
@ -344,25 +350,13 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
let a = self.ctx.bool_type().const_int(1, false);
|
let a = self.ctx.bool_type().const_int(1, false);
|
||||||
self.builder.build_unconditional_branch(cont_bb);
|
self.builder.build_unconditional_branch(cont_bb);
|
||||||
self.builder.position_at_end(b_bb);
|
self.builder.position_at_end(b_bb);
|
||||||
let b = if let BasicValueEnum::IntValue(b) =
|
let b = assert_int_val(self.gen_expr(&values[1]).unwrap());
|
||||||
self.gen_expr(&values[1]).unwrap()
|
|
||||||
{
|
|
||||||
b
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
self.builder.build_unconditional_branch(cont_bb);
|
self.builder.build_unconditional_branch(cont_bb);
|
||||||
(a, b)
|
(a, b)
|
||||||
}
|
}
|
||||||
Boolop::And => {
|
Boolop::And => {
|
||||||
self.builder.position_at_end(a_bb);
|
self.builder.position_at_end(a_bb);
|
||||||
let a = if let BasicValueEnum::IntValue(a) =
|
let a = assert_int_val(self.gen_expr(&values[1]).unwrap());
|
||||||
self.gen_expr(&values[1]).unwrap()
|
|
||||||
{
|
|
||||||
a
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
self.builder.build_unconditional_branch(cont_bb);
|
self.builder.build_unconditional_branch(cont_bb);
|
||||||
self.builder.position_at_end(b_bb);
|
self.builder.position_at_end(b_bb);
|
||||||
let b = self.ctx.bool_type().const_int(0, false);
|
let b = self.ctx.bool_type().const_int(0, false);
|
||||||
|
@ -396,8 +390,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
let ty = self.unifier.get_representative(operand.custom.unwrap());
|
let ty = self.unifier.get_representative(operand.custom.unwrap());
|
||||||
let val = self.gen_expr(operand).unwrap();
|
let val = self.gen_expr(operand).unwrap();
|
||||||
if ty == self.primitives.bool {
|
if ty == self.primitives.bool {
|
||||||
let val =
|
let val = assert_int_val(val);
|
||||||
if let BasicValueEnum::IntValue(val) = val { val } else { unreachable!() };
|
|
||||||
match op {
|
match op {
|
||||||
ast::Unaryop::Invert | ast::Unaryop::Not => {
|
ast::Unaryop::Invert | ast::Unaryop::Not => {
|
||||||
self.builder.build_not(val, "not").into()
|
self.builder.build_not(val, "not").into()
|
||||||
|
@ -405,8 +398,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
_ => val.into(),
|
_ => val.into(),
|
||||||
}
|
}
|
||||||
} else if [self.primitives.int32, self.primitives.int64].contains(&ty) {
|
} else if [self.primitives.int32, self.primitives.int64].contains(&ty) {
|
||||||
let val =
|
let val = assert_int_val(val);
|
||||||
if let BasicValueEnum::IntValue(val) = val { val } else { unreachable!() };
|
|
||||||
match op {
|
match op {
|
||||||
ast::Unaryop::USub => self.builder.build_int_neg(val, "neg").into(),
|
ast::Unaryop::USub => self.builder.build_int_neg(val, "neg").into(),
|
||||||
ast::Unaryop::Invert => self.builder.build_not(val, "not").into(),
|
ast::Unaryop::Invert => self.builder.build_not(val, "not").into(),
|
||||||
|
@ -506,12 +498,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
.into() // as there should be at least 1 element, it should never be none
|
.into() // as there should be at least 1 element, it should never be none
|
||||||
}
|
}
|
||||||
ExprKind::IfExp { test, body, orelse } => {
|
ExprKind::IfExp { test, body, orelse } => {
|
||||||
let test = if let BasicValueEnum::IntValue(test) = self.gen_expr(test).unwrap() {
|
let test = assert_int_val(self.gen_expr(test).unwrap());
|
||||||
test
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||||
let then_bb = self.ctx.append_basic_block(current, "then");
|
let then_bb = self.ctx.append_basic_block(current, "then");
|
||||||
let else_bb = self.ctx.append_basic_block(current, "else");
|
let else_bb = self.ctx.append_basic_block(current, "else");
|
||||||
|
@ -551,6 +538,42 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExprKind::Subscript { value, slice, .. } => {
|
||||||
|
if let TypeEnum::TList { ty } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||||
|
if let ExprKind::Slice { .. } = slice.node {
|
||||||
|
unimplemented!()
|
||||||
|
} else {
|
||||||
|
// TODO: bound check
|
||||||
|
let i32_type = self.ctx.i32_type();
|
||||||
|
let v = assert_pointer_val(self.gen_expr(value).unwrap());
|
||||||
|
let index = assert_int_val(self.gen_expr(slice).unwrap());
|
||||||
|
unsafe {
|
||||||
|
let ptr_to_arr = self.builder.build_in_bounds_gep(
|
||||||
|
v,
|
||||||
|
&[i32_type.const_zero(), i32_type.const_int(1, false)],
|
||||||
|
"ptr_to_arr",
|
||||||
|
);
|
||||||
|
let arr_ptr =
|
||||||
|
assert_pointer_val(self.builder.build_load(ptr_to_arr, "loadptr"));
|
||||||
|
let ptr = self.builder.build_gep(arr_ptr, &[index], "loadarrgep");
|
||||||
|
println!("building element pointer");
|
||||||
|
self.builder.build_load(ptr, "loadarr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let i32_type = self.ctx.i32_type();
|
||||||
|
let v = assert_pointer_val(self.gen_expr(value).unwrap());
|
||||||
|
let index = assert_int_val(self.gen_expr(slice).unwrap());
|
||||||
|
unsafe {
|
||||||
|
let ptr_to_elem = self.builder.build_in_bounds_gep(
|
||||||
|
v,
|
||||||
|
&[i32_type.const_zero(), index],
|
||||||
|
"ptr_to_elem",
|
||||||
|
);
|
||||||
|
self.builder.build_load(ptr_to_elem, "loadelem")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,14 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
ptr.const_in_bounds_gep(&[
|
self.builder.build_in_bounds_gep(
|
||||||
|
ptr,
|
||||||
|
&[
|
||||||
self.ctx.i32_type().const_zero(),
|
self.ctx.i32_type().const_zero(),
|
||||||
self.ctx.i32_type().const_int(index as u64, false),
|
self.ctx.i32_type().const_int(index as u64, false),
|
||||||
])
|
],
|
||||||
|
"attr",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Subscript { .. } => unimplemented!(),
|
ExprKind::Subscript { .. } => unimplemented!(),
|
||||||
|
@ -47,14 +51,16 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_assignment(&mut self, target: &Expr<Option<Type>>, value: BasicValueEnum<'ctx>) {
|
fn gen_assignment(&mut self, target: &Expr<Option<Type>>, value: BasicValueEnum<'ctx>) {
|
||||||
|
let i32_type = self.ctx.i32_type();
|
||||||
if let ExprKind::Tuple { elts, .. } = &target.node {
|
if let ExprKind::Tuple { elts, .. } = &target.node {
|
||||||
if let BasicValueEnum::PointerValue(ptr) = value {
|
if let BasicValueEnum::PointerValue(ptr) = value {
|
||||||
for (i, elt) in elts.iter().enumerate() {
|
for (i, elt) in elts.iter().enumerate() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let t = ptr.const_in_bounds_gep(&[
|
let t = self.builder.build_in_bounds_gep(
|
||||||
self.ctx.i32_type().const_zero(),
|
ptr,
|
||||||
self.ctx.i32_type().const_int(i as u64, false),
|
&[i32_type.const_zero(), i32_type.const_int(i as u64, false)],
|
||||||
]);
|
"elem",
|
||||||
|
);
|
||||||
let v = self.builder.build_load(t, "tmpload");
|
let v = self.builder.build_load(t, "tmpload");
|
||||||
self.gen_assignment(elt, v);
|
self.gen_assignment(elt, v);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue