forked from M-Labs/nac3
nac3core: codegen refactoring
- No longer check if the statement will return. Instead, we check if the current basic block is terminated, which is simpler and handles exception/break/continue better. - Use invoke statement when unwind is needed. - Moved codegen for a block of statements into a separate function.
This commit is contained in:
parent
b267a656a8
commit
7ea5a5f84d
|
@ -12,12 +12,12 @@ use crate::{
|
||||||
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
||||||
};
|
};
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::{BasicType, BasicTypeEnum},
|
|
||||||
values::{BasicValueEnum, IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
|
types::{BasicType, BasicTypeEnum},
|
||||||
|
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}
|
||||||
};
|
};
|
||||||
use itertools::{chain, izip, zip, Itertools};
|
use itertools::{chain, izip, zip, Itertools};
|
||||||
use nac3parser::ast::{self, Boolop, Comprehension, Constant, Expr, ExprKind, Operator, StrRef};
|
use nac3parser::ast::{self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef};
|
||||||
|
|
||||||
use super::CodeGenerator;
|
use super::CodeGenerator;
|
||||||
|
|
||||||
|
@ -256,6 +256,25 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_call_or_invoke(
|
||||||
|
&self,
|
||||||
|
fun: FunctionValue<'ctx>,
|
||||||
|
params: &[BasicValueEnum<'ctx>],
|
||||||
|
call_name: &str
|
||||||
|
) -> Option<BasicValueEnum<'ctx>> {
|
||||||
|
if let Some(target) = self.unwind_target {
|
||||||
|
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||||
|
let then_block = self.ctx.append_basic_block(current, &format!("after.{}", call_name));
|
||||||
|
let result = self.builder.build_invoke(fun, params, then_block, target, call_name).try_as_basic_value().left();
|
||||||
|
self.builder.position_at_end(then_block);
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let param: Vec<_> = params.iter().map(|v| (*v).into()).collect();
|
||||||
|
self.builder.build_call(fun, ¶m, call_name).try_as_basic_value().left()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
|
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
|
@ -404,7 +423,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
// default value handling
|
// default value handling
|
||||||
for k in keys.into_iter() {
|
for k in keys.into_iter() {
|
||||||
mapping.insert(k.name, ctx.gen_symbol_val(&k.default_value.unwrap()).into());
|
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into());
|
||||||
}
|
}
|
||||||
// reorder the parameters
|
// reorder the parameters
|
||||||
let mut real_params =
|
let mut real_params =
|
||||||
|
@ -474,7 +493,8 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
||||||
};
|
};
|
||||||
ctx.module.add_function(&symbol, fun_ty, None)
|
ctx.module.add_function(&symbol, fun_ty, None)
|
||||||
});
|
});
|
||||||
ctx.builder.build_call(fun_val, ¶m_vals, "call").try_as_basic_value().left()
|
|
||||||
|
ctx.build_call_or_invoke(fun_val, ¶m_vals, "call")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destructure_range<'ctx, 'a>(
|
pub fn destructure_range<'ctx, 'a>(
|
||||||
|
@ -715,7 +735,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
||||||
Some(match &expr.node {
|
Some(match &expr.node {
|
||||||
ExprKind::Constant { value, .. } => {
|
ExprKind::Constant { value, .. } => {
|
||||||
let ty = expr.custom.unwrap();
|
let ty = expr.custom.unwrap();
|
||||||
ctx.gen_const(value, ty).into()
|
ctx.gen_const(generator, value, ty).into()
|
||||||
}
|
}
|
||||||
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
|
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
|
||||||
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
|
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
|
||||||
|
|
|
@ -117,67 +117,45 @@ pub trait CodeGenerator {
|
||||||
|
|
||||||
/// Generate code for a while expression.
|
/// Generate code for a while expression.
|
||||||
/// Return true if the while loop must early return
|
/// Return true if the while loop must early return
|
||||||
fn gen_while<'ctx, 'a>(
|
fn gen_while<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||||
&mut self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
stmt: &Stmt<Option<Type>>,
|
|
||||||
) -> bool
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
gen_while(self, ctx, stmt);
|
gen_while(self, ctx, stmt);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate code for a while expression.
|
/// Generate code for a while expression.
|
||||||
/// Return true if the while loop must early return
|
/// Return true if the while loop must early return
|
||||||
fn gen_for<'ctx, 'a>(
|
fn gen_for<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||||
&mut self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
stmt: &Stmt<Option<Type>>,
|
|
||||||
) -> bool
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
gen_for(self, ctx, stmt);
|
gen_for(self, ctx, stmt);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate code for an if expression.
|
/// Generate code for an if expression.
|
||||||
/// Return true if the statement must early return
|
/// Return true if the statement must early return
|
||||||
fn gen_if<'ctx, 'a>(
|
fn gen_if<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||||
&mut self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
stmt: &Stmt<Option<Type>>,
|
|
||||||
) -> bool
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
gen_if(self, ctx, stmt)
|
gen_if(self, ctx, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_with<'ctx, 'a>(
|
fn gen_with<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||||
&mut self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
stmt: &Stmt<Option<Type>>,
|
|
||||||
) -> bool
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
gen_with(self, ctx, stmt)
|
gen_with(self, ctx, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate code for a statement
|
/// Generate code for a statement
|
||||||
/// Return true if the statement must early return
|
/// Return true if the statement must early return
|
||||||
fn gen_stmt<'ctx, 'a>(
|
fn gen_stmt<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||||
&mut self,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
stmt: &Stmt<Option<Type>>,
|
|
||||||
) -> bool
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
gen_stmt(self, ctx, stmt)
|
gen_stmt(self, ctx, stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use inkwell::{
|
||||||
module::Module,
|
module::Module,
|
||||||
passes::{PassManager, PassManagerBuilder},
|
passes::{PassManager, PassManagerBuilder},
|
||||||
types::{BasicType, BasicTypeEnum},
|
types::{BasicType, BasicTypeEnum},
|
||||||
values::{FunctionValue, PointerValue},
|
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
|
||||||
AddressSpace, OptimizationLevel,
|
AddressSpace, OptimizationLevel,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -60,11 +60,28 @@ pub struct CodeGenContext<'ctx, 'a> {
|
||||||
pub primitives: PrimitiveStore,
|
pub primitives: PrimitiveStore,
|
||||||
pub calls: Arc<HashMap<CodeLocation, CallId>>,
|
pub calls: Arc<HashMap<CodeLocation, CallId>>,
|
||||||
pub registry: &'a WorkerRegistry,
|
pub registry: &'a WorkerRegistry,
|
||||||
|
// const string cache
|
||||||
|
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>,
|
||||||
// stores the alloca for variables
|
// stores the alloca for variables
|
||||||
pub init_bb: BasicBlock<'ctx>,
|
pub init_bb: BasicBlock<'ctx>,
|
||||||
// where continue and break should go to respectively
|
|
||||||
// the first one is the test_bb, and the second one is bb after the loop
|
// the first one is the test_bb, and the second one is bb after the loop
|
||||||
pub loop_bb: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>,
|
pub loop_target: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>,
|
||||||
|
// unwind target bb
|
||||||
|
pub unwind_target: Option<BasicBlock<'ctx>>,
|
||||||
|
// return target bb, just emit ret if no such target
|
||||||
|
pub return_target: Option<BasicBlock<'ctx>>,
|
||||||
|
pub return_buffer: Option<PointerValue<'ctx>>,
|
||||||
|
// outer finally block function
|
||||||
|
pub outer_final: Option<(PointerValue<'ctx>, Vec<BasicBlock<'ctx>>, Vec<BasicBlock<'ctx>>)>,
|
||||||
|
// outer catch clauses
|
||||||
|
pub outer_catch_clauses:
|
||||||
|
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
|
pub fn is_terminated(&self) -> bool {
|
||||||
|
self.builder.get_insert_block().unwrap().get_terminator().is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fp = Box<dyn Fn(&Module) + Send + Sync>;
|
type Fp = Box<dyn Fn(&Module) + Send + Sync>;
|
||||||
|
@ -182,7 +199,7 @@ impl WorkerRegistry {
|
||||||
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
|
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let mut builder = context.create_builder();
|
let mut builder = context.create_builder();
|
||||||
let mut module = context.create_module(generator.get_name());
|
let module = context.create_module(generator.get_name());
|
||||||
|
|
||||||
let pass_builder = PassManagerBuilder::create();
|
let pass_builder = PassManagerBuilder::create();
|
||||||
pass_builder.set_optimization_level(OptimizationLevel::Default);
|
pass_builder.set_optimization_level(OptimizationLevel::Default);
|
||||||
|
@ -190,10 +207,12 @@ impl WorkerRegistry {
|
||||||
pass_builder.populate_function_pass_manager(&passes);
|
pass_builder.populate_function_pass_manager(&passes);
|
||||||
|
|
||||||
while let Some(task) = self.receiver.recv().unwrap() {
|
while let Some(task) = self.receiver.recv().unwrap() {
|
||||||
let result = gen_func(&context, generator, self, builder, module, task);
|
let tmp_module = context.create_module("tmp");
|
||||||
|
let result = gen_func(&context, generator, self, builder, tmp_module, task);
|
||||||
builder = result.0;
|
builder = result.0;
|
||||||
module = result.1;
|
|
||||||
passes.run_on(&result.2);
|
passes.run_on(&result.2);
|
||||||
|
module.link_in_module(result.1).unwrap();
|
||||||
|
// module = result.1;
|
||||||
*self.task_count.lock() -= 1;
|
*self.task_count.lock() -= 1;
|
||||||
self.wait_condvar.notify_all();
|
self.wait_condvar.notify_all();
|
||||||
}
|
}
|
||||||
|
@ -235,20 +254,26 @@ fn get_llvm_type<'ctx>(
|
||||||
// we assume the type cache should already contain primitive types,
|
// we assume the type cache should already contain primitive types,
|
||||||
// and they should be passed by value instead of passing as pointer.
|
// and they should be passed by value instead of passing as pointer.
|
||||||
type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| {
|
type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| {
|
||||||
let ty = unifier.get_ty(ty);
|
let ty_enum = unifier.get_ty(ty);
|
||||||
match &*ty {
|
let result = match &*ty_enum {
|
||||||
TObj { obj_id, fields, .. } => {
|
TObj { obj_id, fields, .. } => {
|
||||||
|
// check to avoid treating primitives as classes
|
||||||
|
if obj_id.0 <= 7 {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
// a struct with fields in the order of declaration
|
// a struct with fields in the order of declaration
|
||||||
let top_level_defs = top_level.definitions.read();
|
let top_level_defs = top_level.definitions.read();
|
||||||
let definition = top_level_defs.get(obj_id.0).unwrap();
|
let definition = top_level_defs.get(obj_id.0).unwrap();
|
||||||
let ty = if let TopLevelDef::Class { fields: fields_list, .. } = &*definition.read()
|
let ty = if let TopLevelDef::Class { name, fields: fields_list, .. } = &*definition.read()
|
||||||
{
|
{
|
||||||
|
let struct_type = ctx.opaque_struct_type(&name.to_string());
|
||||||
let fields = fields.borrow();
|
let fields = fields.borrow();
|
||||||
let fields = fields_list
|
let fields = fields_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| get_llvm_type(ctx, generator, unifier, top_level, type_cache, fields[&f.0].0))
|
.map(|f| get_llvm_type(ctx, generator, unifier, top_level, type_cache, fields[&f.0].0))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
struct_type.set_body(&fields, false);
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic).into()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
@ -270,8 +295,10 @@ fn get_llvm_type<'ctx>(
|
||||||
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
||||||
}
|
}
|
||||||
TVirtual { .. } => unimplemented!(),
|
TVirtual { .. } => unimplemented!(),
|
||||||
_ => unreachable!("{}", ty.get_type_name()),
|
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||||
}
|
};
|
||||||
|
type_cache.insert(unifier.get_representative(ty), result);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,6 +442,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||||
builder.build_store(alloca, param);
|
builder.build_store(alloca, param);
|
||||||
var_assignment.insert(arg.name, (alloca, None, 0));
|
var_assignment.insert(arg.name, (alloca, None, 0));
|
||||||
}
|
}
|
||||||
|
let return_buffer = fn_type.get_return_type().map(|v| builder.build_alloca(v, "$ret"));
|
||||||
let static_values = {
|
let static_values = {
|
||||||
let store = registry.static_value_store.lock();
|
let store = registry.static_value_store.lock();
|
||||||
store.store[task.id].clone()
|
store.store[task.id].clone()
|
||||||
|
@ -432,7 +460,13 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||||
resolver: task.resolver,
|
resolver: task.resolver,
|
||||||
top_level: top_level_ctx.as_ref(),
|
top_level: top_level_ctx.as_ref(),
|
||||||
calls: task.calls,
|
calls: task.calls,
|
||||||
loop_bb: None,
|
loop_target: None,
|
||||||
|
return_target: None,
|
||||||
|
return_buffer,
|
||||||
|
unwind_target: None,
|
||||||
|
outer_final: None,
|
||||||
|
outer_catch_clauses: None,
|
||||||
|
const_strings: Default::default(),
|
||||||
registry,
|
registry,
|
||||||
var_assignment,
|
var_assignment,
|
||||||
type_cache,
|
type_cache,
|
||||||
|
@ -444,15 +478,14 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||||
static_value_store,
|
static_value_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut returned = false;
|
|
||||||
for stmt in task.body.iter() {
|
for stmt in task.body.iter() {
|
||||||
returned = generator.gen_stmt(&mut code_gen_context, stmt);
|
generator.gen_stmt(&mut code_gen_context, stmt);
|
||||||
if returned {
|
if code_gen_context.is_terminated() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// after static analysis, only void functions can have no return at the end.
|
// after static analysis, only void functions can have no return at the end.
|
||||||
if !returned {
|
if !code_gen_context.is_terminated() {
|
||||||
code_gen_context.builder.build_return(None);
|
code_gen_context.builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,17 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::expr::gen_binop_expr,
|
codegen::expr::gen_binop_expr,
|
||||||
typecheck::typedef::{Type, TypeEnum},
|
toplevel::{DefinitionId, TopLevelDef},
|
||||||
|
typecheck::typedef::{Type, TypeEnum, FunSignature}
|
||||||
};
|
};
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
|
attributes::{Attribute, AttributeLoc},
|
||||||
|
basic_block::BasicBlock,
|
||||||
types::BasicTypeEnum,
|
types::BasicTypeEnum,
|
||||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue},
|
||||||
|
IntPredicate::EQ,
|
||||||
};
|
};
|
||||||
use nac3parser::ast::{Expr, ExprKind, Stmt, StmtKind};
|
use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, Constant};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub fn gen_var<'ctx, 'a>(
|
pub fn gen_var<'ctx, 'a>(
|
||||||
|
@ -173,7 +177,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||||
let orelse_bb =
|
let orelse_bb =
|
||||||
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
||||||
// store loop bb information and restore it later
|
// store loop bb information and restore it later
|
||||||
let loop_bb = ctx.loop_bb.replace((test_bb, cont_bb));
|
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
|
||||||
|
|
||||||
let iter_val = generator.gen_expr(ctx, iter).unwrap().to_basic_value_enum(ctx, generator);
|
let iter_val = generator.gen_expr(ctx, iter).unwrap().to_basic_value_enum(ctx, generator);
|
||||||
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
||||||
|
@ -234,23 +238,23 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||||
generator.gen_assign(ctx, target, val.into());
|
generator.gen_assign(ctx, target, val.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
for stmt in body.iter() {
|
gen_block(generator, ctx, body.iter());
|
||||||
generator.gen_stmt(ctx, stmt);
|
|
||||||
}
|
|
||||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||||
if counter != counter2 {
|
if counter != counter2 {
|
||||||
*static_val = None;
|
*static_val = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !ctx.is_terminated() {
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
|
}
|
||||||
if !orelse.is_empty() {
|
if !orelse.is_empty() {
|
||||||
ctx.builder.position_at_end(orelse_bb);
|
ctx.builder.position_at_end(orelse_bb);
|
||||||
for stmt in orelse.iter() {
|
gen_block(generator, ctx, orelse.iter());
|
||||||
generator.gen_stmt(ctx, stmt);
|
if !ctx.is_terminated() {
|
||||||
}
|
|
||||||
ctx.builder.build_unconditional_branch(cont_bb);
|
ctx.builder.build_unconditional_branch(cont_bb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||||
if counter != counter2 {
|
if counter != counter2 {
|
||||||
|
@ -258,7 +262,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
ctx.loop_bb = loop_bb;
|
ctx.loop_target = loop_bb;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -282,7 +286,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||||
let orelse_bb =
|
let orelse_bb =
|
||||||
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
||||||
// store loop bb information and restore it later
|
// store loop bb information and restore it later
|
||||||
let loop_bb = ctx.loop_bb.replace((test_bb, cont_bb));
|
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
ctx.builder.position_at_end(test_bb);
|
ctx.builder.position_at_end(test_bb);
|
||||||
let test = generator.gen_expr(ctx, test).unwrap().to_basic_value_enum(ctx, generator);
|
let test = generator.gen_expr(ctx, test).unwrap().to_basic_value_enum(ctx, generator);
|
||||||
|
@ -292,23 +296,23 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
ctx.builder.position_at_end(body_bb);
|
ctx.builder.position_at_end(body_bb);
|
||||||
for stmt in body.iter() {
|
gen_block(generator, ctx, body.iter());
|
||||||
generator.gen_stmt(ctx, stmt);
|
|
||||||
}
|
|
||||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||||
if counter != counter2 {
|
if counter != counter2 {
|
||||||
*static_val = None;
|
*static_val = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !ctx.is_terminated() {
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
|
}
|
||||||
if !orelse.is_empty() {
|
if !orelse.is_empty() {
|
||||||
ctx.builder.position_at_end(orelse_bb);
|
ctx.builder.position_at_end(orelse_bb);
|
||||||
for stmt in orelse.iter() {
|
gen_block(generator, ctx, orelse.iter());
|
||||||
generator.gen_stmt(ctx, stmt);
|
if !ctx.is_terminated() {
|
||||||
}
|
|
||||||
ctx.builder.build_unconditional_branch(cont_bb);
|
ctx.builder.build_unconditional_branch(cont_bb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||||
if counter != counter2 {
|
if counter != counter2 {
|
||||||
|
@ -316,7 +320,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
ctx.loop_bb = loop_bb;
|
ctx.loop_target = loop_bb;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -326,7 +330,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
stmt: &Stmt<Option<Type>>,
|
stmt: &Stmt<Option<Type>>,
|
||||||
) -> bool {
|
) {
|
||||||
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
|
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
|
||||||
// var_assignment static values may be changed in another branch
|
// var_assignment static values may be changed in another branch
|
||||||
// if so, remove the static value as it may not be correct in this branch
|
// if so, remove the static value as it may not be correct in this branch
|
||||||
|
@ -352,13 +356,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
ctx.builder.position_at_end(body_bb);
|
ctx.builder.position_at_end(body_bb);
|
||||||
let mut exited = false;
|
gen_block(generator, ctx, body.iter());
|
||||||
for stmt in body.iter() {
|
|
||||||
exited = generator.gen_stmt(ctx, stmt);
|
|
||||||
if exited {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||||
if counter != counter2 {
|
if counter != counter2 {
|
||||||
|
@ -366,32 +364,22 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exited {
|
if !ctx.is_terminated() {
|
||||||
if cont_bb.is_none() {
|
if cont_bb.is_none() {
|
||||||
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
||||||
}
|
}
|
||||||
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
||||||
}
|
}
|
||||||
let then_exited = exited;
|
if !orelse.is_empty() {
|
||||||
let else_exited = if !orelse.is_empty() {
|
|
||||||
exited = false;
|
|
||||||
ctx.builder.position_at_end(orelse_bb);
|
ctx.builder.position_at_end(orelse_bb);
|
||||||
for stmt in orelse.iter() {
|
gen_block(generator, ctx, orelse.iter());
|
||||||
exited = generator.gen_stmt(ctx, stmt);
|
if !ctx.is_terminated() {
|
||||||
if exited {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !exited {
|
|
||||||
if cont_bb.is_none() {
|
if cont_bb.is_none() {
|
||||||
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
||||||
}
|
}
|
||||||
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
||||||
}
|
}
|
||||||
exited
|
}
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
if let Some(cont_bb) = cont_bb {
|
if let Some(cont_bb) = cont_bb {
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
}
|
}
|
||||||
|
@ -401,7 +389,11 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||||
*static_val = None;
|
*static_val = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
then_exited && else_exited
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exn_constructor<'ctx, 'a>(
|
pub fn exn_constructor<'ctx, 'a>(
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||||
|
@ -481,23 +473,37 @@ pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
|
value: &Option<Box<Expr<Option<Type>>>>,
|
||||||
|
) {
|
||||||
|
let value = value
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| generator.gen_expr(ctx, v).unwrap().to_basic_value_enum(ctx, generator));
|
||||||
|
if let Some(return_target) = ctx.return_target {
|
||||||
|
if let Some(value) = value {
|
||||||
|
ctx.builder.build_store(ctx.return_buffer.unwrap(), value);
|
||||||
|
}
|
||||||
|
ctx.builder.build_unconditional_branch(return_target);
|
||||||
|
} else {
|
||||||
|
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
||||||
|
ctx.builder.build_return(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
stmt: &Stmt<Option<Type>>,
|
stmt: &Stmt<Option<Type>>,
|
||||||
) -> bool {
|
) {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::Pass { .. } => {}
|
StmtKind::Pass { .. } => {}
|
||||||
StmtKind::Expr { value, .. } => {
|
StmtKind::Expr { value, .. } => {
|
||||||
generator.gen_expr(ctx, value);
|
generator.gen_expr(ctx, value);
|
||||||
}
|
}
|
||||||
StmtKind::Return { value, .. } => {
|
StmtKind::Return { value, .. } => {
|
||||||
let value = value
|
gen_return(generator, ctx, value);
|
||||||
.as_ref()
|
|
||||||
.map(|v| generator.gen_expr(ctx, v).unwrap().to_basic_value_enum(ctx, generator));
|
|
||||||
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
|
||||||
ctx.builder.build_return(value);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
StmtKind::AnnAssign { target, value, .. } => {
|
StmtKind::AnnAssign { target, value, .. } => {
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
|
@ -512,17 +518,15 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Continue { .. } => {
|
StmtKind::Continue { .. } => {
|
||||||
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().0);
|
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
StmtKind::Break { .. } => {
|
StmtKind::Break { .. } => {
|
||||||
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().1);
|
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().1);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
StmtKind::If { .. } => return generator.gen_if(ctx, stmt),
|
StmtKind::If { .. } => generator.gen_if(ctx, stmt),
|
||||||
StmtKind::While { .. } => return generator.gen_while(ctx, stmt),
|
StmtKind::While { .. } => generator.gen_while(ctx, stmt),
|
||||||
StmtKind::For { .. } => return generator.gen_for(ctx, stmt),
|
StmtKind::For { .. } => generator.gen_for(ctx, stmt),
|
||||||
StmtKind::With { .. } => return generator.gen_with(ctx, stmt),
|
StmtKind::With { .. } => generator.gen_with(ctx, stmt),
|
||||||
StmtKind::AugAssign { target, op, value, .. } => {
|
StmtKind::AugAssign { target, op, value, .. } => {
|
||||||
let value = gen_binop_expr(generator, ctx, target, op, value);
|
let value = gen_binop_expr(generator, ctx, target, op, value);
|
||||||
generator.gen_assign(ctx, target, value);
|
generator.gen_assign(ctx, target, value);
|
||||||
|
@ -530,4 +534,15 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
false
|
false
|
||||||
|
pub fn gen_block<'ctx, 'a, 'b, G: CodeGenerator, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
|
stmts: I,
|
||||||
|
) {
|
||||||
|
for stmt in stmts {
|
||||||
|
generator.gen_stmt(ctx, stmt);
|
||||||
|
if ctx.is_terminated() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue