Exception and RPC implementation #181

Merged
sb10q merged 7 commits from exception-rpc into master 2022-02-13 10:40:13 +08:00
23 changed files with 1873 additions and 400 deletions

View File

@ -0,0 +1,57 @@
class EmbeddingMap:
def __init__(self):
self.object_inverse_map = {}
self.object_map = {}
self.string_map = {}
self.string_reverse_map = {}
self.function_map = {}
# preallocate exception names
self.preallocate_runtime_exception_names(["RuntimeError",
"RTIOUnderflow",
"RTIOOverflow",
"RTIODestinationUnreachable",
"DMAError",
"I2CError",
"CacheError",
"SPIError",
"0:ZeroDivisionError",
"0:IndexError"])
def preallocate_runtime_exception_names(self, names):
for i, name in enumerate(names):
if ":" not in name:
name = "0:artiq.coredevice.exceptions." + name
exn_id = self.store_str(name)
assert exn_id == i
def store_function(self, key, fun):
self.function_map[key] = fun
return key
def store_object(self, obj):
obj_id = id(obj)
if obj_id in self.object_inverse_map:
return self.object_inverse_map[obj_id]
key = len(self.object_map)
self.object_map[key] = obj
self.object_inverse_map[obj_id] = key
return key
def store_str(self, s):
if s in self.string_reverse_map:
return self.string_reverse_map[s]
key = len(self.string_map)
self.string_map[key] = s
self.string_reverse_map[s] = key
return key
def retrieve_function(self, key):
return self.function_map[key]
def retrieve_object(self, key):
return self.object_map[key]
def retrieve_str(self, key):
return self.string_map[key]

View File

@ -6,13 +6,14 @@ from typing import Generic, TypeVar
from math import floor, ceil
import nac3artiq
from embedding_map import EmbeddingMap
__all__ = [
"Kernel", "KernelInvariant", "virtual",
"round64", "floor64", "ceil64",
"extern", "kernel", "portable", "nac3",
"ms", "us", "ns",
"rpc", "ms", "us", "ns",
"print_int32", "print_int64",
"Core", "TTLOut",
"parallel", "sequential"
@ -65,6 +66,10 @@ def extern(function):
register_function(function)
return function
def rpc(function):
"""Decorates a function declaration defined by the core device runtime."""
register_function(function)
return function
def kernel(function_or_method):
"""Decorates a function or method to be executed on the core device."""
@ -146,6 +151,9 @@ class Core:
def run(self, method, *args, **kwargs):
global allow_registration
embedding = EmbeddingMap()
if allow_registration:
compiler.analyze(registered_functions, registered_classes)
allow_registration = False
@ -157,7 +165,7 @@ class Core:
obj = method
name = ""
compiler.compile_method_to_file(obj, name, args, "module.elf")
compiler.compile_method_to_file(obj, name, args, "module.elf", embedding)
@kernel
def reset(self):

View File

@ -1,16 +1,30 @@
use nac3core::{
codegen::{expr::gen_call, stmt::gen_with, CodeGenContext, CodeGenerator},
codegen::{
expr::gen_call,
stmt::{gen_block, gen_with},
CodeGenContext, CodeGenerator,
},
symbol_resolver::ValueEnum,
toplevel::DefinitionId,
toplevel::{DefinitionId, GenCall},
typecheck::typedef::{FunSignature, Type},
};
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
use inkwell::{context::Context, types::IntType, values::BasicValueEnum};
use inkwell::{
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
};
use crate::timeline::TimeFns;
use std::{
collections::hash_map::DefaultHasher,
collections::HashMap,
convert::TryInto,
hash::{Hash, Hasher},
sync::Arc,
};
pub struct ArtiqCodeGenerator<'a> {
name: String,
size_t: u32,
@ -21,16 +35,13 @@ pub struct ArtiqCodeGenerator<'a> {
}
impl<'a> ArtiqCodeGenerator<'a> {
pub fn new(name: String, size_t: u32, timeline: &'a (dyn TimeFns + Sync)) -> ArtiqCodeGenerator<'a> {
pub fn new(
name: String,
size_t: u32,
timeline: &'a (dyn TimeFns + Sync),
) -> ArtiqCodeGenerator<'a> {
assert!(size_t == 32 || size_t == 64);
ArtiqCodeGenerator {
name,
size_t,
name_counter: 0,
start: None,
end: None,
timeline,
}
ArtiqCodeGenerator { name, size_t, name_counter: 0, start: None, end: None, timeline }
}
}
@ -86,7 +97,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool {
) {
if let StmtKind::With { items, body, .. } = &stmt.node {
if items.len() == 1 && items[0].optional_vars.is_none() {
let item = &items[0];
@ -108,9 +119,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)
.unwrap()
.to_basic_value_enum(ctx, self)
self.gen_expr(ctx, old_start).unwrap().to_basic_value_enum(ctx, self)
} else {
self.timeline.emit_now_mu(ctx)
};
@ -126,10 +135,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name {
id: start,
ctx: name_ctx.clone(),
},
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let start = self.gen_store_target(ctx, &start_expr);
@ -140,40 +146,41 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name {
id: end,
ctx: name_ctx.clone(),
},
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let end = self.gen_store_target(ctx, &end_expr);
ctx.builder.build_store(end, now);
self.end = Some(end_expr);
self.name_counter += 1;
let mut exited = false;
for stmt in body.iter() {
if self.gen_stmt(ctx, stmt) {
exited = true;
break;
}
}
gen_block(self, ctx, body.iter());
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self
.gen_expr(ctx, &end_expr)
.unwrap()
.to_basic_value_enum(ctx, self);
let end_val =
self.gen_expr(ctx, &end_expr).unwrap().to_basic_value_enum(ctx, self);
// inside an sequential block
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
if let Some(old_end) = &old_end {
let outer_end_val = self
.gen_expr(ctx, old_end)
.unwrap()
.to_basic_value_enum(ctx, self);
let outer_end_val =
self.gen_expr(ctx, old_end).unwrap().to_basic_value_enum(ctx, self);
let smax =
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -194,24 +201,294 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
}
self.start = old_start;
self.end = old_end;
return exited;
if reset_position {
ctx.builder.position_at_end(current);
}
return;
} else if id == &"sequential".into() {
let start = self.start.take();
for stmt in body.iter() {
if self.gen_stmt(ctx, stmt) {
self.start = start;
return true;
self.gen_stmt(ctx, stmt);
if ctx.is_terminated() {
break;
}
}
self.start = start;
return false;
return
}
}
}
// not parallel/sequential
gen_with(self, ctx, stmt)
gen_with(self, ctx, stmt);
} else {
unreachable!()
}
}
}
fn gen_rpc_tag<'ctx, 'a>(ctx: &mut CodeGenContext<'ctx, 'a>, ty: Type, buffer: &mut Vec<u8>) {
use nac3core::typecheck::typedef::TypeEnum::*;
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let bool = ctx.primitives.bool;
let str = ctx.primitives.str;
let none = ctx.primitives.none;
if ctx.unifier.unioned(ty, int32) {
buffer.push(b'i');
} else if ctx.unifier.unioned(ty, int64) {
buffer.push(b'I');
} else if ctx.unifier.unioned(ty, float) {
buffer.push(b'f');
} else if ctx.unifier.unioned(ty, bool) {
buffer.push(b'b');
} else if ctx.unifier.unioned(ty, str) {
buffer.push(b's');
} else if ctx.unifier.unioned(ty, none) {
buffer.push(b'n');
} else {
let ty = ctx.unifier.get_ty(ty);
match &*ty {
TTuple { ty } => {
buffer.push(b't');
buffer.push(ty.len() as u8);
for ty in ty {
gen_rpc_tag(ctx, *ty, buffer);
}
}
TList { ty } => {
buffer.push(b'l');
gen_rpc_tag(ctx, *ty, buffer);
}
// we should return an error, this will be fixed after improving error message
// as this requires returning an error during codegen
_ => unimplemented!(),
}
}
}
fn rpc_codegen_callback_fn<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
generator: &mut dyn CodeGenerator,
) -> Option<BasicValueEnum<'ctx>> {
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::Generic);
let size_type = generator.get_size_type(ctx.ctx);
let int8 = ctx.ctx.i8_type();
let int32 = ctx.ctx.i32_type();
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1.0 as u64, false);
// -- setup rpc tags
let mut tag = Vec::new();
if obj.is_some() {
tag.push(b'O');
}
for arg in fun.0.args.iter() {
gen_rpc_tag(ctx, arg.ty, &mut tag);
}
tag.push(b':');
gen_rpc_tag(ctx, fun.0.ret, &mut tag);
let mut hasher = DefaultHasher::new();
tag.hash(&mut hasher);
let hash = format!("{}", hasher.finish());
let tag_ptr = ctx
.module
.get_global(hash.as_str())
.unwrap_or_else(|| {
let tag_arr_ptr = ctx.module.add_global(
int8.array_type(tag.len() as u32),
None,
format!("tagptr{}", fun.1 .0).as_str(),
);
tag_arr_ptr.set_initializer(&int8.const_array(
&tag.iter().map(|v| int8.const_int(*v as u64, false)).collect::<Vec<_>>(),
));
tag_arr_ptr.set_linkage(Linkage::Private);
let tag_ptr = ctx.module.add_global(tag_ptr_type, None, &hash);
tag_ptr.set_linkage(Linkage::Private);
tag_ptr.set_initializer(&ctx.ctx.const_struct(
&[
tag_arr_ptr.as_pointer_value().const_cast(ptr_type).into(),
size_type.const_int(tag.len() as u64, false).into(),
],
false,
));
tag_ptr
})
.as_pointer_value();
let arg_length = args.len() + if obj.is_some() { 1 } else { 0 };
let stacksave = ctx.module.get_function("llvm.stacksave").unwrap_or_else(|| {
ctx.module.add_function("llvm.stacksave", ptr_type.fn_type(&[], false), None)
});
let stackrestore = ctx.module.get_function("llvm.stackrestore").unwrap_or_else(|| {
ctx.module.add_function(
"llvm.stackrestore",
ctx.ctx.void_type().fn_type(&[ptr_type.into()], false),
None,
)
});
let stackptr = ctx.builder.build_call(stacksave, &[], "rpc.stack");
let args_ptr = ctx.builder.build_array_alloca(
ptr_type,
ctx.ctx.i32_type().const_int(arg_length as u64, false),
"argptr",
);
// -- rpc args handling
let mut keys = fun.0.args.clone();
let mut mapping = HashMap::new();
for (key, value) in args.into_iter() {
mapping.insert(key.unwrap_or_else(|| keys.remove(0).name), value);
}
// default value handling
for k in keys.into_iter() {
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into());
}
// reorder the parameters
let mut real_params = fun
.0
.args
.iter()
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator))
.collect::<Vec<_>>();
if let Some(obj) = obj {
if let ValueEnum::Static(obj) = obj.1 {
real_params.insert(0, obj.get_const_obj(ctx, generator));
} else {
// should be an error here...
panic!("only host object is allowed");
}
}
for (i, arg) in real_params.iter().enumerate() {
let arg_slot = if arg.is_pointer_value() {
arg.into_pointer_value()
} else {
let arg_slot = ctx.builder.build_alloca(arg.get_type(), &format!("rpc.arg{}", i));
ctx.builder.build_store(arg_slot, *arg);
arg_slot
};
let arg_slot = ctx.builder.build_bitcast(arg_slot, ptr_type, "rpc.arg");
let arg_ptr = unsafe {
ctx.builder.build_gep(
args_ptr,
&[int32.const_int(i as u64, false)],
&format!("rpc.arg{}", i),
)
};
ctx.builder.build_store(arg_ptr, arg_slot);
}
// call
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {
ctx.module.add_function(
"rpc_send",
ctx.ctx.void_type().fn_type(
&[
int32.into(),
tag_ptr_type.ptr_type(AddressSpace::Generic).into(),
ptr_type.ptr_type(AddressSpace::Generic).into(),
],
false,
),
None,
)
});
ctx.builder.build_call(
rpc_send,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
);
// reclaim stack space used by arguments
ctx.builder.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
);
// -- receive value:
// T result = {
// void *ret_ptr = alloca(sizeof(T));
// void *ptr = ret_ptr;
// loop: int size = rpc_recv(ptr);
// // Non-zero: Provide `size` bytes of extra storage for variable-length data.
// if(size) { ptr = alloca(size); goto loop; }
// else *(T*)ret_ptr
// }
let rpc_recv = ctx.module.get_function("rpc_recv").unwrap_or_else(|| {
ctx.module.add_function("rpc_recv", int32.fn_type(&[ptr_type.into()], false), None)
});
if ctx.unifier.unioned(fun.0.ret, ctx.primitives.none) {
ctx.build_call_or_invoke(rpc_recv, &[ptr_type.const_null().into()], "rpc_recv");
return None
}
let prehead_bb = ctx.builder.get_insert_block().unwrap();
let current_function = prehead_bb.get_parent().unwrap();
let head_bb = ctx.ctx.append_basic_block(current_function, "rpc.head");
let alloc_bb = ctx.ctx.append_basic_block(current_function, "rpc.continue");
let tail_bb = ctx.ctx.append_basic_block(current_function, "rpc.tail");
let mut ret_ty = ctx.get_llvm_type(generator, fun.0.ret);
let need_load = !ret_ty.is_pointer_type();
if ret_ty.is_pointer_type() {
ret_ty = ret_ty.into_pointer_type().get_element_type().try_into().unwrap();
}
let slot = ctx.builder.build_alloca(ret_ty, "rpc.ret.slot");
let slotgen = ctx.builder.build_bitcast(slot, ptr_type, "rpc.ret.ptr");
ctx.builder.build_unconditional_branch(head_bb);
ctx.builder.position_at_end(head_bb);
let phi = ctx.builder.build_phi(ptr_type, "rpc.ptr");
phi.add_incoming(&[(&slotgen, prehead_bb)]);
let alloc_size = ctx
.build_call_or_invoke(rpc_recv, &[phi.as_basic_value()], "rpc.size.next")
.unwrap()
.into_int_value();
let is_done = ctx.builder.build_int_compare(
inkwell::IntPredicate::EQ,
int32.const_zero(),
alloc_size,
"rpc.done",
);
ctx.builder.build_conditional_branch(is_done, tail_bb, alloc_bb);
ctx.builder.position_at_end(alloc_bb);
let alloc_ptr = ctx.builder.build_array_alloca(ptr_type, alloc_size, "rpc.alloc");
let alloc_ptr = ctx.builder.build_bitcast(alloc_ptr, ptr_type, "rpc.alloc.ptr");
phi.add_incoming(&[(&alloc_ptr, alloc_bb)]);
ctx.builder.build_unconditional_branch(head_bb);
ctx.builder.position_at_end(tail_bb);
if need_load {
let result = ctx.builder.build_load(slot, "rpc.result");
ctx.builder.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
);
Some(result)
} else {
Some(slot.into())
}
}
pub fn rpc_codegen_callback() -> Arc<GenCall> {
Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| {
rpc_codegen_callback_fn(ctx, obj, fun, args, generator)
})))
}

View File

@ -12,7 +12,7 @@ use inkwell::{
};
use nac3core::typecheck::typedef::{Unifier, TypeEnum};
use nac3parser::{
ast::{self, Stmt, StrRef},
ast::{self, ExprKind, Stmt, StmtKind, StrRef},
parser::{self, parse_program},
};
use pyo3::prelude::*;
@ -24,7 +24,10 @@ use nac3core::{
codegen::{concrete_type::ConcreteTypeStore, CodeGenTask, WithCall, WorkerRegistry},
codegen::irrt::load_irrt,
symbol_resolver::SymbolResolver,
toplevel::{composer::{TopLevelComposer, ComposerConfig}, DefinitionId, GenCall, TopLevelDef},
toplevel::{
composer::{ComposerConfig, TopLevelComposer},
DefinitionId, GenCall, TopLevelDef,
},
typecheck::typedef::{FunSignature, FuncArg},
typecheck::{type_inferencer::PrimitiveStore, typedef::Type},
};
@ -32,7 +35,7 @@ use nac3core::{
use tempfile::{self, TempDir};
use crate::{
codegen::ArtiqCodeGenerator,
codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
symbol_resolver::{InnerResolver, PythonHelper, Resolver},
};
@ -61,6 +64,7 @@ pub struct PrimitivePythonId {
tuple: u64,
typevar: u64,
none: u64,
exception: u64,
generic_alias: (u64, u64),
virtual_id: u64,
}
@ -81,6 +85,7 @@ struct Nac3 {
primitive_ids: PrimitivePythonId,
working_directory: TempDir,
top_levels: Vec<TopLevelComponent>,
string_store: Arc<RwLock<HashMap<String, i32>>>,
}
impl Nac3 {
@ -127,9 +132,13 @@ impl Nac3 {
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
match &base.node {
ast::ExprKind::Name { id, .. } => {
let base_obj = module.getattr(py, id.to_string())?;
let base_id = id_fn.call1((base_obj,))?.extract()?;
Ok(registered_class_ids.contains(&base_id))
if *id == "Exception".into() {
Ok(true)
} else {
let base_obj = module.getattr(py, id.to_string())?;
let base_id = id_fn.call1((base_obj,))?.extract()?;
Ok(registered_class_ids.contains(&base_id))
}
}
_ => Ok(true),
}
@ -143,7 +152,9 @@ impl Nac3 {
{
decorator_list.iter().any(|decorator| {
if let ast::ExprKind::Name { id, .. } = decorator.node {
id.to_string() == "kernel" || id.to_string() == "portable"
id.to_string() == "kernel"
|| id.to_string() == "portable"
|| id.to_string() == "rpc"
} else {
false
}
@ -159,7 +170,7 @@ impl Nac3 {
} => decorator_list.iter().any(|decorator| {
if let ast::ExprKind::Name { id, .. } = decorator.node {
let id = id.to_string();
id == "extern" || id == "portable" || id == "kernel"
id == "extern" || id == "portable" || id == "kernel" || id == "rpc"
} else {
false
}
@ -188,7 +199,7 @@ impl Nac3 {
Ok(ty) => ty,
Err(e) => return Some(format!("type error inside object launching kernel: {}", e))
};
let fun_ty = if method_name.is_empty() {
base_ty
} else if let TypeEnum::TObj { fields, .. } = &*unifier.get_ty(base_ty) {
@ -201,7 +212,7 @@ impl Nac3 {
} else {
return Some("cannot launch kernel by calling a non-callable".into())
};
if let TypeEnum::TFunc(sig) = &*unifier.get_ty(fun_ty) {
let FunSignature { args, .. } = &*sig.borrow();
if arg_names.len() > args.len() {
@ -269,7 +280,7 @@ impl Nac3 {
ret: primitive.int64,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, _| {
Arc::new(GenCall::new(Box::new(move |ctx, _, _, _, _| {
Some(time_fns.emit_now_mu(ctx))
}))),
),
@ -284,8 +295,9 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args| {
time_fns.emit_at_mu(ctx, args[0].1);
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
time_fns.emit_at_mu(ctx, arg);
None
}))),
),
@ -300,16 +312,20 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args| {
time_fns.emit_delay_mu(ctx, args[0].1);
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
time_fns.emit_delay_mu(ctx, arg);
None
}))),
),
];
let (_, builtins_def, builtins_ty) = TopLevelComposer::new(builtins.clone(), ComposerConfig {
kernel_ann: Some("Kernel"),
kernel_invariant_ann: "KernelInvariant"
});
let (_, builtins_def, builtins_ty) = TopLevelComposer::new(
builtins.clone(),
ComposerConfig {
kernel_ann: Some("Kernel"),
kernel_invariant_ann: "KernelInvariant",
},
);
let builtins_mod = PyModule::import(py, "builtins").unwrap();
let id_fn = builtins_mod.getattr("id").unwrap();
@ -385,6 +401,11 @@ impl Nac3 {
.unwrap()
.extract()
.unwrap(),
exception: id_fn
.call1((builtins_mod.getattr("tuple").unwrap(),))
.unwrap()
.extract()
.unwrap(),
};
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
@ -405,6 +426,7 @@ impl Nac3 {
top_levels: Default::default(),
pyid_to_def: Default::default(),
working_directory,
string_store: Default::default()
})
}
@ -441,6 +463,7 @@ impl Nac3 {
method_name: &str,
args: Vec<&PyAny>,
filename: &str,
embedding_map: &PyAny,
py: Python,
) -> PyResult<()> {
let (mut composer, _, _) = TopLevelComposer::new(self.builtins.clone(), ComposerConfig {
@ -451,17 +474,26 @@ impl Nac3 {
let builtins = PyModule::import(py, "builtins")?;
let typings = PyModule::import(py, "typing")?;
let id_fn = builtins.getattr("id")?;
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
let store_fun = embedding_map
.getattr("store_function")
.unwrap()
.to_object(py);
let helper = PythonHelper {
id_fn: builtins.getattr("id").unwrap().to_object(py),
len_fn: builtins.getattr("len").unwrap().to_object(py),
type_fn: builtins.getattr("type").unwrap().to_object(py),
origin_ty_fn: typings.getattr("get_origin").unwrap().to_object(py),
args_ty_fn: typings.getattr("get_args").unwrap().to_object(py),
store_obj,
store_str
};
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
let pyid_to_type = Arc::new(RwLock::new(HashMap::<u64, Type>::new()));
let global_value_ids = Arc::new(RwLock::new(HashSet::<u64>::new()));
let mut rpc_ids = vec![];
for (stmt, path, module) in self.top_levels.iter() {
let py_module: &PyAny = module.extract(py)?;
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
@ -492,6 +524,7 @@ impl Nac3 {
id_to_primitive: Default::default(),
field_to_val: Default::default(),
helper,
string_store: self.string_store.clone(),
})))
as Arc<dyn SymbolResolver + Send + Sync>;
let name_to_pyid = Rc::new(name_to_pyid);
@ -502,7 +535,30 @@ impl Nac3 {
let (name, def_id, ty) = composer
.register_top_level(stmt.clone(), Some(resolver.clone()), path.clone())
.map_err(|e| exceptions::PyRuntimeError::new_err(format!("nac3 compilation failure: {}", e)))?;
.map_err(|e| {
exceptions::PyRuntimeError::new_err(format!("nac3 compilation failure: {}", e))
})?;
match &stmt.node {
StmtKind::FunctionDef { decorator_list, .. } => {
if decorator_list.iter().any(|decorator| matches!(decorator.node, ExprKind::Name { id, .. } if id == "rpc".into())) {
store_fun.call1(py, (def_id.0.into_py(py), module.getattr(py, name.to_string()).unwrap())).unwrap();
rpc_ids.push((None, def_id));
}
}
StmtKind::ClassDef { name, body, .. } => {
let class_obj = module.getattr(py, name.to_string()).unwrap();
for stmt in body.iter() {
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
if decorator_list.iter().any(|decorator| matches!(decorator.node, ExprKind::Name { id, .. } if id == "rpc".into())) {
rpc_ids.push((Some((class_obj.clone(), *name)), def_id));
}
}
}
}
_ => ()
}
let id = *name_to_pyid.get(&name).unwrap();
self.pyid_to_def.write().insert(id, def_id);
{
@ -552,6 +608,7 @@ impl Nac3 {
name_to_pyid,
module: module.to_object(py),
helper,
string_store: self.string_store.clone(),
}))) as Arc<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer
.register_top_level(
@ -595,6 +652,45 @@ impl Nac3 {
}
}
let top_level = Arc::new(composer.make_top_level_context());
{
let rpc_codegen = rpc_codegen_callback();
let defs = top_level.definitions.read();
for (class_data, id) in rpc_ids.iter() {
let mut def = defs[id.0].write();
match &mut *def {
TopLevelDef::Function {
codegen_callback, ..
} => {
*codegen_callback = Some(rpc_codegen.clone());
}
TopLevelDef::Class { methods, .. } => {
let (class_def, method_name) = class_data.as_ref().unwrap();
for (name, _, id) in methods.iter() {
if name != method_name {
continue;
}
if let TopLevelDef::Function {
codegen_callback, ..
} = &mut *defs[id.0].write()
{
*codegen_callback = Some(rpc_codegen.clone());
store_fun
.call1(
py,
(
id.0.into_py(py),
class_def.getattr(py, name.to_string()).unwrap(),
),
)
.unwrap();
}
}
}
}
}
}
let instance = {
let defs = top_level.definitions.read();
let mut definition = defs[def_id.0].write();
@ -634,15 +730,17 @@ impl Nac3 {
let buffer = buffer.as_slice().into();
membuffer.lock().push(buffer);
})));
let size_t = if self.isa == Isa::Host {
64
} else {
32
};
let size_t = if self.isa == Isa::Host { 64 } else { 32 };
let thread_names: Vec<String> = (0..4).map(|_| "main".to_string()).collect();
let threads: Vec<_> = thread_names
.iter()
.map(|s| Box::new(ArtiqCodeGenerator::new(s.to_string(), size_t, self.time_fns)))
.map(|s| {
Box::new(ArtiqCodeGenerator::new(
s.to_string(),
size_t,
self.time_fns,
))
})
.collect();
py.allow_threads(|| {
@ -759,11 +857,12 @@ impl Nac3 {
obj: &PyAny,
method_name: &str,
args: Vec<&PyAny>,
embedding_map: &PyAny,
py: Python,
) -> PyResult<PyObject> {
let filename_path = self.working_directory.path().join("module.elf");
let filename = filename_path.to_str().unwrap();
self.compile_method_to_file(obj, method_name, args, filename, py)?;
self.compile_method_to_file(obj, method_name, args, filename, embedding_map, py)?;
Ok(PyBytes::new(py, &fs::read(filename).unwrap()).into())
}
}

View File

@ -42,6 +42,7 @@ pub struct InnerResolver {
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
pub primitive_ids: PrimitivePythonId,
pub helper: PythonHelper,
pub string_store: Arc<RwLock<HashMap<String, i32>>>,
// module specific
pub name_to_pyid: HashMap<StrRef, u64>,
pub module: PyObject,
@ -56,11 +57,14 @@ pub struct PythonHelper {
pub id_fn: PyObject,
pub origin_ty_fn: PyObject,
pub args_ty_fn: PyObject,
pub store_obj: PyObject,
pub store_str: PyObject,
}
struct PythonValue {
id: u64,
value: PyObject,
store_obj: PyObject,
resolver: Arc<InnerResolver>,
}
@ -69,6 +73,36 @@ impl StaticValue for PythonValue {
self.id
}
fn get_const_obj<'ctx, 'a>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
_: &mut dyn CodeGenerator,
) -> BasicValueEnum<'ctx> {
ctx.module
.get_global(self.id.to_string().as_str())
.map(|val| val.as_pointer_value().into())
.unwrap_or_else(|| {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
let id: u32 = self.store_obj.call1(py, (self.value.clone(),))?.extract(py)?;
let struct_type = ctx.ctx.struct_type(&[ctx.ctx.i32_type().into()], false);
let global =
ctx.module
.add_global(struct_type, None, format!("{}_const", self.id).as_str());
global.set_constant(true);
global.set_initializer(&ctx.ctx.const_struct(
&[ctx.ctx.i32_type().const_int(id as u64, false).into()],
false,
));
let global2 =
ctx.module
.add_global(struct_type.ptr_type(AddressSpace::Generic), None, format!("{}_const2", self.id).as_str());
global2.set_initializer(&global.as_pointer_value());
Ok(global2.as_pointer_value().into())
})
.unwrap()
})
}
fn to_basic_value_enum<'ctx, 'a>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
@ -140,6 +174,7 @@ impl StaticValue for PythonValue {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: obj,
store_obj: self.store_obj.clone(),
resolver: self.resolver.clone(),
}))
})
@ -208,7 +243,9 @@ impl InnerResolver {
Ok(Ok((primitives.bool, true)))
} else if ty_id == self.primitive_ids.float {
Ok(Ok((primitives.float, true)))
} else if ty_id == self.primitive_ids.list {
} else if ty_id == self.primitive_ids.exception {
Ok(Ok((primitives.exception, true)))
}else if ty_id == self.primitive_ids.list {
// do not handle type var param and concrete check here
let var = unifier.get_fresh_var().0;
let list = unifier.add_ty(TypeEnum::TList { ty: var });
@ -755,9 +792,7 @@ impl InnerResolver {
.get_llvm_type(generator, ty)
.into_pointer_type()
.get_element_type()
.into_struct_type()
.as_basic_type_enum();
.into_struct_type();
{
if self.global_value_ids.read().contains(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
@ -783,7 +818,7 @@ impl InnerResolver {
.collect();
let values = values?;
if let Some(values) = values {
let val = ctx.ctx.const_struct(&values, false);
let val = ty.const_named_struct(&values);
let global = ctx
.module
.add_global(ty, Some(AddressSpace::Generic), &id_str);
@ -948,6 +983,7 @@ impl SymbolResolver for Resolver {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: v,
store_obj: self.0.helper.store_obj.clone(),
resolver: self.0.clone(),
}))
})
@ -971,4 +1007,17 @@ impl SymbolResolver for Resolver {
result
})
}
fn get_string_id(&self, s: &str) -> i32 {
let mut string_store = self.0.string_store.write();
if let Some(id) = string_store.get(s) {
*id
} else {
let id = Python::with_gil(|py| -> PyResult<i32> {
self.0.helper.store_str.call1(py, (s, ))?.extract(py)
}).unwrap();
string_store.insert(s.into(), id);
id
}
}
}

View File

@ -3,7 +3,7 @@ use crate::ast_gen::StrRef;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FileName(StrRef);
pub struct FileName(pub StrRef);
impl Default for FileName {
fn default() -> Self {
FileName("unknown".into())
@ -19,9 +19,9 @@ impl From<String> for FileName {
/// A location somewhere in the sourcecode.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Location {
row: usize,
column: usize,
file: FileName
pub row: usize,
pub column: usize,
pub file: FileName
}
impl fmt::Display for Location {

View File

@ -31,6 +31,9 @@ pub enum Primitive {
Float,
Bool,
None,
Range,
Str,
Exception
}
#[derive(Debug)]
@ -66,6 +69,9 @@ impl ConcreteTypeStore {
ConcreteTypeEnum::TPrimitive(Primitive::Float),
ConcreteTypeEnum::TPrimitive(Primitive::Bool),
ConcreteTypeEnum::TPrimitive(Primitive::None),
ConcreteTypeEnum::TPrimitive(Primitive::Range),
ConcreteTypeEnum::TPrimitive(Primitive::Str),
ConcreteTypeEnum::TPrimitive(Primitive::Exception),
],
}
}
@ -118,6 +124,12 @@ impl ConcreteTypeStore {
ConcreteType(3)
} else if unifier.unioned(ty, primitives.none) {
ConcreteType(4)
} else if unifier.unioned(ty, primitives.range) {
ConcreteType(5)
} else if unifier.unioned(ty, primitives.str) {
ConcreteType(6)
} else if unifier.unioned(ty, primitives.exception) {
ConcreteType(7)
} else if let Some(cty) = cache.get(&ty) {
if let Some(cty) = cty {
*cty
@ -211,6 +223,9 @@ impl ConcreteTypeStore {
Primitive::Float => primitives.float,
Primitive::Bool => primitives.bool,
Primitive::None => primitives.none,
Primitive::Range => primitives.range,
Primitive::Str => primitives.str,
Primitive::Exception => primitives.exception,
};
*cache.get_mut(&cty).unwrap() = Some(ty);
return ty;

View File

@ -3,6 +3,7 @@ use std::{collections::HashMap, convert::TryInto, iter::once};
use crate::{
codegen::{
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
stmt::gen_raise,
get_llvm_type,
irrt::*,
CodeGenContext, CodeGenTask,
@ -12,12 +13,12 @@ use crate::{
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
};
use inkwell::{
types::{BasicType, BasicTypeEnum},
values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace,
types::{BasicType, BasicTypeEnum},
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}
};
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;
@ -76,14 +77,20 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
index
}
fn gen_symbol_val(&mut self, val: &SymbolValue) -> BasicValueEnum<'ctx> {
pub fn gen_symbol_val(&mut self, generator: &mut dyn CodeGenerator, val: &SymbolValue) -> BasicValueEnum<'ctx> {
match val {
SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(),
SymbolValue::I64(v) => self.ctx.i64_type().const_int(*v as u64, true).into(),
SymbolValue::Bool(v) => self.ctx.bool_type().const_int(*v as u64, true).into(),
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => {
let str_ptr = self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false);
let ty = self.get_llvm_type(generator, self.primitives.str).into_struct_type();
ty.const_named_struct(&[str_ptr, size.into()]).into()
}
SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(v)).collect_vec();
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v)).collect_vec();
let fields = vals.iter().map(|v| v.get_type()).collect_vec();
let ty = self.ctx.struct_type(&fields, false);
let ptr = self.builder.build_alloca(ty, "tuple");
@ -118,7 +125,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
)
}
fn gen_const(&mut self, value: &Constant, ty: Type) -> BasicValueEnum<'ctx> {
pub fn gen_const(&mut self, generator: &mut dyn CodeGenerator, value: &Constant, ty: Type) -> BasicValueEnum<'ctx> {
match value {
Constant::Bool(v) => {
assert!(self.unifier.unioned(ty, self.primitives.bool));
@ -145,7 +152,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let types =
if let TypeEnum::TTuple { ty } = &*ty { ty.clone() } else { unreachable!() };
let values = zip(types.into_iter(), v.iter())
.map(|(ty, v)| self.gen_const(v, ty))
.map(|(ty, v)| self.gen_const(generator, v, ty))
.collect_vec();
let types = values.iter().map(BasicValueEnum::get_type).collect_vec();
let ty = self.ctx.struct_type(&types, false);
@ -153,7 +160,16 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
}
Constant::Str(v) => {
assert!(self.unifier.unioned(ty, self.primitives.str));
self.builder.build_global_string_ptr(v, "const").as_pointer_value().into()
if let Some(v) = self.const_strings.get(v) {
*v
} else {
let str_ptr = self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false);
let ty = self.get_llvm_type(generator, self.primitives.str);
let val = ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
self.const_strings.insert(v.to_string(), val);
val
}
}
_ => unreachable!(),
}
@ -241,6 +257,93 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
_ => 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, &param, call_name).try_as_basic_value().left()
}
}
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
&mut self,
generator: &mut G,
s: S
) -> BasicValueEnum<'ctx> {
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
}
pub fn raise_exn<G: CodeGenerator>(
&mut self,
generator: &mut G,
name: &str,
msg: BasicValueEnum<'ctx>,
params: [Option<IntValue<'ctx>>; 3],
loc: Location
) {
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type();
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into();
let zelf = self.builder.build_alloca(zelf_ty, "alloca");
let int32 = self.ctx.i32_type();
let zero = int32.const_zero();
unsafe {
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id");
let id = self.resolver.get_string_id(name);
self.builder.build_store(id_ptr, int32.const_int(id as u64, false));
let ptr = self.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(5, false)], "exn.msg");
self.builder.build_store(ptr, msg);
let i64_zero = self.ctx.i64_type().const_zero();
for (i, attr_ind) in [6, 7, 8].iter().enumerate() {
let ptr = self.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(*attr_ind, false)], "exn.param");
let val = params[i].map_or(i64_zero, |v| self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext"));
self.builder.build_store(ptr, val);
}
}
gen_raise(generator, self, Some(&zelf.into()), loc);
}
pub fn make_assert<G: CodeGenerator>(
&mut self,
generator: &mut G,
cond: IntValue<'ctx>,
err_name: &str,
err_msg: &str,
params: [Option<IntValue<'ctx>>; 3],
loc: Location
) {
let i1 = self.ctx.bool_type();
let i1_true = i1.const_all_ones();
let expect_fun = self.module.get_function("llvm.expect.i1").unwrap_or_else(|| {
self.module.add_function("llvm.expect", i1.fn_type(&[i1.into(), i1.into()], false), None)
});
// we assume that the condition is most probably true, so the normal path is the most
// probable path
// even if this assumption is violated, it does not matter as exception unwinding is
// slow anyway...
let cond = self.builder.build_call(expect_fun, &[cond.into(), i1_true.into()], "expect")
.try_as_basic_value().left().unwrap().into_int_value();
let current_fun = self.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_block = self.ctx.append_basic_block(current_fun, "succ");
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
self.builder.build_conditional_branch(cond, then_block, exn_block);
self.builder.position_at_end(exn_block);
let err_msg = self.gen_string(generator, err_msg);
self.raise_exn(generator, err_name, err_msg, params, loc);
self.builder.position_at_end(then_block);
}
}
pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
@ -371,13 +474,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
..
} => {
if let Some(callback) = codegen_callback {
// TODO: Change signature
let obj = obj.map(|(t, v)| (t, v.to_basic_value_enum(ctx, generator)));
let params = params
.into_iter()
.map(|(name, val)| (name, val.to_basic_value_enum(ctx, generator)))
.collect();
return callback.run(ctx, obj, fun, params);
return callback.run(ctx, obj, fun, params, generator);
}
let old_key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), fun.0, None);
let mut keys = fun.0.args.clone();
@ -387,7 +484,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
}
// default value handling
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
let mut real_params =
@ -431,7 +528,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
};
param_vals = real_params
.into_iter()
.map(|p| p.to_basic_value_enum(ctx, generator).into())
.map(|p| p.to_basic_value_enum(ctx, generator))
.collect_vec();
instance_to_symbol.get(&key).cloned()
}
@ -457,7 +554,8 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
};
ctx.module.add_function(&symbol, fun_ty, None)
});
ctx.builder.build_call(fun_val, &param_vals, "call").try_as_basic_value().left()
ctx.build_call_or_invoke(fun_val, &param_vals, "call")
}
pub fn destructure_range<'ctx, 'a>(
@ -698,7 +796,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
Some(match &expr.node {
ExprKind::Constant { value, .. } => {
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) {
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
@ -1088,12 +1186,23 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
);
res_array_ret.into()
} else {
// TODO: bound check
let index = generator
let len = ctx.build_gep_and_load(v, &[zero, int32.const_int(1, false)])
.into_int_value();
let raw_index = generator
.gen_expr(ctx, slice)
.unwrap()
.to_basic_value_enum(ctx, generator)
.into_int_value();
// handle negative index
let is_negative = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, raw_index,
generator.get_size_type(ctx.ctx).const_zero(), "is_neg");
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
let index = ctx.builder.build_select(is_negative, adjusted, raw_index, "index").into_int_value();
// unsigned less than is enough, because negative index after adjustment is
// bigger than the length (for unsigned cmp)
let bound_check = ctx.builder.build_int_compare(inkwell::IntPredicate::ULT, index, len, "inbound");
ctx.make_assert(generator, bound_check, "0:IndexError", "index {0} out of bounds 0:{1}",
[Some(raw_index), Some(len), None], expr.location);
ctx.build_gep_and_load(arr_ptr, &[index])
}
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {

View File

@ -117,67 +117,45 @@ pub trait CodeGenerator {
/// Generate code for a while expression.
/// Return true if the while loop must early return
fn gen_while<'ctx, 'a>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool
fn gen_while<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
where
Self: Sized,
{
gen_while(self, ctx, stmt);
false
}
/// Generate code for a while expression.
/// Return true if the while loop must early return
fn gen_for<'ctx, 'a>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool
fn gen_for<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
where
Self: Sized,
{
gen_for(self, ctx, stmt);
false
}
/// Generate code for an if expression.
/// Return true if the statement must early return
fn gen_if<'ctx, 'a>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool
fn gen_if<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
where
Self: Sized,
{
gen_if(self, ctx, stmt)
gen_if(self, ctx, stmt);
}
fn gen_with<'ctx, 'a>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool
fn gen_with<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
where
Self: Sized,
{
gen_with(self, ctx, stmt)
gen_with(self, ctx, stmt);
}
/// Generate code for a statement
/// Return true if the statement must early return
fn gen_stmt<'ctx, 'a>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool
fn gen_stmt<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
where
Self: Sized,
{
gen_stmt(self, ctx, stmt)
gen_stmt(self, ctx, stmt);
}
}

View File

@ -14,7 +14,7 @@ use inkwell::{
module::Module,
passes::{PassManager, PassManagerBuilder},
types::{BasicType, BasicTypeEnum},
values::{FunctionValue, PointerValue},
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
AddressSpace, OptimizationLevel,
};
use itertools::Itertools;
@ -60,11 +60,28 @@ pub struct CodeGenContext<'ctx, 'a> {
pub primitives: PrimitiveStore,
pub calls: Arc<HashMap<CodeLocation, CallId>>,
pub registry: &'a WorkerRegistry,
// const string cache
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>,
// stores the alloca for variables
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
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>;
@ -182,7 +199,7 @@ impl WorkerRegistry {
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
let context = Context::create();
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();
pass_builder.set_optimization_level(OptimizationLevel::Default);
@ -190,10 +207,12 @@ impl WorkerRegistry {
pass_builder.populate_function_pass_manager(&passes);
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;
module = result.1;
passes.run_on(&result.2);
module.link_in_module(result.1).unwrap();
// module = result.1;
*self.task_count.lock() -= 1;
self.wait_condvar.notify_all();
}
@ -235,20 +254,26 @@ fn get_llvm_type<'ctx>(
// we assume the type cache should already contain primitive types,
// and they should be passed by value instead of passing as pointer.
type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| {
let ty = unifier.get_ty(ty);
match &*ty {
let ty_enum = unifier.get_ty(ty);
let result = match &*ty_enum {
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
let top_level_defs = top_level.definitions.read();
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_list
.iter()
.map(|f| get_llvm_type(ctx, generator, unifier, top_level, type_cache, fields[&f.0].0))
.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 {
unreachable!()
};
@ -270,8 +295,10 @@ fn get_llvm_type<'ctx>(
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
}
TVirtual { .. } => unimplemented!(),
_ => unreachable!("{}", ty.get_type_name()),
}
_ => unreachable!("{}", ty_enum.get_type_name()),
};
type_cache.insert(unifier.get_representative(ty), result);
result
})
}
@ -314,25 +341,50 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
none: unifier.get_representative(primitives.none),
range: unifier.get_representative(primitives.range),
str: unifier.get_representative(primitives.str),
exception: unifier.get_representative(primitives.exception),
};
let mut type_cache: HashMap<_, _> = [
(unifier.get_representative(primitives.int32), context.i32_type().into()),
(unifier.get_representative(primitives.int64), context.i64_type().into()),
(unifier.get_representative(primitives.float), context.f64_type().into()),
(unifier.get_representative(primitives.bool), context.bool_type().into()),
(primitives.int32, context.i32_type().into()),
(primitives.int64, context.i64_type().into()),
(primitives.float, context.f64_type().into()),
(primitives.bool, context.bool_type().into()),
(primitives.str, {
let str_type = context.opaque_struct_type("str");
let fields = [
context.i8_type().ptr_type(AddressSpace::Generic).into(),
generator.get_size_type(context).into(),
];
str_type.set_body(&fields, false);
str_type.into()
}),
(
unifier.get_representative(primitives.str),
context.i8_type().ptr_type(AddressSpace::Generic).into(),
),
(
unifier.get_representative(primitives.range),
context.i32_type().array_type(3).ptr_type(AddressSpace::Generic).into()
primitives.range,
context.i32_type().array_type(3).ptr_type(AddressSpace::Generic).into(),
),
]
.iter()
.cloned()
.collect();
type_cache.insert(primitives.exception, {
let exception = context.opaque_struct_type("Exception");
let int32 = context.i32_type().into();
let int64 = context.i64_type().into();
let str_ty = *type_cache.get(&primitives.str).unwrap();
let fields = [
int32,
str_ty,
int32,
int32,
str_ty,
str_ty,
int64,
int64,
int64
];
exception.set_body(&fields, false);
exception.ptr_type(AddressSpace::Generic).into()
});
let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
task.store.get(task.signature)
@ -390,6 +442,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
builder.build_store(alloca, param);
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 store = registry.static_value_store.lock();
store.store[task.id].clone()
@ -407,7 +460,13 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
resolver: task.resolver,
top_level: top_level_ctx.as_ref(),
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,
var_assignment,
type_cache,
@ -419,15 +478,14 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
static_value_store,
};
let mut returned = false;
for stmt in task.body.iter() {
returned = generator.gen_stmt(&mut code_gen_context, stmt);
if returned {
generator.gen_stmt(&mut code_gen_context, stmt);
if code_gen_context.is_terminated() {
break;
}
}
// 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);
}

View File

@ -6,13 +6,17 @@ use super::{
};
use crate::{
codegen::expr::gen_binop_expr,
typecheck::typedef::{Type, TypeEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::typedef::{Type, TypeEnum, FunSignature}
};
use inkwell::{
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
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;
pub fn gen_var<'ctx, 'a>(
@ -173,7 +177,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
let orelse_bb =
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
// 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);
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
@ -234,22 +238,22 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
generator.gen_assign(ctx, target, val.into());
}
for stmt in body.iter() {
generator.gen_stmt(ctx, stmt);
}
gen_block(generator, ctx, body.iter());
for (k, (_, _, counter)) in var_assignment.iter() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
if counter != counter2 {
*static_val = None;
}
}
ctx.builder.build_unconditional_branch(test_bb);
if !ctx.is_terminated() {
ctx.builder.build_unconditional_branch(test_bb);
}
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
for stmt in orelse.iter() {
generator.gen_stmt(ctx, stmt);
gen_block(generator, ctx, orelse.iter());
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() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
@ -258,7 +262,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
}
}
ctx.builder.position_at_end(cont_bb);
ctx.loop_bb = loop_bb;
ctx.loop_target = loop_bb;
} else {
unreachable!()
}
@ -282,7 +286,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
let orelse_bb =
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
// 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.position_at_end(test_bb);
let test = generator.gen_expr(ctx, test).unwrap().to_basic_value_enum(ctx, generator);
@ -292,22 +296,22 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
ctx.builder.position_at_end(body_bb);
for stmt in body.iter() {
generator.gen_stmt(ctx, stmt);
}
gen_block(generator, ctx, body.iter());
for (k, (_, _, counter)) in var_assignment.iter() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
if counter != counter2 {
*static_val = None;
}
}
ctx.builder.build_unconditional_branch(test_bb);
if !ctx.is_terminated() {
ctx.builder.build_unconditional_branch(test_bb);
}
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
for stmt in orelse.iter() {
generator.gen_stmt(ctx, stmt);
gen_block(generator, ctx, orelse.iter());
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() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
@ -316,7 +320,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
}
}
ctx.builder.position_at_end(cont_bb);
ctx.loop_bb = loop_bb;
ctx.loop_target = loop_bb;
} else {
unreachable!()
}
@ -326,7 +330,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool {
) {
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
// 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
@ -352,13 +356,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
ctx.builder.position_at_end(body_bb);
let mut exited = false;
for stmt in body.iter() {
exited = generator.gen_stmt(ctx, stmt);
if exited {
break;
}
}
gen_block(generator, ctx, body.iter());
for (k, (_, _, counter)) in var_assignment.iter() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
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() {
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
}
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
}
let then_exited = exited;
let else_exited = if !orelse.is_empty() {
exited = false;
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
for stmt in orelse.iter() {
exited = generator.gen_stmt(ctx, stmt);
if exited {
break;
}
}
if !exited {
gen_block(generator, ctx, orelse.iter());
if !ctx.is_terminated() {
if cont_bb.is_none() {
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
}
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
}
exited
} else {
false
};
}
if let Some(cont_bb) = cont_bb {
ctx.builder.position_at_end(cont_bb);
}
@ -401,7 +389,464 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
*static_val = None;
}
}
then_exited && else_exited
} else {
unreachable!()
}
}
pub fn final_proxy<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
target: BasicBlock<'ctx>,
block: BasicBlock<'ctx>,
) {
let (final_state, final_targets, final_paths) = ctx.outer_final.as_mut().unwrap();
let prev = ctx.builder.get_insert_block().unwrap();
ctx.builder.position_at_end(block);
unsafe {
ctx.builder.build_store(*final_state, target.get_address().unwrap());
}
ctx.builder.position_at_end(prev);
final_targets.push(target);
final_paths.push(block);
}
pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
symbol: &str,
) -> FunctionValue<'ctx> {
ctx.module.get_function(symbol).unwrap_or_else(|| {
let ty = match symbol {
"__artiq_raise" => ctx.ctx.void_type().fn_type(
&[ctx.get_llvm_type(generator, ctx.primitives.exception).into()],
false,
),
"__artiq_resume" => ctx.ctx.void_type().fn_type(&[], false),
"__artiq_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
_ => unimplemented!(),
};
let fun = ctx.module.add_function(symbol, ty, None);
if symbol == "__artiq_raise" || symbol == "__artiq_resume" {
fun.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 1),
);
}
fun
})
}
pub fn exn_constructor<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
obj: Option<(Type, ValueEnum<'ctx>)>,
_fun: (&FunSignature, DefinitionId),
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
generator: &mut dyn CodeGenerator
) -> Option<BasicValueEnum<'ctx>> {
let (zelf_ty, zelf) = obj.unwrap();
let zelf = zelf.to_basic_value_enum(ctx, generator).into_pointer_value();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let zelf_id = {
if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(zelf_ty) {
obj_id.0
} else {
unreachable!()
}
};
let defs = ctx.top_level.definitions.read();
let def = defs[zelf_id].read();
let zelf_name = if let TopLevelDef::Class { name, .. } = &*def {
*name
} else {
unreachable!()
};
let exception_name = format!("0:{}", zelf_name);
unsafe {
let id_ptr = ctx.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id");
let id = ctx.resolver.get_string_id(&exception_name);
ctx.builder.build_store(id_ptr, int32.const_int(id as u64, false));
let empty_string = ctx.gen_const(generator, &Constant::Str("".into()), ctx.primitives.str);
let ptr = ctx.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(5, false)], "exn.msg");
let msg = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)
} else {
empty_string
};
ctx.builder.build_store(ptr, msg);
for i in [6, 7, 8].iter() {
let value = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)
} else {
ctx.ctx.i64_type().const_zero().into()
};
let ptr = ctx.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(*i, false)], "exn.param");
ctx.builder.build_store(ptr, value);
}
// set file, func to empty string
for i in [1, 4].iter() {
let ptr = ctx.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(*i, false)], "exn.str");
ctx.builder.build_store(ptr, empty_string);
}
// set ints to zero
for i in [2, 3].iter() {
let ptr = ctx.builder.build_in_bounds_gep(
zelf, &[zero, int32.const_int(*i, false)], "exn.ints");
ctx.builder.build_store(ptr, zero);
}
}
Some(zelf.into())
}
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
exception: Option<&BasicValueEnum<'ctx>>,
loc: Location,
) {
if let Some(exception) = exception {
unsafe {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let exception = exception.into_pointer_value();
let file_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(1, false)], "file_ptr");
let filename = ctx.gen_string(generator, loc.file.0);
ctx.builder.build_store(file_ptr, filename);
let row_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr");
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false));
let col_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr");
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false));
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
let name_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr");
ctx.builder.build_store(name_ptr, fun_name);
}
let raise = get_builtins(generator, ctx, "__artiq_raise");
let exception = *exception;
ctx.build_call_or_invoke(raise, &[exception], "raise");
} else {
let resume = get_builtins(generator, ctx, "__artiq_resume");
ctx.build_call_or_invoke(resume, &[], "resume");
}
ctx.builder.build_unreachable();
}
pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
target: &Stmt<Option<Type>>,
) {
if let StmtKind::Try { body, handlers, orelse, finalbody, .. } = &target.node {
// if we need to generate anything related to exception, we must have personality defined
let personality_symbol = ctx.top_level.personality_symbol.as_ref().unwrap();
let personality = ctx.module.get_function(personality_symbol).unwrap_or_else(|| {
let ty = ctx.ctx.i32_type().fn_type(&[], true);
ctx.module.add_function(personality_symbol, ty, None)
});
let exception_type = ctx.get_llvm_type(generator, ctx.primitives.exception);
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::Generic);
let current_block = ctx.builder.get_insert_block().unwrap();
let current_fun = current_block.get_parent().unwrap();
let landingpad = ctx.ctx.append_basic_block(current_fun, "try.landingpad");
let dispatcher = ctx.ctx.append_basic_block(current_fun, "try.dispatch");
let mut dispatcher_end = dispatcher;
ctx.builder.position_at_end(dispatcher);
let exn = ctx.builder.build_phi(exception_type, "exn");
ctx.builder.position_at_end(current_block);
let mut cleanup = None;
let mut old_loop_target = None;
let mut old_return = None;
let mut old_outer_final = None;
let has_cleanup = if !finalbody.is_empty() {
let final_state = generator.gen_var_alloc(ctx, ptr_type.into());
old_outer_final = ctx.outer_final.replace((final_state, Vec::new(), Vec::new()));
if let Some((continue_target, break_target)) = ctx.loop_target {
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
final_proxy(ctx, break_target, break_proxy);
final_proxy(ctx, continue_target, continue_proxy);
old_loop_target = ctx.loop_target.replace((continue_proxy, break_proxy));
}
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
if let Some(return_target) = ctx.return_target {
final_proxy(ctx, return_target, return_proxy);
} else {
let return_target = ctx.ctx.append_basic_block(current_fun, "try.return_target");
ctx.builder.position_at_end(return_target);
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
ctx.builder.position_at_end(current_block);
final_proxy(ctx, return_target, return_proxy);
}
old_return = ctx.return_target.replace(return_proxy);
cleanup = Some(ctx.ctx.append_basic_block(current_fun, "try.cleanup"));
true
} else {
ctx.outer_final.is_some()
};
let mut clauses = Vec::new();
let mut found_catch_all = false;
for handler_node in handlers.iter() {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler_node.node;
// none or Exception
if type_.is_none() || ctx.unifier.unioned(type_.as_ref().unwrap().custom.unwrap(), ctx.primitives.exception) {
clauses.push(None);
found_catch_all = true;
break;
} else {
let type_ = type_.as_ref().unwrap();
let exn_name = ctx.resolver.get_type_name(
&ctx.top_level.definitions.read(),
&mut ctx.unifier,
type_.custom.unwrap(),
);
let exn_id = ctx.resolver.get_string_id(&format!("0:{}", exn_name));
let exn_id_global =
ctx.module.add_global(ctx.ctx.i32_type(), None, &format!("exn.{}", exn_id));
exn_id_global.set_initializer(&ctx.ctx.i32_type().const_int(exn_id as u64, false));
clauses.push(Some(exn_id_global.as_pointer_value().as_basic_value_enum()));
}
}
let mut all_clauses = clauses.clone();
if let Some(old_clauses) = &ctx.outer_catch_clauses {
if !found_catch_all {
all_clauses.extend_from_slice(&old_clauses.0)
}
}
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
let old_unwind = ctx.unwind_target.replace(landingpad);
gen_block(generator, ctx, body.iter());
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
gen_block(generator, ctx, orelse.iter());
}
let body = ctx.builder.get_insert_block().unwrap();
// reset old_clauses and old_unwind
let (all_clauses, _, _) = ctx.outer_catch_clauses.take().unwrap();
ctx.outer_catch_clauses = old_clauses;
ctx.unwind_target = old_unwind;
ctx.return_target = old_return;
ctx.loop_target = old_loop_target;
old_loop_target = None;
let old_unwind = if !finalbody.is_empty() {
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
ctx.builder.position_at_end(final_landingpad);
ctx.builder.build_landing_pad(
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
personality,
&[],
true,
"try.catch.final",
);
ctx.builder.build_unconditional_branch(cleanup.unwrap());
ctx.builder.position_at_end(body);
ctx.unwind_target.replace(final_landingpad)
} else {
None
};
// run end_catch before continue/break/return
let mut final_proxy_lambda =
|ctx: &mut CodeGenContext<'ctx, 'a>,
target: BasicBlock<'ctx>,
block: BasicBlock<'ctx>| final_proxy(ctx, target, block);
let mut redirect_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
target: BasicBlock<'ctx>,
block: BasicBlock<'ctx>| {
ctx.builder.position_at_end(block);
ctx.builder.build_unconditional_branch(target);
ctx.builder.position_at_end(body);
};
let redirect = if ctx.outer_final.is_some() {
&mut final_proxy_lambda
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
} else {
&mut redirect_lambda
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
};
let resume = get_builtins(generator, ctx, "__artiq_resume");
let end_catch = get_builtins(generator, ctx, "__artiq_end_catch");
if let Some((continue_target, break_target)) = ctx.loop_target.take() {
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
ctx.builder.position_at_end(break_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch");
ctx.builder.position_at_end(continue_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch");
ctx.builder.position_at_end(body);
redirect(ctx, break_target, break_proxy);
redirect(ctx, continue_target, continue_proxy);
ctx.loop_target = Some((continue_proxy, break_proxy));
old_loop_target = Some((continue_target, break_target));
}
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
ctx.builder.position_at_end(return_proxy);
ctx.builder.build_call(end_catch, &[], "end_catch");
let return_target = ctx.return_target.take().unwrap_or_else(|| {
let doreturn = ctx.ctx.append_basic_block(current_fun, "try.doreturn");
ctx.builder.position_at_end(doreturn);
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
doreturn
});
redirect(ctx, return_target, return_proxy);
ctx.return_target = Some(return_proxy);
old_return = Some(return_target);
let mut post_handlers = Vec::new();
let exnid = if !handlers.is_empty() {
ctx.builder.position_at_end(dispatcher);
unsafe {
let zero = ctx.ctx.i32_type().const_zero();
let exnid_ptr = ctx.builder.build_gep(
exn.as_basic_value().into_pointer_value(),
&[zero, zero],
"exnidptr",
);
Some(ctx.builder.build_load(exnid_ptr, "exnid"))
}
} else {
None
};
for (handler_node, exn_type) in handlers.iter().zip(clauses.iter()) {
let ExcepthandlerKind::ExceptHandler { type_, name, body } = &handler_node.node;
let handler_bb = ctx.ctx.append_basic_block(current_fun, "try.handler");
ctx.builder.position_at_end(handler_bb);
if let Some(name) = name {
let exn_ty = ctx.get_llvm_type(generator, type_.as_ref().unwrap().custom.unwrap());
let exn_store = generator.gen_var_alloc(ctx, exn_ty);
ctx.var_assignment.insert(*name, (exn_store, None, 0));
ctx.builder.build_store(exn_store, exn.as_basic_value());
}
gen_block(generator, ctx, body.iter());
let current = ctx.builder.get_insert_block().unwrap();
// only need to call end catch if not terminated
// otherwise, we already handled in return/break/continue/raise
if current.get_terminator().is_none() {
ctx.builder.build_call(end_catch, &[], "end_catch");
}
post_handlers.push(current);
ctx.builder.position_at_end(dispatcher_end);
if let Some(exn_type) = exn_type {
let dispatcher_cont =
ctx.ctx.append_basic_block(current_fun, "try.dispatcher_cont");
let actual_id = exnid.unwrap().into_int_value();
let expected_id = ctx
.builder
.build_load(exn_type.into_pointer_value(), "expected_id")
.into_int_value();
let result = ctx.builder.build_int_compare(EQ, actual_id, expected_id, "exncheck");
ctx.builder.build_conditional_branch(result, handler_bb, dispatcher_cont);
dispatcher_end = dispatcher_cont;
} else {
ctx.builder.build_unconditional_branch(handler_bb);
break;
}
}
ctx.unwind_target = old_unwind;
ctx.loop_target = old_loop_target;
ctx.return_target = old_return;
ctx.builder.position_at_end(landingpad);
let clauses: Vec<_> = if finalbody.is_empty() { &all_clauses } else { &clauses }
.iter()
.map(|v| v.unwrap_or(ptr_type.const_zero().into()))
.collect();
let landingpad_value = ctx
.builder
.build_landing_pad(
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
personality,
&clauses,
has_cleanup,
"try.landingpad",
)
.into_struct_value();
let exn_val = ctx.builder.build_extract_value(landingpad_value, 1, "exn").unwrap();
ctx.builder.build_unconditional_branch(dispatcher);
exn.add_incoming(&[(&exn_val, landingpad)]);
if dispatcher_end.get_terminator().is_none() {
ctx.builder.position_at_end(dispatcher_end);
if let Some(cleanup) = cleanup {
ctx.builder.build_unconditional_branch(cleanup);
} else if let Some((_, outer_dispatcher, phi)) = ctx.outer_catch_clauses {
phi.add_incoming(&[(&exn_val, dispatcher_end)]);
ctx.builder.build_unconditional_branch(outer_dispatcher);
} else {
ctx.build_call_or_invoke(resume, &[], "resume");
ctx.builder.build_unreachable();
}
}
if finalbody.is_empty() {
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
if body.get_terminator().is_none() {
ctx.builder.position_at_end(body);
ctx.builder.build_unconditional_branch(tail);
}
if matches!(cleanup, Some(cleanup) if cleanup.get_terminator().is_none()) {
ctx.builder.position_at_end(cleanup.unwrap());
ctx.builder.build_unconditional_branch(tail);
}
for post_handler in post_handlers {
if post_handler.get_terminator().is_none() {
ctx.builder.position_at_end(post_handler);
ctx.builder.build_unconditional_branch(tail);
}
}
ctx.builder.position_at_end(tail);
} else {
let final_branches = ctx.outer_final.take().unwrap();
ctx.outer_final = old_outer_final;
// exception path
let cleanup = cleanup.unwrap();
ctx.builder.position_at_end(cleanup);
gen_block(generator, ctx, finalbody.iter());
if !ctx.is_terminated() {
ctx.build_call_or_invoke(resume, &[], "resume");
ctx.builder.build_unreachable();
}
// normal path
let (final_state, mut final_targets, final_paths) = final_branches;
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
final_targets.push(tail);
let finalizer = ctx.ctx.append_basic_block(current_fun, "try.finally");
ctx.builder.position_at_end(finalizer);
gen_block(generator, ctx, finalbody.iter());
if !ctx.is_terminated() {
let dest = ctx.builder.build_load(final_state, "final_dest");
ctx.builder.build_indirect_branch(dest, &final_targets);
}
for block in final_paths.iter() {
if block.get_terminator().is_none() {
ctx.builder.position_at_end(*block);
ctx.builder.build_unconditional_branch(finalizer);
}
}
for block in [body].iter().chain(post_handlers.iter()) {
if block.get_terminator().is_none() {
ctx.builder.position_at_end(*block);
unsafe {
ctx.builder.build_store(final_state, tail.get_address().unwrap());
}
ctx.builder.build_unconditional_branch(finalizer);
}
}
ctx.builder.position_at_end(tail);
}
} else {
unreachable!()
}
@ -416,23 +861,37 @@ pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
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>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>,
) -> bool {
) {
match &stmt.node {
StmtKind::Pass { .. } => {}
StmtKind::Expr { value, .. } => {
generator.gen_expr(ctx, value);
}
StmtKind::Return { value, .. } => {
let value = 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;
gen_return(generator, ctx, value);
}
StmtKind::AnnAssign { target, value, .. } => {
if let Some(value) = value {
@ -447,22 +906,37 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
}
}
StmtKind::Continue { .. } => {
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().0);
return true;
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().0);
}
StmtKind::Break { .. } => {
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().1);
return true;
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().1);
}
StmtKind::If { .. } => return generator.gen_if(ctx, stmt),
StmtKind::While { .. } => return generator.gen_while(ctx, stmt),
StmtKind::For { .. } => return generator.gen_for(ctx, stmt),
StmtKind::With { .. } => return generator.gen_with(ctx, stmt),
StmtKind::If { .. } => generator.gen_if(ctx, stmt),
StmtKind::While { .. } => generator.gen_while(ctx, stmt),
StmtKind::For { .. } => generator.gen_for(ctx, stmt),
StmtKind::With { .. } => generator.gen_with(ctx, stmt),
StmtKind::AugAssign { target, op, value, .. } => {
let value = gen_binop_expr(generator, ctx, target, op, value);
generator.gen_assign(ctx, target, value);
}
StmtKind::Try { .. } => gen_try(generator, ctx, stmt),
StmtKind::Raise { exc, .. } => {
let exc = exc.as_ref().map(|exc| generator.gen_expr(ctx, exc).unwrap().to_basic_value_enum(ctx, generator));
gen_raise(generator, ctx, exc.as_ref(), stmt.location)
}
_ => unimplemented!(),
};
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;
}
}
}

View File

@ -65,6 +65,10 @@ impl SymbolResolver for Resolver {
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
self.id_to_def.read().get(&id).cloned()
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
}
#[test]
@ -120,6 +124,7 @@ fn test_primitives() {
virtual_checks: &mut virtual_checks,
calls: &mut calls,
defined_identifiers: identifiers.clone(),
in_handler: false
};
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
inferencer.variable_mapping.insert("b".into(), inferencer.primitives.int32);
@ -263,6 +268,7 @@ fn test_simple_call() {
virtual_checks: &mut virtual_checks,
calls: &mut calls,
defined_identifiers: identifiers.clone(),
in_handler: false
};
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
inferencer.variable_mapping.insert("foo".into(), fun_ty);

View File

@ -2,14 +2,17 @@ use std::collections::HashMap;
use std::fmt::Debug;
use std::{cell::RefCell, sync::Arc};
use crate::{codegen::CodeGenerator, typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
}};
use crate::{
codegen::CodeGenContext,
toplevel::{DefinitionId, TopLevelDef},
};
use crate::{
codegen::CodeGenerator,
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
},
};
use crate::{location::Location, typecheck::typedef::TypeEnum};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
use itertools::{chain, izip};
@ -20,6 +23,7 @@ use parking_lot::RwLock;
pub enum SymbolValue {
I32(i32),
I64(i64),
Str(String),
Double(f64),
Bool(bool),
Tuple(Vec<SymbolValue>),
@ -28,10 +32,16 @@ pub enum SymbolValue {
pub trait StaticValue {
fn get_unique_identifier(&self) -> u64;
fn get_const_obj<'ctx, 'a>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
) -> BasicValueEnum<'ctx>;
fn to_basic_value_enum<'ctx, 'a>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator
generator: &mut dyn CodeGenerator,
) -> BasicValueEnum<'ctx>;
fn get_field<'ctx, 'a>(
@ -105,11 +115,12 @@ pub trait SymbolResolver {
fn get_symbol_location(&self, str: StrRef) -> Option<Location>;
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
fn get_string_id(&self, s: &str) -> i32;
// handle function call etc.
}
thread_local! {
static IDENTIFIER_ID: [StrRef; 8] = [
static IDENTIFIER_ID: [StrRef; 10] = [
"int32".into(),
"int64".into(),
"float".into(),
@ -117,7 +128,9 @@ thread_local! {
"None".into(),
"virtual".into(),
"list".into(),
"tuple".into()
"tuple".into(),
"str".into(),
"Exception".into(),
];
}
@ -139,6 +152,8 @@ pub fn parse_type_annotation<T>(
let virtual_id = ids[5];
let list_id = ids[6];
let tuple_id = ids[7];
let str_id = ids[8];
let exn_id = ids[9];
let name_handling = |id: &StrRef, unifier: &mut Unifier| {
if *id == int32_id {
@ -151,6 +166,10 @@ pub fn parse_type_annotation<T>(
Ok(primitives.bool)
} else if *id == none_id {
Ok(primitives.none)
} else if *id == str_id {
Ok(primitives.str)
} else if *id == exn_id {
Ok(primitives.exception)
} else {
let obj_id = resolver.get_identifier_def(*id);
if let Some(obj_id) = obj_id {
@ -179,8 +198,7 @@ pub fn parse_type_annotation<T>(
}
} else {
// it could be a type variable
let ty = resolver
.get_symbol_type(unifier, top_level_defs, primitives, *id)?;
let ty = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id)?;
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
Ok(ty)
} else {
@ -192,35 +210,17 @@ pub fn parse_type_annotation<T>(
let subscript_name_handle = |id: &StrRef, slice: &Expr<T>, unifier: &mut Unifier| {
if *id == virtual_id {
let ty = parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
slice,
)?;
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
} else if *id == list_id {
let ty = parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
slice,
)?;
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
Ok(unifier.add_ty(TypeEnum::TList { ty }))
} else if *id == tuple_id {
if let Tuple { elts, .. } = &slice.node {
let ty = elts
.iter()
.map(|elt| {
parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
elt,
)
parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
@ -231,23 +231,11 @@ pub fn parse_type_annotation<T>(
let types = if let Tuple { elts, .. } = &slice.node {
elts.iter()
.map(|v| {
parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
v,
)
parse_type_annotation(resolver, top_level_defs, unifier, primitives, v)
})
.collect::<Result<Vec<_>, _>>()?
} else {
vec![parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
slice,
)?]
vec![parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?]
};
let obj_id = resolver
@ -316,6 +304,25 @@ impl dyn SymbolResolver + Send + Sync {
) -> Result<Type, String> {
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
}
pub fn get_type_name(
&self,
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
ty: Type,
) -> String {
unifier.stringify(
ty,
&mut |id| {
if let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() {
name.to_string()
} else {
unreachable!("expected class definition")
}
},
&mut |id| format!("var{}", id),
)
}
}
impl Debug for dyn SymbolResolver + Send + Sync {

View File

@ -1,6 +1,6 @@
use super::*;
use crate::{
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range},
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range, stmt::exn_constructor},
symbol_resolver::SymbolValue,
};
use inkwell::{FloatPredicate, IntPredicate};
@ -21,6 +21,47 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let num_ty = primitives.1.get_fresh_var_with_range(&[int32, int64, float, boolean]);
let var_map: HashMap<_, _> = vec![(num_ty.1, num_ty.0)].into_iter().collect();
let exception_fields = vec![
("__name__".into(), int32, true),
("__file__".into(), string, true),
("__line__".into(), int32, true),
("__col__".into(), int32, true),
("__func__".into(), string, true),
("__message__".into(), string, true),
("__param0__".into(), int64, true),
("__param1__".into(), int64, true),
("__param2__".into(), int64, true),
];
let div_by_zero = primitives.1.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: RefCell::new(exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect()),
params: Default::default()
});
let index_error = primitives.1.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(11),
fields: RefCell::new(exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect()),
params: Default::default()
});
let exn_cons_args = vec![
FuncArg { name: "msg".into(), ty: string,
default_value: Some(SymbolValue::Str("".into()))},
FuncArg { name: "param0".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
FuncArg { name: "param1".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
FuncArg { name: "param2".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
];
let div_by_zero_signature = primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
args: exn_cons_args.clone(),
ret: div_by_zero,
vars: Default::default()
})));
let index_error_signature = primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
args: exn_cons_args,
ret: index_error,
vars: Default::default()
})));
let top_level_def_list = vec![
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
0,
@ -49,6 +90,62 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(6, None, "str".into(), None))),
Arc::new(RwLock::new(TopLevelDef::Class {
name: "Exception".into(),
object_id: DefinitionId(7),
type_vars: Default::default(),
fields: exception_fields.clone(),
methods: Default::default(),
ancestors: vec![],
constructor: None,
resolver: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "ZeroDivisionError.__init__".into(),
simple_name: "__init__".into(),
signature: div_by_zero_signature,
var_id: Default::default(),
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "IndexError.__init__".into(),
simple_name: "__init__".into(),
signature: index_error_signature,
var_id: Default::default(),
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
})),
Arc::new(RwLock::new(TopLevelDef::Class {
name: "ZeroDivisionError".into(),
object_id: DefinitionId(10),
type_vars: Default::default(),
fields: exception_fields.clone(),
methods: vec![("__init__".into(), div_by_zero_signature, DefinitionId(8))],
ancestors: vec![
TypeAnnotation::CustomClass { id: DefinitionId(10), params: Default::default() },
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
],
constructor: Some(div_by_zero_signature),
resolver: None,
})),
Arc::new(RwLock::new(TopLevelDef::Class {
name: "IndexError".into(),
object_id: DefinitionId(11),
type_vars: Default::default(),
fields: exception_fields,
methods: vec![("__init__".into(), index_error_signature, DefinitionId(9))],
ancestors: vec![
TypeAnnotation::CustomClass { id: DefinitionId(11), params: Default::default() },
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
],
constructor: Some(index_error_signature),
resolver: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "int32".into(),
simple_name: "int32".into(),
@ -62,13 +159,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args| {
|ctx, _, fun, args, generator| {
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
if ctx.unifier.unioned(arg_ty, boolean) {
Some(
ctx.builder
@ -120,13 +217,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args| {
|ctx, _, fun, args, generator| {
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, int32)
{
@ -170,18 +267,18 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args| {
|ctx, _, fun, args, generator| {
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let boolean = ctx.primitives.bool;
let float = ctx.primitives.float;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, int32)
|| ctx.unifier.unioned(arg_ty, int64)
{
let arg = args[0].1.into_int_value();
let arg = arg.into_int_value();
let val = ctx
.builder
.build_signed_int_to_float(arg, ctx.ctx.f64_type(), "sitofp")
@ -207,8 +304,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -244,8 +341,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -294,7 +391,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let mut start = None;
let mut stop = None;
let mut step = None;
@ -302,17 +399,17 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let zero = int32.const_zero();
for (i, arg) in args.iter().enumerate() {
if arg.0 == Some("start".into()) {
start = Some(arg.1);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
} else if arg.0 == Some("stop".into()) {
stop = Some(arg.1);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
} else if arg.0 == Some("step".into()) {
step = Some(arg.1);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
} else if i == 0 {
start = Some(arg.1);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
} else if i == 1 {
stop = Some(arg.1);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
} else if i == 2 {
step = Some(arg.1);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
}
}
// TODO: error when step == 0
@ -356,8 +453,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|_, _, _, args| {
Some(args[0].1)
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
Some(args[0].1.clone().to_basic_value_enum(ctx, generator))
})))),
})),
Arc::new(RwLock::new(TopLevelDef::Function {
@ -373,13 +470,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args| {
|ctx, _, fun, args, generator| {
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
if ctx.unifier.unioned(arg_ty, boolean) {
Some(arg)
} else if ctx.unifier.unioned(arg_ty, int32) {
@ -424,8 +521,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -461,8 +558,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -498,8 +595,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -535,8 +632,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
let arg = args[0].1;
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -581,10 +678,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args| {
|ctx, _, fun, args, generator| {
let range_ty = ctx.primitives.range;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg);
@ -609,6 +706,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
(
izip!(top_level_def_list, ast_list).collect_vec(),
&[
"ZeroDivisionError",
"IndexError",
"int32",
"int64",
"float",

View File

@ -4,7 +4,8 @@ use nac3parser::ast::fold::Fold;
use crate::{
typecheck::type_inferencer::{FunctionData, Inferencer},
codegen::expr::get_subst_key,
codegen::{expr::get_subst_key, stmt::exn_constructor},
symbol_resolver::SymbolValue,
};
use super::*;
@ -90,8 +91,13 @@ impl TopLevelComposer {
assert!(name == *simple_name);
builtin_ty.insert(name, *signature);
builtin_id.insert(name, DefinitionId(id));
} else {
unreachable!()
} else if let TopLevelDef::Class { name, constructor, object_id, type_vars, .. } = &*def {
assert!(id == object_id.0);
assert!(type_vars.is_empty());
if let Some(constructor) = constructor {
builtin_ty.insert(*name, *constructor);
}
builtin_id.insert(*name, DefinitionId(id));
}
}
@ -142,7 +148,7 @@ impl TopLevelComposer {
self.unifier.get_shared_unifier(),
self.primitives_ty,
)])),
personality_symbol: None,
personality_symbol: Some("__artiq_personality".into()),
}
}
@ -160,7 +166,7 @@ impl TopLevelComposer {
) -> Result<(StrRef, DefinitionId, Option<Type>), String> {
let defined_names = &mut self.defined_names;
match &ast.node {
ast::StmtKind::ClassDef { name: class_name, body, .. } => {
ast::StmtKind::ClassDef { name: class_name, bases, body, .. } => {
if self.keyword_list.contains(class_name) {
return Err(format!(
"cannot use keyword `{}` as a class name (at {})",
@ -168,11 +174,12 @@ impl TopLevelComposer {
ast.location
));
}
if !defined_names.insert({
let mut n = mod_path.clone();
n.push_str(&class_name.to_string());
n
}) {
let fully_qualified_class_name = if mod_path.is_empty() {
*class_name
} else {
format!("{}.{}", &mod_path, class_name).into()
};
if !defined_names.insert(fully_qualified_class_name.into()) {
return Err(format!(
"duplicate definition of class `{}` (at {})",
class_name,
@ -190,7 +197,7 @@ impl TopLevelComposer {
Arc::new(RwLock::new(Self::make_top_level_class_def(
class_def_id,
resolver.clone(),
class_name,
fully_qualified_class_name,
Some(constructor_ty),
))),
None,
@ -212,8 +219,13 @@ impl TopLevelComposer {
// we do not push anything to the def list, so we keep track of the index
// and then push in the correct order after the for loop
let mut class_method_index_offset = 0;
let mut contains_constructor = false;
let init_id = "__init__".into();
let exception_id = "Exception".into();
// TODO: Fix this hack. We will generate constructor for classes that inherit
// from Exception class (directly or indirectly), but this code cannot handle
// subclass of other exception classes.
let mut contains_constructor = bases
.iter().any(|base| matches!(base.node, ast::ExprKind::Name { id, .. } if id == exception_id));
for b in body {
if let ast::StmtKind::FunctionDef { name: method_name, .. } = &b.node {
if method_name == &init_id {
@ -226,21 +238,14 @@ impl TopLevelComposer {
b.location
));
}
let global_class_method_name = {
let mut n = mod_path.clone();
n.push_str(
Self::make_class_method_name(
class_name.into(),
&method_name.to_string(),
)
.as_str(),
);
n
};
let global_class_method_name = Self::make_class_method_name(
fully_qualified_class_name.into(),
&method_name.to_string(),
);
if !defined_names.insert(global_class_method_name.clone()) {
return Err(format!(
"class method `{}` defined twice (at {})",
&global_class_method_name[mod_path.len()..],
global_class_method_name,
b.location
));
}
@ -298,15 +303,15 @@ impl TopLevelComposer {
// if self.keyword_list.contains(name) {
// return Err("cannot use keyword as a top level function name".into());
// }
let global_fun_name = {
let mut n = mod_path.clone();
n.push_str(&name.to_string());
n
let global_fun_name = if mod_path.is_empty() {
name.to_string()
} else {
format!("{}.{}", mod_path, name)
};
if !defined_names.insert(global_fun_name.clone()) {
return Err(format!(
"top level function `{}` defined twice (at {})",
&global_fun_name[mod_path.len()..],
global_fun_name,
ast.location
));
}
@ -471,7 +476,6 @@ impl TopLevelComposer {
let unifier = self.unifier.borrow_mut();
// first, only push direct parent into the list
// skip 5 to skip analyzing the primitives
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
let mut class_def = class_def.write();
let (class_def_id, class_bases, class_ancestors, class_resolver, class_type_vars) = {
@ -540,7 +544,6 @@ impl TopLevelComposer {
// second, get all ancestors
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = Default::default();
// skip 5 to skip analyzing the primitives
for (class_def, _) in self.definition_ast_list.iter().skip(self.builtin_num) {
let class_def = class_def.read();
let (class_ancestors, class_id) = {
@ -562,8 +565,7 @@ impl TopLevelComposer {
}
// insert the ancestors to the def list
// skip 5 to skip analyzing the primitives
for (class_def, _) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
let mut class_def = class_def.write();
let (class_ancestors, class_id, class_type_vars) = {
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } =
@ -581,6 +583,26 @@ impl TopLevelComposer {
// insert self type annotation to the front of the vector to maintain the order
class_ancestors
.insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id));
// special case classes that inherit from Exception
if class_ancestors.iter().any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) {
// if inherited from Exception, the body should be a pass
if let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node {
if body.len() != 1 || !matches!(body[0].node, ast::StmtKind::Pass { .. }) {
return Err("Classes inherited from exception should have `pass` as body".into());
}
} else {
unreachable!()
}
}
}
// deal with ancestor of Exception object
if let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *self.definition_ast_list[7].0.write() {
assert_eq!(*name, "Exception".into());
ancestors.push(make_self_type_annotation(&[], *object_id));
} else {
unreachable!();
}
Ok(())
@ -595,7 +617,6 @@ impl TopLevelComposer {
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
// skip 5 to skip analyzing the primitives
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
Self::analyze_single_class_methods_fields(
@ -610,8 +631,6 @@ impl TopLevelComposer {
}
}
// println!("type_var_to_concrete_def1: {:?}", type_var_to_concrete_def);
// handle the inheritanced methods and fields
let mut current_ancestor_depth: usize = 2;
loop {
@ -646,19 +665,8 @@ impl TopLevelComposer {
}
}
// println!("type_var_to_concrete_def3: {:?}\n", type_var_to_concrete_def);
// unification of previously assigned typevar
for (ty, def) in type_var_to_concrete_def {
// println!(
// "{:?}_{} -> {:?}\n",
// ty,
// unifier.stringify(ty,
// &mut |id| format!("class{}", id),
// &mut |id| format!("tvar{}", id)
// ),
// def
// );
let target_ty =
get_type_from_type_annotation_kinds(&temp_def_list, unifier, primitives, &def)?;
unifier.unify(ty, target_ty)?;
@ -946,7 +954,7 @@ impl TopLevelComposer {
))
}
}
if name == &"__init__".into() && !defined_paramter_name.contains(&zelf) {
return Err(format!("__init__ method must have a `self` parameter (at {})", b.location));
}
@ -1301,10 +1309,14 @@ impl TopLevelComposer {
fn analyze_function_instance(&mut self) -> Result<(), String> {
// first get the class contructor type correct for the following type check in function body
// also do class field instantiation check
for (def, ast) in self.definition_ast_list.iter().skip(self.builtin_num) {
let init_str_id = "__init__".into();
let mut definition_extension = Vec::new();
let mut constructors = Vec::new();
for (i, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num) {
let class_def = def.read();
if let TopLevelDef::Class {
constructor,
ancestors,
methods,
fields,
type_vars,
@ -1314,13 +1326,54 @@ impl TopLevelComposer {
..
} = &*class_def
{
let self_type = get_type_from_type_annotation_kinds(
self.extract_def_list().as_slice(),
&mut self.unifier,
&self.primitives_ty,
&make_self_type_annotation(type_vars, *object_id),
)?;
if ancestors.iter().any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) {
// create constructor for these classes
let string = self.primitives_ty.str;
let int64 = self.primitives_ty.int64;
let signature = self.unifier.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
args: vec![
FuncArg { name: "msg".into(), ty: string,
default_value: Some(SymbolValue::Str("".into()))},
FuncArg { name: "param0".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
FuncArg { name: "param1".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
FuncArg { name: "param2".into(), ty: int64,
default_value: Some(SymbolValue::I64(0))},
],
ret: self_type,
vars: Default::default()
})));
let cons_fun = TopLevelDef::Function {
name: format!("{}.{}", class_name, "__init__"),
simple_name: init_str_id,
signature,
var_id: Default::default(),
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
};
constructors.push((i, signature, definition_extension.len()));
definition_extension.push((Arc::new(RwLock::new(cons_fun)), None));
self.unifier
.unify(constructor.unwrap(), signature)
.map_err(|old| format!("{} (at {})", old, ast.as_ref().unwrap().location))?;
continue;
}
let mut init_id: Option<DefinitionId> = None;
// get the class contructor type correct
let (contor_args, contor_type_vars) = {
let mut constructor_args: Vec<FuncArg> = Vec::new();
let mut type_vars: HashMap<u32, Type> = HashMap::new();
for (name, func_sig, id) in methods {
if name == &"__init__".into() {
if *name == init_str_id {
init_id = Some(*id);
if let TypeEnum::TFunc(sig) = self.unifier.get_ty(*func_sig).as_ref() {
let FunSignature { args, vars, .. } = &*sig.borrow();
@ -1333,12 +1386,6 @@ impl TopLevelComposer {
}
(constructor_args, type_vars)
};
let self_type = get_type_from_type_annotation_kinds(
self.extract_def_list().as_slice(),
&mut self.unifier,
&self.primitives_ty,
&make_self_type_annotation(type_vars, *object_id),
)?;
let contor_type = self.unifier.add_ty(TypeEnum::TFunc(
FunSignature { args: contor_args, ret: self_type, vars: contor_type_vars }
.into(),
@ -1352,7 +1399,7 @@ impl TopLevelComposer {
let init_ast =
self.definition_ast_list.get(init_id.0).unwrap().1.as_ref().unwrap();
if let ast::StmtKind::FunctionDef { name, body, .. } = &init_ast.node {
if name != &"__init__".into() {
if *name != init_str_id {
unreachable!("must be init function here")
}
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
@ -1370,11 +1417,23 @@ impl TopLevelComposer {
}
}
}
for (i, signature, id) in constructors.into_iter() {
if let TopLevelDef::Class { methods, .. } = &mut *self.definition_ast_list[i].0.write() {
methods.push((init_str_id, signature,
DefinitionId(self.definition_ast_list.len() + id)));
} else {
unreachable!()
}
}
self.definition_ast_list.extend_from_slice(&definition_extension);
let ctx = Arc::new(self.make_top_level_context());
// type inference inside function body
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num)
{
if ast.is_none() {
continue;
}
let mut function_def = def.write();
if let TopLevelDef::Function {
instance_to_stmt,
@ -1522,6 +1581,7 @@ impl TopLevelComposer {
primitives: &self.primitives_ty,
virtual_checks: &mut Vec::new(),
calls: &mut calls,
in_handler: false
};
let fun_body =
@ -1535,6 +1595,13 @@ impl TopLevelComposer {
instance_to_symbol.insert("".into(), simple_name.to_string());
continue;
}
if !decorator_list.is_empty()
&& matches!(&decorator_list[0].node,
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
{
instance_to_symbol.insert("".into(), simple_name.to_string());
continue;
}
body
} else {
unreachable!("must be function def ast")
@ -1545,7 +1612,42 @@ impl TopLevelComposer {
let returned =
inferencer.check_block(fun_body.as_slice(), &mut identifiers)?;
{
// check virtuals
let defs = ctx.definitions.read();
for (subtype, base, loc) in inferencer.virtual_checks.iter() {
let base_id = {
let base = inferencer.unifier.get_ty(*base);
if let TypeEnum::TObj { obj_id, .. } = &*base {
*obj_id
} else {
return Err(format!("Base type should be a class (at {})", loc))
}
};
let subtype_id = {
let ty = inferencer.unifier.get_ty(*subtype);
if let TypeEnum::TObj { obj_id, .. } = &*ty {
*obj_id
} else {
let base_repr = inferencer.unifier.default_stringify(*base);
let subtype_repr = inferencer.unifier.default_stringify(*subtype);
return Err(format!("Expected a subtype of {}, but got {} (at {})", base_repr, subtype_repr, loc))
}
};
let subtype_entry = defs[subtype_id.0].read();
if let TopLevelDef::Class { ancestors, .. } = &*subtype_entry {
let m = ancestors.iter()
.find(|kind| matches!(kind, TypeAnnotation::CustomClass { id, .. } if *id == base_id));
if m.is_none() {
let base_repr = inferencer.unifier.default_stringify(*base);
let subtype_repr = inferencer.unifier.default_stringify(*subtype);
return Err(format!("Expected a subtype of {}, but got {} (at {})", base_repr, subtype_repr, loc))
}
} else {
unreachable!();
}
}
}
if !self.unifier.unioned(inst_ret, self.primitives_ty.none) && !returned {
let def_ast_list = &self.definition_ast_list;
let ret_str = self.unifier.stringify(

View File

@ -90,7 +90,22 @@ impl TopLevelComposer {
fields: HashMap::new().into(),
params: HashMap::new().into(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
fields: vec![
("__name__".into(), (int32, true)),
("__file__".into(), (int32, true)),
("__line__".into(), (int32, true)),
("__col__".into(), (int32, true)),
("__func__".into(), (str, true)),
("__message__".into(), (str, true)),
("__param0__".into(), (int64, true)),
("__param1__".into(), (int64, true)),
("__param2__".into(), (int64, true)),
].into_iter().collect::<HashMap<_, _>>().into(),
params: HashMap::new().into(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
(primitives, unifier)
}
@ -381,6 +396,13 @@ impl TopLevelComposer {
Some("int64".to_string())
}
}
SymbolValue::Str(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) {
None
} else {
Some("str".to_string())
}
}
SymbolValue::Tuple(elts) => {
if let TypeAnnotation::Tuple(elts_ty) = ty {
for (e, t) in elts.iter().zip(elts_ty.iter()) {

View File

@ -11,20 +11,21 @@ use super::codegen::CodeGenContext;
use super::typecheck::type_inferencer::PrimitiveStore;
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier};
use crate::{
symbol_resolver::SymbolResolver,
codegen::CodeGenerator,
symbol_resolver::{SymbolResolver, ValueEnum},
typecheck::{type_inferencer::CodeLocation, typedef::CallId},
};
use itertools::{izip, Itertools};
use parking_lot::RwLock;
use nac3parser::ast::{self, Stmt, StrRef};
use inkwell::values::BasicValueEnum;
use itertools::{izip, Itertools};
use nac3parser::ast::{self, Stmt, StrRef};
use parking_lot::RwLock;
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
pub struct DefinitionId(pub usize);
pub mod builtins;
pub mod composer;
pub mod helper;
pub mod builtins;
pub mod type_annotation;
use composer::*;
use type_annotation::*;
@ -34,9 +35,10 @@ mod test;
type GenCallCallback = Box<
dyn for<'ctx, 'a> Fn(
&mut CodeGenContext<'ctx, 'a>,
Option<(Type, BasicValueEnum)>,
Option<(Type, ValueEnum<'ctx>)>,
(&FunSignature, DefinitionId),
Vec<(Option<StrRef>, BasicValueEnum<'ctx>)>,
Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
&mut dyn CodeGenerator,
) -> Option<BasicValueEnum<'ctx>>
+ Send
+ Sync,
@ -54,11 +56,12 @@ impl GenCall {
pub fn run<'ctx, 'a>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
obj: Option<(Type, BasicValueEnum<'ctx>)>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
args: Vec<(Option<StrRef>, BasicValueEnum<'ctx>)>,
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
generator: &mut dyn CodeGenerator,
) -> Option<BasicValueEnum<'ctx>> {
(self.fp)(ctx, obj, fun, args)
(self.fp)(ctx, obj, fun, args, generator)
}
}
@ -120,7 +123,7 @@ pub enum TopLevelDef {
// symbol resolver of the module defined the class
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
// custom codegen callback
codegen_callback: Option<Arc<GenCall>>
codegen_callback: Option<Arc<GenCall>>,
},
}

View File

@ -38,7 +38,7 @@ impl SymbolResolver for Resolver {
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
unimplemented!()
}
fn get_symbol_type(
&self,
_: &mut Unifier,
@ -69,6 +69,10 @@ impl SymbolResolver for Resolver {
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
self.0.id_to_def.lock().get(&id).cloned()
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
}
#[test_case(

View File

@ -63,6 +63,8 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::Primitive(primitives.none))
} else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str))
} else if id == &"Exception".into() {
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() })
} else if let Some(obj_id) = resolver.get_identifier_def(*id) {
let type_vars = {
let def_read = top_level_defs[obj_id.0].try_read();

View File

@ -227,6 +227,20 @@ impl<'a> Inferencer<'a> {
self.check_block(body, &mut new_defined_identifiers)?;
Ok(false)
}
StmtKind::Try { body, handlers, orelse, finalbody, .. } => {
self.check_block(body, &mut defined_identifiers.clone())?;
self.check_block(orelse, &mut defined_identifiers.clone())?;
for handler in handlers.iter() {
let mut defined_identifiers = defined_identifiers.clone();
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
if let Some(name) = name {
defined_identifiers.insert(*name);
}
self.check_block(body, &mut defined_identifiers)?;
}
self.check_block(finalbody, defined_identifiers)?;
Ok(false)
}
StmtKind::Expr { value, .. } => {
self.check_expr(value, defined_identifiers)?;
Ok(false)

View File

@ -37,6 +37,7 @@ pub struct PrimitiveStore {
pub none: Type,
pub range: Type,
pub str: Type,
pub exception: Type,
}
pub struct FunctionData {
@ -51,9 +52,10 @@ pub struct Inferencer<'a> {
pub function_data: &'a mut FunctionData,
pub unifier: &'a mut Unifier,
pub primitives: &'a PrimitiveStore,
pub virtual_checks: &'a mut Vec<(Type, Type)>,
pub virtual_checks: &'a mut Vec<(Type, Type, Location)>,
pub variable_mapping: HashMap<StrRef, Type>,
pub calls: &'a mut HashMap<CodeLocation, CallId>,
pub in_handler: bool,
}
struct NaiveFolder();
@ -122,6 +124,56 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
},
}
}
ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment } => {
let body = body.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
let outer_in_handler = self.in_handler;
let mut exception_handlers = Vec::with_capacity(handlers.len());
self.in_handler = true;
{
let top_level_defs = self.top_level.definitions.read();
let mut naive_folder = NaiveFolder();
for handler in handlers.into_iter() {
let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = handler.node;
let type_ = if let Some(type_) = type_ {
let typ = self.function_data.resolver.parse_type_annotation(
top_level_defs.as_slice(),
self.unifier,
self.primitives,
&type_
)?;
self.virtual_checks.push((typ, self.primitives.exception, handler.location));
if let Some(name) = name {
if !self.defined_identifiers.contains(&name) {
self.defined_identifiers.insert(name);
}
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
self.unifier.unify(old_typ, typ)?;
}
}
let mut type_ = naive_folder.fold_expr(*type_)?;
type_.custom = Some(typ);
Some(Box::new(type_))
} else {
None
};
let body = body.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
exception_handlers.push(Located {
location: handler.location,
node: ast::ExcepthandlerKind::ExceptHandler { type_, name, body },
custom: None
});
}
}
self.in_handler = outer_in_handler;
let handlers = exception_handlers;
let orelse = orelse.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
let finalbody = finalbody .into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
Located {
location: node.location,
node: ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment },
custom: None
}
}
ast::StmtKind::For { target, iter, body, orelse, config_comment, type_comment } => {
self.infer_pattern(&target)?;
let target = self.fold_expr(*target)?;
@ -228,7 +280,8 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
_ => fold::fold_stmt(self, node)?,
};
match &stmt.node {
ast::StmtKind::For { .. } => {}
ast::StmtKind::For { .. } => {},
ast::StmtKind::Try { .. } => {},
ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
}
@ -241,6 +294,16 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
ast::StmtKind::Break { .. }
| ast::StmtKind::Continue { .. }
| ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Raise { exc, cause, .. } => {
if let Some(cause) = cause {
return report_error("raise ... from cause is not supported", cause.location);
}
if let Some(exc) = exc {
self.virtual_checks.push((exc.custom.unwrap(), self.primitives.exception, exc.location));
} else if !self.in_handler {
return report_error("cannot reraise outside exception handlers", stmt.location);
}
}
ast::StmtKind::With { items, .. } => {
for item in items.iter() {
let ty = item.context_expr.custom.unwrap();
@ -536,6 +599,8 @@ impl<'a> Inferencer<'a> {
top_level: self.top_level,
defined_identifiers,
variable_mapping,
// lambda should not be considered in exception handler
in_handler: false,
};
let fun = FunSignature {
args: fn_args
@ -582,6 +647,8 @@ impl<'a> Inferencer<'a> {
primitives: self.primitives,
calls: self.calls,
defined_identifiers,
// listcomp expr should not be considered as inside an exception handler...
in_handler: false
};
let generator = generators.pop().unwrap();
if generator.is_async {
@ -660,7 +727,7 @@ impl<'a> Inferencer<'a> {
} else {
self.unifier.get_fresh_var().0
};
self.virtual_checks.push((arg0.custom.unwrap(), ty));
self.virtual_checks.push((arg0.custom.unwrap(), ty, func_location));
let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty }));
return Ok(Located {
location,

View File

@ -22,7 +22,7 @@ impl SymbolResolver for Resolver {
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
unimplemented!()
}
fn get_symbol_type(
&self,
_: &mut Unifier,
@ -48,6 +48,10 @@ impl SymbolResolver for Resolver {
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
self.id_to_def.get(&id).cloned()
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
}
struct TestEnvironment {
@ -56,7 +60,7 @@ struct TestEnvironment {
pub primitives: PrimitiveStore,
pub id_to_name: HashMap<usize, StrRef>,
pub identifier_mapping: HashMap<StrRef, Type>,
pub virtual_checks: Vec<(Type, Type)>,
pub virtual_checks: Vec<(Type, Type, nac3parser::ast::Location)>,
pub calls: HashMap<CodeLocation, CallId>,
pub top_level: TopLevelContext,
}
@ -111,7 +115,12 @@ impl TestEnvironment {
fields: HashMap::new().into(),
params: HashMap::new().into(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
fields: HashMap::new().into(),
params: HashMap::new().into(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
set_primitives_magic_methods(&primitives, &mut unifier);
let id_to_name = [
@ -122,6 +131,7 @@ impl TestEnvironment {
(4, "none".into()),
(5, "range".into()),
(6, "str".into()),
(7, "exception".into()),
]
.iter()
.cloned()
@ -206,9 +216,14 @@ impl TestEnvironment {
fields: HashMap::new().into(),
params: HashMap::new().into(),
});
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
fields: HashMap::new().into(),
params: HashMap::new().into(),
});
identifier_mapping.insert("None".into(), none);
for (i, name) in
["int32", "int64", "float", "bool", "none", "range", "str"].iter().enumerate()
["int32", "int64", "float", "bool", "none", "range", "str", "Exception"].iter().enumerate()
{
top_level_defs.push(
RwLock::new(TopLevelDef::Class {
@ -224,20 +239,21 @@ impl TestEnvironment {
.into(),
);
}
let defs = 7;
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
let (v0, id) = unifier.get_fresh_var();
let foo_ty = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
obj_id: DefinitionId(defs + 1),
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>().into(),
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>().into(),
});
top_level_defs.push(
RwLock::new(TopLevelDef::Class {
name: "Foo".into(),
object_id: DefinitionId(7),
object_id: DefinitionId(defs + 1),
type_vars: vec![v0],
fields: [("a".into(), v0, true)].into(),
methods: Default::default(),
@ -264,7 +280,7 @@ impl TestEnvironment {
FunSignature { args: vec![], ret: int32, vars: Default::default() }.into(),
));
let bar = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(8),
obj_id: DefinitionId(defs + 2),
fields: [("a".into(), (int32, true)), ("b".into(), (fun, true))]
.iter()
.cloned()
@ -275,7 +291,7 @@ impl TestEnvironment {
top_level_defs.push(
RwLock::new(TopLevelDef::Class {
name: "Bar".into(),
object_id: DefinitionId(8),
object_id: DefinitionId(defs + 2),
type_vars: Default::default(),
fields: [("a".into(), int32, true), ("b".into(), fun, true)].into(),
methods: Default::default(),
@ -293,7 +309,7 @@ impl TestEnvironment {
);
let bar2 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(9),
obj_id: DefinitionId(defs + 3),
fields: [("a".into(), (bool, true)), ("b".into(), (fun, false))]
.iter()
.cloned()
@ -304,7 +320,7 @@ impl TestEnvironment {
top_level_defs.push(
RwLock::new(TopLevelDef::Class {
name: "Bar2".into(),
object_id: DefinitionId(9),
object_id: DefinitionId(defs + 3),
type_vars: Default::default(),
fields: [("a".into(), bool, true), ("b".into(), fun, false)].into(),
methods: Default::default(),
@ -323,19 +339,21 @@ impl TestEnvironment {
let class_names = [("Bar".into(), bar), ("Bar2".into(), bar2)].iter().cloned().collect();
let id_to_name = [
(0, "int32".into()),
(1, "int64".into()),
(2, "float".into()),
(3, "bool".into()),
(4, "none".into()),
(5, "range".into()),
(6, "str".into()),
(7, "Foo".into()),
(8, "Bar".into()),
(9, "Bar2".into()),
"int32".into(),
"int64".into(),
"float".into(),
"bool".into(),
"none".into(),
"range".into(),
"str".into(),
"exception".into(),
"Foo".into(),
"Bar".into(),
"Bar2".into(),
]
.iter()
.cloned()
.enumerate()
.map(|(a, b)| (a, *b))
.collect();
let top_level = TopLevelContext {
@ -347,9 +365,9 @@ impl TestEnvironment {
let resolver = Arc::new(Resolver {
id_to_type: identifier_mapping.clone(),
id_to_def: [
("Foo".into(), DefinitionId(7)),
("Bar".into(), DefinitionId(8)),
("Bar2".into(), DefinitionId(9)),
("Foo".into(), DefinitionId(defs + 1)),
("Bar".into(), DefinitionId(defs + 2)),
("Bar2".into(), DefinitionId(defs + 3)),
]
.iter()
.cloned()
@ -383,6 +401,7 @@ impl TestEnvironment {
virtual_checks: &mut self.virtual_checks,
calls: &mut self.calls,
defined_identifiers: Default::default(),
in_handler: false
}
}
}
@ -488,7 +507,7 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
assert_eq!(format!("{}: {}", k, v), format!("{}: {}", k, name));
}
assert_eq!(inferencer.virtual_checks.len(), virtuals.len());
for ((a, b), (x, y)) in zip(inferencer.virtual_checks.iter(), virtuals) {
for ((a, b, _), (x, y)) in zip(inferencer.virtual_checks.iter(), virtuals) {
let a = inferencer.unifier.stringify(
*a,
&mut |v| (*id_to_name.get(&v).unwrap()).into(),

View File

@ -70,4 +70,8 @@ impl SymbolResolver for Resolver {
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
self.0.id_to_def.lock().get(&id).cloned()
}
fn get_string_id(&self, _: &str) -> i32 {
unimplemented!()
}
}