1
0
forked from M-Labs/nac3

core: Extract LLVM intrinsic functions to their functions

This commit is contained in:
David Mak 2024-02-22 01:47:26 +08:00
parent 4efdd17513
commit 82fdb02d13
7 changed files with 720 additions and 469 deletions

View File

@ -1,6 +1,7 @@
use nac3core::{
codegen::{
expr::gen_call,
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
stmt::{gen_block, gen_with},
CodeGenContext, CodeGenerator,
},
@ -15,7 +16,7 @@ use inkwell::{
context::Context,
module::Linkage,
types::IntType,
values::{BasicValueEnum, CallSiteValue},
values::BasicValueEnum,
AddressSpace,
};
@ -29,7 +30,6 @@ use std::{
hash::{Hash, Hasher},
sync::Arc,
};
use itertools::Either;
/// The parallelism mode within a block.
#[derive(Copy, Clone, Eq, PartialEq)]
@ -133,20 +133,12 @@ impl<'a> ArtiqCodeGenerator<'a> {
.unwrap()
.to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
ctx.module.add_function(
"llvm.smax.i64",
i64.fn_type(&[i64.into(), i64.into()], false),
None,
)
});
let max = ctx
.builder
.build_call(smax, &[old_end.into(), now.into()], "smax")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let max = call_int_smax(
ctx,
old_end.into_int_value(),
now.into_int_value(),
Some("smax")
);
let end_store = self.gen_store_target(
ctx,
&end,
@ -471,18 +463,7 @@ fn rpc_codegen_callback_fn<'ctx>(
let arg_length = args.len() + usize::from(obj.is_some());
let stacksave = ctx.module.get_function("llvm.stacksave").unwrap_or_else(|| {
ctx.module.add_function("llvm.stacksave", ptr_type.fn_type(&[], false), None)
});
let stackrestore = ctx.module.get_function("llvm.stackrestore").unwrap_or_else(|| {
ctx.module.add_function(
"llvm.stackrestore",
ctx.ctx.void_type().fn_type(&[ptr_type.into()], false),
None,
)
});
let stackptr = ctx.builder.build_call(stacksave, &[], "rpc.stack").unwrap();
let stackptr = call_stacksave(ctx, Some("rpc.stack"));
let args_ptr = ctx.builder
.build_array_alloca(
ptr_type,
@ -558,13 +539,7 @@ fn rpc_codegen_callback_fn<'ctx>(
.unwrap();
// reclaim stack space used by arguments
ctx.builder
.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
)
.unwrap();
call_stackrestore(ctx, stackptr);
// -- receive value:
// T result = {
@ -624,13 +599,7 @@ fn rpc_codegen_callback_fn<'ctx>(
let result = ctx.builder.build_load(slot, "rpc.result").unwrap();
if need_load {
ctx.builder
.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
)
.unwrap();
call_stackrestore(ctx, stackptr);
}
Ok(Some(result))
}

View File

@ -1,13 +1,13 @@
use inkwell::{
IntPredicate,
types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType},
values::{ArrayValue, BasicValueEnum, CallSiteValue, IntValue, PointerValue},
values::{ArrayValue, BasicValueEnum, IntValue, PointerValue},
};
use itertools::Either;
use crate::codegen::{
CodeGenContext,
CodeGenerator,
irrt::{call_ndarray_calc_size, call_ndarray_flatten_index, call_ndarray_flatten_index_const},
llvm_intrinsics::call_int_umin,
stmt::gen_for_callback,
};
@ -924,22 +924,7 @@ impl<'ctx> NDArrayDataProxy<'ctx> {
let indices_len = indices.load_size(ctx, None);
let ndarray_len = self.0.load_ndims(ctx);
let min_fn_name = format!("llvm.umin.i{}", llvm_usize.get_bit_width());
let min_fn = ctx.module.get_function(min_fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_usize.fn_type(
&[llvm_usize.into(), llvm_usize.into()],
false
);
ctx.module.add_function(min_fn_name.as_str(), fn_type, None)
});
let len = ctx
.builder
.build_call(min_fn, &[indices_len.into(), ndarray_len.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
let len = call_int_umin(ctx, indices_len, ndarray_len, None);
let i = ctx.builder.build_load(i_addr, "")
.map(BasicValueEnum::into_int_value)

View File

@ -8,6 +8,7 @@ use crate::{
get_llvm_type,
get_llvm_abi_type,
irrt::*,
llvm_intrinsics::{call_expect, call_float_floor, call_float_pow, call_float_powi},
stmt::{gen_raise, gen_var},
CodeGenContext, CodeGenTask,
},
@ -30,7 +31,7 @@ use nac3parser::ast::{
self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
};
use super::{CodeGenerator, need_sret};
use super::{CodeGenerator, llvm_intrinsics::call_memcpy_generic, need_sret};
pub fn get_subst_key(
unifier: &mut Unifier,
@ -371,7 +372,6 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let (BasicValueEnum::FloatValue(lhs), BasicValueEnum::FloatValue(rhs)) = (lhs, rhs) else {
unreachable!()
};
let float = self.ctx.f64_type();
match op {
Operator::Add => self.builder.build_float_add(lhs, rhs, "fadd").map(Into::into).unwrap(),
Operator::Sub => self.builder.build_float_sub(lhs, rhs, "fsub").map(Into::into).unwrap(),
@ -380,28 +380,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
Operator::Mod => self.builder.build_float_rem(lhs, rhs, "fmod").map(Into::into).unwrap(),
Operator::FloorDiv => {
let div = self.builder.build_float_div(lhs, rhs, "fdiv").unwrap();
let floor_intrinsic =
self.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let fn_type = float.fn_type(&[float.into()], false);
self.module.add_function("llvm.floor.f64", fn_type, None)
});
self.builder
.build_call(floor_intrinsic, &[div.into()], "floor")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap()
}
Operator::Pow => {
let pow_intrinsic = self.module.get_function("llvm.pow.f64").unwrap_or_else(|| {
let fn_type = float.fn_type(&[float.into(), float.into()], false);
self.module.add_function("llvm.pow.f64", fn_type, None)
});
self.builder
.build_call(pow_intrinsic, &[lhs.into(), rhs.into()], "f_pow")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap()
call_float_floor(self, div, Some("floor")).into()
}
Operator::Pow => call_float_pow(self, lhs, rhs, Some("f_pow")).into(),
// special implementation?
_ => unimplemented!(),
}
@ -585,24 +566,11 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
) {
let i1 = self.ctx.bool_type();
let i1_true = i1.const_all_ones();
let expect_fun = self.module.get_function("llvm.expect.i1").unwrap_or_else(|| {
self.module.add_function(
"llvm.expect.i1",
i1.fn_type(&[i1.into(), i1.into()], false),
None,
)
});
// we assume that the condition is most probably true, so the normal path is the most
// probable path
// even if this assumption is violated, it does not matter as exception unwinding is
// slow anyway...
let cond = self
.builder
.build_call(expect_fun, &[cond.into(), i1_true.into()], "expect")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
let cond = call_expect(self, cond, i1_true, Some("expect"));
let current_fun = self.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_block = self.ctx.append_basic_block(current_fun, "succ");
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
@ -1150,17 +1118,12 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int
assert_eq!(*op, Operator::Pow);
let i32_t = ctx.ctx.i32_type();
let pow_intr = ctx.module.get_function("llvm.powi.f64.i32").unwrap_or_else(|| {
let f64_t = ctx.ctx.f64_type();
let ty = f64_t.fn_type(&[f64_t.into(), i32_t.into()], false);
ctx.module.add_function("llvm.powi.f64.i32", ty, None)
});
let res = ctx.builder
.build_call(pow_intr, &[left_val.into(), right_val.into()], "f_pow_i")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let res = call_float_powi(
ctx,
left_val.into_float_value(),
right_val.into_int_value(),
Some("f_pow_i")
);
Ok(Some(res.into()))
} else {
let left_ty_enum = ctx.unifier.get_ty_immutable(left.custom.unwrap());
@ -1229,11 +1192,8 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>(
v: NDArrayValue<'ctx>,
slice: &Expr<Option<Type>>,
) -> Result<Option<ValueEnum<'ctx>>, String> {
let llvm_void = ctx.ctx.void_type();
let llvm_i1 = ctx.ctx.bool_type();
let llvm_i8 = ctx.ctx.i8_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let TypeEnum::TLiteral { values, .. } = &*ctx.unifier.get_ty_immutable(ndims) else {
unreachable!()
@ -1333,24 +1293,6 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>(
let ndarray_num_dims = ndarray.load_ndims(ctx);
ndarray.create_dims(ctx, llvm_usize, ndarray_num_dims);
let memcpy_fn_name = format!(
"llvm.memcpy.p0i8.p0i8.i{}",
generator.get_size_type(ctx.ctx).get_bit_width(),
);
let memcpy_fn = ctx.module.get_function(memcpy_fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_void.fn_type(
&[
llvm_pi8.into(),
llvm_pi8.into(),
llvm_usize.into(),
llvm_i1.into(),
],
false,
);
ctx.module.add_function(memcpy_fn_name.as_str(), fn_type, None)
});
let ndarray_num_dims = ndarray.load_ndims(ctx);
let v_dims_src_ptr = v.get_dims().ptr_offset(
ctx,
@ -1358,37 +1300,16 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>(
llvm_usize.const_int(1, false),
None,
);
ctx.builder.build_call(
memcpy_fn,
&[
ctx.builder
.build_bitcast(
ndarray.get_dims().get_ptr(ctx),
llvm_pi8,
"",
)
.map(Into::into)
.unwrap(),
ctx.builder
.build_bitcast(
v_dims_src_ptr,
llvm_pi8,
"",
)
.map(Into::into)
.unwrap(),
ctx.builder
.build_int_mul(
ndarray_num_dims,
llvm_usize.size_of(),
"",
)
.map(Into::into)
.unwrap(),
llvm_i1.const_zero().into(),
],
"",
).unwrap();
call_memcpy_generic(
ctx,
ndarray.get_dims().get_ptr(ctx),
v_dims_src_ptr,
ctx.builder
.build_int_mul(ndarray_num_dims, llvm_usize.size_of(), "")
.map(Into::into)
.unwrap(),
llvm_i1.const_zero(),
);
let ndarray_num_elems = call_ndarray_calc_size(
generator,
@ -1404,37 +1325,16 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>(
ctx.ctx.i32_type().const_array(&[index]),
None
);
ctx.builder.build_call(
memcpy_fn,
&[
ctx.builder
.build_bitcast(
ndarray.get_data().get_ptr(ctx),
llvm_pi8,
"",
)
.map(Into::into)
.unwrap(),
ctx.builder
.build_bitcast(
v_data_src_ptr,
llvm_pi8,
"",
)
.map(Into::into)
.unwrap(),
ctx.builder
.build_int_mul(
ndarray_num_elems,
llvm_ndarray_data_t.size_of().unwrap(),
"",
)
.map(Into::into)
.unwrap(),
llvm_i1.const_zero().into(),
],
"",
).unwrap();
call_memcpy_generic(
ctx,
ndarray.get_data().get_ptr(ctx),
v_data_src_ptr,
ctx.builder
.build_int_mul(ndarray_num_elems, llvm_ndarray_data_t.size_of().unwrap(), "")
.map(Into::into)
.unwrap(),
llvm_i1.const_zero(),
);
Ok(Some(v.get_ptr().into()))
}

View File

@ -0,0 +1,562 @@
use inkwell::AddressSpace;
use inkwell::context::Context;
use inkwell::types::AnyTypeEnum::IntType;
use inkwell::types::FloatType;
use inkwell::values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue};
use itertools::Either;
use crate::codegen::CodeGenContext;
/// Returns the string representation for the floating-point type `ft` when used in intrinsic
/// functions.
fn get_float_intrinsic_repr(ctx: &Context, ft: FloatType) -> &'static str {
// Standard LLVM floating-point types
if ft == ctx.f16_type() {
return "f16"
}
if ft == ctx.f32_type() {
return "f32"
}
if ft == ctx.f64_type() {
return "f64"
}
if ft == ctx.f128_type() {
return "f128"
}
// Non-standard floating-point types
if ft == ctx.x86_f80_type() {
return "f80"
}
if ft == ctx.ppc_f128_type() {
return "ppcf128"
}
unreachable!()
}
/// Invokes the [`llvm.stacksave`](https://llvm.org/docs/LangRef.html#llvm-stacksave-intrinsic)
/// intrinsic.
pub fn call_stacksave<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&str>,
) -> PointerValue<'ctx> {
const FN_NAME: &str = "llvm.stacksave";
let intrinsic_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let fn_type = llvm_p0i8.fn_type(&[], false);
ctx.module.add_function(FN_NAME, fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_pointer_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the
/// [`llvm.stackrestore`](https://llvm.org/docs/LangRef.html#llvm-stackrestore-intrinsic) intrinsic.
pub fn call_stackrestore<'ctx>(ctx: &CodeGenContext<'ctx, '_>, ptr: PointerValue<'ctx>) {
const FN_NAME: &str = "llvm.stackrestore";
let intrinsic_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let llvm_void = ctx.ctx.void_type();
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let fn_type = llvm_void.fn_type(&[llvm_p0i8.into()], false);
ctx.module.add_function(FN_NAME, fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[ptr.into()], "")
.unwrap();
}
/// Invokes the [`llvm.abs`](https://llvm.org/docs/LangRef.html#llvm-abs-intrinsic) intrinsic.
///
/// * `src` - The value for which the absolute value is to be returned.
/// * `is_int_min_poison` - Whether `poison` is to be returned if `src` is `INT_MIN`.
pub fn call_int_abs<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
src: IntValue<'ctx>,
is_int_min_poison: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(is_int_min_poison.get_type().get_bit_width(), 1);
debug_assert!(is_int_min_poison.is_const());
let llvm_src_t = src.get_type();
let fn_name = format!("llvm.abs.i{}", llvm_src_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let llvm_i1 = ctx.ctx.bool_type();
let fn_type = llvm_src_t.fn_type(&[llvm_src_t.into(), llvm_i1.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[src.into(), is_int_min_poison.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.smax`](https://llvm.org/docs/LangRef.html#llvm-smax-intrinsic) intrinsic.
pub fn call_int_smax<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.smax.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.smin`](https://llvm.org/docs/LangRef.html#llvm-smin-intrinsic) intrinsic.
pub fn call_int_smin<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.smin.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.umax`](https://llvm.org/docs/LangRef.html#llvm-umax-intrinsic) intrinsic.
pub fn call_int_umax<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.umax.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.umin`](https://llvm.org/docs/LangRef.html#llvm-umin-intrinsic) intrinsic.
pub fn call_int_umin<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.umin.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.memcpy`](https://llvm.org/docs/LangRef.html#llvm-memcpy-intrinsic) intrinsic.
///
/// * `dest` - The pointer to the destination. Must be a pointer to an integer type.
/// * `src` - The pointer to the source. Must be a pointer to an integer type.
/// * `len` - The number of bytes to copy.
/// * `is_volatile` - Whether the `memcpy` operation should be `volatile`.
pub fn call_memcpy<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
debug_assert!(dest.get_type().get_element_type().is_int_type());
debug_assert!(src.get_type().get_element_type().is_int_type());
debug_assert_eq!(
dest.get_type().get_element_type().into_int_type().get_bit_width(),
src.get_type().get_element_type().into_int_type().get_bit_width(),
);
debug_assert!(matches!(len.get_type().get_bit_width(), 32 | 64));
debug_assert_eq!(is_volatile.get_type().get_bit_width(), 1);
let llvm_dest_t = dest.get_type();
let llvm_src_t = src.get_type();
let llvm_len_t = len.get_type();
let fn_name = format!(
"llvm.memcpy.p0i{}.p0i{}.i{}",
llvm_dest_t.get_element_type().into_int_type().get_bit_width(),
llvm_src_t.get_element_type().into_int_type().get_bit_width(),
llvm_len_t.get_bit_width(),
);
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let llvm_void = ctx.ctx.void_type();
let fn_type = llvm_void.fn_type(
&[
llvm_dest_t.into(),
llvm_src_t.into(),
llvm_len_t.into(),
is_volatile.get_type().into(),
],
false,
);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[dest.into(), src.into(), len.into(), is_volatile.into()], "")
.unwrap();
}
/// Invokes the `llvm.memcpy` intrinsic.
///
/// Unlike [`call_memcpy`], this function accepts any type of pointer value. If `dest` or `src` is
/// not a pointer to an integer, the pointer(s) will be cast to `i8*` before invoking `memcpy`.
pub fn call_memcpy_generic<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let dest_elem_t = dest.get_type().get_element_type();
let src_elem_t = src.get_type().get_element_type();
let dest = if matches!(dest_elem_t, IntType(t) if t.get_bit_width() == 8) {
dest
} else {
ctx.builder
.build_bitcast(dest, llvm_p0i8, "")
.map(BasicValueEnum::into_pointer_value)
.unwrap()
};
let src = if matches!(src_elem_t, IntType(t) if t.get_bit_width() == 8) {
src
} else {
ctx.builder
.build_bitcast(src, llvm_p0i8, "")
.map(BasicValueEnum::into_pointer_value)
.unwrap()
};
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Invokes the [`llvm.powi`](https://llvm.org/docs/LangRef.html#llvm-powi-intrinsic) intrinsic.
pub fn call_float_powi<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
power: IntValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_val_t = val.get_type();
let llvm_power_t = power.get_type();
let fn_name = format!(
"llvm.powi.{}.i{}",
get_float_intrinsic_repr(ctx.ctx, llvm_val_t),
llvm_power_t.get_bit_width(),
);
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_val_t.fn_type(&[llvm_val_t.into(), llvm_power_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), power.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.pow`](https://llvm.org/docs/LangRef.html#llvm-pow-intrinsic) intrinsic.
pub fn call_float_pow<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
power: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val.get_type(), power.get_type());
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.pow.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), power.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.fabs`](https://llvm.org/docs/LangRef.html#llvm-fabs-intrinsic) intrinsic.
pub fn call_float_fabs<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
src: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_src_t = src.get_type();
let fn_name = format!("llvm.fabs.{}", get_float_intrinsic_repr(ctx.ctx, llvm_src_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_src_t.fn_type(&[llvm_src_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[src.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.minnum`](https://llvm.org/docs/LangRef.html#llvm-minnum-intrinsic) intrinsic.
pub fn call_float_minnum<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val1: FloatValue<'ctx>,
val2: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val1.get_type(), val2.get_type());
let llvm_float_t = val1.get_type();
let fn_name = format!("llvm.minnum.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val1.into(), val2.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.maxnum`](https://llvm.org/docs/LangRef.html#llvm-maxnum-intrinsic) intrinsic.
pub fn call_float_maxnum<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val1: FloatValue<'ctx>,
val2: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val1.get_type(), val2.get_type());
let llvm_float_t = val1.get_type();
let fn_name = format!("llvm.maxnum.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val1.into(), val2.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.floor`](https://llvm.org/docs/LangRef.html#llvm-floor-intrinsic) intrinsic.
pub fn call_float_floor<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.floor.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.ceil`](https://llvm.org/docs/LangRef.html#llvm-ceil-intrinsic) intrinsic.
pub fn call_float_ceil<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.ceil.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.round`](https://llvm.org/docs/LangRef.html#llvm-round-intrinsic) intrinsic.
pub fn call_float_round<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.round.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the
/// [`llvm.roundeven`](https://llvm.org/docs/LangRef.html#llvm-roundeven-intrinsic) intrinsic.
pub fn call_float_roundeven<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.roundeven.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.expect`](https://llvm.org/docs/LangRef.html#llvm-expect-intrinsic) intrinsic.
pub fn call_expect<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: IntValue<'ctx>,
expected_val: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(val.get_type().get_bit_width(), expected_val.get_type().get_bit_width());
let llvm_int_t = val.get_type();
let fn_name = format!("llvm.expect.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), expected_val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}

View File

@ -39,6 +39,7 @@ pub mod concrete_type;
pub mod expr;
mod generator;
pub mod irrt;
pub mod llvm_intrinsics;
pub mod stmt;
#[cfg(test)]

View File

@ -3,25 +3,12 @@ use crate::{
codegen::{
classes::RangeValue,
expr::destructure_range,
irrt::{
calculate_len_for_slice_range,
call_gamma,
call_gammaln,
call_isinf,
call_isnan,
call_j0,
},
irrt::*,
llvm_intrinsics::*,
stmt::exn_constructor,
},
symbol_resolver::SymbolValue,
toplevel::numpy::{
gen_ndarray_empty,
gen_ndarray_eye,
gen_ndarray_full,
gen_ndarray_identity,
gen_ndarray_ones,
gen_ndarray_zeros,
},
toplevel::numpy::*,
};
use inkwell::{
attributes::{Attribute, AttributeLoc},
@ -1010,26 +997,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.round.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_round(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i32, "round")
.build_float_to_signed_int(val, llvm_i32, "round")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1041,26 +1017,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.round.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_round(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i64, "round")
.build_float_to_signed_int(val, llvm_i64, "round")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1072,24 +1037,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.roundeven.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
let val = call_float_roundeven(ctx, arg, None);
ctx.module.add_function("llvm.roundeven.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(Some(val))
Ok(Some(val.into()))
}),
),
Arc::new(RwLock::new(TopLevelDef::Function {
@ -1290,26 +1244,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.floor.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_floor(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i32, "floor")
.build_float_to_signed_int(val, llvm_i32, "floor")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1321,26 +1264,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.floor.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_floor(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i64, "floor")
.build_float_to_signed_int(val, llvm_i64, "floor")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1352,24 +1284,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.floor.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(Some(val))
let val = call_float_floor(ctx, arg, None);
Ok(Some(val.into()))
}),
),
create_fn_by_codegen(
@ -1379,26 +1299,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.ceil.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_ceil(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i32, "ceil")
.build_float_to_signed_int(val, llvm_i32, "ceil")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1410,26 +1319,15 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.ceil.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val = call_float_ceil(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val.into_float_value(), llvm_i64, "ceil")
.build_float_to_signed_int(val, llvm_i64, "ceil")
.unwrap();
Ok(Some(val_toint.into()))
}),
@ -1441,24 +1339,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_f64 = ctx.ctx.f64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let intrinsic_fn = ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("llvm.ceil.f64", fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic_fn, &[arg.into()], "")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(Some(val))
let val = call_float_ceil(ctx, arg, None);
Ok(Some(val.into()))
}),
),
Arc::new(RwLock::new({
@ -1568,40 +1454,38 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let uint32 = ctx.primitives.uint32;
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
let llvm_i8 = ctx.ctx.i8_type().as_basic_type_enum();
let llvm_i32 = ctx.ctx.i32_type().as_basic_type_enum();
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umin.i8", llvm_i8)
} else if is_type(m_ty, n_ty) && is_type(n_ty, int32) {
("llvm.smin.i32", llvm_i32)
} else if is_type(m_ty, n_ty) && is_type(n_ty, int64) {
("llvm.smin.i64", llvm_i64)
} else if is_type(m_ty, n_ty) && is_type(n_ty, uint32) {
("llvm.umin.i32", llvm_i32)
} else if is_type(m_ty, n_ty) && is_type(n_ty, uint64) {
("llvm.umin.i64", llvm_i64)
if !is_type(m_ty, n_ty) {
unreachable!()
}
let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) {
call_int_umin(
ctx,
m_val.into_int_value(),
n_val.into_int_value(),
Some("min"),
).into()
} else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) {
call_int_smin(
ctx,
m_val.into_int_value(),
n_val.into_int_value(),
Some("min"),
).into()
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
("llvm.minnum.f64", llvm_f64)
call_float_minnum(
ctx,
m_val.into_float_value(),
n_val.into_float_value(),
Some("min"),
).into()
} else {
unreachable!()
};
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
ctx.module.add_function(fun_name, fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic, &[m_val.into(), n_val.into()], "min")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(val.into())
},
)))),
@ -1630,40 +1514,38 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let uint32 = ctx.primitives.uint32;
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
let llvm_i8 = ctx.ctx.i8_type().as_basic_type_enum();
let llvm_i32 = ctx.ctx.i32_type().as_basic_type_enum();
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umax.i8", llvm_i8)
} else if is_type(m_ty, n_ty) && is_type(n_ty, int32) {
("llvm.smax.i32", llvm_i32)
} else if is_type(m_ty, n_ty) && is_type(n_ty, int64) {
("llvm.smax.i64", llvm_i64)
} else if is_type(m_ty, n_ty) && is_type(n_ty, uint32) {
("llvm.umax.i32", llvm_i32)
} else if is_type(m_ty, n_ty) && is_type(n_ty, uint64) {
("llvm.umax.i64", llvm_i64)
if !is_type(m_ty, n_ty) {
unreachable!()
}
let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) {
call_int_umax(
ctx,
m_val.into_int_value(),
n_val.into_int_value(),
Some("max"),
).into()
} else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) {
call_int_smax(
ctx,
m_val.into_int_value(),
n_val.into_int_value(),
Some("max"),
).into()
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
("llvm.maxnum.f64", llvm_f64)
call_float_maxnum(
ctx,
m_val.into_float_value(),
n_val.into_float_value(),
Some("max"),
).into()
} else {
unreachable!()
};
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
ctx.module.add_function(fun_name, fn_type, None)
});
let val = ctx
.builder
.build_call(intrinsic, &[m_val.into(), n_val.into()], "max")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(val.into())
},
)))),
@ -1690,49 +1572,27 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
let llvm_i1 = ctx.ctx.bool_type();
let llvm_i32 = ctx.ctx.i32_type().as_basic_type_enum();
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let n_ty = fun.0.args[0].ty;
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let mut is_float = false;
let (fun_name, arg_ty) =
if is_type(n_ty, boolean) || is_type(n_ty, uint32) || is_type(n_ty, uint64)
{
return Ok(n_val.into());
} else if is_type(n_ty, int32) {
("llvm.abs.i32", llvm_i32)
} else if is_type(n_ty, int64) {
("llvm.abs.i64", llvm_i64)
} else if is_type(n_ty, float) {
is_float = true;
("llvm.fabs.f64", llvm_f64)
} else {
unreachable!()
};
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
let fn_type = if is_float {
arg_ty.fn_type(&[arg_ty.into()], false)
} else {
arg_ty.fn_type(&[arg_ty.into(), llvm_i1.into()], false)
};
ctx.module.add_function(fun_name, fn_type, None)
});
let val = ctx
.builder
.build_call(
intrinsic,
&if is_float {
vec![n_val.into()]
} else {
vec![n_val.into(), llvm_i1.const_int(0, false).into()]
},
"abs",
)
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) {
n_val
} else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) {
call_int_abs(
ctx,
n_val.into_int_value(),
llvm_i1.const_zero(),
Some("abs"),
).into()
} else if is_type(n_ty, float) {
call_float_fabs(
ctx,
n_val.into_float_value(),
Some("abs"),
).into()
} else {
unreachable!()
};
Ok(val.into())
},
)))),

View File

@ -1,4 +1,4 @@
use inkwell::{AddressSpace, IntPredicate, types::BasicType, values::{BasicValueEnum, PointerValue}};
use inkwell::{IntPredicate, types::BasicType, values::{BasicValueEnum, PointerValue}};
use inkwell::values::{AggregateValueEnum, ArrayValue, IntValue};
use nac3parser::ast::StrRef;
use crate::{
@ -11,6 +11,7 @@ use crate::{
call_ndarray_calc_size,
call_ndarray_init_dims,
},
llvm_intrinsics::call_memcpy_generic,
stmt::gen_for_callback
},
symbol_resolver::ValueEnum,
@ -406,7 +407,7 @@ fn call_ndarray_ones_impl<'ctx>(
Ok(ndarray)
}
/// LLVM-typed implementation for generating the implementation for `ndarray.ones`.
/// LLVM-typed implementation for generating the implementation for `ndarray.full`.
///
/// * `elem_ty` - The element type of the `NDArray`.
/// * `shape` - The `shape` parameter used to construct the `NDArray`.
@ -424,44 +425,17 @@ fn call_ndarray_full_impl<'ctx>(
ndarray,
|generator, ctx, _| {
let value = if fill_value.is_pointer_value() {
let llvm_void = ctx.ctx.void_type();
let llvm_i1 = ctx.ctx.bool_type();
let llvm_i8 = ctx.ctx.i8_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let copy = generator.gen_var_alloc(ctx, fill_value.get_type(), None)?;
let memcpy_fn_name = format!(
"llvm.memcpy.p0i8.p0i8.i{}",
generator.get_size_type(ctx.ctx).get_bit_width(),
call_memcpy_generic(
ctx,
copy,
fill_value.into_pointer_value(),
fill_value.get_type().size_of().map(Into::into).unwrap(),
llvm_i1.const_zero(),
);
let memcpy_fn = ctx.module.get_function(memcpy_fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_void.fn_type(
&[
llvm_pi8.into(),
llvm_pi8.into(),
llvm_usize.into(),
llvm_i1.into(),
],
false,
);
ctx.module.add_function(memcpy_fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(
memcpy_fn,
&[
copy.into(),
fill_value.into(),
fill_value.get_type().size_of().unwrap().into(),
llvm_i1.const_zero().into(),
],
"",
)
.unwrap();
copy.into()
} else if fill_value.is_int_value() || fill_value.is_float_value() {