List Slice Support (#72) #140

Merged
sb10q merged 1 commits from list_slice into master 2022-01-13 18:53:48 +08:00
5 changed files with 510 additions and 111 deletions
Showing only changes of commit 4976e89ae2 - Show all commits

View File

@ -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()) {
if let ExprKind::Slice { .. } = slice.node {
unimplemented!()
} else {
// TODO: bound check
let v = generator let v = generator
.gen_expr(ctx, value) .gen_expr(ctx, value)
.unwrap() .unwrap()
.to_basic_value_enum(ctx, generator) .to_basic_value_enum(ctx, generator)
.into_pointer_value(); .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 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()

View File

@ -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)

View File

@ -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
Outdated
Review

Better use IntPredicate::EQ here, those short constants are a bit confusing.

Better use ``IntPredicate::EQ`` here, those short constants are a bit confusing.
"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",
Review

When would we use that?

When would we use that?
Review

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.

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
Outdated
Review

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);
}

View File

@ -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,6 +103,41 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
unreachable!() unreachable!()
} }
} else { } else {
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); let ptr = generator.gen_store_target(ctx, target);
if let ExprKind::Name { id, .. } = &target.node { if let ExprKind::Name { id, .. } = &target.node {
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap(); let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
@ -109,6 +150,8 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
ctx.builder.build_store(ptr, val); ctx.builder.build_store(ptr, val);
} }
} }
}
}
pub fn gen_for<'ctx, 'a, G: CodeGenerator>( pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
generator: &mut G, generator: &mut G,

View File

@ -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()
}