forked from M-Labs/nac3
nac3core: list slice support
This commit is contained in:
parent
82509d60ec
commit
4976e89ae2
@ -4,7 +4,7 @@ use crate::{
|
||||
codegen::{
|
||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||
get_llvm_type,
|
||||
irrt::integer_power,
|
||||
irrt::*,
|
||||
CodeGenContext, CodeGenTask,
|
||||
},
|
||||
symbol_resolver::{SymbolValue, ValueEnum},
|
||||
@ -1028,31 +1028,59 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
if let TypeEnum::TList { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
if let ExprKind::Slice { .. } = slice.node {
|
||||
unimplemented!()
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
let ty = ctx.get_llvm_type(generator, *ty);
|
||||
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]).into_pointer_value();
|
||||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||
let one = int32.const_int(1, false);
|
||||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, v);
|
||||
let length = calculate_len_for_slice_range(
|
||||
ctx,
|
||||
start,
|
||||
ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
step,
|
||||
zero,
|
||||
"is_neg",
|
||||
),
|
||||
ctx.builder.build_int_sub(end, one, "e_min_one"),
|
||||
ctx.builder.build_int_add(end, one, "e_add_one"),
|
||||
"final_e",
|
||||
)
|
||||
.into_int_value(),
|
||||
step,
|
||||
);
|
||||
let res_array_ret = allocate_list(generator, ctx, ty, length);
|
||||
let res_ind =
|
||||
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret);
|
||||
list_slice_assignment(
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
ty,
|
||||
res_array_ret,
|
||||
res_ind,
|
||||
v,
|
||||
(start, end, step),
|
||||
);
|
||||
res_array_ret.into()
|
||||
} else {
|
||||
// TODO: bound check
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
let index = generator
|
||||
.gen_expr(ctx, slice)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
let zero = int32.const_zero();
|
||||
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]);
|
||||
ctx.build_gep_and_load(arr_ptr.into_pointer_value(), &[index])
|
||||
ctx.build_gep_and_load(arr_ptr, &[index])
|
||||
}
|
||||
} else {
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
let index = generator
|
||||
.gen_expr(ctx, slice)
|
||||
.unwrap()
|
||||
|
@ -5,9 +5,12 @@ typedef unsigned _ExtInt(32) uint32_t;
|
||||
typedef _ExtInt(64) int64_t;
|
||||
typedef unsigned _ExtInt(64) uint64_t;
|
||||
|
||||
# define MAX(a, b) (a > b ? a : b)
|
||||
# define MIN(a, b) (a > b ? b : a)
|
||||
|
||||
// adapted from GNU Scientific Library: https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
||||
// need to make sure `exp >= 0` before calling this function
|
||||
#define DEF_INT_EXP(T) T __nac3_irrt_int_exp_##T( \
|
||||
#define DEF_INT_EXP(T) T __nac3_int_exp_##T( \
|
||||
T base, \
|
||||
T exp \
|
||||
) { \
|
||||
@ -23,3 +26,106 @@ typedef unsigned _ExtInt(64) uint64_t;
|
||||
|
||||
DEF_INT_EXP(int32_t)
|
||||
DEF_INT_EXP(int64_t)
|
||||
|
||||
|
||||
int32_t __nac3_slice_index_bound(int32_t i, const int32_t len) {
|
||||
if (i < 0) {
|
||||
i = len + i;
|
||||
}
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
} else if (i > len) {
|
||||
return len;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int32_t __nac3_range_slice_len(const int32_t start, const int32_t end, const int32_t step) {
|
||||
int32_t diff = end - start;
|
||||
if (diff > 0 && step > 0) {
|
||||
return ((diff - 1) / step) + 1;
|
||||
} else if (diff < 0 && step < 0) {
|
||||
return ((diff + 1) / step) + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle list assignment and dropping part of the list when
|
||||
// both dest_step and src_step are +1.
|
||||
// - All the index must *not* be out-of-bound or negative,
|
||||
// - The end index is *inclusive*,
|
||||
// - The length of src and dest slice size should already
|
||||
// be checked: if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest)
|
||||
# define DEF_SLICE_ASSIGN(T) int32_t __nac3_list_slice_assign_##T( \
|
||||
int32_t dest_start, \
|
||||
int32_t dest_end, \
|
||||
int32_t dest_step, \
|
||||
T *dest_arr, \
|
||||
int32_t dest_arr_len, \
|
||||
int32_t src_start, \
|
||||
int32_t src_end, \
|
||||
int32_t src_step, \
|
||||
T *src_arr, \
|
||||
int32_t src_arr_len \
|
||||
) { \
|
||||
/* if dest_arr_len == 0, do nothing since we do not support extending list */ \
|
||||
if (dest_arr_len == 0) return dest_arr_len; \
|
||||
/* if both step is 1, memmove directly, handle the dropping of the list, and shrink size */ \
|
||||
if (src_step == dest_step && dest_step == 1) { \
|
||||
const int32_t src_len = (src_end >= src_start) ? (src_end - src_start + 1) : 0; \
|
||||
const int32_t dest_len = (dest_end >= dest_start) ? (dest_end - dest_start + 1) : 0; \
|
||||
if (src_len > 0) { \
|
||||
__builtin_memmove( \
|
||||
dest_arr + dest_start, \
|
||||
src_arr + src_start, \
|
||||
src_len * sizeof(T) \
|
||||
); \
|
||||
} \
|
||||
if (dest_len > 0) { \
|
||||
/* dropping */ \
|
||||
__builtin_memmove( \
|
||||
dest_arr + dest_start + src_len, \
|
||||
dest_arr + dest_end + 1, \
|
||||
(dest_arr_len - dest_end - 1) * sizeof(T) \
|
||||
); \
|
||||
} \
|
||||
/* shrink size */ \
|
||||
return dest_arr_len - (dest_len - src_len); \
|
||||
} \
|
||||
/* if two range overlaps, need alloca */ \
|
||||
uint8_t need_alloca = \
|
||||
(dest_arr == src_arr) \
|
||||
&& !( \
|
||||
MAX(dest_start, dest_end) < MIN(src_start, src_end) \
|
||||
|| MAX(src_start, src_end) < MIN(dest_start, dest_end) \
|
||||
); \
|
||||
if (need_alloca) { \
|
||||
T *tmp = alloca(src_arr_len * sizeof(T)); \
|
||||
__builtin_memcpy(tmp, src_arr, src_arr_len * sizeof(T)); \
|
||||
src_arr = tmp; \
|
||||
} \
|
||||
int32_t src_ind = src_start; \
|
||||
int32_t dest_ind = dest_start; \
|
||||
for (; \
|
||||
(src_step > 0) ? (src_ind <= src_end) : (src_ind >= src_end); \
|
||||
src_ind += src_step, dest_ind += dest_step \
|
||||
) { \
|
||||
dest_arr[dest_ind] = src_arr[src_ind]; \
|
||||
} \
|
||||
/* only dest_step == 1 can we shrink the dest list. */ \
|
||||
/* size should be ensured prior to calling this function */ \
|
||||
if (dest_step == 1 && dest_end >= dest_start) { \
|
||||
__builtin_memmove( \
|
||||
dest_arr + dest_ind, \
|
||||
dest_arr + dest_end + 1, \
|
||||
(dest_arr_len - dest_end - 1) * sizeof(T) \
|
||||
); \
|
||||
return dest_arr_len - (dest_end - dest_ind) - 1; \
|
||||
} \
|
||||
return dest_arr_len; \
|
||||
} \
|
||||
|
||||
DEF_SLICE_ASSIGN(uint8_t)
|
||||
DEF_SLICE_ASSIGN(uint32_t)
|
||||
DEF_SLICE_ASSIGN(uint64_t)
|
||||
|
@ -1,11 +1,16 @@
|
||||
use super::CodeGenContext;
|
||||
use crate::typecheck::typedef::Type;
|
||||
|
||||
use super::{CodeGenContext, CodeGenerator};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
context::Context,
|
||||
memory_buffer::MemoryBuffer,
|
||||
module::Module,
|
||||
values::IntValue,
|
||||
types::{BasicTypeEnum, IntType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate,
|
||||
};
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
pub fn load_irrt(ctx: &Context) -> Module {
|
||||
let bitcode_buf = MemoryBuffer::create_from_memory_range(
|
||||
@ -14,7 +19,12 @@ pub fn load_irrt(ctx: &Context) -> Module {
|
||||
);
|
||||
let irrt_mod = Module::parse_bitcode_from_buffer(&bitcode_buf, ctx).unwrap();
|
||||
let inline_attr = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
for symbol in &["__nac3_irrt_int_exp_int32_t", "__nac3_irrt_int_exp_int64_t"] {
|
||||
for symbol in &[
|
||||
"__nac3_int_exp_int32_t",
|
||||
"__nac3_int_exp_int64_t",
|
||||
"__nac3_range_slice_len",
|
||||
"__nac3_slice_index_bound",
|
||||
] {
|
||||
let function = irrt_mod.get_function(symbol).unwrap();
|
||||
function.add_attribute(AttributeLoc::Function, ctx.create_enum_attribute(inline_attr, 0));
|
||||
}
|
||||
@ -29,8 +39,8 @@ pub fn integer_power<'ctx, 'a>(
|
||||
exp: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width()) {
|
||||
(32, 32) => "__nac3_irrt_int_exp_int32_t",
|
||||
(64, 64) => "__nac3_irrt_int_exp_int64_t",
|
||||
(32, 32) => "__nac3_int_exp_int32_t",
|
||||
(64, 64) => "__nac3_int_exp_int64_t",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let base_type = base.get_type();
|
||||
@ -45,3 +55,287 @@ pub fn integer_power<'ctx, 'a>(
|
||||
.unwrap_left()
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
start: IntValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
step: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
const SYMBOL: &str = "__nac3_range_slice_len";
|
||||
let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
|
||||
let i32_t = ctx.ctx.i32_type();
|
||||
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false);
|
||||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
// TODO: assert step != 0, throw exception if not
|
||||
ctx.builder
|
||||
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
/// NOTE: the output value of the end index of this function should be compared ***inclusively***,
|
||||
/// because python allows `a[2::-1]`, whose semantic is `[a[2], a[1], a[0]]`, which is equivalent to
|
||||
/// NO numeric slice in python.
|
||||
///
|
||||
/// equivalent code:
|
||||
/// ```pseudo_code
|
||||
/// match (start, end, step):
|
||||
/// case (s, e, None | Some(step)) if step > 0:
|
||||
/// return (
|
||||
/// match s:
|
||||
/// case None:
|
||||
/// 0
|
||||
/// case Some(s):
|
||||
/// handle_in_bound(s)
|
||||
/// ,match e:
|
||||
/// case None:
|
||||
/// length - 1
|
||||
/// case Some(e):
|
||||
/// handle_in_bound(e) - 1
|
||||
/// ,step == None ? 1 : step
|
||||
/// )
|
||||
/// case (s, e, Some(step)) if step < 0:
|
||||
/// return (
|
||||
/// match s:
|
||||
/// case None:
|
||||
/// length - 1
|
||||
/// case Some(s):
|
||||
/// s = handle_in_bound(s)
|
||||
/// if s == length:
|
||||
/// s - 1
|
||||
/// else:
|
||||
/// s
|
||||
/// ,match e:
|
||||
/// case None:
|
||||
/// 0
|
||||
/// case Some(e):
|
||||
/// handle_in_bound(e) + 1
|
||||
/// ,step
|
||||
/// )
|
||||
/// ```
|
||||
pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
||||
start: &Option<Box<Expr<Option<Type>>>>,
|
||||
end: &Option<Box<Expr<Option<Type>>>>,
|
||||
step: &Option<Box<Expr<Option<Type>>>>,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut G,
|
||||
list: PointerValue<'ctx>,
|
||||
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
|
||||
// TODO: throw exception when step is 0
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let one = int32.const_int(1, false);
|
||||
let length = ctx.build_gep_and_load(list, &[zero, one]).into_int_value();
|
||||
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32");
|
||||
match (start, end, step) {
|
||||
(s, e, None) => (
|
||||
s.as_ref().map_or_else(
|
||||
|| int32.const_zero(),
|
||||
|s| handle_slice_index_bound(s, ctx, generator, length),
|
||||
),
|
||||
{
|
||||
let e = e.as_ref().map_or_else(
|
||||
|| length,
|
||||
|e| handle_slice_index_bound(e, ctx, generator, length),
|
||||
);
|
||||
ctx.builder.build_int_sub(e, one, "final_end")
|
||||
},
|
||||
one,
|
||||
),
|
||||
(s, e, Some(step)) => {
|
||||
let step = generator
|
||||
.gen_expr(ctx, step)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1");
|
||||
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg");
|
||||
(
|
||||
match s {
|
||||
Some(s) => {
|
||||
let s = handle_slice_index_bound(s, ctx, generator, length);
|
||||
ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_and(
|
||||
ctx.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
s,
|
||||
length,
|
||||
"s_eq_len",
|
||||
),
|
||||
neg,
|
||||
"should_minus_one",
|
||||
),
|
||||
ctx.builder.build_int_sub(s, one, "s_min"),
|
||||
s,
|
||||
"final_start",
|
||||
)
|
||||
.into_int_value()
|
||||
}
|
||||
None => ctx.builder.build_select(neg, len_id, zero, "stt").into_int_value(),
|
||||
},
|
||||
match e {
|
||||
Some(e) => {
|
||||
let e = handle_slice_index_bound(e, ctx, generator, length);
|
||||
ctx.builder
|
||||
.build_select(
|
||||
neg,
|
||||
ctx.builder.build_int_add(e, one, "end_add_one"),
|
||||
ctx.builder.build_int_sub(e, one, "end_sub_one"),
|
||||
"final_end",
|
||||
)
|
||||
.into_int_value()
|
||||
}
|
||||
None => ctx.builder.build_select(neg, zero, len_id, "end").into_int_value(),
|
||||
},
|
||||
step,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// this function allows index out of range, since python
|
||||
/// allows index out of range in slice (`a = [1,2,3]; a[1:10] == [2,3]`).
|
||||
pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
||||
i: &Expr<Option<Type>>,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut G,
|
||||
length: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
const SYMBOL: &str = "__nac3_slice_index_bound";
|
||||
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
|
||||
let i32_t = ctx.ctx.i32_type();
|
||||
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into()], false);
|
||||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
let i = generator.gen_expr(ctx, i).unwrap().to_basic_value_enum(ctx, generator);
|
||||
ctx.builder
|
||||
.build_call(func, &[i.into(), length.into()], "bounded_ind")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
/// This function handles 'end' **inclusively**.
|
||||
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
|
||||
/// Negative index should be handled before entering this function
|
||||
pub fn list_slice_assignment<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
size_ty: IntType<'ctx>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
dest_arr: PointerValue<'ctx>,
|
||||
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||
src_arr: PointerValue<'ctx>,
|
||||
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||
) {
|
||||
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let int32_ptr = int32.ptr_type(AddressSpace::Generic);
|
||||
let int64_ptr = ctx.ctx.i64_type().ptr_type(AddressSpace::Generic);
|
||||
let fun_symbol = if let BasicTypeEnum::IntType(ty) = ty {
|
||||
match ty.get_bit_width() {
|
||||
w if w < 32 => "__nac3_list_slice_assign_uint8_t",
|
||||
32 => "__nac3_list_slice_assign_uint32_t",
|
||||
64 => "__nac3_list_slice_assign_uint64_t",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else if ty.is_float_type() {
|
||||
"__nac3_list_slice_assign_uint64_t"
|
||||
} else if ty.is_pointer_type() {
|
||||
match size_ty.get_bit_width() {
|
||||
32 => "__nac3_list_slice_assign_uint32_t",
|
||||
64 => "__nac3_list_slice_assign_uint64_t",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let elem_ptr_type = match fun_symbol {
|
||||
"__nac3_list_slice_assign_uint8_t" => int8_ptr,
|
||||
"__nac3_list_slice_assign_uint32_t" => int32_ptr,
|
||||
"__nac3_list_slice_assign_uint64_t" => int64_ptr,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let slice_assign_fun = ctx.module.get_function(fun_symbol).unwrap_or_else(|| {
|
||||
let fn_t = int32.fn_type(
|
||||
&[
|
||||
int32.into(), // dest start idx
|
||||
int32.into(), // dest end idx
|
||||
int32.into(), // dest step
|
||||
elem_ptr_type.into(), // dest arr ptr
|
||||
int32.into(), // dest arr len
|
||||
int32.into(), // src start idx
|
||||
int32.into(), // src end idx
|
||||
int32.into(), // src step
|
||||
elem_ptr_type.into(), // src arr ptr
|
||||
int32.into(), // src arr len
|
||||
],
|
||||
false,
|
||||
);
|
||||
ctx.module.add_function(fun_symbol, fn_t, None)
|
||||
});
|
||||
|
||||
let zero = int32.const_zero();
|
||||
let one = int32.const_int(1, false);
|
||||
let dest_arr_ptr = ctx.build_gep_and_load(dest_arr, &[zero, zero]);
|
||||
let dest_arr_ptr = ctx.builder.build_pointer_cast(
|
||||
dest_arr_ptr.into_pointer_value(),
|
||||
elem_ptr_type,
|
||||
"dest_arr_ptr_cast",
|
||||
);
|
||||
let dest_len = ctx.build_gep_and_load(dest_arr, &[zero, one]).into_int_value();
|
||||
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32");
|
||||
let src_arr_ptr = ctx.build_gep_and_load(src_arr, &[zero, zero]);
|
||||
let src_arr_ptr = ctx.builder.build_pointer_cast(
|
||||
src_arr_ptr.into_pointer_value(),
|
||||
elem_ptr_type,
|
||||
"src_arr_ptr_cast",
|
||||
);
|
||||
let src_len = ctx.build_gep_and_load(src_arr, &[zero, one]).into_int_value();
|
||||
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
|
||||
|
||||
// index in bound and positive should be done
|
||||
// TODO: assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
||||
// throw exception if not satisfied
|
||||
let new_len = ctx
|
||||
.builder
|
||||
.build_call(
|
||||
slice_assign_fun,
|
||||
&[
|
||||
dest_idx.0.into(), // dest start idx
|
||||
dest_idx.1.into(), // dest end idx
|
||||
dest_idx.2.into(), // dest step
|
||||
dest_arr_ptr.into(), // dest arr ptr
|
||||
dest_len.into(), // dest arr len
|
||||
src_idx.0.into(), // src start idx
|
||||
src_idx.1.into(), // src end idx
|
||||
src_idx.2.into(), // src step
|
||||
src_arr_ptr.into(), // src arr ptr
|
||||
src_len.into(), // src arr len
|
||||
],
|
||||
"slice_assign",
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.unwrap_left()
|
||||
.into_int_value();
|
||||
// update length
|
||||
let need_update =
|
||||
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update");
|
||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let update_bb = ctx.ctx.append_basic_block(current, "update");
|
||||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb);
|
||||
ctx.builder.position_at_end(update_bb);
|
||||
let dest_len_ptr = unsafe { ctx.builder.build_gep(dest_arr, &[zero, one], "dest_len_ptr") };
|
||||
let new_len = ctx.builder.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len");
|
||||
ctx.builder.build_store(dest_len_ptr, new_len);
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
use super::{
|
||||
super::symbol_resolver::ValueEnum, expr::destructure_range, CodeGenContext, CodeGenerator,
|
||||
super::symbol_resolver::ValueEnum,
|
||||
expr::destructure_range,
|
||||
irrt::{handle_slice_indices, list_slice_assignment},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
use crate::{
|
||||
codegen::expr::gen_binop_expr,
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
use crate::{codegen::expr::gen_binop_expr, typecheck::typedef::Type};
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
@ -97,16 +103,53 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
||||
unreachable!()
|
||||
}
|
||||
} else {
|
||||
let ptr = generator.gen_store_target(ctx, target);
|
||||
if let ExprKind::Name { id, .. } = &target.node {
|
||||
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
|
||||
*counter += 1;
|
||||
if let ValueEnum::Static(s) = &value {
|
||||
*static_value = Some(s.clone());
|
||||
match &target.node {
|
||||
ExprKind::Subscript { value: ls, slice, .. }
|
||||
if matches!(&slice.node, ExprKind::Slice { .. }) =>
|
||||
{
|
||||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||
let ls = generator
|
||||
.gen_expr(ctx, ls)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, ls);
|
||||
let value = value.to_basic_value_enum(ctx, generator).into_pointer_value();
|
||||
let ty = if let TypeEnum::TList { ty } =
|
||||
&*ctx.unifier.get_ty(target.custom.unwrap())
|
||||
{
|
||||
ctx.get_llvm_type(generator, *ty)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value);
|
||||
list_slice_assignment(
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
ty,
|
||||
ls,
|
||||
(start, end, step),
|
||||
value,
|
||||
src_ind,
|
||||
)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let ptr = generator.gen_store_target(ctx, target);
|
||||
if let ExprKind::Name { id, .. } = &target.node {
|
||||
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
|
||||
*counter += 1;
|
||||
if let ValueEnum::Static(s) = &value {
|
||||
*static_value = Some(s.clone());
|
||||
}
|
||||
}
|
||||
let val = value.to_basic_value_enum(ctx, generator);
|
||||
ctx.builder.build_store(ptr, val);
|
||||
}
|
||||
}
|
||||
let val = value.to_basic_value_enum(ctx, generator);
|
||||
ctx.builder.build_store(ptr, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
use std::cell::RefCell;
|
||||
use inkwell::{IntPredicate::{self, *}, FloatPredicate, values::IntValue};
|
||||
use crate::{symbol_resolver::SymbolValue, codegen::expr::destructure_range};
|
||||
use super::*;
|
||||
use crate::{
|
||||
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range},
|
||||
symbol_resolver::SymbolValue,
|
||||
};
|
||||
use inkwell::{FloatPredicate, IntPredicate};
|
||||
use std::cell::RefCell;
|
||||
|
||||
type BuiltinInfo = (
|
||||
Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>,
|
||||
@ -622,78 +625,3 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
// equivalent code:
|
||||
// def length(start, end, step != 0):
|
||||
// diff = end - start
|
||||
// if diff > 0 and step > 0:
|
||||
// return ((diff - 1) // step) + 1
|
||||
// elif diff < 0 and step < 0:
|
||||
// return ((diff + 1) // step) + 1
|
||||
// else:
|
||||
// return 0
|
||||
pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
start: IntValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
step: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let start = ctx.builder.build_int_s_extend(start, int32, "start");
|
||||
let end = ctx.builder.build_int_s_extend(end, int32, "end");
|
||||
let step = ctx.builder.build_int_s_extend(step, int32, "step");
|
||||
let diff = ctx.builder.build_int_sub(end, start, "diff");
|
||||
|
||||
let diff_pos = ctx.builder.build_int_compare(SGT, diff, int32.const_zero(), "diffpos");
|
||||
let step_pos = ctx.builder.build_int_compare(SGT, step, int32.const_zero(), "steppos");
|
||||
let test_1 = ctx.builder.build_and(diff_pos, step_pos, "bothpos");
|
||||
|
||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let then_bb = ctx.ctx.append_basic_block(current, "then");
|
||||
let else_bb = ctx.ctx.append_basic_block(current, "else");
|
||||
let then_bb_2 = ctx.ctx.append_basic_block(current, "then_2");
|
||||
let else_bb_2 = ctx.ctx.append_basic_block(current, "else_2");
|
||||
let cont_bb_2 = ctx.ctx.append_basic_block(current, "cont_2");
|
||||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||
ctx.builder.build_conditional_branch(test_1, then_bb, else_bb);
|
||||
|
||||
ctx.builder.position_at_end(then_bb);
|
||||
let length_pos = {
|
||||
let diff_pos_min_1 = ctx.builder.build_int_sub(diff, int32.const_int(1, false), "diffminone");
|
||||
let length_pos = ctx.builder.build_int_signed_div(diff_pos_min_1, step, "div");
|
||||
ctx.builder.build_int_add(length_pos, int32.const_int(1, false), "add1")
|
||||
};
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
|
||||
ctx.builder.position_at_end(else_bb);
|
||||
let phi_1 = {
|
||||
let diff_neg = ctx.builder.build_int_compare(SLT, diff, int32.const_zero(), "diffneg");
|
||||
let step_neg = ctx.builder.build_int_compare(SLT, step, int32.const_zero(), "stepneg");
|
||||
let test_2 = ctx.builder.build_and(diff_neg, step_neg, "bothneg");
|
||||
|
||||
ctx.builder.build_conditional_branch(test_2, then_bb_2, else_bb_2);
|
||||
|
||||
ctx.builder.position_at_end(then_bb_2);
|
||||
let length_neg = {
|
||||
let diff_neg_add_1 = ctx.builder.build_int_add(diff, int32.const_int(1, false), "diffminone");
|
||||
let length_neg = ctx.builder.build_int_signed_div(diff_neg_add_1, step, "div");
|
||||
ctx.builder.build_int_add(length_neg, int32.const_int(1, false), "add1")
|
||||
};
|
||||
ctx.builder.build_unconditional_branch(cont_bb_2);
|
||||
|
||||
ctx.builder.position_at_end(else_bb_2);
|
||||
let length_zero = int32.const_zero();
|
||||
ctx.builder.build_unconditional_branch(cont_bb_2);
|
||||
|
||||
ctx.builder.position_at_end(cont_bb_2);
|
||||
let phi_1 = ctx.builder.build_phi(int32, "lenphi1");
|
||||
phi_1.add_incoming(&[(&length_neg, then_bb_2), (&length_zero, else_bb_2)]);
|
||||
phi_1.as_basic_value().into_int_value()
|
||||
};
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
let phi = ctx.builder.build_phi(int32, "lenphi");
|
||||
phi.add_incoming(&[(&length_pos, then_bb), (&phi_1, cont_bb_2)]);
|
||||
phi.as_basic_value().into_int_value()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user