Add intrinsics for sqrt and round

This commit is contained in:
Richard Feldman 2020-04-23 17:30:24 -04:00
parent 0191910715
commit b43e582c7a
4 changed files with 97 additions and 3 deletions

View File

@ -323,6 +323,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
SolvedType::Func(vec![float_type()], Box::new(float_type())),
);
// round : Float -> Int
add_type(
Symbol::FLOAT_ROUND,
SolvedType::Func(vec![float_type()], Box::new(int_type())),
);
// highest : Float
add_type(Symbol::FLOAT_HIGHEST, float_type());

View File

@ -386,6 +386,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
unique_function(vec![float_type(UVAR1)], float_type(UVAR2)),
);
// round : Float -> Int
add_type(
Symbol::FLOAT_ROUND,
unique_function(vec![float_type(UVAR1)], int_type(UVAR2)),
);
// highest : Float
add_type(Symbol::FLOAT_HIGHEST, float_type(UVAR1));

View File

@ -5,7 +5,7 @@ use inkwell::context::Context;
use inkwell::memory_buffer::MemoryBuffer;
use inkwell::module::{Linkage, Module};
use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{BasicTypeEnum, IntType, StructType};
use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType};
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel};
@ -57,8 +57,48 @@ pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Modu
let memory_buffer =
MemoryBuffer::create_from_memory_range(include_bytes!("builtins.bc"), module_name);
Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err))
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err));
// Add LLVM intrinsics.
add_intrinsics(ctx, &module);
module
}
fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
// List of all supported LLVM intrinsics:
//
// https://releases.llvm.org/10.0.0/docs/LangRef.html#standard-c-library-intrinsics
let i64_type = ctx.i64_type();
let f64_type = ctx.f64_type();
add_intrinsic(
module,
LLVM_SQRT_F64,
f64_type.fn_type(&[f64_type.into()], false),
);
add_intrinsic(
module,
LLVM_LROUND_I64_F64,
i64_type.fn_type(&[f64_type.into()], false),
);
}
static LLVM_SQRT_F64: &str = "llvm.sqrt.f64";
static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
fn add_intrinsic<'ctx>(
module: &Module<'ctx>,
intrinsic_name: &'static str,
fn_type: FunctionType<'ctx>,
) -> FunctionValue<'ctx> {
let fn_val = module.add_function(intrinsic_name, fn_type, None);
fn_val.set_call_conventions(C_CALL_CONV);
fn_val
}
pub fn add_passes(fpm: &PassManager<FunctionValue<'_>>, opt_level: OptLevel) {
@ -1182,6 +1222,8 @@ fn call_with_args<'a, 'ctx, 'env>(
}
}
}
Symbol::FLOAT_SQRT => call_intrinsic(LLVM_SQRT_F64, env, args),
Symbol::FLOAT_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args),
Symbol::LIST_SET => list_set(parent, args, env, InPlace::Clone),
Symbol::LIST_SET_IN_PLACE => list_set(parent, args, env, InPlace::InPlace),
_ => {
@ -1209,6 +1251,36 @@ fn call_with_args<'a, 'ctx, 'env>(
}
}
fn call_intrinsic<'a, 'ctx, 'env>(
intrinsic_name: &'static str,
env: &Env<'a, 'ctx, 'env>,
args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)],
) -> BasicValueEnum<'ctx> {
let fn_val = env
.module
.get_function(intrinsic_name)
.unwrap_or_else(|| panic!("Unrecognized intrinsic function: {}", intrinsic_name));
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
for (arg, _layout) in args.iter() {
arg_vals.push(*arg);
}
let call = env
.builder
.build_call(fn_val, arg_vals.into_bump_slice(), "call");
call.set_call_convention(fn_val.get_call_conventions());
call.try_as_basic_value().left().unwrap_or_else(|| {
panic!(
"LLVM error: Invalid call by name for intrinsic {}",
intrinsic_name
)
})
}
fn load_list_len<'ctx>(
builder: &Builder<'ctx>,
wrapper_struct: StructValue<'ctx>,

View File

@ -27,6 +27,16 @@ mod gen_builtins {
use roc_mono::layout::Layout;
use roc_types::subs::Subs;
#[test]
fn f64_sqrt() {
assert_evals_to!("Float.sqrt 144", 12.0, f64);
}
#[test]
fn f64_round() {
assert_evals_to!("Float.round 3.6", 4, i64);
}
#[test]
fn empty_list_literal() {
assert_evals_to!("[]", &[], &'static [i64]);