codegen: gep related fixes

we can now compile simple programs that uses tuples and lists
This commit is contained in:
pca006132 2021-08-19 16:54:15 +08:00
parent 39545c0005
commit 0e2da0d180
2 changed files with 104 additions and 75 deletions

View File

@ -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!(),
}) })
} }

View File

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