List Slice Support (#72) #140
@ -4,7 +4,7 @@ use crate::{
|
|||||||
codegen::{
|
codegen::{
|
||||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||||
get_llvm_type,
|
get_llvm_type,
|
||||||
irrt::integer_power,
|
irrt::*,
|
||||||
CodeGenContext, CodeGenTask,
|
CodeGenContext, CodeGenTask,
|
||||||
},
|
},
|
||||||
symbol_resolver::{SymbolValue, ValueEnum},
|
symbol_resolver::{SymbolValue, ValueEnum},
|
||||||
@ -1028,31 +1028,59 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Subscript { value, slice, .. } => {
|
ExprKind::Subscript { value, slice, .. } => {
|
||||||
if let TypeEnum::TList { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
let v = generator
|
||||||
if let ExprKind::Slice { .. } = slice.node {
|
.gen_expr(ctx, value)
|
||||||
unimplemented!()
|
.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 {
|
} else {
|
||||||
// TODO: bound check
|
// TODO: bound check
|
||||||
let v = generator
|
|
||||||
.gen_expr(ctx, value)
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator)
|
|
||||||
.into_pointer_value();
|
|
||||||
let index = generator
|
let index = generator
|
||||||
.gen_expr(ctx, slice)
|
.gen_expr(ctx, slice)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_basic_value_enum(ctx, generator)
|
.to_basic_value_enum(ctx, generator)
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
let zero = int32.const_zero();
|
ctx.build_gep_and_load(arr_ptr, &[index])
|
||||||
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]);
|
|
||||||
ctx.build_gep_and_load(arr_ptr.into_pointer_value(), &[index])
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let v = generator
|
|
||||||
.gen_expr(ctx, value)
|
|
||||||
.unwrap()
|
|
||||||
.to_basic_value_enum(ctx, generator)
|
|
||||||
.into_pointer_value();
|
|
||||||
let index = generator
|
let index = generator
|
||||||
.gen_expr(ctx, slice)
|
.gen_expr(ctx, slice)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -5,9 +5,12 @@ typedef unsigned _ExtInt(32) uint32_t;
|
|||||||
typedef _ExtInt(64) int64_t;
|
typedef _ExtInt(64) int64_t;
|
||||||
typedef unsigned _ExtInt(64) uint64_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
|
// 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
|
// 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 base, \
|
||||||
T exp \
|
T exp \
|
||||||
) { \
|
) { \
|
||||||
@ -23,3 +26,106 @@ typedef unsigned _ExtInt(64) uint64_t;
|
|||||||
|
|
||||||
DEF_INT_EXP(int32_t)
|
DEF_INT_EXP(int32_t)
|
||||||
DEF_INT_EXP(int64_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::{
|
use inkwell::{
|
||||||
attributes::{Attribute, AttributeLoc},
|
attributes::{Attribute, AttributeLoc},
|
||||||
context::Context,
|
context::Context,
|
||||||
memory_buffer::MemoryBuffer,
|
memory_buffer::MemoryBuffer,
|
||||||
module::Module,
|
module::Module,
|
||||||
values::IntValue,
|
types::{BasicTypeEnum, IntType},
|
||||||
|
values::{IntValue, PointerValue},
|
||||||
|
AddressSpace, IntPredicate,
|
||||||
};
|
};
|
||||||
|
use nac3parser::ast::Expr;
|
||||||
|
|
||||||
pub fn load_irrt(ctx: &Context) -> Module {
|
pub fn load_irrt(ctx: &Context) -> Module {
|
||||||
let bitcode_buf = MemoryBuffer::create_from_memory_range(
|
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 irrt_mod = Module::parse_bitcode_from_buffer(&bitcode_buf, ctx).unwrap();
|
||||||
let inline_attr = Attribute::get_named_enum_kind_id("alwaysinline");
|
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();
|
let function = irrt_mod.get_function(symbol).unwrap();
|
||||||
function.add_attribute(AttributeLoc::Function, ctx.create_enum_attribute(inline_attr, 0));
|
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>,
|
exp: IntValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width()) {
|
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width()) {
|
||||||
(32, 32) => "__nac3_irrt_int_exp_int32_t",
|
(32, 32) => "__nac3_int_exp_int32_t",
|
||||||
(64, 64) => "__nac3_irrt_int_exp_int64_t",
|
(64, 64) => "__nac3_int_exp_int64_t",
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let base_type = base.get_type();
|
let base_type = base.get_type();
|
||||||
@ -45,3 +55,287 @@ pub fn integer_power<'ctx, 'a>(
|
|||||||
.unwrap_left()
|
.unwrap_left()
|
||||||
.into_int_value()
|
.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,
|
||||||
ychenfo marked this conversation as resolved
Outdated
|
|||||||
|
"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",
|
||||||
sb10q
commented
When would we use that? When would we use that?
ychenfo
commented
We can have list of boolean, which is of width 1, and is represented by a byte in our supported targets? I tried to make clang to output Maybe it would be better to change the condition here to exactly We can have list of boolean, which is of width 1, and is represented by a byte in our supported targets?
I tried to make clang to output `i1` but the `_ExtInt` must require `bitwidth >= 2`.
Maybe it would be better to change the condition here to exactly `w == 1`, or maybe `w <= 8`? since I think we do not have other type represented by bit width smaller than 32.
|
|||||||
|
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
|
||||||
ychenfo marked this conversation as resolved
Outdated
sb10q
commented
Add comments with the parameter names. It's hard to follow this long list. Add comments with the parameter names. It's hard to follow this long list.
|
|||||||
|
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::{
|
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::{
|
use inkwell::{
|
||||||
types::BasicTypeEnum,
|
types::BasicTypeEnum,
|
||||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||||
@ -97,16 +103,53 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ptr = generator.gen_store_target(ctx, target);
|
match &target.node {
|
||||||
if let ExprKind::Name { id, .. } = &target.node {
|
ExprKind::Subscript { value: ls, slice, .. }
|
||||||
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
|
if matches!(&slice.node, ExprKind::Slice { .. }) =>
|
||||||
*counter += 1;
|
{
|
||||||
if let ValueEnum::Static(s) = &value {
|
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||||
*static_value = Some(s.clone());
|
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 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 = (
|
type BuiltinInfo = (
|
||||||
Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>,
|
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
Better use
IntPredicate::EQ
here, those short constants are a bit confusing.