forked from M-Labs/nac3
core/model: add and use CSlice and Exception
This commit is contained in:
parent
5b185792cb
commit
ff60343787
|
@ -43,6 +43,12 @@ use nac3parser::ast::{
|
||||||
use std::iter::{repeat, repeat_with};
|
use std::iter::{repeat, repeat_with};
|
||||||
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
|
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
|
||||||
|
|
||||||
|
use super::structure::cslice::CSlice;
|
||||||
|
use super::{
|
||||||
|
model::*,
|
||||||
|
structure::exception::{Exception, ExceptionId},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn get_subst_key(
|
pub fn get_subst_key(
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
obj: Option<Type>,
|
obj: Option<Type>,
|
||||||
|
@ -284,24 +290,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Constant::Str(v) => {
|
Constant::Str(s) => Some(self.gen_string(generator, s).value.into()),
|
||||||
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::Ellipsis => {
|
Constant::Ellipsis => {
|
||||||
let msg = self.gen_string(generator, "NotImplementedError");
|
let msg = self.gen_string(generator, "NotImplementedError");
|
||||||
|
|
||||||
|
@ -568,96 +557,114 @@ 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) -> BasicValueEnum<'ctx>
|
pub fn gen_string<G>(&mut self, generator: &mut G, string: &str) -> Struct<'ctx, CSlice>
|
||||||
where
|
where
|
||||||
G: CodeGenerator + ?Sized,
|
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 type_context = generator.type_context(self.ctx);
|
||||||
|
let sizet_model = IntModel(SizeT);
|
||||||
|
let pbyte_model = PtrModel(IntModel(Byte));
|
||||||
|
let cslice_model = StructModel(CSlice);
|
||||||
|
|
||||||
|
let base = self.builder.build_global_string_ptr(string, "constant_string").unwrap();
|
||||||
|
let base = pbyte_model.believe_value(base.as_pointer_value());
|
||||||
|
|
||||||
|
let len = sizet_model.constant(type_context, self.ctx, string.len() as u64);
|
||||||
|
|
||||||
|
let cslice = cslice_model.create_const(type_context, self, base, len);
|
||||||
|
|
||||||
|
self.const_strings.insert(string.to_owned(), cslice);
|
||||||
|
|
||||||
|
cslice
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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: Struct<'ctx, CSlice>,
|
||||||
params: [Option<IntValue<'ctx>>; 3],
|
params: [Option<Int<'ctx, Int64>>; 3],
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) {
|
) {
|
||||||
let zelf = if let Some(exception_val) = self.exception_val {
|
let type_context = generator.type_context(self.ctx);
|
||||||
exception_val
|
let exn_model = StructModel(Exception);
|
||||||
} else {
|
let exn_id_model = IntModel(ExceptionId::default());
|
||||||
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 exn_id =
|
||||||
let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
|
exn_id_model.constant(type_context, self.ctx, self.resolver.get_string_id(name) as u64);
|
||||||
*self.exception_val.insert(zelf)
|
let exn = self.exception_val.unwrap_or_else(|| {
|
||||||
};
|
let exn = exn_model.var_alloca(generator, self, Some("exn")).unwrap();
|
||||||
let int32 = self.ctx.i32_type();
|
*self.exception_val.insert(exn)
|
||||||
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()
|
|
||||||
});
|
});
|
||||||
self.builder.build_store(ptr, val).unwrap();
|
|
||||||
|
exn.gep(self, |f| f.id).store(self, exn_id);
|
||||||
|
exn.gep(self, |f| f.msg).store(self, msg);
|
||||||
|
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>(
|
pub fn make_assert<G: CodeGenerator + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
cond: IntValue<'ctx>,
|
cond: IntValue<'ctx>, // IntType can have arbitrary bit width
|
||||||
err_name: &str,
|
err_name: &str,
|
||||||
err_msg: &str,
|
err_msg: &str,
|
||||||
params: [Option<IntValue<'ctx>>; 3],
|
params: [Option<IntValue<'ctx>>; 3],
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) {
|
) {
|
||||||
|
let type_context = generator.type_context(self.ctx);
|
||||||
|
let param_model = IntModel(Int64);
|
||||||
|
|
||||||
let err_msg = self.gen_string(generator, err_msg);
|
let err_msg = self.gen_string(generator, err_msg);
|
||||||
|
|
||||||
|
let ctx = self.ctx;
|
||||||
|
let params =
|
||||||
|
params.map(|p| p.map(|p| param_model.check_value(type_context, ctx, p).unwrap()));
|
||||||
|
|
||||||
self.make_assert_impl(generator, cond, err_name, err_msg, 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>(
|
||||||
&mut self,
|
&mut self,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
cond: IntValue<'ctx>,
|
cond: IntValue<'ctx>, // IntType can have arbitrary bit width
|
||||||
err_name: &str,
|
err_name: &str,
|
||||||
err_msg: BasicValueEnum<'ctx>,
|
err_msg: Struct<'ctx, CSlice>,
|
||||||
params: [Option<IntValue<'ctx>>; 3],
|
params: [Option<Int<'ctx, Int64>>; 3],
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) {
|
) {
|
||||||
let i1 = self.ctx.bool_type();
|
let type_context = generator.type_context(self.ctx);
|
||||||
let i1_true = i1.const_all_ones();
|
let bool_model = IntModel(Bool);
|
||||||
// we assume that the condition is most probably true, so the normal path is the most
|
|
||||||
// probable path
|
// We assume that the condition is most probably true, so the normal path is the most
|
||||||
// even if this assumption is violated, it does not matter as exception unwinding is
|
// probable path even if this assumption is violated, it does not matter as exception unwinding is.
|
||||||
// slow anyway...
|
let cond = call_expect(
|
||||||
let cond = call_expect(self, cond, i1_true, Some("expect"));
|
self,
|
||||||
|
generator.bool_to_i1(self, cond),
|
||||||
|
bool_model.const_true(type_context, self.ctx).value,
|
||||||
|
Some("expect"),
|
||||||
|
);
|
||||||
|
|
||||||
let current_bb = self.builder.get_insert_block().unwrap();
|
let current_bb = self.builder.get_insert_block().unwrap();
|
||||||
let current_fun = current_bb.get_parent().unwrap();
|
let current_fun = current_bb.get_parent().unwrap();
|
||||||
|
|
||||||
let then_block = self.ctx.insert_basic_block_after(current_bb, "succ");
|
let then_block = self.ctx.insert_basic_block_after(current_bb, "succ");
|
||||||
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
|
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
|
||||||
|
|
||||||
self.builder.build_conditional_branch(cond, then_block, exn_block).unwrap();
|
self.builder.build_conditional_branch(cond, then_block, exn_block).unwrap();
|
||||||
|
|
||||||
|
// Inserting into `exn_block`
|
||||||
self.builder.position_at_end(exn_block);
|
self.builder.position_at_end(exn_block);
|
||||||
self.raise_exn(generator, err_name, err_msg, params, loc);
|
self.raise_exn(generator, err_name, err_msg, params, loc);
|
||||||
|
|
||||||
|
// Continuation
|
||||||
self.builder.position_at_end(then_block);
|
self.builder.position_at_end(then_block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::typecheck::typedef::Type;
|
||||||
|
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
use super::model::*;
|
||||||
use super::{
|
use super::{
|
||||||
classes::{
|
classes::{
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue,
|
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue,
|
||||||
|
@ -416,14 +417,29 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1").unwrap();
|
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1").unwrap();
|
||||||
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond").unwrap();
|
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond").unwrap();
|
||||||
|
|
||||||
|
// TODO: Temporary fix. Rewrite `list_slice_assignment` later
|
||||||
|
// Exception params should have been i64
|
||||||
|
{
|
||||||
|
let type_context = generator.type_context(ctx.ctx);
|
||||||
|
let param_model = IntModel(Int64);
|
||||||
|
|
||||||
|
let src_slice_len =
|
||||||
|
param_model.s_extend_or_bit_cast(type_context, ctx, src_slice_len, "src_slice_len");
|
||||||
|
let dest_slice_len =
|
||||||
|
param_model.s_extend_or_bit_cast(type_context, ctx, dest_slice_len, "dest_slice_len");
|
||||||
|
let dest_idx_2 =
|
||||||
|
param_model.s_extend_or_bit_cast(type_context, ctx, dest_idx.2, "dest_idx_2");
|
||||||
|
|
||||||
ctx.make_assert(
|
ctx.make_assert(
|
||||||
generator,
|
generator,
|
||||||
cond,
|
cond,
|
||||||
"0:ValueError",
|
"0:ValueError",
|
||||||
"attempt to assign sequence of size {0} to slice of size {1} with step size {2}",
|
"attempt to assign sequence of size {0} to slice of size {1} with step size {2}",
|
||||||
[Some(src_slice_len), Some(dest_slice_len), Some(dest_idx.2)],
|
[Some(src_slice_len.value), Some(dest_slice_len.value), Some(dest_idx_2.value)],
|
||||||
ctx.current_loc,
|
ctx.current_loc,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let new_len = {
|
let new_len = {
|
||||||
let args = vec![
|
let args = vec![
|
||||||
|
|
|
@ -24,6 +24,7 @@ use inkwell::{
|
||||||
AddressSpace, IntPredicate, OptimizationLevel,
|
AddressSpace, IntPredicate, OptimizationLevel,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use model::*;
|
||||||
use nac3parser::ast::{Location, Stmt, StrRef};
|
use nac3parser::ast::{Location, Stmt, StrRef};
|
||||||
use parking_lot::{Condvar, Mutex};
|
use parking_lot::{Condvar, Mutex};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -32,6 +33,7 @@ use std::sync::{
|
||||||
Arc,
|
Arc,
|
||||||
};
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use structure::{cslice::CSlice, exception::Exception};
|
||||||
|
|
||||||
pub mod builtin_fns;
|
pub mod builtin_fns;
|
||||||
pub mod classes;
|
pub mod classes;
|
||||||
|
@ -44,6 +46,7 @@ pub mod llvm_intrinsics;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod numpy;
|
pub mod numpy;
|
||||||
pub mod stmt;
|
pub mod stmt;
|
||||||
|
pub mod structure;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
@ -169,11 +172,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, Struct<'ctx, CSlice>>,
|
||||||
|
|
||||||
/// [`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<Ptr<'ctx, StructModel<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.
|
||||||
|
@ -701,43 +704,20 @@ pub fn gen_func_impl<
|
||||||
..primitives
|
..primitives
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut type_cache: HashMap<_, _> = [
|
let type_context = generator.type_context(context);
|
||||||
|
let cslice_model = StructModel(CSlice);
|
||||||
|
let pexn_model = PtrModel(StructModel(Exception));
|
||||||
|
|
||||||
|
let mut type_cache: HashMap<_, BasicTypeEnum<'ctx>> = [
|
||||||
(primitives.int32, context.i32_type().into()),
|
(primitives.int32, context.i32_type().into()),
|
||||||
(primitives.int64, context.i64_type().into()),
|
(primitives.int64, context.i64_type().into()),
|
||||||
(primitives.uint32, context.i32_type().into()),
|
(primitives.uint32, context.i32_type().into()),
|
||||||
(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, cslice_model.get_type(type_context, 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, RangeType::new(context).as_base_type().into()),
|
(primitives.range, RangeType::new(context).as_base_type().into()),
|
||||||
(primitives.exception, {
|
(primitives.exception, pexn_model.get_type(type_context, 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()
|
||||||
|
|
|
@ -257,7 +257,7 @@ fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
||||||
ctx.ctx.bool_type().const_zero().into()
|
ctx.ctx.bool_type().const_zero().into()
|
||||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||||
ctx.gen_string(generator, "")
|
ctx.gen_string(generator, "").value.into()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
} else if ctx.unifier.unioned(elem_ty, 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(elem_ty, ctx.primitives.str) {
|
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||||
ctx.gen_string(generator, "1")
|
ctx.gen_string(generator, "1").value.into()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
use super::model::*;
|
||||||
|
use super::structure::cslice::CSlice;
|
||||||
use super::{
|
use super::{
|
||||||
super::symbol_resolver::ValueEnum,
|
super::symbol_resolver::ValueEnum,
|
||||||
expr::destructure_range,
|
expr::destructure_range,
|
||||||
irrt::{handle_slice_indices, list_slice_assignment},
|
irrt::{handle_slice_indices, list_slice_assignment},
|
||||||
CodeGenContext, CodeGenerator,
|
structure::exception::Exception,
|
||||||
|
CodeGenContext, CodeGenerator, Int32, IntModel, Ptr, StructModel,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
|
@ -1259,47 +1262,37 @@ 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<Ptr<'ctx, StructModel<Exception>>>,
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) {
|
) {
|
||||||
if let Some(exception) = exception {
|
if let Some(pexn) = exception {
|
||||||
unsafe {
|
let type_context = generator.type_context(ctx.ctx);
|
||||||
let int32 = ctx.ctx.i32_type();
|
let i32_model = IntModel(Int32);
|
||||||
let zero = int32.const_zero();
|
let cslice_model = StructModel(CSlice);
|
||||||
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();
|
// Get and store filename
|
||||||
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
|
let filename = loc.file.0;
|
||||||
let name_ptr = ctx
|
let filename = ctx.gen_string(generator, &String::from(filename)).value;
|
||||||
.builder
|
let filename = cslice_model.check_value(type_context, ctx.ctx, filename).unwrap();
|
||||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr")
|
pexn.gep(ctx, |f| f.filename).store(ctx, filename);
|
||||||
.unwrap();
|
|
||||||
ctx.builder.build_store(name_ptr, fun_name).unwrap();
|
let row = i32_model.constant(type_context, ctx.ctx, loc.row as u64);
|
||||||
}
|
pexn.gep(ctx, |f| f.line).store(ctx, row);
|
||||||
|
|
||||||
|
let column = i32_model.constant(type_context, 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).store(ctx, 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, &[pexn.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");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.builder.build_unreachable().unwrap();
|
ctx.builder.build_unreachable().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1761,30 +1754,43 @@ pub fn gen_stmt<G: CodeGenerator>(
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
|
||||||
|
let type_context = generator.type_context(ctx.ctx);
|
||||||
|
let pexn_model = PtrModel(StructModel(Exception));
|
||||||
|
let exn = pexn_model.check_value(type_context, ctx.ctx, exc).unwrap();
|
||||||
|
|
||||||
|
gen_raise(generator, ctx, Some(exn), stmt.location);
|
||||||
} else {
|
} else {
|
||||||
gen_raise(generator, ctx, None, stmt.location);
|
gen_raise(generator, ctx, None, stmt.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Assert { test, msg, .. } => {
|
StmtKind::Assert { test, msg, .. } => {
|
||||||
let test = if let Some(v) = generator.gen_expr(ctx, test)? {
|
let type_context = generator.type_context(ctx.ctx);
|
||||||
v.to_basic_value_enum(ctx, generator, test.custom.unwrap())?
|
let byte_model = IntModel(Byte);
|
||||||
} else {
|
let cslice_model = StructModel(CSlice);
|
||||||
|
|
||||||
|
let Some(test) = generator.gen_expr(ctx, test)? else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
let test = test.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?;
|
||||||
|
let test = byte_model.check_value(type_context, ctx.ctx, test).unwrap(); // Python `bool` is represented as `i8` in nac3core
|
||||||
|
|
||||||
|
// Check `msg`
|
||||||
let err_msg = match msg {
|
let err_msg = match msg {
|
||||||
Some(msg) => {
|
Some(msg) => {
|
||||||
if let Some(v) = generator.gen_expr(ctx, msg)? {
|
let Some(msg) = generator.gen_expr(ctx, msg)? else {
|
||||||
v.to_basic_value_enum(ctx, generator, msg.custom.unwrap())?
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let msg = msg.to_basic_value_enum(ctx, generator, ctx.primitives.str)?;
|
||||||
|
cslice_model.check_value(type_context, ctx.ctx, msg).unwrap()
|
||||||
}
|
}
|
||||||
None => ctx.gen_string(generator, ""),
|
None => ctx.gen_string(generator, ""),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.make_assert_impl(
|
ctx.make_assert_impl(
|
||||||
generator,
|
generator,
|
||||||
generator.bool_to_i1(ctx, test.into_int_value()),
|
test.value,
|
||||||
"0:AssertionError",
|
"0:AssertionError",
|
||||||
err_msg,
|
err_msg,
|
||||||
[None, None, None],
|
[None, None, None],
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::codegen::{model::*, CodeGenContext};
|
||||||
|
|
||||||
|
/// Fields of [`CSlice<'ctx>`].
|
||||||
|
pub struct CSliceFields<'ctx, F: FieldTraversal<'ctx>> {
|
||||||
|
/// Pointer to data.
|
||||||
|
pub base: F::Out<PtrModel<IntModel<Byte>>>,
|
||||||
|
/// Number of bytes of data.
|
||||||
|
pub len: F::Out<IntModel<SizeT>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See <https://crates.io/crates/cslice>.
|
||||||
|
///
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
impl<'ctx> StructKind<'ctx> for CSlice {
|
||||||
|
type Fields<F: FieldTraversal<'ctx>> = CSliceFields<'ctx, F>;
|
||||||
|
|
||||||
|
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
|
||||||
|
Self::Fields { base: traversal.add_auto("base"), len: traversal.add_auto("len") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructModel<CSlice> {
|
||||||
|
/// Create a [`CSlice`].
|
||||||
|
///
|
||||||
|
/// `base` and `len` must be LLVM global constants.
|
||||||
|
pub fn create_const<'ctx>(
|
||||||
|
&self,
|
||||||
|
type_context: TypeContext<'ctx>,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
base: Ptr<'ctx, IntModel<Byte>>,
|
||||||
|
len: Int<'ctx, SizeT>,
|
||||||
|
) -> Struct<'ctx, CSlice> {
|
||||||
|
let value = self
|
||||||
|
.0
|
||||||
|
.get_struct_type(type_context, ctx.ctx)
|
||||||
|
.const_named_struct(&[base.value.into(), len.value.into()]);
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
use crate::codegen::model::*;
|
||||||
|
|
||||||
|
use super::cslice::CSlice;
|
||||||
|
|
||||||
|
/// The LLVM int type of an Exception ID.
|
||||||
|
pub type ExceptionId = Int32;
|
||||||
|
|
||||||
|
/// 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>> {
|
||||||
|
/// nac3core's ID of the exception
|
||||||
|
pub id: F::Out<IntModel<ExceptionId>>,
|
||||||
|
/// The name of the file this `Exception` was raised in.
|
||||||
|
pub filename: F::Out<StructModel<CSlice>>,
|
||||||
|
/// The line number in the file this `Exception` was raised in.
|
||||||
|
pub line: F::Out<IntModel<Int32>>,
|
||||||
|
/// The column number in the file this `Exception` was raised in.
|
||||||
|
pub column: F::Out<IntModel<Int32>>,
|
||||||
|
/// The name of the Python function this `Exception` was raised in.
|
||||||
|
pub function: F::Out<StructModel<CSlice>>,
|
||||||
|
/// The message of this Exception.
|
||||||
|
///
|
||||||
|
/// The message can optionally contain integer parameters `{0}`, `{1}`, and `{2}` in its string,
|
||||||
|
/// where they will be substituted by `params[0]`, `params[1]`, and `params[2]` respectively (as `int64_t`s).
|
||||||
|
/// Here is an example:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// "Index {0} is out of bounds! List only has {1} element(s)."
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In this case, `params[0]` and `params[1]` must be specified, and `params[2]` is ***unused***.
|
||||||
|
/// Having only 3 parameters is a constraint in ARTIQ.
|
||||||
|
pub msg: F::Out<StructModel<CSlice>>,
|
||||||
|
pub params: [F::Out<IntModel<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]"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod cslice;
|
||||||
|
pub mod exception;
|
Loading…
Reference in New Issue