forked from M-Labs/nac3
core: refactor to use ListObject / List
This commit is contained in:
parent
2d799d13e2
commit
d2650e6979
|
@ -1,10 +1,9 @@
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{ListValue, UntypedArrayLikeAccessor},
|
|
||||||
expr::gen_call,
|
expr::gen_call,
|
||||||
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
|
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
|
||||||
model::*,
|
model::*,
|
||||||
object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject},
|
object::{any::AnyObject, list::ListObject, ndarray::NDArrayObject, range::RangeObject},
|
||||||
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
},
|
},
|
||||||
|
@ -1015,14 +1014,16 @@ fn polymorphic_print<'ctx>(
|
||||||
args.extend(&[str_len.into(), str_data.into()]);
|
args.extend(&[str_len.into(), str_data.into()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||||
let elem_ty = *params.iter().next().unwrap().1;
|
|
||||||
|
|
||||||
fmt.push('[');
|
fmt.push('[');
|
||||||
flush(ctx, generator, &mut fmt, &mut args);
|
flush(ctx, generator, &mut fmt, &mut args);
|
||||||
|
|
||||||
let val = ListValue::from_ptr_val(value.into_pointer_value(), llvm_usize, None);
|
let list = AnyObject { ty, value };
|
||||||
let len = val.load_size(ctx, None);
|
let list = ListObject::from_object(generator, ctx, list);
|
||||||
|
|
||||||
|
let items = list.instance.get(generator, ctx, |f| f.items);
|
||||||
|
|
||||||
|
let len = list.instance.get(generator, ctx, |f| f.len).value;
|
||||||
let last =
|
let last =
|
||||||
ctx.builder.build_int_sub(len, llvm_usize.const_int(1, false), "").unwrap();
|
ctx.builder.build_int_sub(len, llvm_usize.const_int(1, false), "").unwrap();
|
||||||
|
|
||||||
|
@ -1033,12 +1034,12 @@ fn polymorphic_print<'ctx>(
|
||||||
llvm_usize.const_zero(),
|
llvm_usize.const_zero(),
|
||||||
(len, false),
|
(len, false),
|
||||||
|generator, ctx, _, i| {
|
|generator, ctx, _, i| {
|
||||||
let elem = unsafe { val.data().get_unchecked(ctx, generator, &i, None) };
|
let item = items.get_index(generator, ctx, i).value;
|
||||||
|
|
||||||
polymorphic_print(
|
polymorphic_print(
|
||||||
ctx,
|
ctx,
|
||||||
generator,
|
generator,
|
||||||
&[(elem_ty, elem.into())],
|
&[(list.item_type, item.into())],
|
||||||
"",
|
"",
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{
|
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, ProxyType, ProxyValue,
|
|
||||||
UntypedArrayLikeAccessor,
|
|
||||||
},
|
|
||||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||||
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
|
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
|
||||||
irrt::*,
|
irrt::*,
|
||||||
llvm_intrinsics::{
|
llvm_intrinsics::{
|
||||||
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
|
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
|
||||||
call_int_umin, call_memcpy_generic,
|
call_int_umin,
|
||||||
},
|
},
|
||||||
need_sret,
|
need_sret,
|
||||||
object::ndarray::{NDArrayOut, ScalarOrNDArray},
|
object::{
|
||||||
|
list::List,
|
||||||
|
ndarray::{NDArrayOut, ScalarOrNDArray},
|
||||||
|
},
|
||||||
stmt::{
|
stmt::{
|
||||||
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||||
gen_var,
|
gen_var,
|
||||||
|
@ -38,16 +37,17 @@ use inkwell::{
|
||||||
use itertools::{chain, izip, Either, Itertools};
|
use itertools::{chain, izip, Either, Itertools};
|
||||||
use nac3parser::ast::{
|
use nac3parser::ast::{
|
||||||
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
||||||
Unaryop,
|
|
||||||
};
|
};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
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 util::gen_for_model;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
model::*,
|
model::*,
|
||||||
object::{
|
object::{
|
||||||
any::AnyObject,
|
any::AnyObject,
|
||||||
|
list::ListObject,
|
||||||
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
|
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
|
||||||
range::RangeObject,
|
range::RangeObject,
|
||||||
},
|
},
|
||||||
|
@ -1081,33 +1081,6 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||||
Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call"))
|
Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
|
|
||||||
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
|
|
||||||
///
|
|
||||||
/// Setting `ty` to [`None`] implies that the list is empty **and** does not have a known element
|
|
||||||
/// type, and will therefore set the `list.data` type as `size_t*`. It is undefined behavior to
|
|
||||||
/// generate a sized list with an unknown element type.
|
|
||||||
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
ty: Option<BasicTypeEnum<'ctx>>,
|
|
||||||
length: IntValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
) -> ListValue<'ctx> {
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let llvm_elem_ty = ty.unwrap_or(llvm_usize.into());
|
|
||||||
|
|
||||||
// List structure; type { ty*, size_t }
|
|
||||||
let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty);
|
|
||||||
let list = arr_ty.new_value(generator, ctx, name);
|
|
||||||
|
|
||||||
let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap();
|
|
||||||
list.store_size(ctx, generator, length);
|
|
||||||
list.create_data(ctx, llvm_elem_ty, None);
|
|
||||||
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates LLVM IR for a [list comprehension expression][expr].
|
/// Generates LLVM IR for a [list comprehension expression][expr].
|
||||||
pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
|
@ -1148,8 +1121,10 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?;
|
let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?;
|
||||||
ctx.builder.build_store(index, zero_size_t).unwrap();
|
ctx.builder.build_store(index, zero_size_t).unwrap();
|
||||||
|
|
||||||
let elem_ty = ctx.get_llvm_type(generator, elt.custom.unwrap());
|
let elem_ty = elt.custom.unwrap();
|
||||||
let list;
|
let elem_ty_llvm = ctx.get_llvm_type(generator, elem_ty);
|
||||||
|
|
||||||
|
let list: ListObject<'ctx>;
|
||||||
|
|
||||||
match &*ctx.unifier.get_ty(iter_ty) {
|
match &*ctx.unifier.get_ty(iter_ty) {
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
@ -1184,14 +1159,11 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
zero_size_t,
|
zero_size_t,
|
||||||
"listcomp.alloc_size",
|
"listcomp.alloc_size",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap()
|
||||||
list = allocate_list(
|
.into_int_value();
|
||||||
generator,
|
|
||||||
ctx,
|
let list_alloc_size = Int(SizeT).believe_value(list_alloc_size);
|
||||||
Some(elem_ty),
|
list = ListObject::alloca(generator, ctx, elem_ty, list_alloc_size);
|
||||||
list_alloc_size.into_int_value(),
|
|
||||||
Some("listcomp.addr"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
||||||
ctx.builder
|
ctx.builder
|
||||||
|
@ -1230,14 +1202,12 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
let length = ctx
|
let source_list = AnyObject { ty: iter_ty, value: iter_val };
|
||||||
.build_gep_and_load(
|
let source_list = ListObject::from_object(generator, ctx, source_list);
|
||||||
iter_val.into_pointer_value(),
|
|
||||||
&[zero_size_t, int32.const_int(1, false)],
|
let len = source_list.instance.get(generator, ctx, |f| f.len);
|
||||||
Some("length"),
|
|
||||||
)
|
list = ListObject::alloca(generator, ctx, elem_ty, len);
|
||||||
.into_int_value();
|
|
||||||
list = allocate_list(generator, ctx, Some(elem_ty), length, Some("listcomp"));
|
|
||||||
|
|
||||||
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
||||||
// counter = -1
|
// counter = -1
|
||||||
|
@ -1249,7 +1219,8 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
ctx.builder.build_load(counter, "i").map(BasicValueEnum::into_int_value).unwrap();
|
ctx.builder.build_load(counter, "i").map(BasicValueEnum::into_int_value).unwrap();
|
||||||
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc").unwrap();
|
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc").unwrap();
|
||||||
ctx.builder.build_store(counter, tmp).unwrap();
|
ctx.builder.build_store(counter, tmp).unwrap();
|
||||||
let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, length, "cmp").unwrap();
|
let cmp =
|
||||||
|
ctx.builder.build_int_compare(IntPredicate::SLT, tmp, len.value, "cmp").unwrap();
|
||||||
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb).unwrap();
|
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb).unwrap();
|
||||||
|
|
||||||
ctx.builder.position_at_end(body_bb);
|
ctx.builder.position_at_end(body_bb);
|
||||||
|
@ -1272,14 +1243,15 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emits the content of `cont_bb`
|
// Emits the content of `cont_bb`
|
||||||
let emit_cont_bb =
|
let emit_cont_bb = |ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|ctx: &CodeGenContext<'ctx, '_>, generator: &dyn CodeGenerator, list: ListValue<'ctx>| {
|
generator: &mut dyn CodeGenerator,
|
||||||
|
list: ListObject<'ctx>| {
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
list.store_size(
|
|
||||||
ctx,
|
let index = ctx.builder.build_load(index, "index").unwrap();
|
||||||
generator,
|
let index = Int(SizeT).check_value(generator, ctx.ctx, index).unwrap();
|
||||||
ctx.builder.build_load(index, "index").map(BasicValueEnum::into_int_value).unwrap(),
|
|
||||||
);
|
list.instance.set(ctx, |f| f.len, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
for cond in ifs {
|
for cond in ifs {
|
||||||
|
@ -1306,10 +1278,13 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let i = ctx.builder.build_load(index, "i").map(BasicValueEnum::into_int_value).unwrap();
|
let i = ctx.builder.build_load(index, "i").map(BasicValueEnum::into_int_value).unwrap();
|
||||||
let elem_ptr =
|
|
||||||
unsafe { list.data().ptr_offset_unchecked(ctx, generator, &i, Some("elem_ptr")) };
|
let elem_ptr = list.instance.get(generator, ctx, |f| f.items).offset(ctx, i);
|
||||||
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
|
|
||||||
ctx.builder.build_store(elem_ptr, val).unwrap();
|
let elem = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
|
||||||
|
let elem = Any(elem_ty_llvm).check_value(generator, ctx.ctx, elem).unwrap();
|
||||||
|
elem_ptr.store(ctx, elem);
|
||||||
|
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.build_store(
|
.build_store(
|
||||||
index,
|
index,
|
||||||
|
@ -1320,7 +1295,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
|
|
||||||
emit_cont_bb(ctx, generator, list);
|
emit_cont_bb(ctx, generator, list);
|
||||||
|
|
||||||
Ok(Some(list.as_base_value().into()))
|
Ok(Some(list.instance.value.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates LLVM IR for a binary operator expression using the [`Type`] and
|
/// Generates LLVM IR for a binary operator expression using the [`Type`] and
|
||||||
|
@ -1375,167 +1350,87 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||||
debug_assert_eq!(ty1.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
debug_assert_eq!(ty1.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
||||||
debug_assert_eq!(ty2.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
debug_assert_eq!(ty2.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
||||||
|
|
||||||
let elem_ty1 =
|
let lhs = AnyObject { ty: ty1, value: left_val };
|
||||||
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty_immutable(ty1) {
|
let lhs = ListObject::from_object(generator, ctx, lhs);
|
||||||
ctx.unifier.get_representative(*params.iter().next().unwrap().1)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
let elem_ty2 =
|
|
||||||
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty_immutable(ty2) {
|
|
||||||
ctx.unifier.get_representative(*params.iter().next().unwrap().1)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
debug_assert!(ctx.unifier.unioned(elem_ty1, elem_ty2));
|
|
||||||
|
|
||||||
let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty1);
|
let rhs = AnyObject { ty: ty2, value: right_val };
|
||||||
let sizeof_elem = llvm_elem_ty.size_of().unwrap();
|
let rhs = ListObject::from_object(generator, ctx, rhs);
|
||||||
|
|
||||||
let lhs = ListValue::from_ptr_val(left_val.into_pointer_value(), llvm_usize, None);
|
debug_assert!(ctx.unifier.unioned(lhs.item_type, rhs.item_type));
|
||||||
let rhs = ListValue::from_ptr_val(right_val.into_pointer_value(), llvm_usize, None);
|
let common_item_type = lhs.item_type;
|
||||||
|
|
||||||
let size = ctx
|
let lhs_len = lhs.instance.get(generator, ctx, |f| f.len);
|
||||||
.builder
|
let rhs_len = rhs.instance.get(generator, ctx, |f| f.len);
|
||||||
.build_int_add(lhs.load_size(ctx, None), rhs.load_size(ctx, None), "")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_list = allocate_list(generator, ctx, Some(llvm_elem_ty), size, None);
|
let concat_len = lhs_len.add(ctx, rhs_len);
|
||||||
|
let concat = ListObject::alloca(generator, ctx, common_item_type, concat_len);
|
||||||
|
|
||||||
let lhs_size = ctx
|
let lhs_items = lhs.instance.get(generator, ctx, |f| f.items);
|
||||||
.builder
|
let rhs_items = rhs.instance.get(generator, ctx, |f| f.items);
|
||||||
.build_int_z_extend_or_bit_cast(
|
let concat_items = concat.instance.get(generator, ctx, |f| f.items);
|
||||||
lhs.load_size(ctx, None),
|
|
||||||
sizeof_elem.get_type(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let lhs_len = ctx.builder.build_int_mul(lhs_size, sizeof_elem, "").unwrap();
|
|
||||||
|
|
||||||
let rhs_size = ctx
|
// Copy lhs
|
||||||
.builder
|
concat_items.copy_from(generator, ctx, lhs_items, lhs_len.value);
|
||||||
.build_int_z_extend_or_bit_cast(
|
|
||||||
rhs.load_size(ctx, None),
|
|
||||||
sizeof_elem.get_type(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let rhs_len = ctx.builder.build_int_mul(rhs_size, sizeof_elem, "").unwrap();
|
|
||||||
|
|
||||||
let list_ptr = new_list.data().base_ptr(ctx, generator);
|
// Copy rhs
|
||||||
call_memcpy_generic(
|
concat_items.offset(ctx, lhs_len.value).copy_from(
|
||||||
ctx,
|
|
||||||
list_ptr,
|
|
||||||
lhs.data().base_ptr(ctx, generator),
|
|
||||||
lhs_len,
|
|
||||||
ctx.ctx.bool_type().const_zero(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let list_ptr = unsafe {
|
|
||||||
new_list.data().ptr_offset_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
generator,
|
||||||
&lhs.load_size(ctx, None),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
call_memcpy_generic(
|
|
||||||
ctx,
|
ctx,
|
||||||
list_ptr,
|
rhs_items,
|
||||||
rhs.data().base_ptr(ctx, generator),
|
rhs_len.value,
|
||||||
rhs_len,
|
|
||||||
ctx.ctx.bool_type().const_zero(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Some(new_list.as_base_value().into()))
|
Ok(Some(concat_items.value.as_basic_value_enum().into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
Operator::Mult => {
|
Operator::Mult => {
|
||||||
let (elem_ty, list_val, int_val) =
|
let (list, int) = if ty1
|
||||||
if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
|
.obj_id(&ctx.unifier)
|
||||||
let elem_ty = if let TypeEnum::TObj { params, .. } =
|
.is_some_and(|id| id == PrimDef::List.id())
|
||||||
&*ctx.unifier.get_ty_immutable(ty1)
|
|
||||||
{
|
{
|
||||||
*params.iter().next().unwrap().1
|
// Handle `[1, 2, 3] * int`
|
||||||
} else {
|
let list = AnyObject { ty: ty1, value: left_val };
|
||||||
unreachable!()
|
let list = ListObject::from_object(generator, ctx, list);
|
||||||
};
|
|
||||||
|
|
||||||
(elem_ty, left_val, right_val)
|
let int =
|
||||||
|
Int(SizeT).s_extend_or_bit_cast(generator, ctx, right_val.into_int_value());
|
||||||
|
(list, int)
|
||||||
} else if ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
|
} else if ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
|
||||||
let elem_ty = if let TypeEnum::TObj { params, .. } =
|
// Handle `int * [1, 2, 3]`
|
||||||
&*ctx.unifier.get_ty_immutable(ty2)
|
|
||||||
{
|
let list = AnyObject { ty: ty2, value: right_val };
|
||||||
*params.iter().next().unwrap().1
|
let list = ListObject::from_object(generator, ctx, list);
|
||||||
|
|
||||||
|
let int =
|
||||||
|
Int(SizeT).s_extend_or_bit_cast(generator, ctx, left_val.into_int_value());
|
||||||
|
(list, int)
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
(elem_ty, right_val, left_val)
|
// [...] * (i where i <= 0) => []
|
||||||
} else {
|
let int = call_int_smax(ctx, int.value, llvm_usize.const_zero(), None);
|
||||||
unreachable!()
|
let int = Int(SizeT).check_value(generator, ctx.ctx, int).unwrap();
|
||||||
};
|
|
||||||
let list_val =
|
|
||||||
ListValue::from_ptr_val(list_val.into_pointer_value(), llvm_usize, None);
|
|
||||||
let int_val = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_s_extend(int_val.into_int_value(), llvm_usize, "")
|
|
||||||
.unwrap();
|
|
||||||
// [...] * (i where i < 0) => []
|
|
||||||
let int_val = call_int_smax(ctx, int_val, llvm_usize.const_zero(), None);
|
|
||||||
|
|
||||||
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
|
let list_items = list.instance.get(generator, ctx, |f| f.items);
|
||||||
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
|
let list_len = list.instance.get(generator, ctx, |f| f.len);
|
||||||
|
|
||||||
let new_list = allocate_list(
|
let new_list_len = int.mul(ctx, list_len);
|
||||||
generator,
|
let new_list = ListObject::alloca(generator, ctx, list.item_type, new_list_len);
|
||||||
ctx,
|
let new_list_items = new_list.instance.get(generator, ctx, |f| f.items);
|
||||||
Some(elem_llvm_ty),
|
|
||||||
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
gen_for_callback_incrementing(
|
let num_0 = Int(SizeT).const_int(generator, ctx.ctx, 0);
|
||||||
generator,
|
let num_1 = Int(SizeT).const_int(generator, ctx.ctx, 1);
|
||||||
ctx,
|
gen_for_model(generator, ctx, num_0, int, num_1, |generator, ctx, _hooks, i| {
|
||||||
None,
|
let offset = list_len.mul(ctx, i);
|
||||||
llvm_usize.const_zero(),
|
let ptr = new_list_items.offset(ctx, offset.value);
|
||||||
(int_val, false),
|
|
||||||
|generator, ctx, _, i| {
|
|
||||||
let offset = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_mul(i, list_val.load_size(ctx, None), "")
|
|
||||||
.unwrap();
|
|
||||||
let ptr = unsafe {
|
|
||||||
new_list.data().ptr_offset_unchecked(ctx, generator, &offset, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let list_size = ctx
|
|
||||||
.builder
|
|
||||||
.build_int_z_extend_or_bit_cast(
|
|
||||||
list_val.load_size(ctx, None),
|
|
||||||
sizeof_elem.get_type(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let memcpy_sz =
|
|
||||||
ctx.builder.build_int_mul(list_size, sizeof_elem, "").unwrap();
|
|
||||||
|
|
||||||
call_memcpy_generic(
|
|
||||||
ctx,
|
|
||||||
ptr,
|
|
||||||
list_val.data().base_ptr(ctx, generator),
|
|
||||||
memcpy_sz,
|
|
||||||
ctx.ctx.bool_type().const_zero(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
ptr.copy_from(generator, ctx, list_items, list_len.value);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
})
|
||||||
llvm_usize.const_int(1, false),
|
.unwrap();
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Some(new_list.as_base_value().into()))
|
Ok(Some(new_list.instance.value.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => todo!("Operator not supported"),
|
_ => todo!("Operator not supported"),
|
||||||
|
@ -2074,8 +1969,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||||
.iter()
|
.iter()
|
||||||
.any(|ty| ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()))
|
.any(|ty| ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()))
|
||||||
{
|
{
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
let gen_list_cmpop = |generator: &mut G,
|
let gen_list_cmpop = |generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>|
|
ctx: &mut CodeGenContext<'ctx, '_>|
|
||||||
-> Result<IntValue<'ctx>, String> {
|
-> Result<IntValue<'ctx>, String> {
|
||||||
|
@ -2098,145 +1991,109 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||||
return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)));
|
return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_elem_ty = if let TypeEnum::TObj { params, .. } =
|
|
||||||
&*ctx.unifier.get_ty_immutable(left_ty)
|
|
||||||
{
|
|
||||||
*params.iter().next().unwrap().1
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
let right_elem_ty = if let TypeEnum::TObj { params, .. } =
|
|
||||||
&*ctx.unifier.get_ty_immutable(right_ty)
|
|
||||||
{
|
|
||||||
*params.iter().next().unwrap().1
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !ctx.unifier.unioned(left_elem_ty, right_elem_ty) {
|
|
||||||
return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ![Cmpop::Eq, Cmpop::NotEq].contains(op) {
|
if ![Cmpop::Eq, Cmpop::NotEq].contains(op) {
|
||||||
todo!("Only __eq__ and __ne__ is implemented for lists")
|
todo!("Only __eq__ and __ne__ is implemented for lists")
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_val =
|
let lhs = AnyObject { ty: left_ty, value: *lhs };
|
||||||
ListValue::from_ptr_val(lhs.into_pointer_value(), llvm_usize, None);
|
let lhs = ListObject::from_object(generator, ctx, lhs);
|
||||||
let right_val =
|
|
||||||
ListValue::from_ptr_val(rhs.into_pointer_value(), llvm_usize, None);
|
|
||||||
|
|
||||||
Ok(gen_if_else_expr_callback(
|
let rhs = AnyObject { ty: right_ty, value: *rhs };
|
||||||
|
let rhs = ListObject::from_object(generator, ctx, rhs);
|
||||||
|
|
||||||
|
let lhs_len = lhs.instance.get(generator, ctx, |f| f.len);
|
||||||
|
let rhs_len = rhs.instance.get(generator, ctx, |f| f.len);
|
||||||
|
|
||||||
|
let result = gen_if_else_expr_callback(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
|_, ctx| {
|
|_, ctx| {
|
||||||
Ok(ctx
|
let same_len = lhs_len.compare(ctx, IntPredicate::EQ, rhs_len);
|
||||||
.builder
|
Ok(same_len.value)
|
||||||
.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
|
||||||
left_val.load_size(ctx, None),
|
|
||||||
right_val.load_size(ctx, None),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap())
|
|
||||||
},
|
},
|
||||||
|generator, ctx| {
|
|generator, ctx| {
|
||||||
let acc_addr = generator
|
// The list lengths match. Compare their contents.
|
||||||
.gen_var_alloc(ctx, ctx.ctx.bool_type().into(), None)
|
let common_len = lhs_len;
|
||||||
.unwrap();
|
|
||||||
ctx.builder
|
|
||||||
.build_store(acc_addr, ctx.ctx.bool_type().const_all_ones())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
gen_for_callback_incrementing(
|
let bool_false = Int(Bool).const_false(generator, ctx.ctx);
|
||||||
|
let bool_true = Int(Bool).const_true(generator, ctx.ctx);
|
||||||
|
|
||||||
|
let list_equal = Int(Bool).alloca(generator, ctx);
|
||||||
|
list_equal.store(ctx, bool_true);
|
||||||
|
|
||||||
|
let lhs_items = lhs.instance.get(generator, ctx, |f| f.items);
|
||||||
|
let rhs_items = rhs.instance.get(generator, ctx, |f| f.items);
|
||||||
|
|
||||||
|
let num_0 = Int(SizeT).const_int(generator, ctx.ctx, 0);
|
||||||
|
let num_1 = Int(SizeT).const_int(generator, ctx.ctx, 1);
|
||||||
|
gen_for_model(generator, ctx, num_0, common_len, num_1, |generator, ctx, hooks, i| {
|
||||||
|
let lhs_item = lhs_items.get_index(generator, ctx, i.value);
|
||||||
|
let rhs_item = rhs_items.get_index(generator, ctx, i.value);
|
||||||
|
|
||||||
|
let equal = gen_cmpop_expr_with_values(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
None,
|
(Some(lhs.item_type), lhs_item.value),
|
||||||
llvm_usize.const_zero(),
|
|
||||||
(left_val.load_size(ctx, None), false),
|
|
||||||
|generator, ctx, hooks, i| {
|
|
||||||
let left = unsafe {
|
|
||||||
left_val.data().get_unchecked(ctx, generator, &i, None)
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
right_val.data().get_unchecked(ctx, generator, &i, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = gen_cmpop_expr_with_values(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
(Some(left_elem_ty), left),
|
|
||||||
&[Cmpop::Eq],
|
&[Cmpop::Eq],
|
||||||
&[(Some(right_elem_ty), right)],
|
&[(Some(rhs.item_type), rhs_item.value)],
|
||||||
)?
|
)?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
|
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
|
// equal is an i8, convert it into a i1 with this.
|
||||||
|
let equal = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_compare(
|
||||||
|
IntPredicate::NE,
|
||||||
|
equal,
|
||||||
|
equal.get_type().const_zero(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// if (!equal) { break; }
|
||||||
gen_if_callback(
|
gen_if_callback(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
|_, ctx| {
|
|_generator, _ctx| {
|
||||||
Ok(ctx
|
Ok(equal) },
|
||||||
.builder
|
|_generator, _ctx| {
|
||||||
.build_int_compare(
|
// Items match. Do nothing
|
||||||
IntPredicate::EQ,
|
|
||||||
res,
|
|
||||||
res.get_type().const_zero(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap())
|
|
||||||
},
|
|
||||||
|_, ctx| {
|
|
||||||
ctx.builder
|
|
||||||
.build_store(
|
|
||||||
acc_addr,
|
|
||||||
ctx.ctx.bool_type().const_zero(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
ctx.builder
|
|
||||||
.build_unconditional_branch(hooks.exit_bb)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|_, _| Ok(()),
|
|_generator, ctx| {
|
||||||
)
|
// Items don't match.
|
||||||
.unwrap();
|
list_equal.store(ctx, bool_false);
|
||||||
|
ctx.builder.build_unconditional_branch(hooks.exit_bb).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
llvm_usize.const_int(1, false),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let acc = ctx
|
Ok(())
|
||||||
.builder
|
})?;
|
||||||
.build_load(acc_addr, "")
|
|
||||||
.map(BasicValueEnum::into_int_value)
|
let bool_result = match op {
|
||||||
.unwrap();
|
Cmpop::Eq => {
|
||||||
let acc = if *op == Cmpop::NotEq {
|
list_equal.load(generator, ctx)
|
||||||
gen_unaryop_expr_with_values(
|
},
|
||||||
generator,
|
Cmpop::NotEq => {
|
||||||
ctx,
|
list_equal.load(generator, ctx).not(ctx)
|
||||||
Unaryop::Not,
|
},
|
||||||
(&Some(ctx.primitives.bool), acc.into()),
|
_ => unreachable!()
|
||||||
)?
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
|
|
||||||
.into_int_value()
|
|
||||||
} else {
|
|
||||||
acc
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(generator.bool_to_i8(ctx, acc)))
|
Ok(Some(generator.bool_to_i8(ctx, bool_result.value)))
|
||||||
},
|
},
|
||||||
|generator, ctx| {
|
|generator, ctx| {
|
||||||
|
// The list lengths don't match, return false / true.
|
||||||
Ok(Some(generator.bool_to_i8(ctx, gen_bool_const(ctx, false))))
|
Ok(Some(generator.bool_to_i8(ctx, gen_bool_const(ctx, false))))
|
||||||
},
|
},
|
||||||
)?
|
)?.unwrap();
|
||||||
.map(BasicValueEnum::into_int_value)
|
|
||||||
.unwrap())
|
let result = result.into_int_value();
|
||||||
|
Ok(result)
|
||||||
};
|
};
|
||||||
|
|
||||||
gen_list_cmpop(generator, ctx)?
|
gen_list_cmpop(generator, ctx)?
|
||||||
|
@ -2436,7 +2293,6 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||||
ctx.current_loc = expr.location;
|
ctx.current_loc = expr.location;
|
||||||
let int32 = ctx.ctx.i32_type();
|
let int32 = ctx.ctx.i32_type();
|
||||||
let usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let zero = int32.const_int(0, false);
|
let zero = int32.const_int(0, false);
|
||||||
|
|
||||||
let loc = ctx.debug_info.0.create_debug_location(
|
let loc = ctx.debug_info.0.create_debug_location(
|
||||||
|
@ -2520,8 +2376,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty = if elements.is_empty() {
|
let item_ty = if elements.is_empty() {
|
||||||
let ty = if let TypeEnum::TObj { obj_id, params, .. } =
|
let item_type = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||||
&*ctx.unifier.get_ty(expr.custom.unwrap())
|
&*ctx.unifier.get_ty(expr.custom.unwrap())
|
||||||
{
|
{
|
||||||
assert_eq!(*obj_id, PrimDef::List.id());
|
assert_eq!(*obj_id, PrimDef::List.id());
|
||||||
|
@ -2531,27 +2387,37 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(ty) {
|
if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(item_type) {
|
||||||
|
// `item_type` is not resolved.
|
||||||
|
// This could happen when the user wrote a `[]` and the typechecker has no hints about `item_type`.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(ctx.get_llvm_type(generator, ty))
|
Some(ctx.get_llvm_type(generator, item_type))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(elements[0].get_type())
|
Some(elements[0].get_type())
|
||||||
};
|
};
|
||||||
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
|
|
||||||
let arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list"));
|
let list_len = Int(SizeT).const_int(generator, ctx.ctx, elements.len() as u64);
|
||||||
let arr_ptr = arr_str_ptr.data();
|
let list_item_ty = Any(item_ty.unwrap_or_else(|| {
|
||||||
for (i, v) in elements.iter().enumerate() {
|
// The list is an empty list with an unresolved item_type.
|
||||||
let elem_ptr = arr_ptr.ptr_offset(
|
|
||||||
ctx,
|
// TODO: LLVM 14 requires all pointer types to have a type.
|
||||||
generator,
|
// We will use `size_t` as a placeholder for now.
|
||||||
&usize.const_int(i as u64, false),
|
generator.get_size_type(ctx.ctx).as_basic_type_enum()
|
||||||
Some("elem_ptr"),
|
}));
|
||||||
);
|
|
||||||
ctx.builder.build_store(elem_ptr, *v).unwrap();
|
// Allocate list_items and load in the items
|
||||||
|
let list_items = list_item_ty.array_alloca(generator, ctx, list_len.value);
|
||||||
|
for (i, item) in elements.iter().enumerate() {
|
||||||
|
let item = list_item_ty.believe_value(*item);
|
||||||
|
list_items.set_index_const(ctx, i as u64, item);
|
||||||
}
|
}
|
||||||
arr_str_ptr.as_base_value().into()
|
|
||||||
|
let list = Struct(List { item: list_item_ty }).alloca(generator, ctx);
|
||||||
|
list.set(ctx, |f| f.items, list_items);
|
||||||
|
list.set(ctx, |f| f.len, list_len);
|
||||||
|
list.value.as_basic_value_enum().into()
|
||||||
}
|
}
|
||||||
ExprKind::Tuple { elts, .. } => {
|
ExprKind::Tuple { elts, .. } => {
|
||||||
let elements_val = elts
|
let elements_val = elts
|
||||||
|
@ -2957,18 +2823,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
ExprKind::Subscript { value, slice, .. } => {
|
ExprKind::Subscript { value, slice, .. } => {
|
||||||
match &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
match &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||||
let ty = params.iter().next().unwrap().1;
|
|
||||||
|
|
||||||
let v = if let Some(v) = generator.gen_expr(ctx, value)? {
|
let v = if let Some(v) = generator.gen_expr(ctx, value)? {
|
||||||
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||||
.into_pointer_value()
|
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let v = ListValue::from_ptr_val(v, usize, Some("arr"));
|
|
||||||
let ty = ctx.get_llvm_type(generator, *ty);
|
let list = AnyObject { ty: value.custom.unwrap(), value: v };
|
||||||
|
let list = ListObject::from_object(generator, ctx, list);
|
||||||
|
|
||||||
|
let list_len = list.instance.get(generator, ctx, |f| f.len);
|
||||||
|
|
||||||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||||
|
// Handle `my_list[lower:upper:step]`
|
||||||
let one = int32.const_int(1, false);
|
let one = int32.const_int(1, false);
|
||||||
let Some((start, end, step)) = handle_slice_indices(
|
let Some((start, end, step)) = handle_slice_indices(
|
||||||
lower,
|
lower,
|
||||||
|
@ -2976,7 +2844,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
step,
|
step,
|
||||||
ctx,
|
ctx,
|
||||||
generator,
|
generator,
|
||||||
v.load_size(ctx, None),
|
list_len.value,
|
||||||
)?
|
)?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -2998,37 +2866,37 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
step,
|
step,
|
||||||
);
|
);
|
||||||
let res_array_ret =
|
|
||||||
allocate_list(generator, ctx, Some(ty), length, Some("ret"));
|
let sublist_length =
|
||||||
let Some(res_ind) = handle_slice_indices(
|
Int(SizeT).s_extend_or_bit_cast(generator, ctx, length);
|
||||||
&None,
|
let sublist =
|
||||||
&None,
|
ListObject::alloca(generator, ctx, list.item_type, sublist_length);
|
||||||
&None,
|
let Some(res_ind) =
|
||||||
ctx,
|
handle_slice_indices(&None, &None, &None, ctx, generator, length)?
|
||||||
generator,
|
|
||||||
res_array_ret.load_size(ctx, None),
|
|
||||||
)?
|
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let list_item_type_llvm = ctx.get_llvm_type(generator, list.item_type);
|
||||||
list_slice_assignment(
|
list_slice_assignment(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
ty,
|
list_item_type_llvm,
|
||||||
res_array_ret,
|
sublist.instance,
|
||||||
res_ind,
|
res_ind,
|
||||||
v,
|
list.instance,
|
||||||
(start, end, step),
|
(start, end, step),
|
||||||
);
|
);
|
||||||
res_array_ret.as_base_value().into()
|
sublist.instance.value.as_basic_value_enum().into()
|
||||||
} else {
|
} else {
|
||||||
let len = v.load_size(ctx, Some("len"));
|
// Handle `my_list[i]`
|
||||||
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
|
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
|
||||||
v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
||||||
.into_int_value()
|
.into_int_value()
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let raw_index = ctx
|
let raw_index = ctx
|
||||||
.builder
|
.builder
|
||||||
.build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext")
|
.build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext")
|
||||||
|
@ -3043,8 +2911,10 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
"is_neg",
|
"is_neg",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let adjusted =
|
let adjusted = ctx
|
||||||
ctx.builder.build_int_add(raw_index, len, "adjusted").unwrap();
|
.builder
|
||||||
|
.build_int_add(raw_index, list_len.value, "adjusted")
|
||||||
|
.unwrap();
|
||||||
let index = ctx
|
let index = ctx
|
||||||
.builder
|
.builder
|
||||||
.build_select(is_negative, adjusted, raw_index, "index")
|
.build_select(is_negative, adjusted, raw_index, "index")
|
||||||
|
@ -3054,17 +2924,19 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
// bigger than the length (for unsigned cmp)
|
// bigger than the length (for unsigned cmp)
|
||||||
let bound_check = ctx
|
let bound_check = ctx
|
||||||
.builder
|
.builder
|
||||||
.build_int_compare(IntPredicate::ULT, index, len, "inbound")
|
.build_int_compare(IntPredicate::ULT, index, list_len.value, "inbound")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx.make_assert(
|
ctx.make_assert(
|
||||||
generator,
|
generator,
|
||||||
bound_check,
|
bound_check,
|
||||||
"0:IndexError",
|
"0:IndexError",
|
||||||
"index {0} out of bounds 0:{1}",
|
"index {0} out of bounds 0:{1}",
|
||||||
[Some(raw_index), Some(len), None],
|
[Some(raw_index), Some(list_len.value), None],
|
||||||
expr.location,
|
expr.location,
|
||||||
);
|
);
|
||||||
v.data().get(ctx, generator, &index, None).into()
|
|
||||||
|
let item = list.instance.get_index(generator, ctx, index);
|
||||||
|
item.value.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
|
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
classes::{ArrayLikeValue, ListValue},
|
|
||||||
model::*,
|
model::*,
|
||||||
object::{
|
object::{
|
||||||
list::List,
|
list::List,
|
||||||
|
@ -327,12 +326,11 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
ty: BasicTypeEnum<'ctx>,
|
ty: BasicTypeEnum<'ctx>,
|
||||||
dest_arr: ListValue<'ctx>,
|
dest_arr: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
|
||||||
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||||
src_arr: ListValue<'ctx>,
|
src_arr: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
|
||||||
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||||
) {
|
) {
|
||||||
let size_ty = generator.get_size_type(ctx.ctx);
|
|
||||||
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
||||||
let int32 = ctx.ctx.i32_type();
|
let int32 = ctx.ctx.i32_type();
|
||||||
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
|
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
|
||||||
|
@ -358,16 +356,14 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
|
||||||
let zero = int32.const_zero();
|
let zero = int32.const_zero();
|
||||||
let one = int32.const_int(1, false);
|
let one = int32.const_int(1, false);
|
||||||
let dest_arr_ptr = dest_arr.data().base_ptr(ctx, generator);
|
|
||||||
let dest_arr_ptr =
|
let dest_arr_ptr =
|
||||||
ctx.builder.build_pointer_cast(dest_arr_ptr, elem_ptr_type, "dest_arr_ptr_cast").unwrap();
|
dest_arr.get(generator, ctx, |f| f.items).pointer_cast(generator, ctx, Int(Byte)).value;
|
||||||
let dest_len = dest_arr.load_size(ctx, Some("dest.len"));
|
|
||||||
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32").unwrap();
|
|
||||||
let src_arr_ptr = src_arr.data().base_ptr(ctx, generator);
|
|
||||||
let src_arr_ptr =
|
let src_arr_ptr =
|
||||||
ctx.builder.build_pointer_cast(src_arr_ptr, elem_ptr_type, "src_arr_ptr_cast").unwrap();
|
src_arr.get(generator, ctx, |f| f.items).pointer_cast(generator, ctx, Int(Byte)).value;
|
||||||
let src_len = src_arr.load_size(ctx, Some("src.len"));
|
|
||||||
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32").unwrap();
|
let dest_len = dest_arr.get(generator, ctx, |f| f.len).truncate(generator, ctx, Int32).value;
|
||||||
|
let src_len = src_arr.get(generator, ctx, |f| f.len).truncate(generator, ctx, Int32).value;
|
||||||
|
|
||||||
// index in bound and positive should be done
|
// index in bound and positive should be done
|
||||||
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
||||||
|
@ -463,8 +459,10 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||||
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
|
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
|
||||||
ctx.builder.position_at_end(update_bb);
|
ctx.builder.position_at_end(update_bb);
|
||||||
let new_len = ctx.builder.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len").unwrap();
|
|
||||||
dest_arr.store_size(ctx, generator, new_len);
|
let new_len = Int(SizeT).z_extend_or_bit_cast(generator, ctx, new_len);
|
||||||
|
dest_arr.set(ctx, |f| f.len, new_len);
|
||||||
|
|
||||||
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
|
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
|
||||||
ctx.builder.position_at_end(cont_bb);
|
ctx.builder.position_at_end(cont_bb);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::classes::{ListType, ProxyType},
|
|
||||||
symbol_resolver::{StaticValue, SymbolResolver},
|
symbol_resolver::{StaticValue, SymbolResolver},
|
||||||
toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef},
|
toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef},
|
||||||
typecheck::{
|
typecheck::{
|
||||||
|
@ -26,7 +25,7 @@ use inkwell::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use model::*;
|
use model::*;
|
||||||
use nac3parser::ast::{Location, Stmt, StrRef};
|
use nac3parser::ast::{Location, Stmt, StrRef};
|
||||||
use object::{ndarray::NDArray, range::Range};
|
use object::{list::List, ndarray::NDArray, range::Range};
|
||||||
use parking_lot::{Condvar, Mutex};
|
use parking_lot::{Condvar, Mutex};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
|
@ -488,8 +487,8 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
type_cache,
|
type_cache,
|
||||||
*params.iter().next().unwrap().1,
|
*params.iter().next().unwrap().1,
|
||||||
);
|
);
|
||||||
|
let item = Any(element_type);
|
||||||
ListType::new(generator, ctx, element_type).as_base_type().into()
|
Ptr(Struct(List { item })).get_type(generator, ctx).as_basic_type_enum()
|
||||||
}
|
}
|
||||||
|
|
||||||
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||||
|
|
|
@ -109,4 +109,25 @@ impl<'ctx> ListObject<'ctx> {
|
||||||
|
|
||||||
opaque_list
|
opaque_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate a list on the stack given its `item_type` and `len`.
|
||||||
|
///
|
||||||
|
/// The returned list's content will be:
|
||||||
|
/// - `items`: allocated with an array of length `len` with uninitialized values.
|
||||||
|
/// - `len`: set to `len`.
|
||||||
|
pub fn alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
item_type: Type,
|
||||||
|
len: Instance<'ctx, Int<SizeT>>,
|
||||||
|
) -> Self {
|
||||||
|
let item_type_llvm = ctx.get_llvm_type(generator, item_type);
|
||||||
|
let items = Any(item_type_llvm).array_alloca(generator, ctx, len.value);
|
||||||
|
|
||||||
|
let instance = Struct(List { item: Any(item_type_llvm) }).alloca(generator, ctx);
|
||||||
|
instance.set(ctx, |f| f.items, items);
|
||||||
|
instance.set(ctx, |f| f.len, len);
|
||||||
|
|
||||||
|
ListObject { item_type, instance }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use super::{
|
||||||
irrt::{handle_slice_indices, list_slice_assignment},
|
irrt::{handle_slice_indices, list_slice_assignment},
|
||||||
object::{
|
object::{
|
||||||
any::AnyObject,
|
any::AnyObject,
|
||||||
|
list::ListObject,
|
||||||
ndarray::{
|
ndarray::{
|
||||||
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
|
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
|
||||||
},
|
},
|
||||||
|
@ -11,11 +12,7 @@ use super::{
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{classes::ArraySliceValue, expr::gen_binop_expr, gen_in_range_check, model::*},
|
||||||
classes::{ArrayLikeIndexer, ArraySliceValue, ListValue},
|
|
||||||
expr::gen_binop_expr,
|
|
||||||
gen_in_range_check,
|
|
||||||
},
|
|
||||||
toplevel::{DefinitionId, TopLevelDef},
|
toplevel::{DefinitionId, TopLevelDef},
|
||||||
typecheck::{
|
typecheck::{
|
||||||
magic_methods::Binop,
|
magic_methods::Binop,
|
||||||
|
@ -296,63 +293,56 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
|
||||||
let key_ty = key.custom.unwrap();
|
let key_ty = key.custom.unwrap();
|
||||||
|
|
||||||
match &*ctx.unifier.get_ty(target_ty) {
|
match &*ctx.unifier.get_ty(target_ty) {
|
||||||
TypeEnum::TObj { obj_id, params: list_params, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
// Handle list item assignment
|
// Handle list item assignment
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
let target_item_ty = iter_type_vars(list_params).next().unwrap().ty;
|
|
||||||
|
|
||||||
let target = generator
|
let target = generator
|
||||||
.gen_expr(ctx, target)?
|
.gen_expr(ctx, target)?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_basic_value_enum(ctx, generator, target_ty)?
|
.to_basic_value_enum(ctx, generator, target_ty)?;
|
||||||
.into_pointer_value();
|
|
||||||
let target = ListValue::from_ptr_val(target, llvm_usize, None);
|
let target = AnyObject { ty: target_ty, value: target };
|
||||||
|
let target = ListObject::from_object(generator, ctx, target);
|
||||||
|
|
||||||
|
let target_len = target.instance.get(generator, ctx, |f| f.len);
|
||||||
|
let target_item_type_llvm = ctx.get_llvm_type(generator, target.item_type);
|
||||||
|
|
||||||
if let ExprKind::Slice { .. } = &key.node {
|
if let ExprKind::Slice { .. } = &key.node {
|
||||||
// Handle assigning to a slice
|
// Handle assigning to a slice
|
||||||
let ExprKind::Slice { lower, upper, step } = &key.node else { unreachable!() };
|
let ExprKind::Slice { lower, upper, step } = &key.node else { unreachable!() };
|
||||||
let Some((start, end, step)) = handle_slice_indices(
|
let Some((start, end, step)) =
|
||||||
lower,
|
handle_slice_indices(lower, upper, step, ctx, generator, target_len.value)?
|
||||||
upper,
|
|
||||||
step,
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
target.load_size(ctx, None),
|
|
||||||
)?
|
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let value =
|
let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
|
||||||
value.to_basic_value_enum(ctx, generator, value_ty)?.into_pointer_value();
|
|
||||||
let value = ListValue::from_ptr_val(value, llvm_usize, None);
|
|
||||||
|
|
||||||
let target_item_ty = ctx.get_llvm_type(generator, target_item_ty);
|
let value = AnyObject { ty: value_ty, value };
|
||||||
let Some(src_ind) = handle_slice_indices(
|
let value = ListObject::from_object(generator, ctx, value);
|
||||||
&None,
|
|
||||||
&None,
|
let value_len = value.instance.get(generator, ctx, |f| f.len);
|
||||||
&None,
|
|
||||||
ctx,
|
let Some(src_ind) =
|
||||||
generator,
|
handle_slice_indices(&None, &None, &None, ctx, generator, value_len.value)?
|
||||||
value.load_size(ctx, None),
|
|
||||||
)?
|
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
list_slice_assignment(
|
list_slice_assignment(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
target_item_ty,
|
target_item_type_llvm,
|
||||||
target,
|
target.instance,
|
||||||
(start, end, step),
|
(start, end, step),
|
||||||
value,
|
value.instance,
|
||||||
src_ind,
|
src_ind,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Handle assigning to an index
|
// Handle assigning to an index
|
||||||
let len = target.load_size(ctx, Some("len"));
|
let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
|
||||||
|
let value =
|
||||||
|
Any(target_item_type_llvm).check_value(generator, ctx.ctx, value).unwrap();
|
||||||
|
|
||||||
let index = generator
|
let index = generator
|
||||||
.gen_expr(ctx, key)?
|
.gen_expr(ctx, key)?
|
||||||
|
@ -374,7 +364,8 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
|
||||||
"is_neg",
|
"is_neg",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let adjusted = ctx.builder.build_int_add(index, len, "adjusted").unwrap();
|
let adjusted =
|
||||||
|
ctx.builder.build_int_add(index, target_len.value, "adjusted").unwrap();
|
||||||
let index = ctx
|
let index = ctx
|
||||||
.builder
|
.builder
|
||||||
.build_select(is_negative, adjusted, index, "index")
|
.build_select(is_negative, adjusted, index, "index")
|
||||||
|
@ -385,22 +376,23 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
|
||||||
// bigger than the length (for unsigned cmp)
|
// bigger than the length (for unsigned cmp)
|
||||||
let bound_check = ctx
|
let bound_check = ctx
|
||||||
.builder
|
.builder
|
||||||
.build_int_compare(IntPredicate::ULT, index, len, "inbound")
|
.build_int_compare(IntPredicate::ULT, index, target_len.value, "inbound")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx.make_assert(
|
ctx.make_assert(
|
||||||
generator,
|
generator,
|
||||||
bound_check,
|
bound_check,
|
||||||
"0:IndexError",
|
"0:IndexError",
|
||||||
"index {0} out of bounds 0:{1}",
|
"index {0} out of bounds 0:{1}",
|
||||||
[Some(index), Some(len), None],
|
[Some(index), Some(target_len.value), None],
|
||||||
key.location,
|
key.location,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write value to index on list
|
// Write value to index on list
|
||||||
let item_ptr =
|
target
|
||||||
target.data().ptr_offset(ctx, generator, &index, Some("list_item_ptr"));
|
.instance
|
||||||
let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
|
.get(generator, ctx, |f| f.items)
|
||||||
ctx.builder.build_store(item_ptr, value).unwrap();
|
.offset(ctx, index)
|
||||||
|
.store(ctx, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{ListType, ProxyType},
|
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
|
||||||
concrete_type::ConcreteTypeStore,
|
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
|
||||||
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask,
|
|
||||||
CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry,
|
|
||||||
},
|
},
|
||||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||||
toplevel::{
|
toplevel::{
|
||||||
|
@ -436,15 +434,3 @@ fn test_simple_call() {
|
||||||
registry.add_task(task);
|
registry.add_task(task);
|
||||||
registry.wait_tasks_complete(handles);
|
registry.wait_tasks_complete(handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_classes_list_type_new() {
|
|
||||||
let ctx = inkwell::context::Context::create();
|
|
||||||
let generator = DefaultCodeGenerator::new(String::new(), 64);
|
|
||||||
|
|
||||||
let llvm_i32 = ctx.i32_type();
|
|
||||||
let llvm_usize = generator.get_size_type(&ctx);
|
|
||||||
|
|
||||||
let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into());
|
|
||||||
assert!(ListType::is_type(llvm_list.as_base_type(), llvm_usize).is_ok());
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue