From 4976e89ae201c97323e1655c12106c696f281331 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Sun, 9 Jan 2022 19:55:17 +0800 Subject: [PATCH] nac3core: list slice support --- nac3core/src/codegen/expr.rs | 62 ++++-- nac3core/src/codegen/irrt/irrt.c | 108 ++++++++++- nac3core/src/codegen/irrt/mod.rs | 304 +++++++++++++++++++++++++++++- nac3core/src/codegen/stmt.rs | 63 ++++++- nac3core/src/toplevel/builtins.rs | 84 +-------- 5 files changed, 510 insertions(+), 111 deletions(-) diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 9f1d1fa0..b80a2602 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -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() diff --git a/nac3core/src/codegen/irrt/irrt.c b/nac3core/src/codegen/irrt/irrt.c index 4db916fd..fb395ea8 100644 --- a/nac3core/src/codegen/irrt/irrt.c +++ b/nac3core/src/codegen/irrt/irrt.c @@ -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) diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index 7b3b44bd..6891c69b 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -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>>>, + end: &Option>>>, + step: &Option>>>, + 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>, + 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); +} diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 9903bd88..3b539005 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -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); } } diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index bc85b0be..1693dfa8 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -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>, Option)>, @@ -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() -}