forked from M-Labs/nac3
Compare commits
10 Commits
6528115d6a
...
9b02e144b5
Author | SHA1 | Date | |
---|---|---|---|
9b02e144b5 | |||
e62509ae67 | |||
58be4a8b09 | |||
52da6347ee | |||
a1410833bc | |||
b38d9000f8 | |||
a6e1d354b6 | |||
f698b0c1fa | |||
8eb9094d68 | |||
3f322de9a0 |
@ -4,7 +4,7 @@ use nac3core::{
|
||||
expr::gen_call,
|
||||
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
|
||||
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},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
@ -877,12 +877,12 @@ fn polymorphic_print<'ctx>(
|
||||
});
|
||||
|
||||
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
|
||||
.build_call(
|
||||
print_fn,
|
||||
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
|
||||
&once(fmt.value.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
@ -957,20 +957,23 @@ fn polymorphic_print<'ctx>(
|
||||
fmt.push_str("%.*s");
|
||||
|
||||
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_data =
|
||||
unsafe { false_str.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
let false_len =
|
||||
unsafe { false_str.get_field_at_index_unchecked(1) }.into_int_value();
|
||||
|
||||
let true_data = true_str.get_field(generator, ctx.ctx, |f| f.base);
|
||||
let true_len = true_str.get_field(generator, ctx.ctx, |f| f.len);
|
||||
|
||||
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());
|
||||
|
||||
args.extend([
|
||||
ctx.builder.build_select(bool_val, true_len, false_len, "").unwrap(),
|
||||
ctx.builder.build_select(bool_val, true_data, false_data, "").unwrap(),
|
||||
ctx.builder
|
||||
.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");
|
||||
}
|
||||
|
||||
let str = value.into_struct_value();
|
||||
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();
|
||||
let str = str_model().check_value(generator, ctx.ctx, value).unwrap();
|
||||
|
||||
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() => {
|
||||
|
@ -166,7 +166,7 @@ impl StaticValue for PythonValue {
|
||||
PrimitiveValue::Bool(val) => {
|
||||
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()) {
|
||||
@ -980,7 +980,7 @@ impl InnerResolver {
|
||||
} else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ {
|
||||
let val: String = obj.extract().unwrap();
|
||||
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 {
|
||||
let val: f64 = obj.extract().unwrap();
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <irrt/debug.hpp>
|
||||
#include <irrt/exception.hpp>
|
||||
#include <irrt/int_types.hpp>
|
||||
#include <irrt/slice.hpp>
|
||||
|
||||
@ -16,4 +18,42 @@ template <typename SizeT> struct List
|
||||
uint8_t *items;
|
||||
SizeT len;
|
||||
};
|
||||
|
||||
namespace list
|
||||
{
|
||||
template <typename SizeT> void range_assign(List<SizeT> *dst, SizeT itemsize, Range<SizeT> *range, List<SizeT> *src)
|
||||
{
|
||||
debug_assert(range->step != 0);
|
||||
SizeT assign_len = range->len();
|
||||
|
||||
if (assign_len < src->len)
|
||||
{
|
||||
// Encountered things like
|
||||
// ```
|
||||
// xs = [1, 2, 3, 4, 5]
|
||||
// xs[1:3] = [999, 1000, 1001, 1002] # Note that step has to be 1.
|
||||
// xs = [1, 999, 1000, 1001, 1002, 4, 5] # xs is longer
|
||||
// ```
|
||||
//
|
||||
// We do not support extending lists since that requires allocation.
|
||||
raise_exception(SizeT, EXN_VALUE_ERROR,
|
||||
"List assignment does not support list extension. Attempting to assign {0} item(s) into a "
|
||||
"space of {1} item(s).",
|
||||
src->len, assign_len, NO_PARAM);
|
||||
}
|
||||
|
||||
if (range->step == 1)
|
||||
{
|
||||
// Assigning into a contiguous region. Optimized with memmove.
|
||||
|
||||
uint8_t* p1 = dst->items + range->start * itemsize;
|
||||
uint8_t* p2 = dst->items + range->start * itemsize + assign_len * itemsize;
|
||||
|
||||
__builtin_memmove(cursor, src->items, assign_len * itemsize);
|
||||
cursor += range_len * itemsize;
|
||||
|
||||
__builtin_memmove(cursor, );
|
||||
}
|
||||
}
|
||||
} // namespace list
|
||||
} // namespace
|
@ -46,4 +46,9 @@ extern "C"
|
||||
{
|
||||
return range::len(start, stop, step);
|
||||
}
|
||||
|
||||
int64_t __nac3_range_len_i64(int64_t start, int64_t stop, int64_t step)
|
||||
{
|
||||
return range::len(start, stop, step);
|
||||
}
|
||||
}
|
@ -181,3 +181,22 @@ template <typename T> struct Slice
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void __nac3_slice_indices_i32(bool start_defined, int32_t start, bool stop_defined, int32_t stop, bool step_defined,
|
||||
int32_t step, int32_t length, int32_t *range_start, int32_t *range_stop,
|
||||
int32_t *range_step)
|
||||
{
|
||||
slice::indices(start_defined, start, stop_defined, stop, step_defined, step, length, range_start, range_stop,
|
||||
range_step);
|
||||
}
|
||||
|
||||
void __nac3_slice_indices_i64(bool start_defined, int64_t start, bool stop_defined, int64_t stop, bool step_defined,
|
||||
int64_t step, int64_t length, int64_t *range_start, int64_t *range_stop,
|
||||
int64_t *range_step)
|
||||
{
|
||||
slice::indices(start_defined, start, stop_defined, stop, step_defined, step, length, range_start, range_stop,
|
||||
range_step);
|
||||
}
|
||||
}
|
@ -12,7 +12,11 @@ use crate::{
|
||||
call_int_umin, call_memcpy_generic,
|
||||
},
|
||||
need_sret,
|
||||
object::ndarray::{NDArrayOut, ScalarOrNDArray},
|
||||
object::{
|
||||
exception::Exception,
|
||||
ndarray::{NDArrayOut, ScalarOrNDArray},
|
||||
str::str_model,
|
||||
},
|
||||
stmt::{
|
||||
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||
gen_var,
|
||||
@ -29,10 +33,7 @@ use crate::{
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
types::{AnyType, BasicType, BasicTypeEnum},
|
||||
values::{
|
||||
BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue,
|
||||
StructValue,
|
||||
},
|
||||
values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::{chain, izip, Either, Itertools};
|
||||
@ -50,6 +51,7 @@ use super::{
|
||||
any::AnyObject,
|
||||
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
|
||||
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::Double(v) => self.ctx.f64_type().const_float(*v).into(),
|
||||
SymbolValue::Str(v) => {
|
||||
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).into_struct_type();
|
||||
ty.const_named_struct(&[str_ptr, size.into()]).into()
|
||||
let string = self.gen_string(generator, v);
|
||||
string.value.into()
|
||||
}
|
||||
SymbolValue::Tuple(ls) => {
|
||||
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) => {
|
||||
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)
|
||||
}
|
||||
let string = self.gen_string(generator, v);
|
||||
Some(string.value.into())
|
||||
}
|
||||
Constant::Ellipsis => {
|
||||
let msg = self.gen_string(generator, "NotImplementedError");
|
||||
@ -332,7 +315,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
self.raise_exn(
|
||||
generator,
|
||||
"0:NotImplementedError",
|
||||
msg.into(),
|
||||
msg,
|
||||
[None, None, None],
|
||||
self.current_loc,
|
||||
);
|
||||
@ -592,60 +575,59 @@ 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) -> StructValue<'ctx>
|
||||
pub fn gen_string<G, S>(&mut self, generator: &mut G, string: S) -> Instance<'ctx, Str>
|
||||
where
|
||||
G: CodeGenerator + ?Sized,
|
||||
S: Into<String>,
|
||||
{
|
||||
self.gen_const(generator, &Constant::Str(s.into()), self.primitives.str)
|
||||
.map(BasicValueEnum::into_struct_value)
|
||||
.unwrap()
|
||||
let string = string.into();
|
||||
self.const_strings.get(&string).copied().unwrap_or_else(|| {
|
||||
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>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
name: &str,
|
||||
msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
msg: Instance<'ctx, Str>,
|
||||
params: [Option<IntValue<'ctx>>; 3], // Can have any bit-width.
|
||||
loc: Location,
|
||||
) {
|
||||
let zelf = if let Some(exception_val) = self.exception_val {
|
||||
exception_val
|
||||
let exn = if let Some(exn) = self.exception_val {
|
||||
exn
|
||||
} 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 exn = Struct(Exception).var_alloca(generator, self, Some("exn")).unwrap();
|
||||
*self.exception_val.insert(exn)
|
||||
};
|
||||
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()
|
||||
});
|
||||
self.builder.build_store(ptr, val).unwrap();
|
||||
}
|
||||
|
||||
// Set exception ID
|
||||
let id = self.resolver.get_string_id(name);
|
||||
let id = Int(Int32).const_int(generator, self.ctx, id as u64);
|
||||
exn.set(self, |f| f.id, id);
|
||||
|
||||
// Set message
|
||||
exn.set(self, |f| f.msg, msg);
|
||||
|
||||
// Set parameters.
|
||||
let num_0 = Int(Int64).const_0(generator, self.ctx);
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
// Param can be of any bit-width. We need to cast them.
|
||||
// Defaults to 0 if not provided
|
||||
let param = param
|
||||
.map_or(num_0, |param| Int(Int64).s_extend_or_bit_cast(generator, self, param));
|
||||
|
||||
exn.set(self, |f| f.params[i], param);
|
||||
}
|
||||
gen_raise(generator, self, Some(&zelf.into()), loc);
|
||||
gen_raise(generator, self, Some(exn), loc);
|
||||
}
|
||||
|
||||
pub fn make_assert<G: CodeGenerator + ?Sized>(
|
||||
@ -658,7 +640,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
loc: Location,
|
||||
) {
|
||||
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>(
|
||||
@ -666,7 +648,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
generator: &mut G,
|
||||
cond: IntValue<'ctx>,
|
||||
err_name: &str,
|
||||
err_msg: BasicValueEnum<'ctx>,
|
||||
err_msg: Instance<'ctx, Str>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
loc: Location,
|
||||
) {
|
||||
@ -2899,7 +2881,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
ctx.raise_exn(
|
||||
generator,
|
||||
"0:UnwrapNoneError",
|
||||
err_msg.into(),
|
||||
err_msg,
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
@ -3129,42 +3111,3 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
_ => unimplemented!(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Generate LLVM IR for an [`ExprKind::Slice`]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn gen_slice<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
lower: &Option<Box<Expr<Option<Type>>>>,
|
||||
upper: &Option<Box<Expr<Option<Type>>>>,
|
||||
step: &Option<Box<Expr<Option<Type>>>>,
|
||||
) -> Result<
|
||||
(
|
||||
Option<Instance<'ctx, Int<Int32>>>,
|
||||
Option<Instance<'ctx, Int<Int32>>>,
|
||||
Option<Instance<'ctx, Int<Int32>>>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
let mut help = |value_expr: &Option<Box<Expr<Option<Type>>>>| -> Result<_, String> {
|
||||
Ok(match value_expr {
|
||||
None => None,
|
||||
Some(value_expr) => {
|
||||
let value_expr = generator
|
||||
.gen_expr(ctx, value_expr)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?;
|
||||
|
||||
let value_expr = Int(Int32).check_value(generator, ctx.ctx, value_expr).unwrap();
|
||||
|
||||
Some(value_expr)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let lower = help(lower)?;
|
||||
let upper = help(upper)?;
|
||||
let step = help(step)?;
|
||||
|
||||
Ok((lower, upper, step))
|
||||
}
|
||||
|
@ -609,18 +609,63 @@ pub fn setup_irrt_exceptions<'ctx>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_nac3_range_len_i32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
pub fn call_nac3_range_len<'ctx, G: CodeGenerator + ?Sized, N: IntKind<'ctx>>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
start: Instance<'ctx, Int<Int32>>,
|
||||
stop: Instance<'ctx, Int<Int32>>,
|
||||
step: Instance<'ctx, Int<Int32>>,
|
||||
) -> Instance<'ctx, Int<Int32>> {
|
||||
CallFunction::begin(generator, ctx, "__nac3_range_len_i32")
|
||||
int_kind: N,
|
||||
start: Instance<'ctx, Int<N>>,
|
||||
stop: Instance<'ctx, Int<N>>,
|
||||
step: Instance<'ctx, Int<N>>,
|
||||
) -> Instance<'ctx, Int<N>> {
|
||||
let bit_width = int_kind.get_int_type(generator, ctx.ctx).get_bit_width();
|
||||
let func_name = match bit_width {
|
||||
32 => "__nac3_range_len_i32",
|
||||
64 => "__nac3_range_len_i64",
|
||||
_ => panic!("{bit_width}-bits ints not supported"), // We could add more variants when necessary.
|
||||
};
|
||||
|
||||
CallFunction::begin(generator, ctx, func_name)
|
||||
.arg(start)
|
||||
.arg(stop)
|
||||
.arg(step)
|
||||
.returning_auto("range_len")
|
||||
.returning("range_len", Int(int_kind))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn call_nac3_slice_indices<'ctx, G: CodeGenerator + ?Sized, N: IntKind<'ctx>>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
int_kind: N,
|
||||
start_defined: Instance<'ctx, Int<Bool>>,
|
||||
start: Instance<'ctx, Int<N>>,
|
||||
stop_defined: Instance<'ctx, Int<Bool>>,
|
||||
stop: Instance<'ctx, Int<N>>,
|
||||
step_defined: Instance<'ctx, Int<Bool>>,
|
||||
step: Instance<'ctx, Int<N>>,
|
||||
length: Instance<'ctx, Int<N>>,
|
||||
range_start: Instance<'ctx, Ptr<Int<N>>>,
|
||||
range_stop: Instance<'ctx, Ptr<Int<N>>>,
|
||||
range_step: Instance<'ctx, Ptr<Int<N>>>,
|
||||
) -> Instance<'ctx, Int<N>> {
|
||||
let bit_width = int_kind.get_int_type(generator, ctx.ctx).get_bit_width();
|
||||
let func_name = match bit_width {
|
||||
32 => "__nac3_slice_indices_i32",
|
||||
64 => "__nac3_slice_indices_i64",
|
||||
_ => panic!("{bit_width}-bits ints not supported"), // We could add more variants when necessary.
|
||||
};
|
||||
|
||||
CallFunction::begin(generator, ctx, func_name)
|
||||
.arg(start_defined)
|
||||
.arg(start)
|
||||
.arg(stop_defined)
|
||||
.arg(stop)
|
||||
.arg(step_defined)
|
||||
.arg(step)
|
||||
.arg(length)
|
||||
.arg(range_start)
|
||||
.arg(range_stop)
|
||||
.arg(range_step)
|
||||
.returning("range_len", Int(int_kind))
|
||||
}
|
||||
|
||||
pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
@ -26,7 +26,12 @@ use inkwell::{
|
||||
use itertools::Itertools;
|
||||
use model::*;
|
||||
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 std::collections::{HashMap, HashSet};
|
||||
use std::sync::{
|
||||
@ -172,11 +177,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, Instance<'ctx, Str>>,
|
||||
|
||||
/// [`BasicBlock`] containing all `alloca` statements for the current function.
|
||||
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
|
||||
/// <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.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, str_model().get_type(generator, context).into()),
|
||||
(primitives.range, Ptr(range_model()).get_type(generator, context).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, { Ptr(Struct(Exception)).get_type(generator, context).into() }),
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
|
@ -73,7 +73,7 @@ impl<'ctx, 'a, G: CodeGenerator + ?Sized> FieldTraversal<'ctx> for TypeFieldTrav
|
||||
}
|
||||
}
|
||||
|
||||
/// A traversal to check the types of a field for debug assertions.
|
||||
/// A traversal to check the field types of a [`StructType`].
|
||||
struct CheckTypeFieldTraversal<'ctx, 'a, G: CodeGenerator + ?Sized> {
|
||||
generator: &'a mut G,
|
||||
ctx: &'ctx Context,
|
||||
@ -82,7 +82,7 @@ struct CheckTypeFieldTraversal<'ctx, 'a, G: CodeGenerator + ?Sized> {
|
||||
index: u32,
|
||||
/// The [`StructType`] to check.
|
||||
scrutinee: StructType<'ctx>,
|
||||
/// A list of collected errors so far.
|
||||
/// The list of collected errors so far.
|
||||
errors: Vec<ModelError>,
|
||||
}
|
||||
|
||||
@ -216,7 +216,7 @@ pub struct Struct<S>(pub S);
|
||||
impl<'ctx, S: StructKind<'ctx>> Struct<S> {
|
||||
/// Create a constant struct value.
|
||||
///
|
||||
/// This function also validates `fields` and panic when there is something wrong.
|
||||
/// This function also validates `fields` and panics types don't match.
|
||||
pub fn const_struct<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
|
24
nac3core/src/codegen/object/cslice.rs
Normal file
24
nac3core/src/codegen/object/cslice.rs
Normal 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") }
|
||||
}
|
||||
}
|
41
nac3core/src/codegen/object/exception.rs
Normal file
41
nac3core/src/codegen/object/exception.rs
Normal 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]"),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
pub mod any;
|
||||
pub mod cslice;
|
||||
pub mod exception;
|
||||
pub mod list;
|
||||
pub mod ndarray;
|
||||
pub mod range;
|
||||
pub mod slice;
|
||||
pub mod str;
|
||||
pub mod tuple;
|
||||
|
@ -31,7 +31,7 @@ fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "").into()
|
||||
ctx.gen_string(generator, "").value.into()
|
||||
} else {
|
||||
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) {
|
||||
ctx.ctx.bool_type().const_int(1, false).into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "1").into()
|
||||
ctx.gen_string(generator, "1").value.into()
|
||||
} else {
|
||||
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
use crate::codegen::{irrt::call_nac3_ndarray_index, model::*, CodeGenContext, CodeGenerator};
|
||||
use crate::codegen::{
|
||||
irrt::call_nac3_ndarray_index,
|
||||
model::*,
|
||||
object::slice::{RustSlice, Slice},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
use super::NDArrayObject;
|
||||
|
||||
@ -23,86 +28,11 @@ impl<'ctx> StructKind<'ctx> for NDIndex {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields of [`Slice`]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SliceFields<'ctx, F: FieldTraversal<'ctx>> {
|
||||
pub start_defined: F::Out<Int<Bool>>,
|
||||
pub start: F::Out<Int<Int32>>,
|
||||
pub stop_defined: F::Out<Int<Bool>>,
|
||||
pub stop: F::Out<Int<Int32>>,
|
||||
pub step_defined: F::Out<Int<Bool>>,
|
||||
pub step: F::Out<Int<Int32>>,
|
||||
}
|
||||
|
||||
/// An IRRT representation of an (unresolved) slice.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Slice;
|
||||
|
||||
impl<'ctx> StructKind<'ctx> for Slice {
|
||||
type Fields<F: FieldTraversal<'ctx>> = SliceFields<'ctx, F>;
|
||||
|
||||
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
|
||||
Self::Fields {
|
||||
start_defined: traversal.add_auto("start_defined"),
|
||||
start: traversal.add_auto("start"),
|
||||
stop_defined: traversal.add_auto("stop_defined"),
|
||||
stop: traversal.add_auto("stop"),
|
||||
step_defined: traversal.add_auto("step_defined"),
|
||||
step: traversal.add_auto("step"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience structure to prepare a [`Slice`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RustSlice<'ctx> {
|
||||
pub start: Option<Instance<'ctx, Int<Int32>>>,
|
||||
pub stop: Option<Instance<'ctx, Int<Int32>>>,
|
||||
pub step: Option<Instance<'ctx, Int<Int32>>>,
|
||||
}
|
||||
|
||||
impl<'ctx> RustSlice<'ctx> {
|
||||
/// Write the contents to an LLVM [`Slice`].
|
||||
pub fn write_to_slice<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
dst_slice_ptr: Instance<'ctx, Ptr<Struct<Slice>>>,
|
||||
) {
|
||||
let false_ = Int(Bool).const_false(generator, ctx.ctx);
|
||||
let true_ = Int(Bool).const_true(generator, ctx.ctx);
|
||||
|
||||
match self.start {
|
||||
Some(start) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.start_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.start).store(ctx, start);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.start_defined).store(ctx, false_),
|
||||
}
|
||||
|
||||
match self.stop {
|
||||
Some(stop) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.stop_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.stop).store(ctx, stop);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.stop_defined).store(ctx, false_),
|
||||
}
|
||||
|
||||
match self.step {
|
||||
Some(step) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.step_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.step).store(ctx, step);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.step_defined).store(ctx, false_),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A convenience enum to prepare an [`NDIndex`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RustNDIndex<'ctx> {
|
||||
SingleElement(Instance<'ctx, Int<Int32>>), // TODO: To be SizeT
|
||||
Slice(RustSlice<'ctx>),
|
||||
Slice(RustSlice<'ctx, Int32>), // TODO: To be SizeT
|
||||
NewAxis,
|
||||
Ellipsis,
|
||||
}
|
||||
@ -143,7 +73,7 @@ impl<'ctx> RustNDIndex<'ctx> {
|
||||
.store(ctx, index_ptr.pointer_cast(generator, ctx, Int(Byte)));
|
||||
}
|
||||
RustNDIndex::Slice(in_rust_slice) => {
|
||||
let user_slice_ptr = Struct(Slice).alloca(generator, ctx);
|
||||
let user_slice_ptr = Struct(Slice(Int32)).alloca(generator, ctx);
|
||||
in_rust_slice.write_to_slice(generator, ctx, user_slice_ptr);
|
||||
|
||||
dst_ndindex_ptr
|
||||
@ -225,11 +155,11 @@ pub mod util {
|
||||
use nac3parser::ast::{Expr, ExprKind};
|
||||
|
||||
use crate::{
|
||||
codegen::{expr::gen_slice, model::*, CodeGenContext, CodeGenerator},
|
||||
codegen::{model::*, object::slice::util::gen_slice, CodeGenContext, CodeGenerator},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
|
||||
use super::{RustNDIndex, RustSlice};
|
||||
use super::RustNDIndex;
|
||||
|
||||
/// Generate LLVM code to transform an ndarray subscript expression to
|
||||
/// its list of [`RustNDIndex`]
|
||||
@ -273,8 +203,8 @@ pub mod util {
|
||||
// so the code/implementation looks awkward - we have to do pattern matching on the expression
|
||||
let ndindex = if let ExprKind::Slice { lower, upper, step } = &index_expr.node {
|
||||
// Handle slices
|
||||
let (lower, upper, step) = gen_slice(generator, ctx, lower, upper, step)?;
|
||||
RustNDIndex::Slice(RustSlice { start: lower, stop: upper, step })
|
||||
let slice = gen_slice(generator, ctx, lower, upper, step)?;
|
||||
RustNDIndex::Slice(slice)
|
||||
} else {
|
||||
// Treat and handle everything else as a single element index.
|
||||
let index = generator.gen_expr(ctx, index_expr)?.unwrap().to_basic_value_enum(
|
||||
|
3
nac3core/src/codegen/object/ndarray/str.rs
Normal file
3
nac3core/src/codegen/object/ndarray/str.rs
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
pub fn str_type() {
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use inkwell::{values::IntValue, IntPredicate};
|
||||
|
||||
use crate::codegen::{irrt::call_nac3_range_len_i32, model::*, CodeGenContext, CodeGenerator};
|
||||
use crate::codegen::{irrt::call_nac3_range_len, model::*, CodeGenContext, CodeGenerator};
|
||||
|
||||
use super::any::AnyObject;
|
||||
|
||||
@ -63,6 +63,38 @@ pub fn assert_range_step_non_zero<'ctx, G: CodeGenerator + ?Sized>(
|
||||
);
|
||||
}
|
||||
|
||||
/// A Rust structure that has [`Range`] utilities and looks like a [`Range`] but
|
||||
/// `start`, `stop` and `step` are held by LLVM registers only.
|
||||
///
|
||||
/// This structure exists because many implementations use [`Range`] utilities but
|
||||
/// it might not be good to alloca an actual [`Range`] value on the stack in order
|
||||
/// to perform calculations.
|
||||
pub struct RustRange<'ctx, N: IntKind<'ctx>> {
|
||||
pub start: Instance<'ctx, Int<N>>,
|
||||
pub stop: Instance<'ctx, Int<N>>,
|
||||
pub step: Instance<'ctx, Int<N>>,
|
||||
}
|
||||
|
||||
impl<'ctx, N: IntKind<'ctx>> RustRange<'ctx, N> {
|
||||
pub fn assert_step_non_zero<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) {
|
||||
assert_range_step_non_zero(generator, ctx, self.step.value);
|
||||
}
|
||||
|
||||
/// Calculate the `len()` of this range.
|
||||
pub fn len<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Instance<'ctx, Int<N>> {
|
||||
let int_kind = self.start.model.0;
|
||||
call_nac3_range_len(generator, ctx, int_kind, self.start, self.stop, self.step)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: `RangeObject` in the future will have range32, range64
|
||||
|
||||
/// A NAC3 Python range object.
|
||||
@ -84,14 +116,24 @@ impl<'ctx> RangeObject<'ctx> {
|
||||
RangeObject { instance }
|
||||
}
|
||||
|
||||
/// Convert into a [`RustRange`].
|
||||
pub fn as_rust_range<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> RustRange<'ctx, Int32> {
|
||||
let (start, stop, step) = self.instance.destructure(generator, ctx);
|
||||
RustRange { start, stop, step }
|
||||
}
|
||||
|
||||
/// Get the `len()` of this range.
|
||||
pub fn len<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Instance<'ctx, Int<Int32>> {
|
||||
let (start, stop, step) = self.instance.destructure(generator, ctx);
|
||||
assert_range_step_non_zero(generator, ctx, step.value);
|
||||
call_nac3_range_len_i32(generator, ctx, start, stop, step)
|
||||
let range = self.as_rust_range(generator, ctx);
|
||||
range.assert_step_non_zero(generator, ctx);
|
||||
range.len(generator, ctx)
|
||||
}
|
||||
}
|
||||
|
185
nac3core/src/codegen/object/slice.rs
Normal file
185
nac3core/src/codegen/object/slice.rs
Normal file
@ -0,0 +1,185 @@
|
||||
use crate::codegen::{irrt::call_nac3_slice_indices, model::*, CodeGenContext, CodeGenerator};
|
||||
|
||||
use super::range::RustRange;
|
||||
|
||||
/// Fields of [`Slice`]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SliceFields<'ctx, F: FieldTraversal<'ctx>, N: IntKind<'ctx>> {
|
||||
pub start_defined: F::Out<Int<Bool>>,
|
||||
pub start: F::Out<Int<N>>,
|
||||
pub stop_defined: F::Out<Int<Bool>>,
|
||||
pub stop: F::Out<Int<N>>,
|
||||
pub step_defined: F::Out<Int<Bool>>,
|
||||
pub step: F::Out<Int<N>>,
|
||||
}
|
||||
|
||||
/// An IRRT representation of an (unresolved) slice.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Slice<N>(pub N);
|
||||
|
||||
impl<'ctx, N: IntKind<'ctx>> StructKind<'ctx> for Slice<N> {
|
||||
type Fields<F: FieldTraversal<'ctx>> = SliceFields<'ctx, F, N>;
|
||||
|
||||
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
|
||||
Self::Fields {
|
||||
start_defined: traversal.add_auto("start_defined"),
|
||||
start: traversal.add("start", Int(self.0)),
|
||||
stop_defined: traversal.add_auto("stop_defined"),
|
||||
stop: traversal.add("stop", Int(self.0)),
|
||||
step_defined: traversal.add_auto("step_defined"),
|
||||
step: traversal.add("step", Int(self.0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Rust structure that has [`Slice`] utilities and looks like a [`Slice`] but
|
||||
/// `start`, `stop` and `step` are held by LLVM registers only and possibly
|
||||
/// [`Option::None`] if unspecified.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RustSlice<'ctx, N: IntKind<'ctx>> {
|
||||
// It is possible that `start`, `stop`, and `step` are all `None`.
|
||||
// We need to know the `int_kind` even when that is the case.
|
||||
pub int_kind: N,
|
||||
pub start: Option<Instance<'ctx, Int<N>>>,
|
||||
pub stop: Option<Instance<'ctx, Int<N>>>,
|
||||
pub step: Option<Instance<'ctx, Int<N>>>,
|
||||
}
|
||||
|
||||
impl<'ctx, N: IntKind<'ctx>> RustSlice<'ctx, N> {
|
||||
/// Write the contents to an LLVM [`Slice`].
|
||||
pub fn write_to_slice<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
dst_slice_ptr: Instance<'ctx, Ptr<Struct<Slice<N>>>>,
|
||||
) {
|
||||
let false_ = Int(Bool).const_false(generator, ctx.ctx);
|
||||
let true_ = Int(Bool).const_true(generator, ctx.ctx);
|
||||
|
||||
match self.start {
|
||||
Some(start) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.start_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.start).store(ctx, start);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.start_defined).store(ctx, false_),
|
||||
}
|
||||
|
||||
match self.stop {
|
||||
Some(stop) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.stop_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.stop).store(ctx, stop);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.stop_defined).store(ctx, false_),
|
||||
}
|
||||
|
||||
match self.step {
|
||||
Some(step) => {
|
||||
dst_slice_ptr.gep(ctx, |f| f.step_defined).store(ctx, true_);
|
||||
dst_slice_ptr.gep(ctx, |f| f.step).store(ctx, step);
|
||||
}
|
||||
None => dst_slice_ptr.gep(ctx, |f| f.step_defined).store(ctx, false_),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve this [`RustSlice`] into a [`RustRange`] like `slice.indices` in Python.
|
||||
///
|
||||
/// NOTE: This function does stack allocation.
|
||||
pub fn indices<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
length: Instance<'ctx, Int<N>>,
|
||||
) -> RustRange<'ctx, N> {
|
||||
let mut is_defined = |value: Option<_>| -> Instance<'ctx, Int<Bool>> {
|
||||
Int(Bool).const_int(generator, ctx.ctx, u64::from(value.is_some()))
|
||||
};
|
||||
|
||||
let start_defined = is_defined(self.start);
|
||||
let stop_defined = is_defined(self.stop);
|
||||
let step_defined = is_defined(self.step);
|
||||
|
||||
let mut defined_or_zero = |value: Option<_>| -> Instance<'ctx, Int<N>> {
|
||||
if let Some(value) = value {
|
||||
value
|
||||
} else {
|
||||
// If undefined, return 0 as a placeholder.
|
||||
Int(self.int_kind).const_0(generator, ctx.ctx)
|
||||
}
|
||||
};
|
||||
|
||||
let start = defined_or_zero(self.start);
|
||||
let stop = defined_or_zero(self.stop);
|
||||
let step = defined_or_zero(self.step);
|
||||
|
||||
// Stack allocation here.
|
||||
let range_start = Int(self.int_kind).alloca(generator, ctx);
|
||||
let range_stop = Int(self.int_kind).alloca(generator, ctx);
|
||||
let range_step = Int(self.int_kind).alloca(generator, ctx);
|
||||
|
||||
call_nac3_slice_indices(
|
||||
generator,
|
||||
ctx,
|
||||
self.int_kind,
|
||||
start_defined,
|
||||
start,
|
||||
stop_defined,
|
||||
stop,
|
||||
step_defined,
|
||||
step,
|
||||
length,
|
||||
range_start,
|
||||
range_stop,
|
||||
range_step,
|
||||
);
|
||||
|
||||
let start = range_start.load(generator, ctx);
|
||||
let stop = range_stop.load(generator, ctx);
|
||||
let step = range_step.load(generator, ctx);
|
||||
|
||||
RustRange { start, stop, step }
|
||||
}
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
use crate::{
|
||||
codegen::{model::*, CodeGenContext, CodeGenerator},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
|
||||
use super::RustSlice;
|
||||
|
||||
/// Generate LLVM IR for an [`ExprKind::Slice`] and convert it into a [`RustSlice`].
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn gen_slice<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
lower: &Option<Box<Expr<Option<Type>>>>,
|
||||
upper: &Option<Box<Expr<Option<Type>>>>,
|
||||
step: &Option<Box<Expr<Option<Type>>>>,
|
||||
) -> Result<RustSlice<'ctx, Int32>, String> {
|
||||
let mut help = |value_expr: &Option<Box<Expr<Option<Type>>>>| -> Result<_, String> {
|
||||
Ok(match value_expr {
|
||||
None => None,
|
||||
Some(value_expr) => {
|
||||
let value_expr = generator
|
||||
.gen_expr(ctx, value_expr)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?;
|
||||
|
||||
let value_expr =
|
||||
Int(Int32).check_value(generator, ctx.ctx, value_expr).unwrap();
|
||||
|
||||
Some(value_expr)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let start = help(lower)?;
|
||||
let stop = help(upper)?;
|
||||
let step = help(step)?;
|
||||
|
||||
Ok(RustSlice { int_kind: Int32, start, stop, step })
|
||||
}
|
||||
}
|
11
nac3core/src/codegen/object/str.rs
Normal file
11
nac3core/src/codegen/object/str.rs
Normal 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()
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
use super::{
|
||||
super::symbol_resolver::ValueEnum,
|
||||
irrt::{handle_slice_indices, list_slice_assignment},
|
||||
model::*,
|
||||
object::{
|
||||
any::AnyObject,
|
||||
exception::Exception,
|
||||
ndarray::{
|
||||
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
|
||||
},
|
||||
range::RangeObject,
|
||||
str::str_model,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
@ -30,9 +33,7 @@ use inkwell::{
|
||||
IntPredicate,
|
||||
};
|
||||
use itertools::{izip, Itertools};
|
||||
use nac3parser::ast::{
|
||||
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
||||
};
|
||||
use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef};
|
||||
|
||||
/// See [`CodeGenerator::gen_var_alloc`].
|
||||
pub fn gen_var<'ctx>(
|
||||
@ -1247,65 +1248,58 @@ pub fn exn_constructor<'ctx>(
|
||||
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let (zelf_ty, zelf) = obj.unwrap();
|
||||
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let zelf_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(zelf_ty) {
|
||||
let (exn_ty, exn) = obj.unwrap();
|
||||
let exn = exn.to_basic_value_enum(ctx, generator, exn_ty)?;
|
||||
let exn = Ptr(Struct(Exception)).check_value(generator, ctx.ctx, exn).unwrap();
|
||||
|
||||
// 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
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let defs = ctx.top_level.definitions.read();
|
||||
let def = defs[zelf_id].read();
|
||||
let TopLevelDef::Class { name: zelf_name, .. } = &*def else { unreachable!() };
|
||||
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(zelf_id), zelf_name);
|
||||
unsafe {
|
||||
let id_ptr = ctx.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap();
|
||||
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_string =
|
||||
ctx.gen_const(generator, &Constant::Str(String::new()), ctx.primitives.str);
|
||||
let ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg")
|
||||
.unwrap();
|
||||
let msg = if args.is_empty() {
|
||||
empty_string.unwrap()
|
||||
} else {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
|
||||
};
|
||||
ctx.builder.build_store(ptr, msg).unwrap();
|
||||
for i in &[6, 7, 8] {
|
||||
let value = if args.is_empty() {
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
} else {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
|
||||
};
|
||||
let ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.param")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(ptr, value).unwrap();
|
||||
}
|
||||
// set file, func to empty string
|
||||
for i in &[1, 4] {
|
||||
let ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.str")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(ptr, empty_string.unwrap()).unwrap();
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
let exn_def = defs[exn_def_id].read();
|
||||
let TopLevelDef::Class { name: exn_name, .. } = &*exn_def else { unreachable!() };
|
||||
let exn_name = format!("{}:{}", ctx.resolver.get_exception_id(exn_def_id), exn_name);
|
||||
|
||||
// Initialize the fields of the Exception object.
|
||||
|
||||
let empty_str = ctx.gen_string(generator, "");
|
||||
let num_0 = Int(Int32).const_0(generator, ctx.ctx);
|
||||
|
||||
// Initialize `self.id`.
|
||||
let id = ctx.resolver.get_string_id(&exn_name);
|
||||
let id = Int(Int32).const_int(generator, ctx.ctx, id as u64);
|
||||
exn.set(ctx, |f| f.id, id);
|
||||
|
||||
// Initialize `self.msg`.
|
||||
let msg = if args.is_empty() {
|
||||
// Default to `msg` to "" if the user didn't pass anything.
|
||||
empty_str
|
||||
} else {
|
||||
let msg = args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?;
|
||||
str_model().check_value(generator, ctx.ctx, msg).unwrap()
|
||||
};
|
||||
exn.set(ctx, |f| f.msg, msg);
|
||||
|
||||
// Initialize `self.params`, the arguments after `msg` are the params.
|
||||
for (i, (_, param)) in args.into_iter().enumerate() {
|
||||
assert!(i <= 3, "There should only be at most 3 exception parameters");
|
||||
|
||||
let param = param.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?;
|
||||
let param = Int(Int64).check_value(generator, ctx.ctx, param).unwrap();
|
||||
|
||||
exn.set(ctx, |f| f.params[i], param);
|
||||
}
|
||||
Ok(Some(zelf.into()))
|
||||
|
||||
// Initialize everything else to 0 or "".
|
||||
exn.set(ctx, |f| f.line, num_0);
|
||||
exn.set(ctx, |f| f.column, num_0);
|
||||
exn.set(ctx, |f| f.function, empty_str);
|
||||
exn.set(ctx, |f| f.filename, empty_str);
|
||||
|
||||
Ok(Some(exn.value.into()))
|
||||
}
|
||||
|
||||
/// Generates IR for a `raise` statement.
|
||||
@ -1315,43 +1309,27 @@ 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<Instance<'ctx, Ptr<Struct<Exception>>>>,
|
||||
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(exn) = exception {
|
||||
let filename = loc.file.0;
|
||||
let filename = ctx.gen_string(generator, filename);
|
||||
exn.set(ctx, |f| f.filename, filename);
|
||||
|
||||
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();
|
||||
}
|
||||
let row = Int(Int32).const_int(generator, ctx.ctx, loc.row as u64);
|
||||
exn.set(ctx, |f| f.line, row);
|
||||
|
||||
let column = Int(Int32).const_int(generator, ctx.ctx, loc.column as u64);
|
||||
exn.set(ctx, |f| f.column, column);
|
||||
|
||||
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 exception = *exception;
|
||||
ctx.build_call_or_invoke(raise, &[exception], "raise");
|
||||
ctx.build_call_or_invoke(raise, &[exn.value.into()], "raise");
|
||||
} else {
|
||||
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
@ -1817,7 +1795,9 @@ pub fn gen_stmt<G: CodeGenerator>(
|
||||
} else {
|
||||
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 {
|
||||
gen_raise(generator, ctx, None, stmt.location);
|
||||
}
|
||||
@ -1830,13 +1810,19 @@ pub fn gen_stmt<G: CodeGenerator>(
|
||||
};
|
||||
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())?
|
||||
let msg_ty = 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 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
None => ctx.gen_string(generator, "").into(),
|
||||
None => {
|
||||
// Return an empty string.
|
||||
ctx.gen_string(generator, "")
|
||||
}
|
||||
};
|
||||
ctx.make_assert_impl(
|
||||
generator,
|
||||
|
Loading…
Reference in New Issue
Block a user