nac3/nac3core/src/codegen/irrt/mod.rs

228 lines
8.3 KiB
Rust
Raw Normal View History

use inkwell::{
attributes::{Attribute, AttributeLoc},
context::Context,
memory_buffer::MemoryBuffer,
module::Module,
values::{BasicValue, BasicValueEnum, IntValue},
IntPredicate,
};
2022-01-09 19:55:17 +08:00
use nac3parser::ast::Expr;
use super::{CodeGenContext, CodeGenerator};
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
pub use list::*;
pub use math::*;
pub use ndarray::*;
pub use slice::*;
mod list;
mod math;
mod ndarray;
mod slice;
2023-12-08 17:43:32 +08:00
#[must_use]
pub fn load_irrt<'ctx>(ctx: &'ctx Context, symbol_resolver: &dyn SymbolResolver) -> Module<'ctx> {
let bitcode_buf = MemoryBuffer::create_from_memory_range(
include_bytes!(concat!(env!("OUT_DIR"), "/irrt.bc")),
"irrt_bitcode_buffer",
);
let irrt_mod = Module::parse_bitcode_from_buffer(&bitcode_buf, ctx).unwrap();
let inline_attr = Attribute::get_named_enum_kind_id("alwaysinline");
2022-01-09 19:55:17 +08:00
for symbol in &[
"__nac3_int_exp_int32_t",
"__nac3_int_exp_int64_t",
"__nac3_range_slice_len",
"__nac3_slice_index_bound",
] {
let function = irrt_mod.get_function(symbol).unwrap();
function.add_attribute(AttributeLoc::Function, ctx.create_enum_attribute(inline_attr, 0));
}
// Initialize all global `EXN_*` exception IDs in IRRT with the [`SymbolResolver`].
let exn_id_type = ctx.i32_type();
let errors = &[
("EXN_INDEX_ERROR", "0:IndexError"),
("EXN_VALUE_ERROR", "0:ValueError"),
("EXN_ASSERTION_ERROR", "0:AssertionError"),
("EXN_TYPE_ERROR", "0:TypeError"),
];
for (irrt_name, symbol_name) in errors {
let exn_id = symbol_resolver.get_string_id(symbol_name);
let exn_id = exn_id_type.const_int(exn_id as u64, false).as_basic_value_enum();
let global = irrt_mod.get_global(irrt_name).unwrap_or_else(|| {
panic!("Exception symbol name '{irrt_name}' should exist in the IRRT LLVM module")
});
global.set_initializer(&exn_id);
}
2022-01-09 01:05:17 +08:00
irrt_mod
}
2022-01-09 19:55:17 +08:00
/// NOTE: the output value of the end index of this function should be compared ***inclusively***,
/// because python allows `a[2::-1]`, whose semantic is `[a[2], a[1], a[0]]`, which is equivalent to
/// NO numeric slice in python.
///
/// equivalent code:
/// ```pseudo_code
/// match (start, end, step):
/// case (s, e, None | Some(step)) if step > 0:
/// return (
/// match s:
/// case None:
/// 0
/// case Some(s):
/// handle_in_bound(s)
/// ,match e:
/// case None:
/// length - 1
/// case Some(e):
/// handle_in_bound(e) - 1
/// ,step == None ? 1 : step
/// )
/// case (s, e, Some(step)) if step < 0:
/// return (
/// match s:
/// case None:
/// length - 1
/// case Some(s):
/// s = handle_in_bound(s)
/// if s == length:
/// s - 1
/// else:
/// s
/// ,match e:
/// case None:
/// 0
/// case Some(e):
/// handle_in_bound(e) + 1
/// ,step
/// )
2022-01-09 19:55:17 +08:00
/// ```
2023-12-06 11:49:02 +08:00
pub fn handle_slice_indices<'ctx, G: CodeGenerator>(
2022-01-09 19:55:17 +08:00
start: &Option<Box<Expr<Option<Type>>>>,
end: &Option<Box<Expr<Option<Type>>>>,
step: &Option<Box<Expr<Option<Type>>>>,
2023-12-06 11:49:02 +08:00
ctx: &mut CodeGenContext<'ctx, '_>,
2022-01-09 19:55:17 +08:00
generator: &mut G,
length: IntValue<'ctx>,
) -> Result<Option<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>)>, String> {
2022-01-09 19:55:17 +08:00
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let one = int32.const_int(1, false);
2024-02-19 19:30:25 +08:00
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32").unwrap();
Ok(Some(match (start, end, step) {
2022-01-09 19:55:17 +08:00
(s, e, None) => (
if let Some(s) = s.as_ref() {
match handle_slice_index_bound(s, ctx, generator, length)? {
Some(v) => v,
None => return Ok(None),
}
} else {
int32.const_zero()
},
2022-01-09 19:55:17 +08:00
{
let e = if let Some(s) = e.as_ref() {
match handle_slice_index_bound(s, ctx, generator, length)? {
Some(v) => v,
None => return Ok(None),
}
} else {
length
};
2024-02-19 19:30:25 +08:00
ctx.builder.build_int_sub(e, one, "final_end").unwrap()
2022-01-09 19:55:17 +08:00
},
one,
),
(s, e, Some(step)) => {
let step = if let Some(v) = generator.gen_expr(ctx, step)? {
v.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?.into_int_value()
} else {
2024-06-12 14:45:03 +08:00
return Ok(None);
};
// assert step != 0, throw exception if not
2024-06-12 14:45:03 +08:00
let not_zero = ctx
.builder
.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
)
.unwrap();
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"slice step cannot be zero",
[None, None, None],
ctx.current_loc,
);
2024-02-19 19:30:25 +08:00
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1").unwrap();
2024-06-12 14:45:03 +08:00
let neg = ctx
.builder
.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg")
.unwrap();
2022-01-09 19:55:17 +08:00
(
match s {
Some(s) => {
let Some(s) = handle_slice_index_bound(s, ctx, generator, length)? else {
2024-06-12 14:45:03 +08:00
return Ok(None);
};
2022-01-09 19:55:17 +08:00
ctx.builder
.build_select(
2024-06-12 14:45:03 +08:00
ctx.builder
.build_and(
ctx.builder
.build_int_compare(
IntPredicate::EQ,
s,
length,
"s_eq_len",
)
.unwrap(),
neg,
"should_minus_one",
)
.unwrap(),
2024-02-19 19:30:25 +08:00
ctx.builder.build_int_sub(s, one, "s_min").unwrap(),
2022-01-09 19:55:17 +08:00
s,
"final_start",
)
2024-02-19 19:30:25 +08:00
.map(BasicValueEnum::into_int_value)
.unwrap()
2022-01-09 19:55:17 +08:00
}
2024-06-12 14:45:03 +08:00
None => ctx
.builder
.build_select(neg, len_id, zero, "stt")
2024-02-19 19:30:25 +08:00
.map(BasicValueEnum::into_int_value)
.unwrap(),
2022-01-09 19:55:17 +08:00
},
match e {
Some(e) => {
let Some(e) = handle_slice_index_bound(e, ctx, generator, length)? else {
2024-06-12 14:45:03 +08:00
return Ok(None);
};
2022-01-09 19:55:17 +08:00
ctx.builder
.build_select(
neg,
2024-02-19 19:30:25 +08:00
ctx.builder.build_int_add(e, one, "end_add_one").unwrap(),
ctx.builder.build_int_sub(e, one, "end_sub_one").unwrap(),
2022-01-09 19:55:17 +08:00
"final_end",
)
2024-02-19 19:30:25 +08:00
.map(BasicValueEnum::into_int_value)
.unwrap()
2022-01-09 19:55:17 +08:00
}
2024-06-12 14:45:03 +08:00
None => ctx
.builder
.build_select(neg, zero, len_id, "end")
2024-02-19 19:30:25 +08:00
.map(BasicValueEnum::into_int_value)
.unwrap(),
2022-01-09 19:55:17 +08:00
},
step,
)
}
}))
2022-01-09 19:55:17 +08:00
}