core: Add RangeValue and helper functions

pull/375/head
David Mak 2024-01-23 18:27:00 +08:00
parent 5ee08b585f
commit 148900302e
4 changed files with 168 additions and 17 deletions

View File

@ -28,7 +28,7 @@ impl<'ctx> ListValue<'ctx> {
) -> Result<(), String> {
let llvm_list_ty = value.get_type().get_element_type();
let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else {
panic!("Expected struct type for `list` type, got {llvm_list_ty}")
return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}"))
};
if llvm_list_ty.count_fields() != 2 {
return Err(format!("Expected 2 fields in `list`, got {}", llvm_list_ty.count_fields()))
@ -223,3 +223,160 @@ impl<'ctx> ListDataProxy<'ctx> {
ctx.builder.build_load(ptr, name.unwrap_or_default())
}
}
#[cfg(not(debug_assertions))]
pub fn assert_is_range(_value: PointerValue) {}
#[cfg(debug_assertions)]
pub fn assert_is_range(value: PointerValue) {
if let Err(msg) = RangeValue::is_instance(value) {
panic!("{msg}")
}
}
/// Proxy type for accessing a `range` value in LLVM.
#[derive(Copy, Clone)]
pub struct RangeValue<'ctx>(PointerValue<'ctx>, Option<&'ctx str>);
impl<'ctx> RangeValue<'ctx> {
/// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance.
pub fn is_instance(value: PointerValue<'ctx>) -> Result<(), String> {
let llvm_range_ty = value.get_type().get_element_type();
let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else {
return Err(format!("Expected array type for `range` type, got {llvm_range_ty}"))
};
if llvm_range_ty.len() != 3 {
return Err(format!("Expected 3 elements for `range` type, got {}", llvm_range_ty.len()))
}
let llvm_range_elem_ty = llvm_range_ty.get_element_type();
let Ok(llvm_range_elem_ty) = IntType::try_from(llvm_range_elem_ty) else {
return Err(format!("Expected int type for `range` element type, got {llvm_range_elem_ty}"))
};
if llvm_range_elem_ty.get_bit_width() != 32 {
return Err(format!("Expected 32-bit int type for `range` element type, got {}",
llvm_range_elem_ty.get_bit_width()))
}
Ok(())
}
/// Creates an [RangeValue] from a [PointerValue].
pub fn from_ptr_val(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self {
assert_is_range(ptr);
RangeValue(ptr, name)
}
/// Returns the underlying [PointerValue] pointing to the `range` instance.
pub fn get_ptr(&self) -> PointerValue<'ctx> {
self.0
}
fn get_start_ptr(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let var_name = self.1.map(|v| format!("{v}.start.addr")).unwrap_or_default();
unsafe {
ctx.builder.build_in_bounds_gep(
self.0,
&[llvm_i32.const_zero(), llvm_i32.const_int(0, false)],
var_name.as_str(),
)
}
}
fn get_end_ptr(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let var_name = self.1.map(|v| format!("{v}.end.addr")).unwrap_or_default();
unsafe {
ctx.builder.build_in_bounds_gep(
self.0,
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
var_name.as_str(),
)
}
}
fn get_step_ptr(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let var_name = self.1.map(|v| format!("{v}.step.addr")).unwrap_or_default();
unsafe {
ctx.builder.build_in_bounds_gep(
self.0,
&[llvm_i32.const_zero(), llvm_i32.const_int(2, false)],
var_name.as_str(),
)
}
}
/// Stores the `start` value into this instance.
pub fn store_start(
&self,
ctx: &CodeGenContext<'ctx, '_>,
start: IntValue<'ctx>,
) {
debug_assert_eq!(start.get_type().get_bit_width(), 32);
let pstart = self.get_start_ptr(ctx);
ctx.builder.build_store(pstart, start);
}
/// Returns the `start` value of this `range`.
pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
let pstart = self.get_start_ptr(ctx);
let var_name = name
.map(|v| v.to_string())
.or_else(|| self.1.map(|v| format!("{v}.start")))
.unwrap_or_default();
ctx.builder.build_load(pstart, var_name.as_str()).into_int_value()
}
/// Stores the `end` value into this instance.
pub fn store_end(
&self,
ctx: &CodeGenContext<'ctx, '_>,
end: IntValue<'ctx>,
) {
debug_assert_eq!(end.get_type().get_bit_width(), 32);
let pend = self.get_start_ptr(ctx);
ctx.builder.build_store(pend, end);
}
/// Returns the `end` value of this `range`.
pub fn load_end(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
let pend = self.get_end_ptr(ctx);
let var_name = name
.map(|v| v.to_string())
.or_else(|| self.1.map(|v| format!("{v}.end")))
.unwrap_or_default();
ctx.builder.build_load(pend, var_name.as_str()).into_int_value()
}
/// Stores the `step` value into this instance.
pub fn store_step(
&self,
ctx: &CodeGenContext<'ctx, '_>,
step: IntValue<'ctx>,
) {
debug_assert_eq!(step.get_type().get_bit_width(), 32);
let pstep = self.get_start_ptr(ctx);
ctx.builder.build_store(pstep, step);
}
/// Returns the `step` value of this `range`.
pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
let pstep = self.get_step_ptr(ctx);
let var_name = name
.map(|v| v.to_string())
.or_else(|| self.1.map(|v| format!("{v}.step")))
.unwrap_or_default();
ctx.builder.build_load(pstep, var_name.as_str()).into_int_value()
}
}

View File

@ -2,7 +2,7 @@ use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use crate::{
codegen::{
classes::ListValue,
classes::{ListValue, RangeValue},
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check,
get_llvm_type,
@ -870,18 +870,11 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
/// respectively.
pub fn destructure_range<'ctx>(
ctx: &mut CodeGenContext<'ctx, '_>,
range: PointerValue<'ctx>,
range: RangeValue<'ctx>,
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
let int32 = ctx.ctx.i32_type();
let start = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(0, false)], Some("range.start"))
.into_int_value();
let end = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(1, false)], Some("range.stop"))
.into_int_value();
let step = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(2, false)], Some("range.step"))
.into_int_value();
let start = range.load_start(ctx, None);
let end = range.load_end(ctx, None);
let step = range.load_step(ctx, None);
(start, end, step)
}
@ -965,7 +958,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
let list_content;
if is_range {
let iter_val = iter_val.into_pointer_value();
let iter_val = RangeValue::from_ptr_val(iter_val.into_pointer_value(), Some("range"));
let (start, stop, step) = destructure_range(ctx, iter_val);
let diff = ctx.builder.build_int_sub(stop, start, "diff");
// add 1 to the length as the value is rounded to zero

View File

@ -6,7 +6,7 @@ use super::{
};
use crate::{
codegen::{
classes::ListValue,
classes::{ListValue, RangeValue},
expr::gen_binop_expr,
gen_in_range_check,
},
@ -321,7 +321,7 @@ pub fn gen_for<G: CodeGenerator>(
return Ok(())
};
if is_iterable_range_expr {
let iter_val = iter_val.into_pointer_value();
let iter_val = RangeValue::from_ptr_val(iter_val.into_pointer_value(), Some("range"));
// Internal variable for loop; Cannot be assigned
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed

View File

@ -1,6 +1,7 @@
use super::*;
use crate::{
codegen::{
classes::RangeValue,
expr::destructure_range,
irrt::{
calculate_len_for_slice_range,
@ -1453,7 +1454,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value();
let arg = RangeValue::from_ptr_val(arg.into_pointer_value(), Some("range"));
let (start, end, step) = destructure_range(ctx, arg);
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
} else {