diff --git a/nac3core/irrt/irrt/error_context.hpp b/nac3core/irrt/irrt/error_context.hpp new file mode 100644 index 00000000..34de06d2 --- /dev/null +++ b/nac3core/irrt/irrt/error_context.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +namespace { +// nac3core's "str" struct type definition +template +struct Str { + const char* content; + SizeT length; +}; + +// A limited set of errors IRRT could use. +typedef uint32_t ErrorId; +struct ErrorIds { + ErrorId index_error; + ErrorId value_error; + ErrorId assertion_error; + ErrorId runtime_error; +}; + +struct ErrorContext { + // Context + ErrorIds* error_ids; + + // Error thrown by IRRT + ErrorId error_id; + const char* message_template; // MUST BE `&'static` + uint64_t param1; + uint64_t param2; + uint64_t param3; + + void initialize(ErrorIds* error_ids) { + this->error_ids = error_ids; + clear_error(); + } + + void clear_error() { + // Point the message_template to an empty str. Don't set it to nullptr as a sentinel + this->message_template = ""; + } + + void set_error(ErrorId error_id, const char* message, uint64_t param1 = 0, uint64_t param2 = 0, uint64_t param3 = 0) { + this->error_id = error_id; + this->message_template = message; + this->param1 = param1; + this->param2 = param2; + this->param3 = param3; + } + + bool has_error() { + return !cstr_utils::is_empty(message_template); + } + + template + void get_error_str(Str *dst_str) { + dst_str->content = message_template; + dst_str->length = (SizeT) cstr_utils::length(message_template); + } +}; +} + +extern "C" { +void __nac3_error_context_initialize(ErrorContext* errctx, ErrorIds* error_ids) { + errctx->initialize(error_ids); +} + +bool __nac3_error_context_has_no_error(ErrorContext* errctx) { + return !errctx->has_error(); +} + +void __nac3_error_context_get_error_str(ErrorContext* errctx, Str *dst_str) { + errctx->get_error_str(dst_str); +} + +void __nac3_error_context_get_error_str64(ErrorContext* errctx, Str *dst_str) { + errctx->get_error_str(dst_str); +} + +void __nac3_error_dummy_raise(ErrorContext* errctx) { + errctx->set_error(errctx->error_ids->runtime_error, "THROWN FROM __nac3_error_dummy_raise!!!!!!"); +} +} \ No newline at end of file diff --git a/nac3core/irrt/irrt/utils.hpp b/nac3core/irrt/irrt/utils.hpp index 6735f36b..fdf14fba 100644 --- a/nac3core/irrt/irrt/utils.hpp +++ b/nac3core/irrt/irrt/utils.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace { template const T& max(const T& a, const T& b) { @@ -18,4 +20,59 @@ bool arrays_match(int len, T* as, T* bs) { } return true; } + +namespace cstr_utils { + bool is_empty(const char* str) { + return str[0] == '\0'; + } + + int8_t compare(const char* a, const char* b) { + uint32_t i = 0; + while (true) { + if (a[i] < b[i]) { + return -1; + } else if (a[i] > b[i]) { + return 1; + } else { // a[i] == b[i] + if (a[i] == '\0') { + return 0; + } else { + i++; + } + } + } + } + + int8_t equal(const char* a, const char* b) { + return compare(a, b) == 0; + } + + uint32_t length(const char* str) { + uint32_t length = 0; + while (*str != '\0') { + length++; + str++; + } + return length; + } + + bool copy(const char* src, char* dst, uint32_t dst_max_size) { + for (uint32_t i = 0; i < dst_max_size; i++) { + bool is_last = i + 1 == dst_max_size; + if (is_last && src[i] != '\0') { + dst[i] = '\0'; + return false; + } + + if (src[i] == '\0') { + dst[i] = '\0'; + return true; + } + + dst[i] = src[i]; + } + + __builtin_unreachable(); + } +} } \ No newline at end of file diff --git a/nac3core/irrt/irrt_everything.hpp b/nac3core/irrt/irrt_everything.hpp index a1c45e1e..d0480a5b 100644 --- a/nac3core/irrt/irrt_everything.hpp +++ b/nac3core/irrt/irrt_everything.hpp @@ -2,4 +2,5 @@ #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index c42c8444..c720470a 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -576,6 +576,21 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { params: [Option>; 3], loc: Location, ) { + let error_id = self.resolver.get_string_id(name); + let error_id = self.ctx.i32_type().const_int(error_id as u64, false); + self.raise_exn_by_id(generator, error_id, msg, params, loc); + } + + pub fn raise_exn_by_id( + &mut self, + generator: &mut G, + error_id: IntValue<'ctx>, + msg: BasicValueEnum<'ctx>, + params: [Option>; 3], + loc: Location, + ) { + assert_eq!(error_id.get_type().get_bit_width(), 32); + let zelf = if let Some(exception_val) = self.exception_val { exception_val } else { @@ -588,8 +603,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { 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(); + self.builder.build_store(id_ptr, error_id).unwrap(); let ptr = self .builder .build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") @@ -652,6 +666,32 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { self.raise_exn(generator, err_name, err_msg, params, loc); self.builder.position_at_end(then_block); } + + pub fn make_assert_impl_by_id( + &mut self, + generator: &mut G, + cond: IntValue<'ctx>, + err_id: IntValue<'ctx>, + err_msg: BasicValueEnum<'ctx>, + params: [Option>; 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")); + 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(); + self.builder.position_at_end(exn_block); + self.raise_exn_by_id(generator, err_id, err_msg, params, loc); + self.builder.position_at_end(then_block); + } } /// See [`CodeGenerator::gen_constructor`]. diff --git a/nac3core/src/codegen/irrt/error_context.rs b/nac3core/src/codegen/irrt/error_context.rs index aca77c3e..5a6fdc83 100644 --- a/nac3core/src/codegen/irrt/error_context.rs +++ b/nac3core/src/codegen/irrt/error_context.rs @@ -1,13 +1,15 @@ -use crate::codegen::model::*; +use crate::codegen::{model::*, CodeGenContext, CodeGenerator}; + +use super::util::{get_sized_dependent_function_name, FunctionBuilder}; pub struct StrFields<'ctx> { - content: Field>>, - length: Field>, + pub content: Field>>, + pub length: Field>, } #[derive(Debug, Clone)] pub struct Str<'ctx> { - sizet: IntModel<'ctx>, + pub sizet: IntModel<'ctx>, } impl<'ctx> IsStruct<'ctx> for Str<'ctx> { @@ -27,10 +29,10 @@ impl<'ctx> IsStruct<'ctx> for Str<'ctx> { type ErrorId = Int32; pub struct ErrorIdsFields { - index_error: Field>, - value_error: Field>, - assertion_error: Field>, - runtime_error: Field>, + pub index_error: Field>, + pub value_error: Field>, + pub assertion_error: Field>, + pub runtime_error: Field>, } #[derive(Debug, Clone)] pub struct ErrorIds; @@ -53,11 +55,11 @@ impl<'ctx> IsStruct<'ctx> for ErrorIds { } pub struct ErrorContextFields { - error_id: Field>, - message_template: Field>>, - param1: Field>, - param2: Field>, - param3: Field>, + pub error_id: Field>, + pub message_template: Field>>, + pub param1: Field>, + pub param2: Field>, + pub param3: Field>, } #[derive(Debug, Clone)] pub struct ErrorContext; @@ -79,3 +81,102 @@ impl<'ctx> IsStruct<'ctx> for ErrorContext { } } } + +// Prepare ErrorIds +fn build_error_ids<'ctx>(ctx: &CodeGenContext<'ctx, '_>) -> Pointer<'ctx, StructModel> { + // ErrorIdsLens.get_fields(ctx.ctx).assertion_error. + let error_ids = StructModel(ErrorIds).alloca(ctx, "error_ids"); + let i32_model = FixedIntModel(Int32); + // i32_model.make_constant() + + let get_string_id = + |string_id| i32_model.constant(ctx.ctx, ctx.resolver.get_string_id(string_id) as u64); + + error_ids.gep(ctx, |f| f.index_error).store(ctx, &get_string_id("0:IndexError")); + error_ids.gep(ctx, |f| f.value_error).store(ctx, &get_string_id("0:ValueError")); + error_ids.gep(ctx, |f| f.assertion_error).store(ctx, &get_string_id("0:AssertionError")); + error_ids.gep(ctx, |f| f.runtime_error).store(ctx, &get_string_id("0:RuntimeError")); + + error_ids +} + +pub fn call_nac3_error_context_initialize<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + perrctx: &Pointer<'ctx, StructModel>, + perror_ids: &Pointer<'ctx, StructModel>, +) { + FunctionBuilder::begin(ctx, "__nac3_error_context_initialize") + .arg("errctx", &PointerModel(StructModel(ErrorContext)), perrctx) + .arg("error_ids", &PointerModel(StructModel(ErrorIds)), perror_ids) + .returning_void(); +} + +pub fn call_nac3_error_context_has_no_error<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + errctx: &Pointer<'ctx, StructModel>, +) -> FixedInt<'ctx, Bool> { + FunctionBuilder::begin(ctx, "__nac3_error_context_has_no_error") + .arg("errctx", &PointerModel(StructModel(ErrorContext)), errctx) + .returning("has_error", &FixedIntModel(Bool)) +} + +pub fn call_nac3_error_context_get_error_str<'ctx>( + sizet: IntModel<'ctx>, + ctx: &CodeGenContext<'ctx, '_>, + errctx: &Pointer<'ctx, StructModel>, + dst_str: &Pointer<'ctx, StructModel>>, +) { + FunctionBuilder::begin( + ctx, + &get_sized_dependent_function_name(sizet, "__nac3_error_context_get_error_str"), + ) + .arg("errctx", &PointerModel(StructModel(ErrorContext)), errctx) + .arg("dst_str", &PointerModel(StructModel(Str { sizet })), dst_str) + .returning_void(); +} + +pub fn prepare_error_context<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, +) -> Pointer<'ctx, StructModel> { + let error_ids = build_error_ids(ctx); + let errctx_ptr = StructModel(ErrorContext).alloca(ctx, "errctx"); + call_nac3_error_context_initialize(ctx, &errctx_ptr, &error_ids); + errctx_ptr +} + +pub fn check_error_context<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + errctx_ptr: &Pointer<'ctx, StructModel>, +) { + let sizet = IntModel(generator.get_size_type(ctx.ctx)); + + let has_error = call_nac3_error_context_has_no_error(ctx, errctx_ptr); + let pstr = StructModel(Str { sizet }).alloca(ctx, "error_str"); + call_nac3_error_context_get_error_str(sizet, ctx, errctx_ptr, &pstr); + + let error_id = errctx_ptr.gep(ctx, |f| f.error_id).load(ctx, "error_id"); + let error_str = pstr.load(ctx, "error_str"); + let param1 = errctx_ptr.gep(ctx, |f| f.param1).load(ctx, "param1"); + let param2 = errctx_ptr.gep(ctx, |f| f.param2).load(ctx, "param2"); + let param3 = errctx_ptr.gep(ctx, |f| f.param3).load(ctx, "param3"); + ctx.make_assert_impl_by_id( + generator, + has_error.value, + error_id.value, + error_str.get_llvm_value(), + [Some(param1.value), Some(param2.value), Some(param3.value)], + ctx.current_loc, + ); +} + +pub fn call_nac3_dummy_raise( + generator: &mut G, + ctx: &mut CodeGenContext, +) { + let errctx = prepare_error_context(ctx); + FunctionBuilder::begin(ctx, "__nac3_error_dummy_raise") + .arg("errctx", &PointerModel(StructModel(ErrorContext)), &errctx) + .returning_void(); + check_error_context(generator, ctx, &errctx); +} diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index dfb91611..280c7ff1 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -1,6 +1,8 @@ use crate::typecheck::typedef::Type; +pub mod error_context; mod test; +mod util; use super::{ classes::{ diff --git a/nac3core/src/codegen/irrt/util.rs b/nac3core/src/codegen/irrt/util.rs index 4ca8882c..0716cece 100644 --- a/nac3core/src/codegen/irrt/util.rs +++ b/nac3core/src/codegen/irrt/util.rs @@ -17,9 +17,9 @@ fn get_size_variant(ty: IntType) -> SizeVariant { } #[must_use] -pub fn get_sized_dependent_function_name(ty: IntType, fn_name: &str) -> String { +pub fn get_sized_dependent_function_name(ty: IntModel, fn_name: &str) -> String { let mut fn_name = fn_name.to_owned(); - match get_size_variant(ty) { + match get_size_variant(ty.0) { SizeVariant::Bits32 => { // Do nothing, `fn_name` already has the correct name } @@ -51,7 +51,7 @@ impl<'ctx, 'a> FunctionBuilder<'ctx, 'a> { self } - pub fn returning>(self, name: &'static str, return_model: &M) -> S::MemoryValue { + pub fn returning>(self, name: &'static str, return_model: &M) -> M::Value { let (param_tys, param_vals): (Vec<_>, Vec<_>) = self.arguments.into_iter().unzip(); let function = self.ctx.module.get_function(self.fn_name).unwrap_or_else(|| { diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 85b963bb..36741905 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -23,7 +23,9 @@ use inkwell::{ values::{BasicValueEnum, FunctionValue, IntValue, PhiValue, PointerValue}, AddressSpace, IntPredicate, OptimizationLevel, }; +use irrt::error_context::Str; use itertools::Itertools; +use model::{IntModel, Model, StructModel}; use nac3parser::ast::{Location, Stmt, StrRef}; use parking_lot::{Condvar, Mutex}; use std::collections::{HashMap, HashSet}; @@ -647,7 +649,7 @@ pub fn gen_func_impl< ..primitives }; - let mut type_cache: HashMap<_, _> = [ + let mut type_cache: HashMap<_, BasicTypeEnum<'_>> = [ (primitives.int32, context.i32_type().into()), (primitives.int64, context.i64_type().into()), (primitives.uint32, context.i32_type().into()), @@ -655,19 +657,8 @@ pub fn gen_func_impl< (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(), - } + let sizet = IntModel(generator.get_size_type(context)); + StructModel(Str { sizet }).get_llvm_type(context) }), (primitives.range, RangeType::new(context).as_base_type().into()), (primitives.exception, { @@ -675,10 +666,12 @@ pub fn gen_func_impl< if let Some(t) = module.get_struct_type(name) { t.ptr_type(AddressSpace::default()).as_basic_type_enum() } else { + let sizet = IntModel(generator.get_size_type(context)); + let str_ty = StructModel(Str { sizet }).get_llvm_type(context); + 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() diff --git a/nac3core/src/codegen/numpy.rs b/nac3core/src/codegen/numpy.rs index 7421c894..69a97853 100644 --- a/nac3core/src/codegen/numpy.rs +++ b/nac3core/src/codegen/numpy.rs @@ -9,7 +9,7 @@ use crate::{ irrt::{ calculate_len_for_slice_range, call_ndarray_calc_broadcast, call_ndarray_calc_broadcast_index, call_ndarray_calc_nd_indices, - call_ndarray_calc_size, + call_ndarray_calc_size, error_context::call_nac3_dummy_raise, }, llvm_intrinsics::{self, call_memcpy_generic}, stmt::{gen_for_callback_incrementing, gen_for_range_callback, gen_if_else_expr_callback}, @@ -538,6 +538,8 @@ fn call_ndarray_zeros_impl<'ctx, G: CodeGenerator + ?Sized>( elem_ty: Type, shape: BasicValueEnum<'ctx>, ) -> Result, String> { + call_nac3_dummy_raise(generator, ctx); + let supported_types = [ ctx.primitives.int32, ctx.primitives.int64,