diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index a94a7dfa7d..440afbf8cc 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -323,6 +323,12 @@ pub fn types() -> MutMap { 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()); diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index dbb1c849cc..193251a460 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -386,6 +386,12 @@ pub fn types() -> MutMap { 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)); diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 6a57b21537..278e25de70 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -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>, 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 = 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>, diff --git a/compiler/gen/tests/gen_builtins.rs b/compiler/gen/tests/gen_builtins.rs index bcf02841fc..8f6f62bc7b 100644 --- a/compiler/gen/tests/gen_builtins.rs +++ b/compiler/gen/tests/gen_builtins.rs @@ -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]);