nac3/nac3core/src/toplevel/builtins.rs

2078 lines
78 KiB
Rust

use super::*;
use crate::{
codegen::{
classes::{ArrayLikeValue, NDArrayValue, RangeValue, TypedArrayLikeAccessor},
expr::destructure_range,
irrt::*,
llvm_intrinsics::*,
numpy::*,
stmt::exn_constructor,
},
symbol_resolver::SymbolValue,
toplevel::{
helper::PRIMITIVE_DEF_IDS,
numpy::make_ndarray_ty,
},
typecheck::typedef::VarMap,
};
use inkwell::{
attributes::{Attribute, AttributeLoc},
types::{BasicType, BasicMetadataTypeEnum},
values::{BasicValue, BasicMetadataValueEnum, CallSiteValue},
FloatPredicate,
IntPredicate
};
use itertools::Either;
type BuiltinInfo = Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>;
pub fn get_exn_constructor(
name: &str,
class_id: usize,
cons_id: usize,
unifier: &mut Unifier,
primitives: &PrimitiveStore
)-> (TopLevelDef, TopLevelDef, Type, Type) {
let int32 = primitives.int32;
let int64 = primitives.int64;
let string = primitives.str;
let exception_fields = vec![
("__name__".into(), int32, true),
("__file__".into(), string, true),
("__line__".into(), int32, true),
("__col__".into(), int32, true),
("__func__".into(), string, true),
("__message__".into(), string, true),
("__param0__".into(), int64, true),
("__param1__".into(), int64, true),
("__param2__".into(), int64, true),
];
let exn_cons_args = vec![
FuncArg {
name: "msg".into(),
ty: string,
default_value: Some(SymbolValue::Str(String::new())),
},
FuncArg { name: "param0".into(), ty: int64, default_value: Some(SymbolValue::I64(0)) },
FuncArg { name: "param1".into(), ty: int64, default_value: Some(SymbolValue::I64(0)) },
FuncArg { name: "param2".into(), ty: int64, default_value: Some(SymbolValue::I64(0)) },
];
let exn_type = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(class_id),
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
params: VarMap::default(),
});
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: exn_cons_args,
ret: exn_type,
vars: VarMap::default(),
}));
let fun_def = TopLevelDef::Function {
name: format!("{name}.__init__"),
simple_name: "__init__".into(),
signature,
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor)))),
loc: None,
};
let class_def = TopLevelDef::Class {
name: name.into(),
object_id: DefinitionId(class_id),
type_vars: Vec::default(),
fields: exception_fields,
methods: vec![("__init__".into(), signature, DefinitionId(cons_id))],
ancestors: vec![
TypeAnnotation::CustomClass { id: DefinitionId(class_id), params: Vec::default() },
TypeAnnotation::CustomClass { id: PRIMITIVE_DEF_IDS.exception, params: Vec::default() },
],
constructor: Some(signature),
resolver: None,
loc: None,
};
(fun_def, class_def, signature, exn_type)
}
/// Creates a NumPy [`TopLevelDef`] function by code generation.
///
/// * `name`: The name of the implemented NumPy function.
/// * `ret_ty`: The return type of this function.
/// * `param_ty`: The parameters accepted by this function, represented by a tuple of the
/// [parameter type][Type] and the parameter symbol name.
/// * `codegen_callback`: A lambda generating LLVM IR for the implementation of this function.
fn create_fn_by_codegen(
primitives: &mut (PrimitiveStore, Unifier),
var_map: &VarMap,
name: &'static str,
ret_ty: Type,
param_ty: &[(Type, &'static str)],
codegen_callback: GenCallCallback,
) -> Arc<RwLock<TopLevelDef>> {
Arc::new(RwLock::new(TopLevelDef::Function {
name: name.into(),
simple_name: name.into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: param_ty.iter().map(|p| FuncArg {
name: p.1.into(),
ty: p.0,
default_value: None,
}).collect(),
ret: ret_ty,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(codegen_callback))),
loc: None,
}))
}
/// Creates a NumPy [`TopLevelDef`] function using an LLVM intrinsic.
///
/// * `name`: The name of the implemented NumPy function.
/// * `ret_ty`: The return type of this function.
/// * `param_ty`: The parameters accepted by this function, represented by a tuple of the
/// [parameter type][Type] and the parameter symbol name.
/// * `intrinsic_fn`: The fully-qualified name of the LLVM intrinsic function.
fn create_fn_by_intrinsic(
primitives: &mut (PrimitiveStore, Unifier),
var_map: &VarMap,
name: &'static str,
ret_ty: Type,
params: &[(Type, &'static str)],
intrinsic_fn: &'static str,
) -> Arc<RwLock<TopLevelDef>> {
let param_tys = params.iter()
.map(|p| p.0)
.collect_vec();
create_fn_by_codegen(
primitives,
var_map,
name,
ret_ty,
params,
Box::new(move |ctx, _, fun, args, generator| {
let args_ty = fun.0.args.iter().map(|a| a.ty).collect_vec();
assert!(param_tys.iter().zip(&args_ty)
.all(|(expected, actual)| ctx.unifier.unioned(*expected, *actual)));
let args_val = args_ty.iter().zip_eq(args.iter())
.map(|(ty, arg)| {
arg.1.clone()
.to_basic_value_enum(ctx, generator, *ty)
.unwrap()
})
.map_into::<BasicMetadataValueEnum>()
.collect_vec();
let intrinsic_fn = ctx.module.get_function(intrinsic_fn).unwrap_or_else(|| {
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
let param_llvm_ty = param_tys.iter()
.map(|p| ctx.get_llvm_abi_type(generator, *p))
.map_into::<BasicMetadataTypeEnum>()
.collect_vec();
let fn_type = ret_llvm_ty.fn_type(param_llvm_ty.as_slice(), false);
ctx.module.add_function(intrinsic_fn, fn_type, None)
});
let val = ctx.builder
.build_call(intrinsic_fn, args_val.as_slice(), name)
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(val.into())
}),
)
}
/// Creates a unary NumPy [`TopLevelDef`] function using an extern function (e.g. from `libc` or
/// `libm`).
///
/// * `name`: The name of the implemented NumPy function.
/// * `ret_ty`: The return type of this function.
/// * `param_ty`: The parameters accepted by this function, represented by a tuple of the
/// [parameter type][Type] and the parameter symbol name.
/// * `extern_fn`: The fully-qualified name of the extern function used as the implementation.
/// * `attrs`: The list of attributes to apply to this function declaration. Note that `nounwind` is
/// already implied by the C ABI.
fn create_fn_by_extern(
primitives: &mut (PrimitiveStore, Unifier),
var_map: &VarMap,
name: &'static str,
ret_ty: Type,
params: &[(Type, &'static str)],
extern_fn: &'static str,
attrs: &'static [&str],
) -> Arc<RwLock<TopLevelDef>> {
let param_tys = params.iter()
.map(|p| p.0)
.collect_vec();
create_fn_by_codegen(
primitives,
var_map,
name,
ret_ty,
params,
Box::new(move |ctx, _, fun, args, generator| {
let args_ty = fun.0.args.iter().map(|a| a.ty).collect_vec();
assert!(param_tys.iter().zip(&args_ty)
.all(|(expected, actual)| ctx.unifier.unioned(*expected, *actual)));
let args_val = args_ty.iter().zip_eq(args.iter())
.map(|(ty, arg)| {
arg.1.clone()
.to_basic_value_enum(ctx, generator, *ty)
.unwrap()
})
.map_into::<BasicMetadataValueEnum>()
.collect_vec();
let intrinsic_fn = ctx.module.get_function(extern_fn).unwrap_or_else(|| {
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
let param_llvm_ty = param_tys.iter()
.map(|p| ctx.get_llvm_abi_type(generator, *p))
.map_into::<BasicMetadataTypeEnum>()
.collect_vec();
let fn_type = ret_llvm_ty.fn_type(param_llvm_ty.as_slice(), false);
let func = ctx.module.add_function(extern_fn, fn_type, None);
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0)
);
for attr in attrs {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0)
);
}
func
});
let val = ctx.builder
.build_call(intrinsic_fn, &args_val, name)
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap();
Ok(val.into())
}),
)
}
pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let int32 = primitives.0.int32;
let int64 = primitives.0.int64;
let uint32 = primitives.0.uint32;
let uint64 = primitives.0.uint64;
let float = primitives.0.float;
let boolean = primitives.0.bool;
let range = primitives.0.range;
let string = primitives.0.str;
let ndarray = primitives.0.ndarray;
let ndarray_float = make_ndarray_ty(&mut primitives.1, &primitives.0, Some(float), None);
let ndarray_float_2d = {
let value = match primitives.0.size_t {
64 => SymbolValue::U64(2u64),
32 => SymbolValue::U32(2u32),
_ => unreachable!(),
};
let ndims = primitives.1.add_ty(TypeEnum::TLiteral {
values: vec![value],
loc: None,
});
make_ndarray_ty(&mut primitives.1, &primitives.0, Some(float), Some(ndims))
};
let list_int32 = primitives.1.add_ty(TypeEnum::TList { ty: int32 });
let num_ty = primitives.1.get_fresh_var_with_range(
&[int32, int64, float, boolean, uint32, uint64],
Some("N".into()),
None,
);
let var_map: VarMap = vec![(num_ty.1, num_ty.0)].into_iter().collect();
let exception_fields = vec![
("__name__".into(), int32, true),
("__file__".into(), string, true),
("__line__".into(), int32, true),
("__col__".into(), int32, true),
("__func__".into(), string, true),
("__message__".into(), string, true),
("__param0__".into(), int64, true),
("__param1__".into(), int64, true),
("__param2__".into(), int64, true),
];
// for Option, is_some and is_none share the same type: () -> bool,
// and they are methods under the same class `Option`
let (is_some_ty, unwrap_ty, (option_ty_var, option_ty_var_id)) =
if let TypeEnum::TObj { fields, params, .. } =
primitives.1.get_ty(primitives.0.option).as_ref()
{
(
*fields.get(&"is_some".into()).unwrap(),
*fields.get(&"unwrap".into()).unwrap(),
(*params.iter().next().unwrap().1, *params.iter().next().unwrap().0),
)
} else {
unreachable!()
};
let TypeEnum::TObj {
fields: ndarray_fields,
params: ndarray_params,
..
} = &*primitives.1.get_ty(primitives.0.ndarray) else {
unreachable!()
};
let (ndarray_dtype_ty, ndarray_dtype_var_id) = ndarray_params
.iter()
.next()
.map(|(var_id, ty)| (*ty, *var_id))
.unwrap();
let (ndarray_ndims_ty, ndarray_ndims_var_id) = ndarray_params
.iter()
.nth(1)
.map(|(var_id, ty)| (*ty, *var_id))
.unwrap();
let ndarray_copy_ty = *ndarray_fields.get(&"copy".into()).unwrap();
let ndarray_fill_ty = *ndarray_fields.get(&"fill".into()).unwrap();
let top_level_def_list = vec![
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.int32,
None,
"int32".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.int64,
None,
"int64".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.float,
None,
"float".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.bool,
None,
"bool".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.none,
None,
"none".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.range,
None,
"range".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.str,
None,
"str".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelDef::Class {
name: "Exception".into(),
object_id: PRIMITIVE_DEF_IDS.exception,
type_vars: Vec::default(),
fields: exception_fields,
methods: Vec::default(),
ancestors: vec![],
constructor: None,
resolver: None,
loc: None,
})),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.uint32,
None,
"uint32".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
PRIMITIVE_DEF_IDS.uint64,
None,
"uint64".into(),
None,
None,
))),
Arc::new(RwLock::new({
TopLevelDef::Class {
name: "Option".into(),
object_id: PRIMITIVE_DEF_IDS.option,
type_vars: vec![option_ty_var],
fields: vec![],
methods: vec![
("is_some".into(), is_some_ty.0, DefinitionId(PRIMITIVE_DEF_IDS.option.0 + 1)),
("is_none".into(), is_some_ty.0, DefinitionId(PRIMITIVE_DEF_IDS.option.0 + 2)),
("unwrap".into(), unwrap_ty.0, DefinitionId(PRIMITIVE_DEF_IDS.option.0 + 3)),
],
ancestors: vec![TypeAnnotation::CustomClass {
id: PRIMITIVE_DEF_IDS.option,
params: Vec::default(),
}],
constructor: None,
resolver: None,
loc: None,
}
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_some".into(),
simple_name: "is_some".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
let BasicValueEnum::PointerValue(ptr) = obj_val else {
unreachable!("option must be ptr")
};
Ok(Some(ctx.builder
.build_is_not_null(ptr, "is_some")
.map(Into::into)
.unwrap()
))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_none".into(),
simple_name: "is_none".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
let BasicValueEnum::PointerValue(ptr) = obj_val else {
unreachable!("option must be ptr")
};
Ok(Some(ctx.builder
.build_is_null(ptr, "is_none")
.map(Into::into)
.unwrap()
))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.unwrap".into(),
simple_name: "unwrap".into(),
signature: unwrap_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|_, _, _, _, _| {
unreachable!("handled in gen_expr")
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Class {
name: "ndarray".into(),
object_id: PRIMITIVE_DEF_IDS.ndarray,
type_vars: vec![ndarray_dtype_ty, ndarray_ndims_ty],
fields: Vec::default(),
methods: vec![
("copy".into(), ndarray_copy_ty.0, DefinitionId(PRIMITIVE_DEF_IDS.ndarray.0 + 1)),
("fill".into(), ndarray_fill_ty.0, DefinitionId(PRIMITIVE_DEF_IDS.ndarray.0 + 2)),
],
ancestors: Vec::default(),
constructor: None,
resolver: None,
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "ndarray.copy".into(),
simple_name: "copy".into(),
signature: ndarray_copy_ty.0,
var_id: vec![ndarray_dtype_var_id, ndarray_ndims_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, fun, args, generator| {
gen_ndarray_copy(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "ndarray.fill".into(),
simple_name: "fill".into(),
signature: ndarray_fill_ty.0,
var_id: vec![ndarray_dtype_var_id, ndarray_ndims_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, fun, args, generator| {
gen_ndarray_fill(ctx, &obj, fun, &args, generator)?;
Ok(None)
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "int32".into(),
simple_name: "int32".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: int32,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool: boolean,
..
} = ctx.primitives;
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, boolean) {
Some(
ctx.builder
.build_int_z_extend(
arg.into_int_value(),
ctx.ctx.i32_type(),
"zext",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, int32)
|| ctx.unifier.unioned(arg_ty, uint32)
{
Some(arg)
} else if ctx.unifier.unioned(arg_ty, int64)
|| ctx.unifier.unioned(arg_ty, uint64)
{
Some(
ctx.builder
.build_int_truncate(
arg.into_int_value(),
ctx.ctx.i32_type(),
"trunc",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, float) {
let to_int64 = ctx
.builder
.build_float_to_signed_int(
arg.into_float_value(),
ctx.ctx.i64_type(),
"",
)
.unwrap();
let val = ctx.builder
.build_int_truncate(to_int64, ctx.ctx.i32_type(), "conv")
.unwrap();
Some(val.into())
} else {
unreachable!()
})
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "int64".into(),
simple_name: "int64".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: int64,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool: boolean,
..
} = ctx.primitives;
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, boolean)
|| ctx.unifier.unioned(arg_ty, uint32)
{
Some(
ctx.builder
.build_int_z_extend(
arg.into_int_value(),
ctx.ctx.i64_type(),
"zext",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, int32) {
Some(
ctx.builder
.build_int_s_extend(
arg.into_int_value(),
ctx.ctx.i64_type(),
"sext",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, int64)
|| ctx.unifier.unioned(arg_ty, uint64)
{
Some(arg)
} else if ctx.unifier.unioned(arg_ty, float) {
let val = ctx
.builder
.build_float_to_signed_int(
arg.into_float_value(),
ctx.ctx.i64_type(),
"fptosi",
)
.map(Into::into)
.unwrap();
Some(val)
} else {
unreachable!()
},
)
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "uint32".into(),
simple_name: "uint32".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: uint32,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool: boolean,
..
} = ctx.primitives;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, boolean) {
ctx.builder
.build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext")
.map(Into::into)
.unwrap()
} else if ctx.unifier.unioned(arg_ty, int32)
|| ctx.unifier.unioned(arg_ty, uint32)
{
arg
} else if ctx.unifier.unioned(arg_ty, int64)
|| ctx.unifier.unioned(arg_ty, uint64)
{
ctx.builder
.build_int_truncate(arg.into_int_value(), ctx.ctx.i32_type(), "trunc")
.map(Into::into)
.unwrap()
} else if ctx.unifier.unioned(arg_ty, float) {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_i64 = ctx.ctx.i64_type();
let arg = arg.into_float_value();
let arg_gez = ctx.builder
.build_float_compare(
FloatPredicate::OGE,
arg,
arg.get_type().const_zero(),
"",
)
.unwrap();
let to_int32 = ctx.builder
.build_float_to_signed_int(
arg,
llvm_i32,
"",
)
.unwrap();
let to_uint64 = ctx.builder
.build_float_to_unsigned_int(
arg,
llvm_i64,
"",
)
.unwrap();
let val = ctx.builder
.build_select(
arg_gez,
ctx.builder.build_int_truncate(to_uint64, llvm_i32, "").unwrap(),
to_int32,
"conv",
)
.unwrap();
val
} else {
unreachable!()
};
Ok(Some(res))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "uint64".into(),
simple_name: "uint64".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: uint64,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool: boolean,
..
} = ctx.primitives;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, uint32)
|| ctx.unifier.unioned(arg_ty, boolean)
{
ctx.builder
.build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext")
.map(Into::into)
.unwrap()
} else if ctx.unifier.unioned(arg_ty, int32) {
ctx.builder
.build_int_s_extend(arg.into_int_value(), ctx.ctx.i64_type(), "sext")
.map(Into::into)
.unwrap()
} else if ctx.unifier.unioned(arg_ty, int64)
|| ctx.unifier.unioned(arg_ty, uint64)
{
arg
} else if ctx.unifier.unioned(arg_ty, float) {
let llvm_i64 = ctx.ctx.i64_type();
let arg = arg.into_float_value();
let arg_gez = ctx.builder
.build_float_compare(
FloatPredicate::OGE,
arg,
arg.get_type().const_zero(),
"",
)
.unwrap();
let to_int64 = ctx.builder
.build_float_to_signed_int(arg, llvm_i64, "")
.unwrap();
let to_uint64 = ctx.builder
.build_float_to_unsigned_int(arg, llvm_i64, "")
.unwrap();
let val = ctx.builder
.build_select(arg_gez, to_uint64, to_int64, "conv")
.unwrap();
val
} else {
unreachable!()
};
Ok(Some(res))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "float".into(),
simple_name: "float".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: float,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool: boolean,
..
} = ctx.primitives;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(
if [boolean, int32, int64].iter().any(|ty| ctx.unifier.unioned(arg_ty, *ty))
{
let arg = arg.into_int_value();
let val = ctx
.builder
.build_signed_int_to_float(arg, ctx.ctx.f64_type(), "sitofp")
.map(Into::into)
.unwrap();
Some(val)
} else if [uint32, uint64].iter().any(|ty| ctx.unifier.unioned(arg_ty, *ty)) {
let arg = arg.into_int_value();
let val = ctx
.builder
.build_unsigned_int_to_float(arg, ctx.ctx.f64_type(), "uitofp")
.map(Into::into)
.unwrap();
Some(val)
} else if ctx.unifier.unioned(arg_ty, float) {
Some(arg)
} else {
unreachable!()
},
)
},
)))),
loc: None,
})),
create_fn_by_codegen(
primitives,
&var_map,
"np_ndarray",
ndarray_float,
// We are using List[int32] here, as I don't know a way to specify an n-tuple bound on a
// type variable
&[(list_int32, "shape")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_empty(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_empty",
ndarray_float,
// We are using List[int32] here, as I don't know a way to specify an n-tuple bound on a
// type variable
&[(list_int32, "shape")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_empty(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_zeros",
ndarray_float,
// We are using List[int32] here, as I don't know a way to specify an n-tuple bound on a
// type variable
&[(list_int32, "shape")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_zeros(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_ones",
ndarray_float,
// We are using List[int32] here, as I don't know a way to specify an n-tuple bound on a
// type variable
&[(list_int32, "shape")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_ones(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
),
{
let tv = primitives.1.get_fresh_var(Some("T".into()), None).0;
create_fn_by_codegen(
primitives,
&var_map,
"np_full",
ndarray,
// We are using List[int32] here, as I don't know a way to specify an n-tuple bound on a
// type variable
&[(list_int32, "shape"), (tv, "fill_value")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_full(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
)
},
Arc::new(RwLock::new(TopLevelDef::Function {
name: "np_eye".into(),
simple_name: "np_eye".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg { name: "N".into(), ty: int32, default_value: None },
// TODO(Derppening): Default values current do not work?
FuncArg {
name: "M".into(),
ty: int32,
default_value: Some(SymbolValue::OptionNone)
},
FuncArg { name: "k".into(), ty: int32, default_value: Some(SymbolValue::I32(0)) },
],
ret: ndarray_float_2d,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, fun, args, generator| {
gen_ndarray_eye(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
},
)))),
loc: None,
})),
create_fn_by_codegen(
primitives,
&var_map,
"np_identity",
ndarray_float_2d,
&[(int32, "n")],
Box::new(|ctx, obj, fun, args, generator| {
gen_ndarray_identity(ctx, &obj, fun, &args, generator)
.map(|val| Some(val.as_basic_value_enum()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"round",
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_round(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i32, "round")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"round64",
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_round(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i64, "round")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_round",
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_roundeven(ctx, arg, None);
Ok(Some(val.into()))
}),
),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "range".into(),
simple_name: "range".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg { name: "start".into(), ty: int32, default_value: None },
FuncArg {
name: "stop".into(),
ty: int32,
// placeholder
default_value: Some(SymbolValue::I32(0)),
},
FuncArg {
name: "step".into(),
ty: int32,
default_value: Some(SymbolValue::I32(1)),
},
],
ret: range,
vars: VarMap::default(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let mut start = None;
let mut stop = None;
let mut step = None;
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let ty_i32 = ctx.primitives.int32;
for (i, arg) in args.iter().enumerate() {
if arg.0 == Some("start".into()) {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("stop".into()) {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("step".into()) {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 0 {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 1 {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 2 {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
}
}
let step = match step {
Some(step) => {
let step = step.into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder
.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
)
.unwrap();
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"range() step must not be zero",
[None, None, None],
ctx.current_loc,
);
step
}
None => int32.const_int(1, false),
};
let stop = stop.unwrap_or_else(|| {
let v = start.unwrap();
start = None;
v
});
let start = start.unwrap_or_else(|| int32.const_zero().into());
let ty = int32.array_type(3);
let ptr = generator.gen_var_alloc(ctx, ty.into(), Some("range")).unwrap();
unsafe {
let a = ctx.builder
.build_in_bounds_gep(ptr, &[zero, zero], "start")
.unwrap();
let b = ctx.builder
.build_in_bounds_gep(
ptr,
&[zero, int32.const_int(1, false)],
"end",
)
.unwrap();
let c = ctx.builder.build_in_bounds_gep(
ptr,
&[zero, int32.const_int(2, false)],
"step",
).unwrap();
ctx.builder.build_store(a, start).unwrap();
ctx.builder.build_store(b, stop).unwrap();
ctx.builder.build_store(c, step).unwrap();
}
Ok(Some(ptr.into()))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "str".into(),
simple_name: "str".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "s".into(), ty: string, default_value: None }],
ret: string,
vars: VarMap::default(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?))
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "bool".into(),
simple_name: "bool".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: primitives.0.bool,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
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, boolean) {
Some(arg)
} else if ctx.unifier.unioned(arg_ty, int32) {
Some(
ctx.builder
.build_int_compare(
IntPredicate::NE,
ctx.ctx.i32_type().const_zero(),
arg.into_int_value(),
"bool",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, int64) {
Some(
ctx.builder
.build_int_compare(
IntPredicate::NE,
ctx.ctx.i64_type().const_zero(),
arg.into_int_value(),
"bool",
)
.map(Into::into)
.unwrap(),
)
} else if ctx.unifier.unioned(arg_ty, float) {
let val = ctx
.builder
.build_float_compare(
// UEQ as bool(nan) is True
FloatPredicate::UEQ,
arg.into_float_value(),
ctx.ctx.f64_type().const_zero(),
"bool",
)
.map(Into::into)
.unwrap();
Some(val)
} else {
unreachable!()
})
},
)))),
loc: None,
})),
create_fn_by_codegen(
primitives,
&var_map,
"floor",
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_floor(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i32, "floor")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"floor64",
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_floor(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i64, "floor")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_floor",
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_floor(ctx, arg, None);
Ok(Some(val.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"ceil",
int32,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i32 = ctx.ctx.i32_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_ceil(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i32, "ceil")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"ceil64",
int64,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let llvm_i64 = ctx.ctx.i64_type();
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_ceil(ctx, arg, None);
let val_toint = ctx.builder
.build_float_to_signed_int(val, llvm_i64, "ceil")
.unwrap();
Ok(Some(val_toint.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_ceil",
float,
&[(float, "n")],
Box::new(|ctx, _, _, args, generator| {
let arg = args[0].1.clone()
.to_basic_value_enum(ctx, generator, ctx.primitives.float)?
.into_float_value();
let val = call_float_ceil(ctx, arg, None);
Ok(Some(val.into()))
}),
),
Arc::new(RwLock::new({
let tvar = primitives.1.get_fresh_var(Some("L".into()), None);
let list = primitives.1.add_ty(TypeEnum::TList { ty: tvar.0 });
let ndims = primitives.1.get_fresh_const_generic_var(primitives.0.uint64, Some("N".into()), None);
let ndarray = make_ndarray_ty(
&mut primitives.1,
&primitives.0,
Some(tvar.0),
Some(ndims.0),
);
let arg_ty = primitives.1.get_fresh_var_with_range(
&[list, ndarray, primitives.0.range],
Some("I".into()),
None,
);
TopLevelDef::Function {
name: "len".into(),
simple_name: "len".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "ls".into(), ty: arg_ty.0, default_value: None }],
ret: int32,
vars: vec![(tvar.1, tvar.0), (arg_ty.1, arg_ty.0)]
.into_iter()
.collect(),
})),
var_id: vec![arg_ty.1],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let range_ty = ctx.primitives.range;
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 = 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 {
match &*ctx.unifier.get_ty_immutable(arg_ty) {
TypeEnum::TList { .. } => {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let len = ctx
.build_gep_and_load(
arg.into_pointer_value(),
&[zero, int32.const_int(1, false)],
None,
)
.into_int_value();
if len.get_type().get_bit_width() == 32 {
Some(len.into())
} else {
Some(ctx.builder
.build_int_truncate(len, int32, "len2i32")
.map(Into::into)
.unwrap()
)
}
}
TypeEnum::TObj { obj_id, .. } if *obj_id == PRIMITIVE_DEF_IDS.ndarray => {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let arg = NDArrayValue::from_ptr_val(
arg.into_pointer_value(),
llvm_usize,
None
);
let ndims = arg.dim_sizes().size(ctx, generator);
ctx.make_assert(
generator,
ctx.builder.build_int_compare(
IntPredicate::NE,
ndims,
llvm_usize.const_zero(),
"",
).unwrap(),
"0:TypeError",
"len() of unsized object",
[None, None, None],
ctx.current_loc,
);
let len = unsafe {
arg.dim_sizes().get_typed_unchecked(
ctx,
generator,
llvm_usize.const_zero(),
None,
)
};
if len.get_type().get_bit_width() == 32 {
Some(len.into())
} else {
Some(ctx.builder
.build_int_truncate(len, llvm_i32, "len")
.map(Into::into)
.unwrap()
)
}
}
_ => unreachable!(),
}
})
},
)))),
loc: None,
}
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "min".into(),
simple_name: "min".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg { name: "m".into(), ty: num_ty.0, default_value: None },
FuncArg { name: "n".into(), ty: num_ty.0, default_value: None },
],
ret: num_ty.0,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let boolean = ctx.primitives.bool;
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let uint32 = ctx.primitives.uint32;
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
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);
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) {
call_float_minnum(
ctx,
m_val.into_float_value(),
n_val.into_float_value(),
Some("min"),
).into()
} else {
unreachable!()
};
Ok(val.into())
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "max".into(),
simple_name: "max".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg { name: "m".into(), ty: num_ty.0, default_value: None },
FuncArg { name: "n".into(), ty: num_ty.0, default_value: None },
],
ret: num_ty.0,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let boolean = ctx.primitives.bool;
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let uint32 = ctx.primitives.uint32;
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
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);
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) {
call_float_maxnum(
ctx,
m_val.into_float_value(),
n_val.into_float_value(),
Some("max"),
).into()
} else {
unreachable!()
};
Ok(val.into())
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "abs".into(),
simple_name: "abs".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: num_ty.0,
vars: var_map.clone(),
})),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let boolean = ctx.primitives.bool;
let int32 = ctx.primitives.int32;
let int64 = ctx.primitives.int64;
let uint32 = ctx.primitives.uint32;
let uint64 = ctx.primitives.uint64;
let float = ctx.primitives.float;
let llvm_i1 = ctx.ctx.bool_type();
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 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())
},
)))),
loc: None,
})),
create_fn_by_codegen(
primitives,
&var_map,
"np_isnan",
boolean,
&[(float, "x")],
Box::new(|ctx, _, fun, args, generator| {
let float = ctx.primitives.float;
let x_ty = fun.0.args[0].ty;
let x_val = args[0].1.clone()
.to_basic_value_enum(ctx, generator, x_ty)?;
assert!(ctx.unifier.unioned(x_ty, float));
let val = call_isnan(generator, ctx, x_val.into_float_value());
Ok(Some(val.into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"np_isinf",
boolean,
&[(float, "x")],
Box::new(|ctx, _, fun, args, generator| {
let float = ctx.primitives.float;
let x_ty = fun.0.args[0].ty;
let x_val = args[0].1.clone()
.to_basic_value_enum(ctx, generator, x_ty)?;
assert!(ctx.unifier.unioned(x_ty, float));
let val = call_isinf(generator, ctx, x_val.into_float_value());
Ok(Some(val.into()))
}),
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_sin",
float,
&[(float, "x")],
"llvm.sin.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_cos",
float,
&[(float, "x")],
"llvm.cos.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_exp",
float,
&[(float, "x")],
"llvm.exp.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_exp2",
float,
&[(float, "x")],
"llvm.exp2.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_log",
float,
&[(float, "x")],
"llvm.log.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_log10",
float,
&[(float, "x")],
"llvm.log10.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_log2",
float,
&[(float, "x")],
"llvm.log2.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_fabs",
float,
&[(float, "x")],
"llvm.fabs.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_sqrt",
float,
&[(float, "x")],
"llvm.sqrt.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_rint",
float,
&[(float, "x")],
"llvm.roundeven.f64",
),
create_fn_by_extern(
primitives,
&var_map,
"np_tan",
float,
&[(float, "x")],
"tan",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arcsin",
float,
&[(float, "x")],
"asin",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arccos",
float,
&[(float, "x")],
"acos",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arctan",
float,
&[(float, "x")],
"atan",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_sinh",
float,
&[(float, "x")],
"sinh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_cosh",
float,
&[(float, "x")],
"cosh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_tanh",
float,
&[(float, "x")],
"tanh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arcsinh",
float,
&[(float, "x")],
"asinh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arccosh",
float,
&[(float, "x")],
"acosh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_arctanh",
float,
&[(float, "x")],
"atanh",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_expm1",
float,
&[(float, "x")],
"expm1",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_cbrt",
float,
&[(float, "x")],
"cbrt",
&["readnone", "willreturn"],
),
create_fn_by_extern(
primitives,
&var_map,
"sp_spec_erf",
float,
&[(float, "z")],
"erf",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"sp_spec_erfc",
float,
&[(float, "x")],
"erfc",
&[],
),
create_fn_by_codegen(
primitives,
&var_map,
"sp_spec_gamma",
float,
&[(float, "z")],
Box::new(|ctx, _, fun, args, generator| {
let float = ctx.primitives.float;
let z_ty = fun.0.args[0].ty;
let z_val = args[0].1.clone()
.to_basic_value_enum(ctx, generator, z_ty)?;
assert!(ctx.unifier.unioned(z_ty, float));
Ok(Some(call_gamma(ctx, z_val.into_float_value()).into()))
}
)),
create_fn_by_codegen(
primitives,
&var_map,
"sp_spec_gammaln",
float,
&[(float, "x")],
Box::new(|ctx, _, fun, args, generator| {
let float = ctx.primitives.float;
let z_ty = fun.0.args[0].ty;
let z_val = args[0].1.clone()
.to_basic_value_enum(ctx, generator, z_ty)?;
assert!(ctx.unifier.unioned(z_ty, float));
Ok(Some(call_gammaln(ctx, z_val.into_float_value()).into()))
}),
),
create_fn_by_codegen(
primitives,
&var_map,
"sp_spec_j0",
float,
&[(float, "x")],
Box::new(|ctx, _, fun, args, generator| {
let float = ctx.primitives.float;
let z_ty = fun.0.args[0].ty;
let z_val = args[0].1.clone()
.to_basic_value_enum(ctx, generator, z_ty)?;
assert!(ctx.unifier.unioned(z_ty, float));
Ok(Some(call_j0(ctx, z_val.into_float_value()).into()))
}),
),
create_fn_by_extern(
primitives,
&var_map,
"sp_spec_j1",
float,
&[(float, "x")],
"j1",
&[],
),
// Not mapped: jv/yv, libm only supports integer orders.
create_fn_by_extern(
primitives,
&var_map,
"np_arctan2",
float,
&[(float, "x1"), (float, "x2")],
"atan2",
&[],
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_copysign",
float,
&[(float, "x1"), (float, "x2")],
"llvm.copysign.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_fmax",
float,
&[(float, "x1"), (float, "x2")],
"llvm.maxnum.f64",
),
create_fn_by_intrinsic(
primitives,
&var_map,
"np_fmin",
float,
&[(float, "x1"), (float, "x2")],
"llvm.minnum.f64",
),
create_fn_by_extern(
primitives,
&var_map,
"np_ldexp",
float,
&[(float, "x1"), (int32, "x2")],
"ldexp",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_hypot",
float,
&[(float, "x1"), (float, "x2")],
"hypot",
&[],
),
create_fn_by_extern(
primitives,
&var_map,
"np_nextafter",
float,
&[(float, "x1"), (float, "x2")],
"nextafter",
&[],
),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Some".into(),
simple_name: "Some".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: option_ty_var, default_value: None }],
ret: primitives.0.option,
vars: VarMap::from([(option_ty_var_id, option_ty_var)]),
})),
var_id: vec![option_ty_var_id],
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let alloca = generator.gen_var_alloc(ctx, arg_val.get_type(), Some("alloca_some")).unwrap();
ctx.builder.build_store(alloca, arg_val).unwrap();
Ok(Some(alloca.into()))
},
)))),
loc: None,
})),
];
let ast_list: Vec<Option<Stmt<()>>> =
(0..top_level_def_list.len()).map(|_| None).collect();
izip!(top_level_def_list, ast_list).collect_vec()
}