Merge pull request #5496 from roc-lang/remove-libc-instrinsic

Stop using llvm instrinsics that just call libc
This commit is contained in:
Folkert de Vries 2023-06-01 10:46:27 +02:00 committed by GitHub
commit 6d6e3b9c60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 96 deletions

View file

@ -725,7 +725,7 @@ mod cli_run {
Arg::PlainText("81"),
],
&[],
"4\n",
"4.000000000000001\n",
UseValgrind::No,
TestCliCommands::Run,
)

View file

@ -17,7 +17,7 @@ pub fn build(b: *Builder) void {
// Tests
var main_tests = b.addTest(main_path);
main_tests.setBuildMode(mode);
main_tests.linkSystemLibrary("c");
main_tests.linkLibC();
const test_step = b.step("test", "Run tests");
test_step.dependOn(&main_tests.step);

View file

@ -85,8 +85,12 @@ comptime {
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow_int.");
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
num.exportRoundF32(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f32.");
num.exportRoundF64(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f64.");
num.exportRound(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f32.");
num.exportRound(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f64.");
num.exportFloor(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".floor_f32.");
num.exportFloor(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".floor_f64.");
num.exportCeiling(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".ceiling_f32.");
num.exportCeiling(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".ceiling_f64.");
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
num.exportAddOrPanic(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_or_panic.");
@ -126,6 +130,8 @@ comptime {
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow.");
num.exportLog(T, ROC_BUILTINS ++ "." ++ NUM ++ ".log.");
num.exportFAbs(T, ROC_BUILTINS ++ "." ++ NUM ++ ".fabs.");
num.exportSqrt(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sqrt.");
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
num.exportSubWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sub_with_overflow.");

View file

@ -152,7 +152,7 @@ pub fn exportAtan(comptime T: type, comptime name: []const u8) void {
pub fn exportSin(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: T) callconv(.C) T {
return @sin(input);
return math.sin(input);
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
@ -161,7 +161,7 @@ pub fn exportSin(comptime T: type, comptime name: []const u8) void {
pub fn exportCos(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: T) callconv(.C) T {
return @cos(input);
return math.cos(input);
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
@ -170,25 +170,52 @@ pub fn exportCos(comptime T: type, comptime name: []const u8) void {
pub fn exportLog(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: T) callconv(.C) T {
return @log(input);
return math.ln(input);
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
pub fn exportRoundF32(comptime T: type, comptime name: []const u8) void {
pub fn exportFAbs(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: f32) callconv(.C) T {
return @floatToInt(T, (@round(input)));
fn func(input: T) callconv(.C) T {
return math.absFloat(input);
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
pub fn exportRoundF64(comptime T: type, comptime name: []const u8) void {
pub fn exportSqrt(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: f64) callconv(.C) T {
return @floatToInt(T, (@round(input)));
fn func(input: T) callconv(.C) T {
return math.sqrt(input);
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
pub fn exportRound(comptime F: type, comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: F) callconv(.C) T {
return @floatToInt(T, (math.round(input)));
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
pub fn exportFloor(comptime F: type, comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: F) callconv(.C) T {
return @floatToInt(T, (math.floor(input)));
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
pub fn exportCeiling(comptime F: type, comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(input: F) callconv(.C) T {
return @floatToInt(T, (math.ceil(input)));
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });

View file

@ -267,9 +267,15 @@ pub const NUM_IS_INFINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
pub const NUM_LOG: IntrinsicName = float_intrinsic!("roc_builtins.num.log");
pub const NUM_POW: IntrinsicName = float_intrinsic!("roc_builtins.num.pow");
pub const NUM_FABS: IntrinsicName = float_intrinsic!("roc_builtins.num.fabs");
pub const NUM_SQRT: IntrinsicName = float_intrinsic!("roc_builtins.num.sqrt");
pub const NUM_POW_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.pow_int");
pub const NUM_DIV_CEIL: IntrinsicName = int_intrinsic!("roc_builtins.num.div_ceil");
pub const NUM_CEILING_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.ceiling_f32");
pub const NUM_CEILING_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.ceiling_f64");
pub const NUM_FLOOR_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.floor_f32");
pub const NUM_FLOOR_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.floor_f64");
pub const NUM_ROUND_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f32");
pub const NUM_ROUND_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f64");

View file

@ -7,11 +7,12 @@ use inkwell::{
};
use roc_builtins::{
bitcode::{FloatWidth, IntWidth, IntrinsicName},
float_intrinsic, llvm_int_intrinsic,
llvm_int_intrinsic,
};
use super::build::{add_func, FunctionSpec};
#[allow(dead_code)]
fn add_float_intrinsic<'ctx, F>(
ctx: &'ctx Context,
module: &Module<'ctx>,
@ -111,18 +112,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
i8_ptr_type.fn_type(&[], false),
);
add_float_intrinsic(ctx, module, &LLVM_LOG, |t| t.fn_type(&[t.into()], false));
add_float_intrinsic(ctx, module, &LLVM_POW, |t| {
t.fn_type(&[t.into(), t.into()], false)
});
add_float_intrinsic(ctx, module, &LLVM_FABS, |t| t.fn_type(&[t.into()], false));
add_float_intrinsic(ctx, module, &LLVM_SIN, |t| t.fn_type(&[t.into()], false));
add_float_intrinsic(ctx, module, &LLVM_COS, |t| t.fn_type(&[t.into()], false));
add_float_intrinsic(ctx, module, &LLVM_CEILING, |t| {
t.fn_type(&[t.into()], false)
});
add_float_intrinsic(ctx, module, &LLVM_FLOOR, |t| t.fn_type(&[t.into()], false));
add_int_intrinsic(ctx, module, &LLVM_ADD_WITH_OVERFLOW, |t| {
let fields = [t.into(), i1_type.into()];
ctx.struct_type(&fields, false)
@ -150,17 +139,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
});
}
pub const LLVM_POW: IntrinsicName = float_intrinsic!("llvm.pow");
pub const LLVM_FABS: IntrinsicName = float_intrinsic!("llvm.fabs");
pub static LLVM_SQRT: IntrinsicName = float_intrinsic!("llvm.sqrt");
pub static LLVM_LOG: IntrinsicName = float_intrinsic!("llvm.log");
pub static LLVM_SIN: IntrinsicName = float_intrinsic!("llvm.sin");
pub static LLVM_COS: IntrinsicName = float_intrinsic!("llvm.cos");
pub static LLVM_CEILING: IntrinsicName = float_intrinsic!("llvm.ceil");
pub static LLVM_FLOOR: IntrinsicName = float_intrinsic!("llvm.floor");
pub static LLVM_ROUND: IntrinsicName = float_intrinsic!("llvm.round");
pub static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
pub static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";

View file

@ -41,9 +41,13 @@ use crate::llvm::{
self, basic_type_from_layout, zig_num_parse_result_type, zig_to_int_checked_result_type,
},
intrinsics::{
LLVM_ADD_SATURATED, LLVM_ADD_WITH_OVERFLOW, LLVM_CEILING, LLVM_COS, LLVM_FABS, LLVM_FLOOR,
LLVM_LOG, LLVM_MUL_WITH_OVERFLOW, LLVM_POW, LLVM_ROUND, LLVM_SIN, LLVM_SQRT,
LLVM_SUB_SATURATED, LLVM_SUB_WITH_OVERFLOW,
// These instrinsics do not generate calls to libc and are safe to keep.
// If we find that any of them generate calls to libc on some platforms, we need to define them as zig bitcode.
LLVM_ADD_SATURATED,
LLVM_ADD_WITH_OVERFLOW,
LLVM_MUL_WITH_OVERFLOW,
LLVM_SUB_SATURATED,
LLVM_SUB_WITH_OVERFLOW,
},
refcounting::PointerToRefcount,
};
@ -1704,7 +1708,11 @@ fn build_float_binop<'ctx>(
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
NumDivFrac => bd.build_float_div(lhs, rhs, "div_float").into(),
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
NumPow => call_bitcode_fn(
env,
&[lhs.into(), rhs.into()],
&bitcode::NUM_POW[float_width],
),
_ => {
unreachable!("Unrecognized int binary operation: {:?}", op);
}
@ -2316,9 +2324,9 @@ fn build_float_unary_op<'a, 'ctx>(
// TODO: Handle different sized floats
match op {
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
NumAbs => env.call_intrinsic(&LLVM_FABS[float_width], &[arg.into()]),
NumSqrtUnchecked => env.call_intrinsic(&LLVM_SQRT[float_width], &[arg.into()]),
NumLogUnchecked => env.call_intrinsic(&LLVM_LOG[float_width], &[arg.into()]),
NumAbs => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FABS[float_width]),
NumSqrtUnchecked => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_SQRT[float_width]),
NumLogUnchecked => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_LOG[float_width]),
NumToFrac => {
let return_width = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Float(return_width)) => return_width,
@ -2342,64 +2350,46 @@ fn build_float_unary_op<'a, 'ctx>(
}
}
NumCeiling => {
let (return_signed, return_type) = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
int_width.is_signed(),
convert::int_type_from_int_width(env, int_width),
),
let int_width = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
};
let opcode = if return_signed {
InstructionOpcode::FPToSI
} else {
InstructionOpcode::FPToUI
};
env.builder.build_cast(
opcode,
env.call_intrinsic(&LLVM_CEILING[float_width], &[arg.into()]),
return_type,
"num_ceiling",
)
match float_width {
FloatWidth::F32 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_CEILING_F32[int_width])
}
FloatWidth::F64 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_CEILING_F64[int_width])
}
}
}
NumFloor => {
let (return_signed, return_type) = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
int_width.is_signed(),
convert::int_type_from_int_width(env, int_width),
),
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
let int_width = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
_ => internal_error!("Floor return layout is not int: {:?}", layout),
};
let opcode = if return_signed {
InstructionOpcode::FPToSI
} else {
InstructionOpcode::FPToUI
};
env.builder.build_cast(
opcode,
env.call_intrinsic(&LLVM_FLOOR[float_width], &[arg.into()]),
return_type,
"num_floor",
)
match float_width {
FloatWidth::F32 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FLOOR_F32[int_width])
}
FloatWidth::F64 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FLOOR_F64[int_width])
}
}
}
NumRound => {
let (return_signed, return_type) = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
int_width.is_signed(),
convert::int_type_from_int_width(env, int_width),
),
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
let int_width = match layout_interner.get(layout).repr {
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
_ => internal_error!("Round return layout is not int: {:?}", layout),
};
let opcode = if return_signed {
InstructionOpcode::FPToSI
} else {
InstructionOpcode::FPToUI
};
env.builder.build_cast(
opcode,
env.call_intrinsic(&LLVM_ROUND[float_width], &[arg.into()]),
return_type,
"num_round",
)
match float_width {
FloatWidth::F32 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND_F32[int_width])
}
FloatWidth::F64 => {
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND_F64[int_width])
}
}
}
NumIsNan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_NAN[float_width]),
NumIsInfinite => {
@ -2408,8 +2398,8 @@ fn build_float_unary_op<'a, 'ctx>(
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
// trigonometry
NumSin => env.call_intrinsic(&LLVM_SIN[float_width], &[arg.into()]),
NumCos => env.call_intrinsic(&LLVM_COS[float_width], &[arg.into()]),
NumSin => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_SIN[float_width]),
NumCos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_COS[float_width]),
NumAtan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ATAN[float_width]),
NumAcos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ACOS[float_width]),