forked from M-Labs/nac3
core/model: refactor core to use CSlice & Exception
This commit is contained in:
parent
b304df8bcc
commit
8a6dc1c1e1
|
@ -43,6 +43,14 @@ use nac3parser::ast::{
|
|||
Unaryop,
|
||||
};
|
||||
|
||||
use super::{
|
||||
model::*,
|
||||
structs::{
|
||||
cslice::CSlice,
|
||||
exception::{Exception, ExceptionId},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn get_subst_key(
|
||||
unifier: &mut Unifier,
|
||||
obj: Option<Type>,
|
||||
|
@ -281,24 +289,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
None
|
||||
}
|
||||
}
|
||||
Constant::Str(v) => {
|
||||
assert!(self.unifier.unioned(ty, self.primitives.str));
|
||||
if let Some(v) = self.const_strings.get(v) {
|
||||
Some(*v)
|
||||
} else {
|
||||
let str_ptr = self
|
||||
.builder
|
||||
.build_global_string_ptr(v, "const")
|
||||
.map(|v| v.as_pointer_value().into())
|
||||
.unwrap();
|
||||
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);
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
Constant::Str(s) => Some(self.gen_string(generator, s).value.into()),
|
||||
Constant::Ellipsis => {
|
||||
let msg = self.gen_string(generator, "NotImplementedError");
|
||||
|
||||
|
@ -560,96 +551,129 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
|
||||
/// Helper function for generating a LLVM variable storing a [String].
|
||||
pub fn gen_string<G, S>(&mut self, generator: &mut G, s: S) -> BasicValueEnum<'ctx>
|
||||
pub fn gen_string<G>(&mut self, generator: &mut G, string: &str) -> Struct<'ctx, CSlice<'ctx>>
|
||||
where
|
||||
G: CodeGenerator + ?Sized,
|
||||
S: Into<String>,
|
||||
{
|
||||
self.gen_const(generator, &Constant::Str(s.into()), self.primitives.str).unwrap()
|
||||
self.const_strings.get(string).copied().unwrap_or_else(|| {
|
||||
let sizet = generator.get_sizet(self.ctx);
|
||||
let pbyte_model = PointerModel(NIntModel(Byte));
|
||||
let cslice_model = StructModel(CSlice { sizet });
|
||||
|
||||
// Create `base`, it has to be a global cstr
|
||||
let base = self.builder.build_global_string_ptr(string, "constant_string").unwrap();
|
||||
let base = pbyte_model.review_value(self.ctx, base.as_pointer_value()).unwrap();
|
||||
|
||||
// Create `len`, it also has to be global
|
||||
let len = sizet.constant(self.ctx, string.len() as u64);
|
||||
|
||||
// Finally, create the global `CSlice` constant of the string.
|
||||
let cslice = cslice_model.create_const(self, base, len);
|
||||
|
||||
// Cache it
|
||||
self.const_strings.insert(string.to_owned(), cslice);
|
||||
|
||||
cslice
|
||||
})
|
||||
}
|
||||
|
||||
pub fn raise_exn<G: CodeGenerator + ?Sized>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
name: &str,
|
||||
msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
msg: Struct<'ctx, CSlice<'ctx>>,
|
||||
params: [Option<NInt<'ctx, Int64>>; 3],
|
||||
loc: Location,
|
||||
) {
|
||||
let zelf = if let Some(exception_val) = self.exception_val {
|
||||
exception_val
|
||||
} else {
|
||||
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 = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
|
||||
*self.exception_val.insert(zelf)
|
||||
};
|
||||
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").unwrap();
|
||||
let id = self.resolver.get_string_id(name);
|
||||
self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap();
|
||||
let ptr = self
|
||||
.builder
|
||||
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg")
|
||||
.unwrap();
|
||||
self.builder.build_store(ptr, msg).unwrap();
|
||||
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",
|
||||
)
|
||||
.unwrap();
|
||||
let val = params[i].map_or(i64_zero, |v| {
|
||||
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
|
||||
// Define all used models
|
||||
let sizet = generator.get_sizet(self.ctx);
|
||||
let exception_id_model = NIntModel(ExceptionId::default());
|
||||
let exception_model = StructModel(Exception { sizet });
|
||||
|
||||
// Get `exn`
|
||||
let exn = self.exception_val.unwrap_or_else(|| {
|
||||
let exn = exception_model.var_alloc(generator, self, Some("exn")).unwrap();
|
||||
*self.exception_val.insert(exn)
|
||||
});
|
||||
self.builder.build_store(ptr, val).unwrap();
|
||||
|
||||
// Now load everything into `exn`
|
||||
// Store `exception_id`
|
||||
exn.gep(self, |f| f.exception_id).store(
|
||||
self,
|
||||
exception_id_model.constant(self.ctx, self.resolver.get_string_id(name) as u64),
|
||||
);
|
||||
|
||||
// Store `message`
|
||||
exn.gep(self, |f| f.message).store(self, msg);
|
||||
|
||||
// Store `params`
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if let Some(param) = param {
|
||||
exn.gep(self, |f| f.params[i]).store(self, *param);
|
||||
}
|
||||
}
|
||||
gen_raise(generator, self, Some(&zelf.into()), loc);
|
||||
|
||||
gen_raise(generator, self, Some(exn), loc);
|
||||
}
|
||||
|
||||
pub fn make_assert<G: CodeGenerator + ?Sized>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
cond: IntValue<'ctx>,
|
||||
cond: IntValue<'ctx>, // IntType can have arbitrary bit width
|
||||
err_name: &str,
|
||||
err_msg: &str,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
loc: Location,
|
||||
) {
|
||||
// Define all used models
|
||||
let i64_model = NIntModel(Int64);
|
||||
|
||||
// Create a global `CSlice` constant from `err_msg`
|
||||
let err_msg = self.gen_string(generator, err_msg);
|
||||
|
||||
// Check `params`
|
||||
let ctx = self.ctx;
|
||||
let params =
|
||||
params.map(|param| param.map(|param| i64_model.review_value(ctx, param).unwrap()));
|
||||
|
||||
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc);
|
||||
}
|
||||
|
||||
pub fn make_assert_impl<G: CodeGenerator + ?Sized>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
cond: IntValue<'ctx>,
|
||||
cond: IntValue<'ctx>, // IntType can have arbitrary bit width
|
||||
err_name: &str,
|
||||
err_msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
err_msg: Struct<'ctx, CSlice<'ctx>>,
|
||||
params: [Option<NInt<'ctx, Int64>>; 3],
|
||||
loc: Location,
|
||||
) {
|
||||
let i1 = self.ctx.bool_type();
|
||||
let i1_true = i1.const_all_ones();
|
||||
// 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 = call_expect(self, cond, i1_true, Some("expect"));
|
||||
// Define all used models
|
||||
let bool_model = NIntModel(Bool);
|
||||
|
||||
// Truncate `cond` to a `i1`.
|
||||
// So this could accept both nac3core's bool IntType (i8), and i1.
|
||||
let cond = Int::from(cond);
|
||||
let cond = cond.truncate(self, bool_model, "cond_bool");
|
||||
|
||||
// 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.
|
||||
let cond =
|
||||
call_expect(self, cond.value, bool_model.const_true(self.ctx).value, Some("expect"));
|
||||
|
||||
let current_bb = self.builder.get_insert_block().unwrap();
|
||||
let current_fun = current_bb.get_parent().unwrap();
|
||||
|
||||
let then_block = self.ctx.insert_basic_block_after(current_bb, "succ");
|
||||
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
|
||||
|
||||
self.builder.build_conditional_branch(cond, then_block, exn_block).unwrap();
|
||||
|
||||
// Inserting into `exn_block`
|
||||
self.builder.position_at_end(exn_block);
|
||||
self.raise_exn(generator, err_name, err_msg, params, loc);
|
||||
|
||||
// Continuation
|
||||
self.builder.position_at_end(then_block);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use inkwell::{
|
|||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use model::*;
|
||||
use nac3parser::ast::{Location, Stmt, StrRef};
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -32,6 +33,7 @@ use std::sync::{
|
|||
Arc,
|
||||
};
|
||||
use std::thread;
|
||||
use structs::{cslice::CSlice, exception::Exception};
|
||||
|
||||
pub mod builtin_fns;
|
||||
pub mod classes;
|
||||
|
@ -160,11 +162,11 @@ pub struct CodeGenContext<'ctx, 'a> {
|
|||
pub registry: &'a WorkerRegistry,
|
||||
|
||||
/// Cache for constant strings.
|
||||
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>,
|
||||
pub const_strings: HashMap<String, Struct<'ctx, CSlice<'ctx>>>,
|
||||
|
||||
/// [`BasicBlock`] containing all `alloca` statements for the current function.
|
||||
pub init_bb: BasicBlock<'ctx>,
|
||||
pub exception_val: Option<PointerValue<'ctx>>,
|
||||
pub exception_val: Option<Pointer<'ctx, StructModel<Exception<'ctx>>>>,
|
||||
|
||||
/// The header and exit basic blocks of a loop in this context. See
|
||||
/// <https://llvm.org/docs/LoopTerminology.html> for explanation of these terminology.
|
||||
|
@ -648,47 +650,24 @@ pub fn gen_func_impl<
|
|||
..primitives
|
||||
};
|
||||
|
||||
let mut type_cache: HashMap<_, _> = [
|
||||
let sizet = generator.get_sizet(context);
|
||||
let cslice_type = StructModel(CSlice { sizet });
|
||||
let pexception_type = PointerModel(StructModel(Exception { sizet }));
|
||||
|
||||
let mut type_cache: HashMap<_, BasicTypeEnum<'ctx>> = [
|
||||
(primitives.int32, context.i32_type().into()),
|
||||
(primitives.int64, context.i64_type().into()),
|
||||
(primitives.uint32, context.i32_type().into()),
|
||||
(primitives.uint64, context.i64_type().into()),
|
||||
(primitives.float, context.f64_type().into()),
|
||||
(primitives.bool, context.i8_type().into()),
|
||||
(primitives.str, {
|
||||
let name = "str";
|
||||
match module.get_struct_type(name) {
|
||||
None => {
|
||||
let str_type = context.opaque_struct_type("str");
|
||||
let fields = [
|
||||
context.i8_type().ptr_type(AddressSpace::default()).into(),
|
||||
generator.get_size_type(context).into(),
|
||||
];
|
||||
str_type.set_body(&fields, false);
|
||||
str_type.into()
|
||||
}
|
||||
Some(t) => t.as_basic_type_enum(),
|
||||
}
|
||||
}),
|
||||
(primitives.str, cslice_type.get_type(context).into()),
|
||||
(primitives.range, RangeType::new(context).as_base_type().into()),
|
||||
(primitives.exception, {
|
||||
let name = "Exception";
|
||||
if let Some(t) = module.get_struct_type(name) {
|
||||
t.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
} else {
|
||||
let exception = context.opaque_struct_type("Exception");
|
||||
let int32 = context.i32_type().into();
|
||||
let int64 = context.i64_type().into();
|
||||
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
|
||||
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
}
|
||||
}),
|
||||
(primitives.exception, pexception_type.get_type(context).into()),
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// NOTE: special handling of option cannot use this type cache since it contains type var,
|
||||
// handled inside get_llvm_type instead
|
||||
|
||||
|
|
|
@ -55,6 +55,13 @@ impl<'ctx> IntModel<'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<IntValue<'ctx>> for Int<'ctx> {
|
||||
fn from(value: IntValue<'ctx>) -> Self {
|
||||
let model = IntModel(value.get_type());
|
||||
model.believe_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A model representing an [`IntType<'ctx>`] that happens to be defined as `size_t`.
|
||||
///
|
||||
/// This is specifically created to guide developers to write `size_t`-dependent code.
|
||||
|
|
|
@ -252,7 +252,7 @@ fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
|||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "")
|
||||
ctx.gen_string(generator, "").value.into()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
|
|||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_int(1, false).into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "1")
|
||||
ctx.gen_string(generator, "1").value.into()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use super::{
|
|||
super::symbol_resolver::ValueEnum,
|
||||
expr::destructure_range,
|
||||
irrt::{handle_slice_indices, list_slice_assignment},
|
||||
model::*,
|
||||
structs::{cslice::CSlice, exception::Exception},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
use crate::{
|
||||
|
@ -1113,47 +1115,37 @@ pub fn exn_constructor<'ctx>(
|
|||
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
exception: Option<&BasicValueEnum<'ctx>>,
|
||||
exception: Option<Pointer<'ctx, StructModel<Exception<'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")
|
||||
.unwrap();
|
||||
let filename = ctx.gen_string(generator, loc.file.0);
|
||||
ctx.builder.build_store(file_ptr, filename).unwrap();
|
||||
let row_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false)).unwrap();
|
||||
let col_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false)).unwrap();
|
||||
if let Some(pexn) = exception {
|
||||
let sizet = generator.get_sizet(ctx.ctx);
|
||||
let i32_model = NIntModel(Int32);
|
||||
let cslice_model = StructModel(CSlice { sizet });
|
||||
|
||||
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")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(name_ptr, fun_name).unwrap();
|
||||
}
|
||||
// Get and store filename
|
||||
let filename = loc.file.0;
|
||||
let filename = ctx.gen_string(generator, &String::from(filename)).value;
|
||||
let filename = cslice_model.review_value(ctx.ctx, filename).unwrap();
|
||||
pexn.gep(ctx, |f| f.filename).store(ctx, filename);
|
||||
|
||||
let row = i32_model.constant(ctx.ctx, loc.row as u64);
|
||||
pexn.gep(ctx, |f| f.line).store(ctx, row);
|
||||
|
||||
let column = i32_model.constant(ctx.ctx, loc.column as u64);
|
||||
pexn.gep(ctx, |f| f.column).store(ctx, column);
|
||||
|
||||
let current_fn = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let fn_name = ctx.gen_string(generator, current_fn.get_name().to_str().unwrap());
|
||||
pexn.gep(ctx, |f| f.function_name).store(ctx, fn_name);
|
||||
|
||||
let raise = get_builtins(generator, ctx, "__nac3_raise");
|
||||
let exception = *exception;
|
||||
ctx.build_call_or_invoke(raise, &[exception], "raise");
|
||||
ctx.build_call_or_invoke(raise, &[pexn.value.into()], "raise");
|
||||
} else {
|
||||
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
}
|
||||
|
||||
ctx.builder.build_unreachable().unwrap();
|
||||
}
|
||||
|
||||
|
@ -1609,35 +1601,50 @@ pub fn gen_stmt<G: CodeGenerator>(
|
|||
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
|
||||
StmtKind::Raise { exc, .. } => {
|
||||
if let Some(exc) = exc {
|
||||
let exc = if let Some(v) = generator.gen_expr(ctx, exc)? {
|
||||
v.to_basic_value_enum(ctx, generator, exc.custom.unwrap())?
|
||||
} else {
|
||||
// Define all used models
|
||||
let sizet = generator.get_sizet(ctx.ctx);
|
||||
let pexn_model = PointerModel(StructModel(Exception { sizet }));
|
||||
|
||||
let Some(exn) = generator.gen_expr(ctx, exc)? else {
|
||||
return Ok(());
|
||||
};
|
||||
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
||||
let pexn = exn.to_basic_value_enum(ctx, generator, ctx.primitives.exception)?;
|
||||
let pexn = pexn_model.review_value(ctx.ctx, pexn).unwrap();
|
||||
|
||||
gen_raise(generator, ctx, Some(pexn), stmt.location);
|
||||
} else {
|
||||
gen_raise(generator, ctx, None, stmt.location);
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, msg, .. } => {
|
||||
let test = if let Some(v) = generator.gen_expr(ctx, test)? {
|
||||
v.to_basic_value_enum(ctx, generator, test.custom.unwrap())?
|
||||
} else {
|
||||
// Define all used models
|
||||
let sizet = generator.get_sizet(ctx.ctx);
|
||||
let byte_model = NIntModel(Byte);
|
||||
let cslice_model = StructModel(CSlice { sizet });
|
||||
|
||||
// Check `test`
|
||||
let Some(test) = generator.gen_expr(ctx, test)? else {
|
||||
return Ok(());
|
||||
};
|
||||
let test = test.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?;
|
||||
let test = byte_model.review_value(ctx.ctx, test).unwrap(); // Python `bool`s are represented as `i8` in nac3core
|
||||
|
||||
// Check `msg`
|
||||
let err_msg = match msg {
|
||||
Some(msg) => {
|
||||
if let Some(v) = generator.gen_expr(ctx, msg)? {
|
||||
v.to_basic_value_enum(ctx, generator, msg.custom.unwrap())?
|
||||
} else {
|
||||
let Some(msg) = generator.gen_expr(ctx, msg)? else {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let msg = msg.to_basic_value_enum(ctx, generator, ctx.primitives.str)?;
|
||||
cslice_model.review_value(ctx.ctx, msg).unwrap()
|
||||
}
|
||||
None => ctx.gen_string(generator, ""),
|
||||
};
|
||||
|
||||
ctx.make_assert_impl(
|
||||
generator,
|
||||
generator.bool_to_i1(ctx, test.into_int_value()),
|
||||
test.value,
|
||||
"0:AssertionError",
|
||||
err_msg,
|
||||
[None, None, None],
|
||||
|
|
Loading…
Reference in New Issue