core/model: refactor CSlice and Exception with models

This commit is contained in:
lyken 2024-08-25 16:55:56 +08:00
parent 8eb9094d68
commit f698b0c1fa
No known key found for this signature in database
GPG Key ID: 3BD5FC6AC8325DD8
11 changed files with 247 additions and 215 deletions

View File

@ -4,7 +4,7 @@ use nac3core::{
expr::gen_call, expr::gen_call,
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave}, llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
model::*, model::*,
object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject}, object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject, str::str_model},
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
@ -877,12 +877,12 @@ fn polymorphic_print<'ctx>(
}); });
let fmt = ctx.gen_string(generator, fmt); let fmt = ctx.gen_string(generator, fmt);
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value(); let fmt = fmt.get_field(generator, ctx.ctx, |f| f.base);
ctx.builder ctx.builder
.build_call( .build_call(
print_fn, print_fn,
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(), &once(fmt.value.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
"", "",
) )
.unwrap(); .unwrap();
@ -957,20 +957,23 @@ fn polymorphic_print<'ctx>(
fmt.push_str("%.*s"); fmt.push_str("%.*s");
let true_str = ctx.gen_string(generator, "True"); let true_str = ctx.gen_string(generator, "True");
let true_data =
unsafe { true_str.get_field_at_index_unchecked(0) }.into_pointer_value();
let true_len = unsafe { true_str.get_field_at_index_unchecked(1) }.into_int_value();
let false_str = ctx.gen_string(generator, "False"); let false_str = ctx.gen_string(generator, "False");
let false_data =
unsafe { false_str.get_field_at_index_unchecked(0) }.into_pointer_value(); let true_data = true_str.get_field(generator, ctx.ctx, |f| f.base);
let false_len = let true_len = true_str.get_field(generator, ctx.ctx, |f| f.len);
unsafe { false_str.get_field_at_index_unchecked(1) }.into_int_value();
let false_data = false_str.get_field(generator, ctx.ctx, |f| f.base);
let false_len = false_str.get_field(generator, ctx.ctx, |f| f.len);
let bool_val = generator.bool_to_i1(ctx, value.into_int_value()); let bool_val = generator.bool_to_i1(ctx, value.into_int_value());
args.extend([ args.extend([
ctx.builder.build_select(bool_val, true_len, false_len, "").unwrap(), ctx.builder
ctx.builder.build_select(bool_val, true_data, false_data, "").unwrap(), .build_select(bool_val, true_len.value, false_len.value, "")
.unwrap(),
ctx.builder
.build_select(bool_val, true_data.value, false_data.value, "")
.unwrap(),
]); ]);
} }
@ -1008,11 +1011,12 @@ fn polymorphic_print<'ctx>(
fmt.push_str("%.*s"); fmt.push_str("%.*s");
} }
let str = value.into_struct_value(); let str = str_model().check_value(generator, ctx.ctx, value).unwrap();
let str_data = unsafe { str.get_field_at_index_unchecked(0) }.into_pointer_value();
let str_len = unsafe { str.get_field_at_index_unchecked(1) }.into_int_value();
args.extend(&[str_len.into(), str_data.into()]); let str_data = str.get_field(generator, ctx.ctx, |f| f.base);
let str_len = str.get_field(generator, ctx.ctx, |f| f.len);
args.extend(&[str_len.value.into(), str_data.value.into()]);
} }
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {

View File

@ -166,7 +166,7 @@ impl StaticValue for PythonValue {
PrimitiveValue::Bool(val) => { PrimitiveValue::Bool(val) => {
ctx.ctx.i8_type().const_int(u64::from(*val), false).into() ctx.ctx.i8_type().const_int(u64::from(*val), false).into()
} }
PrimitiveValue::Str(val) => ctx.gen_string(generator, val).into(), PrimitiveValue::Str(val) => ctx.gen_string(generator, val).value.into(),
}); });
} }
if let Some(global) = ctx.module.get_global(&self.id.to_string()) { if let Some(global) = ctx.module.get_global(&self.id.to_string()) {
@ -980,7 +980,7 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ { } else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
let val: String = obj.extract().unwrap(); let val: String = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::Str(val.clone())); self.id_to_primitive.write().insert(id, PrimitiveValue::Str(val.clone()));
Ok(Some(ctx.gen_string(generator, val).into())) Ok(Some(ctx.gen_string(generator, val).value.into()))
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 { } else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
let val: f64 = obj.extract().unwrap(); let val: f64 = obj.extract().unwrap();
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val)); self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));

View File

@ -12,7 +12,11 @@ use crate::{
call_int_umin, call_memcpy_generic, call_int_umin, call_memcpy_generic,
}, },
need_sret, need_sret,
object::ndarray::{NDArrayOut, ScalarOrNDArray}, object::{
exception::Exception,
ndarray::{NDArrayOut, ScalarOrNDArray},
str::str_model,
},
stmt::{ stmt::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var, gen_var,
@ -29,10 +33,7 @@ use crate::{
use inkwell::{ use inkwell::{
attributes::{Attribute, AttributeLoc}, attributes::{Attribute, AttributeLoc},
types::{AnyType, BasicType, BasicTypeEnum}, types::{AnyType, BasicType, BasicTypeEnum},
values::{ values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue},
BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue,
StructValue,
},
AddressSpace, IntPredicate, OptimizationLevel, AddressSpace, IntPredicate, OptimizationLevel,
}; };
use itertools::{chain, izip, Either, Itertools}; use itertools::{chain, izip, Either, Itertools};
@ -50,6 +51,7 @@ use super::{
any::AnyObject, any::AnyObject,
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject}, ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
range::RangeObject, range::RangeObject,
str::Str,
}, },
}; };
@ -160,14 +162,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(), SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(),
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(), SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => { SymbolValue::Str(v) => {
let str_ptr = self let string = self.gen_string(generator, v);
.builder string.value.into()
.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).into_struct_type();
ty.const_named_struct(&[str_ptr, size.into()]).into()
} }
SymbolValue::Tuple(ls) => { SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec(); let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
@ -310,21 +306,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} }
Constant::Str(v) => { Constant::Str(v) => {
assert!(self.unifier.unioned(ty, self.primitives.str)); assert!(self.unifier.unioned(ty, self.primitives.str));
if let Some(v) = self.const_strings.get(v) { let string = self.gen_string(generator, v);
Some(*v) Some(string.value.into())
} 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::Ellipsis => { Constant::Ellipsis => {
let msg = self.gen_string(generator, "NotImplementedError"); let msg = self.gen_string(generator, "NotImplementedError");
@ -332,7 +315,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
self.raise_exn( self.raise_exn(
generator, generator,
"0:NotImplementedError", "0:NotImplementedError",
msg.into(), msg,
[None, None, None], [None, None, None],
self.current_loc, self.current_loc,
); );
@ -592,60 +575,59 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} }
/// Helper function for generating a LLVM variable storing a [String]. /// Helper function for generating a LLVM variable storing a [String].
pub fn gen_string<G, S>(&mut self, generator: &mut G, s: S) -> StructValue<'ctx> pub fn gen_string<G, S>(&mut self, generator: &mut G, string: S) -> Instance<'ctx, Str>
where where
G: CodeGenerator + ?Sized, G: CodeGenerator + ?Sized,
S: Into<String>, S: Into<String>,
{ {
self.gen_const(generator, &Constant::Str(s.into()), self.primitives.str) let string = string.into();
.map(BasicValueEnum::into_struct_value) self.const_strings.get(&string).copied().unwrap_or_else(|| {
.unwrap() let str_ptr = self.builder.build_global_string_ptr(&string, "const").unwrap();
let str_ptr = str_ptr.as_basic_value_enum();
let str_len = Int(SizeT).const_int(generator, self.ctx, string.len() as u64);
let str_len = str_len.value.as_basic_value_enum();
let str = str_model().const_struct(generator, self.ctx, &[str_ptr, str_len]);
self.const_strings.insert(string, str);
str
})
} }
pub fn raise_exn<G: CodeGenerator + ?Sized>( pub fn raise_exn<G: CodeGenerator + ?Sized>(
&mut self, &mut self,
generator: &mut G, generator: &mut G,
name: &str, name: &str,
msg: BasicValueEnum<'ctx>, msg: Instance<'ctx, Str>,
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3], // Can have any bit-width.
loc: Location, loc: Location,
) { ) {
let zelf = if let Some(exception_val) = self.exception_val { let exn = if let Some(exn) = self.exception_val {
exception_val exn
} else { } else {
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type(); let exn = Struct(Exception).var_alloca(generator, self, Some("exn")).unwrap();
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into(); *self.exception_val.insert(exn)
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(); // Set exception ID
unsafe {
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap();
let id = self.resolver.get_string_id(name); let id = self.resolver.get_string_id(name);
self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap(); let id = Int(Int32).const_int(generator, self.ctx, id as u64);
let ptr = self exn.set(self, |f| f.id, id);
.builder
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") // Set message
.unwrap(); exn.set(self, |f| f.msg, msg);
self.builder.build_store(ptr, msg).unwrap();
let i64_zero = self.ctx.i64_type().const_zero(); // Set parameters.
for (i, attr_ind) in [6, 7, 8].iter().enumerate() { let num_0 = Int(Int64).const_0(generator, self.ctx);
let ptr = self for (i, param) in params.iter().enumerate() {
.builder // Param can be of any bit-width. We need to cast them.
.build_in_bounds_gep( // Defaults to 0 if not provided
zelf, let param = param
&[zero, int32.const_int(*attr_ind, false)], .map_or(num_0, |param| Int(Int64).s_extend_or_bit_cast(generator, self, param));
"exn.param",
) exn.set(self, |f| f.params[i], param);
.unwrap();
let val = params[i].map_or(i64_zero, |v| {
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
});
self.builder.build_store(ptr, val).unwrap();
} }
} gen_raise(generator, self, Some(exn), loc);
gen_raise(generator, self, Some(&zelf.into()), loc);
} }
pub fn make_assert<G: CodeGenerator + ?Sized>( pub fn make_assert<G: CodeGenerator + ?Sized>(
@ -658,7 +640,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
loc: Location, loc: Location,
) { ) {
let err_msg = self.gen_string(generator, err_msg); let err_msg = self.gen_string(generator, err_msg);
self.make_assert_impl(generator, cond, err_name, err_msg.into(), params, loc); self.make_assert_impl(generator, cond, err_name, err_msg, params, loc);
} }
pub fn make_assert_impl<G: CodeGenerator + ?Sized>( pub fn make_assert_impl<G: CodeGenerator + ?Sized>(
@ -666,7 +648,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
generator: &mut G, generator: &mut G,
cond: IntValue<'ctx>, cond: IntValue<'ctx>,
err_name: &str, err_name: &str,
err_msg: BasicValueEnum<'ctx>, err_msg: Instance<'ctx, Str>,
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3],
loc: Location, loc: Location,
) { ) {
@ -2899,7 +2881,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
ctx.raise_exn( ctx.raise_exn(
generator, generator,
"0:UnwrapNoneError", "0:UnwrapNoneError",
err_msg.into(), err_msg,
[None, None, None], [None, None, None],
ctx.current_loc, ctx.current_loc,
); );

View File

@ -26,7 +26,12 @@ use inkwell::{
use itertools::Itertools; use itertools::Itertools;
use model::*; use model::*;
use nac3parser::ast::{Location, Stmt, StrRef}; use nac3parser::ast::{Location, Stmt, StrRef};
use object::{ndarray::NDArray, range::range_model}; use object::{
exception::Exception,
ndarray::NDArray,
range::range_model,
str::{str_model, Str},
};
use parking_lot::{Condvar, Mutex}; use parking_lot::{Condvar, Mutex};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::{ use std::sync::{
@ -172,11 +177,11 @@ pub struct CodeGenContext<'ctx, 'a> {
pub registry: &'a WorkerRegistry, pub registry: &'a WorkerRegistry,
/// Cache for constant strings. /// Cache for constant strings.
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>, pub const_strings: HashMap<String, Instance<'ctx, Str>>,
/// [`BasicBlock`] containing all `alloca` statements for the current function. /// [`BasicBlock`] containing all `alloca` statements for the current function.
pub init_bb: BasicBlock<'ctx>, pub init_bb: BasicBlock<'ctx>,
pub exception_val: Option<PointerValue<'ctx>>, pub exception_val: Option<Instance<'ctx, Ptr<Struct<Exception>>>>,
/// The header and exit basic blocks of a loop in this context. See /// The header and exit basic blocks of a loop in this context. See
/// <https://llvm.org/docs/LoopTerminology.html> for explanation of these terminology. /// <https://llvm.org/docs/LoopTerminology.html> for explanation of these terminology.
@ -706,36 +711,9 @@ pub fn gen_func_impl<
(primitives.uint64, context.i64_type().into()), (primitives.uint64, context.i64_type().into()),
(primitives.float, context.f64_type().into()), (primitives.float, context.f64_type().into()),
(primitives.bool, context.i8_type().into()), (primitives.bool, context.i8_type().into()),
(primitives.str, { (primitives.str, str_model().get_type(generator, context).into()),
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.range, Ptr(range_model()).get_type(generator, context).into()), (primitives.range, Ptr(range_model()).get_type(generator, context).into()),
(primitives.exception, { (primitives.exception, { Ptr(Struct(Exception)).get_type(generator, context).into() }),
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()
}
}),
] ]
.iter() .iter()
.copied() .copied()

View File

@ -0,0 +1,24 @@
use crate::codegen::model::*;
/// Fields of [`CSlice`]
pub struct CSliceFields<'ctx, F: FieldTraversal<'ctx>, Item: Model<'ctx>> {
/// Pointer to items
pub base: F::Out<Ptr<Item>>,
/// Number of items (not bytes)
pub len: F::Out<Int<SizeT>>,
}
/// See <https://docs.rs/cslice/0.3.0/cslice/struct.CSlice.html>.
///
/// Additionally, see <https://github.com/m-labs/artiq/blob/b0d2705c385f64b6e6711c1726cd9178f40b598e/artiq/firmware/libeh/eh_artiq.rs>)
/// for ARTIQ-specific notes.
#[derive(Debug, Clone, Copy, Default)]
pub struct CSlice<Item>(pub Item);
impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for CSlice<Item> {
type Fields<F: FieldTraversal<'ctx>> = CSliceFields<'ctx, F, Item>;
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
CSliceFields { base: traversal.add("base", Ptr(self.0)), len: traversal.add_auto("len") }
}
}

View File

@ -0,0 +1,41 @@
use crate::codegen::model::*;
use super::str::Str;
/// Fields of [`Exception<'ctx>`]
///
/// The definition came from `pub struct Exception<'a>` in
/// <https://github.com/m-labs/artiq/blob/master/artiq/firmware/libeh/eh_artiq.rs>.
pub struct ExceptionFields<'ctx, F: FieldTraversal<'ctx>> {
pub id: F::Out<Int<Int32>>,
pub filename: F::Out<Str>,
pub line: F::Out<Int<Int32>>,
pub column: F::Out<Int<Int32>>,
pub function: F::Out<Str>,
pub msg: F::Out<Str>,
pub params: [F::Out<Int<Int64>>; 3],
}
/// nac3core & ARTIQ's Exception
#[derive(Debug, Clone, Copy, Default)]
pub struct Exception;
impl<'ctx> StructKind<'ctx> for Exception {
type Fields<F: FieldTraversal<'ctx>> = ExceptionFields<'ctx, F>;
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
Self::Fields {
id: traversal.add_auto("id"),
filename: traversal.add_auto("filename"),
line: traversal.add_auto("line"),
column: traversal.add_auto("column"),
function: traversal.add_auto("function"),
msg: traversal.add_auto("msg"),
params: [
traversal.add_auto("params[0]"),
traversal.add_auto("params[1]"),
traversal.add_auto("params[2]"),
],
}
}
}

View File

@ -1,5 +1,8 @@
pub mod any; pub mod any;
pub mod cslice;
pub mod exception;
pub mod list; pub mod list;
pub mod ndarray; pub mod ndarray;
pub mod range; pub mod range;
pub mod str;
pub mod tuple; pub mod tuple;

View File

@ -31,7 +31,7 @@ fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) { } else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
ctx.ctx.bool_type().const_zero().into() ctx.ctx.bool_type().const_zero().into()
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) { } else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
ctx.gen_string(generator, "").into() ctx.gen_string(generator, "").value.into()
} else { } else {
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype)); panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
} }
@ -60,7 +60,7 @@ fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) { } else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
ctx.ctx.bool_type().const_int(1, false).into() ctx.ctx.bool_type().const_int(1, false).into()
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) { } else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
ctx.gen_string(generator, "1").into() ctx.gen_string(generator, "1").value.into()
} else { } else {
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype)); panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
} }

View File

@ -0,0 +1,3 @@
pub fn str_type() {
}

View File

@ -0,0 +1,11 @@
use super::cslice::CSlice;
use crate::codegen::model::*;
/// A string in NAC3.
pub type Str = Struct<CSlice<Int<Byte>>>;
/// An alias for `Str::default()`
#[must_use]
pub fn str_model() -> Str {
Str::default()
}

View File

@ -1,12 +1,15 @@
use super::{ use super::{
super::symbol_resolver::ValueEnum, super::symbol_resolver::ValueEnum,
irrt::{handle_slice_indices, list_slice_assignment}, irrt::{handle_slice_indices, list_slice_assignment},
model::*,
object::{ object::{
any::AnyObject, any::AnyObject,
exception::Exception,
ndarray::{ ndarray::{
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray, indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
}, },
range::RangeObject, range::RangeObject,
str::str_model,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -30,9 +33,7 @@ use inkwell::{
IntPredicate, IntPredicate,
}; };
use itertools::{izip, Itertools}; use itertools::{izip, Itertools};
use nac3parser::ast::{ use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef};
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
};
/// See [`CodeGenerator::gen_var_alloc`]. /// See [`CodeGenerator::gen_var_alloc`].
pub fn gen_var<'ctx>( pub fn gen_var<'ctx>(
@ -1247,65 +1248,58 @@ pub fn exn_constructor<'ctx>(
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>, mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
generator: &mut dyn CodeGenerator, generator: &mut dyn CodeGenerator,
) -> Result<Option<BasicValueEnum<'ctx>>, String> { ) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let (zelf_ty, zelf) = obj.unwrap(); let (exn_ty, exn) = obj.unwrap();
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value(); let exn = exn.to_basic_value_enum(ctx, generator, exn_ty)?;
let int32 = ctx.ctx.i32_type(); let exn = Ptr(Struct(Exception)).check_value(generator, ctx.ctx, exn).unwrap();
let zero = int32.const_zero();
let zelf_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(zelf_ty) { // Get the Exception name `exn_name` of this Exception object.
let exn_def_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(exn_ty) {
obj_id.0 obj_id.0
} else { } else {
unreachable!() unreachable!()
}; };
let defs = ctx.top_level.definitions.read(); let defs = ctx.top_level.definitions.read();
let def = defs[zelf_id].read(); let exn_def = defs[exn_def_id].read();
let TopLevelDef::Class { name: zelf_name, .. } = &*def else { unreachable!() }; let TopLevelDef::Class { name: exn_name, .. } = &*exn_def else { unreachable!() };
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(zelf_id), zelf_name); let exn_name = format!("{}:{}", ctx.resolver.get_exception_id(exn_def_id), exn_name);
unsafe {
let id_ptr = ctx.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap(); // Initialize the fields of the Exception object.
let id = ctx.resolver.get_string_id(&exception_name);
ctx.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap(); let empty_str = ctx.gen_string(generator, "");
let empty_string = let num_0 = Int(Int32).const_0(generator, ctx.ctx);
ctx.gen_const(generator, &Constant::Str(String::new()), ctx.primitives.str);
let ptr = ctx // Initialize `self.id`.
.builder let id = ctx.resolver.get_string_id(&exn_name);
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") let id = Int(Int32).const_int(generator, ctx.ctx, id as u64);
.unwrap(); exn.set(ctx, |f| f.id, id);
// Initialize `self.msg`.
let msg = if args.is_empty() { let msg = if args.is_empty() {
empty_string.unwrap() // Default to `msg` to "" if the user didn't pass anything.
empty_str
} else { } else {
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)? let msg = args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?;
str_model().check_value(generator, ctx.ctx, msg).unwrap()
}; };
ctx.builder.build_store(ptr, msg).unwrap(); exn.set(ctx, |f| f.msg, msg);
for i in &[6, 7, 8] {
let value = if args.is_empty() { // Initialize `self.params`, the arguments after `msg` are the params.
ctx.ctx.i64_type().const_zero().into() for (i, (_, param)) in args.into_iter().enumerate() {
} else { assert!(i <= 3, "There should only be at most 3 exception parameters");
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
}; let param = param.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?;
let ptr = ctx let param = Int(Int64).check_value(generator, ctx.ctx, param).unwrap();
.builder
.build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.param") exn.set(ctx, |f| f.params[i], param);
.unwrap();
ctx.builder.build_store(ptr, value).unwrap();
} }
// set file, func to empty string
for i in &[1, 4] { // Initialize everything else to 0 or "".
let ptr = ctx exn.set(ctx, |f| f.line, num_0);
.builder exn.set(ctx, |f| f.column, num_0);
.build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.str") exn.set(ctx, |f| f.function, empty_str);
.unwrap(); exn.set(ctx, |f| f.filename, empty_str);
ctx.builder.build_store(ptr, empty_string.unwrap()).unwrap();
} Ok(Some(exn.value.into()))
// set ints to zero
for i in &[2, 3] {
let ptr = ctx
.builder
.build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.ints")
.unwrap();
ctx.builder.build_store(ptr, zero).unwrap();
}
}
Ok(Some(zelf.into()))
} }
/// Generates IR for a `raise` statement. /// Generates IR for a `raise` statement.
@ -1315,43 +1309,27 @@ pub fn exn_constructor<'ctx>(
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>( pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
exception: Option<&BasicValueEnum<'ctx>>, exception: Option<Instance<'ctx, Ptr<Struct<Exception>>>>,
loc: Location, loc: Location,
) { ) {
if let Some(exception) = exception { if let Some(exn) = exception {
unsafe { let filename = loc.file.0;
let int32 = ctx.ctx.i32_type(); let filename = ctx.gen_string(generator, filename);
let zero = int32.const_zero(); exn.set(ctx, |f| f.filename, filename);
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();
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); let row = Int(Int32).const_int(generator, ctx.ctx, loc.row as u64);
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap()); exn.set(ctx, |f| f.line, row);
let name_ptr = ctx
.builder let column = Int(Int32).const_int(generator, ctx.ctx, loc.column as u64);
.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr") exn.set(ctx, |f| f.column, column);
.unwrap();
ctx.builder.build_store(name_ptr, fun_name).unwrap(); let current_fn = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
} let current_fn_name = current_fn.get_name().to_str().unwrap();
let current_fn_name = ctx.gen_string(generator, current_fn_name);
exn.set(ctx, |f| f.function, current_fn_name);
let raise = get_builtins(generator, ctx, "__nac3_raise"); let raise = get_builtins(generator, ctx, "__nac3_raise");
let exception = *exception; ctx.build_call_or_invoke(raise, &[exn.value.into()], "raise");
ctx.build_call_or_invoke(raise, &[exception], "raise");
} else { } else {
let resume = get_builtins(generator, ctx, "__nac3_resume"); let resume = get_builtins(generator, ctx, "__nac3_resume");
ctx.build_call_or_invoke(resume, &[], "resume"); ctx.build_call_or_invoke(resume, &[], "resume");
@ -1817,7 +1795,9 @@ pub fn gen_stmt<G: CodeGenerator>(
} else { } else {
return Ok(()); return Ok(());
}; };
gen_raise(generator, ctx, Some(&exc), stmt.location);
let exc = Ptr(Struct(Exception)).check_value(generator, ctx.ctx, exc).unwrap();
gen_raise(generator, ctx, Some(exc), stmt.location);
} else { } else {
gen_raise(generator, ctx, None, stmt.location); gen_raise(generator, ctx, None, stmt.location);
} }
@ -1830,13 +1810,19 @@ pub fn gen_stmt<G: CodeGenerator>(
}; };
let err_msg = match msg { let err_msg = match msg {
Some(msg) => { Some(msg) => {
if let Some(v) = generator.gen_expr(ctx, msg)? { let msg_ty = msg.custom.unwrap();
v.to_basic_value_enum(ctx, generator, msg.custom.unwrap())? if let Some(msg) = generator.gen_expr(ctx, msg)? {
let msg = msg.to_basic_value_enum(ctx, generator, msg_ty)?;
let msg = str_model().check_value(generator, ctx.ctx, msg).unwrap();
msg
} else { } else {
return Ok(()); return Ok(());
} }
} }
None => ctx.gen_string(generator, "").into(), None => {
// Return an empty string.
ctx.gen_string(generator, "")
}
}; };
ctx.make_assert_impl( ctx.make_assert_impl(
generator, generator,