mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge pull request #1796 from rtfeldman/divTruncate
Intrinsics for more types
This commit is contained in:
commit
cf76ef1f6f
6 changed files with 443 additions and 244 deletions
|
@ -1,4 +1,9 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const math = std.math;
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
|
||||||
|
const ROC_BUILTINS = "roc_builtins";
|
||||||
|
const NUM = "num";
|
||||||
|
|
||||||
// Dec Module
|
// Dec Module
|
||||||
const dec = @import("dec.zig");
|
const dec = @import("dec.zig");
|
||||||
|
@ -72,16 +77,28 @@ comptime {
|
||||||
|
|
||||||
// Num Module
|
// Num Module
|
||||||
const num = @import("num.zig");
|
const num = @import("num.zig");
|
||||||
|
|
||||||
|
const INTEGERS = [_]type{ i8, i16, i32, i64, i128, u8, u16, u32, u64, u128 };
|
||||||
|
const FLOATS = [_]type{ f32, f64 };
|
||||||
|
const NUMBERS = INTEGERS ++ FLOATS;
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
exportNumFn(num.atan, "atan");
|
|
||||||
exportNumFn(num.isFinite, "is_finite");
|
|
||||||
exportNumFn(num.powInt, "pow_int");
|
|
||||||
exportNumFn(num.divCeil, "div_ceil");
|
|
||||||
exportNumFn(num.acos, "acos");
|
|
||||||
exportNumFn(num.asin, "asin");
|
|
||||||
exportNumFn(num.bytesToU16C, "bytes_to_u16");
|
exportNumFn(num.bytesToU16C, "bytes_to_u16");
|
||||||
exportNumFn(num.bytesToU32C, "bytes_to_u32");
|
exportNumFn(num.bytesToU32C, "bytes_to_u32");
|
||||||
exportNumFn(num.round, "round");
|
|
||||||
|
inline for (INTEGERS) |T| {
|
||||||
|
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow_int.");
|
||||||
|
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (FLOATS) |T| {
|
||||||
|
num.exportAsin(T, ROC_BUILTINS ++ "." ++ NUM ++ ".asin.");
|
||||||
|
num.exportAcos(T, ROC_BUILTINS ++ "." ++ NUM ++ ".acos.");
|
||||||
|
num.exportAtan(T, ROC_BUILTINS ++ "." ++ NUM ++ ".atan.");
|
||||||
|
|
||||||
|
num.exportIsFinite(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_finite.");
|
||||||
|
num.exportRound(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str Module
|
// Str Module
|
||||||
|
@ -107,7 +124,7 @@ comptime {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const utils = @import("utils.zig");
|
|
||||||
comptime {
|
comptime {
|
||||||
exportUtilsFn(utils.test_panic, "test_panic");
|
exportUtilsFn(utils.test_panic, "test_panic");
|
||||||
exportUtilsFn(utils.decrefC, "decref");
|
exportUtilsFn(utils.decrefC, "decref");
|
||||||
|
|
|
@ -3,28 +3,67 @@ const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const RocList = @import("list.zig").RocList;
|
const RocList = @import("list.zig").RocList;
|
||||||
|
|
||||||
pub fn atan(num: f64) callconv(.C) f64 {
|
pub fn exportPow(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.atan, .{num});
|
comptime var f = struct {
|
||||||
|
fn func(base: T, exp: T) callconv(.C) T {
|
||||||
|
return std.math.pow(T, base, exp);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isFinite(num: f64) callconv(.C) bool {
|
pub fn exportIsFinite(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.isFinite, .{num});
|
comptime var f = struct {
|
||||||
|
fn func(input: T) callconv(.C) bool {
|
||||||
|
return std.math.isFinite(input);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn powInt(base: i64, exp: i64) callconv(.C) i64 {
|
pub fn exportAsin(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.pow, .{ i64, base, exp });
|
comptime var f = struct {
|
||||||
|
fn func(input: T) callconv(.C) T {
|
||||||
|
return std.math.asin(input);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divCeil(numerator: i64, denominator: i64) callconv(.C) i64 {
|
pub fn exportAcos(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.divCeil, .{ i64, numerator, denominator }) catch unreachable;
|
comptime var f = struct {
|
||||||
|
fn func(input: T) callconv(.C) T {
|
||||||
|
return std.math.acos(input);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acos(num: f64) callconv(.C) f64 {
|
pub fn exportAtan(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.acos, .{num});
|
comptime var f = struct {
|
||||||
|
fn func(input: T) callconv(.C) T {
|
||||||
|
return std.math.atan(input);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asin(num: f64) callconv(.C) f64 {
|
pub fn exportRound(comptime T: type, comptime name: []const u8) void {
|
||||||
return @call(.{ .modifier = always_inline }, math.asin, .{num});
|
comptime var f = struct {
|
||||||
|
fn func(input: T) callconv(.C) i64 {
|
||||||
|
return @floatToInt(i64, (@round(input)));
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportDivCeil(comptime T: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(a: T, b: T) callconv(.C) T {
|
||||||
|
return math.divCeil(T, a, b) catch unreachable;
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
||||||
|
@ -44,7 +83,3 @@ fn bytesToU32(arg: RocList, position: usize) u32 {
|
||||||
const bytes = @ptrCast([*]const u8, arg.bytes);
|
const bytes = @ptrCast([*]const u8, arg.bytes);
|
||||||
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
|
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn round(num: f64) callconv(.C) i64 {
|
|
||||||
return @floatToInt(i32, (@round(num)));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,129 @@
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
pub const OBJ_PATH: &str = env!(
|
pub const OBJ_PATH: &str = env!(
|
||||||
"BUILTINS_O",
|
"BUILTINS_O",
|
||||||
"Env var BUILTINS_O not found. Is there a problem with the build script?"
|
"Env var BUILTINS_O not found. Is there a problem with the build script?"
|
||||||
);
|
);
|
||||||
|
|
||||||
pub const NUM_ASIN: &str = "roc_builtins.num.asin";
|
#[derive(Debug, Default)]
|
||||||
pub const NUM_ACOS: &str = "roc_builtins.num.acos";
|
pub struct IntrinsicName {
|
||||||
pub const NUM_ATAN: &str = "roc_builtins.num.atan";
|
pub options: [&'static str; 14],
|
||||||
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
|
}
|
||||||
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
|
|
||||||
pub const NUM_DIV_CEIL: &str = "roc_builtins.num.div_ceil";
|
impl IntrinsicName {
|
||||||
|
pub const fn default() -> Self {
|
||||||
|
Self { options: [""; 14] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum DecWidth {
|
||||||
|
Dec,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum FloatWidth {
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
|
F128,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum IntWidth {
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
U128,
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
I128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<DecWidth> for IntrinsicName {
|
||||||
|
type Output = str;
|
||||||
|
|
||||||
|
fn index(&self, _: DecWidth) -> &Self::Output {
|
||||||
|
self.options[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<FloatWidth> for IntrinsicName {
|
||||||
|
type Output = str;
|
||||||
|
|
||||||
|
fn index(&self, index: FloatWidth) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
FloatWidth::F32 => self.options[1],
|
||||||
|
FloatWidth::F64 => self.options[2],
|
||||||
|
FloatWidth::F128 => self.options[3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<IntWidth> for IntrinsicName {
|
||||||
|
type Output = str;
|
||||||
|
|
||||||
|
fn index(&self, index: IntWidth) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
IntWidth::U8 => self.options[4],
|
||||||
|
IntWidth::U16 => self.options[5],
|
||||||
|
IntWidth::U32 => self.options[6],
|
||||||
|
IntWidth::U64 => self.options[7],
|
||||||
|
IntWidth::U128 => self.options[8],
|
||||||
|
IntWidth::I8 => self.options[9],
|
||||||
|
IntWidth::I16 => self.options[10],
|
||||||
|
IntWidth::I32 => self.options[11],
|
||||||
|
IntWidth::I64 => self.options[12],
|
||||||
|
IntWidth::I128 => self.options[13],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! float_intrinsic {
|
||||||
|
($name:literal) => {{
|
||||||
|
let mut output = IntrinsicName::default();
|
||||||
|
|
||||||
|
output.options[1] = concat!($name, ".f32");
|
||||||
|
output.options[2] = concat!($name, ".f64");
|
||||||
|
output.options[3] = concat!($name, ".f128");
|
||||||
|
|
||||||
|
output
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! int_intrinsic {
|
||||||
|
($name:literal) => {{
|
||||||
|
let mut output = IntrinsicName::default();
|
||||||
|
|
||||||
|
output.options[4] = concat!($name, ".i8");
|
||||||
|
output.options[5] = concat!($name, ".i16");
|
||||||
|
output.options[6] = concat!($name, ".i32");
|
||||||
|
output.options[7] = concat!($name, ".i64");
|
||||||
|
output.options[8] = concat!($name, ".i128");
|
||||||
|
output.options[9] = concat!($name, ".i8");
|
||||||
|
output.options[10] = concat!($name, ".i16");
|
||||||
|
output.options[11] = concat!($name, ".i32");
|
||||||
|
output.options[12] = concat!($name, ".i64");
|
||||||
|
output.options[13] = concat!($name, ".i128");
|
||||||
|
|
||||||
|
output
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin");
|
||||||
|
pub const NUM_ACOS: IntrinsicName = float_intrinsic!("roc_builtins.num.acos");
|
||||||
|
pub const NUM_ATAN: IntrinsicName = float_intrinsic!("roc_builtins.num.atan");
|
||||||
|
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
||||||
|
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_ROUND: IntrinsicName = float_intrinsic!("roc_builtins.num.round");
|
||||||
|
|
||||||
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
||||||
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
||||||
pub const NUM_ROUND: &str = "roc_builtins.num.round";
|
|
||||||
|
|
||||||
pub const STR_INIT: &str = "roc_builtins.str.init";
|
pub const STR_INIT: &str = "roc_builtins.str.init";
|
||||||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use bumpalo::{collections::Vec, Bump};
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{ModuleName, TagName};
|
use roc_module::ident::{ModuleName, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -400,21 +400,21 @@ where
|
||||||
}
|
}
|
||||||
LowLevel::NumAcos => self.build_fn_call(
|
LowLevel::NumAcos => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_ACOS.to_string(),
|
bitcode::NUM_ACOS[FloatWidth::F64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
),
|
),
|
||||||
LowLevel::NumAsin => self.build_fn_call(
|
LowLevel::NumAsin => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_ASIN.to_string(),
|
bitcode::NUM_ASIN[FloatWidth::F64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
),
|
),
|
||||||
LowLevel::NumAtan => self.build_fn_call(
|
LowLevel::NumAtan => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_ATAN.to_string(),
|
bitcode::NUM_ATAN[FloatWidth::F64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -437,7 +437,7 @@ where
|
||||||
}
|
}
|
||||||
LowLevel::NumPowInt => self.build_fn_call(
|
LowLevel::NumPowInt => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_POW_INT.to_string(),
|
bitcode::NUM_POW_INT[IntWidth::I64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -473,7 +473,7 @@ where
|
||||||
}
|
}
|
||||||
LowLevel::NumRound => self.build_fn_call(
|
LowLevel::NumRound => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_ROUND.to_string(),
|
bitcode::NUM_ROUND[FloatWidth::F64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
|
|
@ -48,7 +48,8 @@ use inkwell::{AddressSpace, IntPredicate};
|
||||||
use morphic_lib::{
|
use morphic_lib::{
|
||||||
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
|
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
|
||||||
};
|
};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth, IntrinsicName};
|
||||||
|
use roc_builtins::{float_intrinsic, int_intrinsic};
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
@ -430,6 +431,58 @@ pub fn module_from_builtins<'ctx>(
|
||||||
module
|
module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_float_intrinsic<'ctx, F>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
module: &Module<'ctx>,
|
||||||
|
name: &IntrinsicName,
|
||||||
|
construct_type: F,
|
||||||
|
) where
|
||||||
|
F: Fn(inkwell::types::FloatType<'ctx>) -> inkwell::types::FunctionType<'ctx>,
|
||||||
|
{
|
||||||
|
macro_rules! check {
|
||||||
|
($width:expr, $typ:expr) => {
|
||||||
|
let full_name = &name[$width];
|
||||||
|
|
||||||
|
if let Some(_) = module.get_function(full_name) {
|
||||||
|
// zig defined this function already
|
||||||
|
} else {
|
||||||
|
add_intrinsic(module, full_name, construct_type($typ));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
check!(FloatWidth::F32, ctx.f32_type());
|
||||||
|
check!(FloatWidth::F64, ctx.f64_type());
|
||||||
|
// check!(IntWidth::F128, ctx.i128_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_int_intrinsic<'ctx, F>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
module: &Module<'ctx>,
|
||||||
|
name: &IntrinsicName,
|
||||||
|
construct_type: F,
|
||||||
|
) where
|
||||||
|
F: Fn(inkwell::types::IntType<'ctx>) -> inkwell::types::FunctionType<'ctx>,
|
||||||
|
{
|
||||||
|
macro_rules! check {
|
||||||
|
($width:expr, $typ:expr) => {
|
||||||
|
let full_name = &name[$width];
|
||||||
|
|
||||||
|
if let Some(_) = module.get_function(full_name) {
|
||||||
|
// zig defined this function already
|
||||||
|
} else {
|
||||||
|
add_intrinsic(module, full_name, construct_type($typ));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
check!(IntWidth::I8, ctx.i8_type());
|
||||||
|
check!(IntWidth::I16, ctx.i16_type());
|
||||||
|
check!(IntWidth::I32, ctx.i32_type());
|
||||||
|
check!(IntWidth::I64, ctx.i64_type());
|
||||||
|
check!(IntWidth::I128, ctx.i128_type());
|
||||||
|
}
|
||||||
|
|
||||||
fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
||||||
// List of all supported LLVM intrinsics:
|
// List of all supported LLVM intrinsics:
|
||||||
//
|
//
|
||||||
|
@ -438,7 +491,6 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
||||||
let i1_type = ctx.bool_type();
|
let i1_type = ctx.bool_type();
|
||||||
let i8_type = ctx.i8_type();
|
let i8_type = ctx.i8_type();
|
||||||
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
|
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
|
||||||
let i16_type = ctx.i16_type();
|
|
||||||
let i32_type = ctx.i32_type();
|
let i32_type = ctx.i32_type();
|
||||||
let i64_type = ctx.i64_type();
|
let i64_type = ctx.i64_type();
|
||||||
let void_type = ctx.void_type();
|
let void_type = ctx.void_type();
|
||||||
|
@ -475,114 +527,56 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_STACK_SAVE, i8_ptr_type.fn_type(&[], false));
|
add_intrinsic(module, LLVM_STACK_SAVE, i8_ptr_type.fn_type(&[], false));
|
||||||
|
|
||||||
add_intrinsic(
|
|
||||||
module,
|
|
||||||
LLVM_LOG_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(
|
add_intrinsic(
|
||||||
module,
|
module,
|
||||||
LLVM_LROUND_I64_F64,
|
LLVM_LROUND_I64_F64,
|
||||||
i64_type.fn_type(&[f64_type.into()], false),
|
i64_type.fn_type(&[f64_type.into()], false),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_intrinsic(
|
add_float_intrinsic(ctx, module, &LLVM_LOG, |t| t.fn_type(&[t.into()], false));
|
||||||
module,
|
add_float_intrinsic(ctx, module, &LLVM_POW, |t| {
|
||||||
LLVM_FABS_F64,
|
t.fn_type(&[t.into(), t.into()], false)
|
||||||
f64_type.fn_type(&[f64_type.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_intrinsic(
|
add_int_intrinsic(ctx, module, &LLVM_SADD_WITH_OVERFLOW, |t| {
|
||||||
module,
|
let fields = [t.into(), i1_type.into()];
|
||||||
LLVM_SIN_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(
|
|
||||||
module,
|
|
||||||
LLVM_COS_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(
|
|
||||||
module,
|
|
||||||
LLVM_POW_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into(), f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(
|
|
||||||
module,
|
|
||||||
LLVM_CEILING_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(
|
|
||||||
module,
|
|
||||||
LLVM_FLOOR_F64,
|
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
|
||||||
);
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I8, {
|
|
||||||
let fields = [i8_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
ctx.struct_type(&fields, false)
|
||||||
.fn_type(&[i8_type.into(), i8_type.into()], false)
|
.fn_type(&[t.into(), t.into()], false)
|
||||||
});
|
});
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I16, {
|
add_int_intrinsic(ctx, module, &LLVM_SSUB_WITH_OVERFLOW, |t| {
|
||||||
let fields = [i16_type.into(), i1_type.into()];
|
let fields = [t.into(), i1_type.into()];
|
||||||
ctx.struct_type(&fields, false)
|
ctx.struct_type(&fields, false)
|
||||||
.fn_type(&[i16_type.into(), i16_type.into()], false)
|
.fn_type(&[t.into(), t.into()], false)
|
||||||
});
|
});
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I32, {
|
add_int_intrinsic(ctx, module, &LLVM_SMUL_WITH_OVERFLOW, |t| {
|
||||||
let fields = [i32_type.into(), i1_type.into()];
|
let fields = [t.into(), i1_type.into()];
|
||||||
ctx.struct_type(&fields, false)
|
ctx.struct_type(&fields, false)
|
||||||
.fn_type(&[i32_type.into(), i32_type.into()], false)
|
.fn_type(&[t.into(), t.into()], false)
|
||||||
});
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I64, {
|
|
||||||
let fields = [i64_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
|
||||||
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
|
||||||
});
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I8, {
|
|
||||||
let fields = [i8_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
|
||||||
.fn_type(&[i8_type.into(), i8_type.into()], false)
|
|
||||||
});
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I16, {
|
|
||||||
let fields = [i16_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
|
||||||
.fn_type(&[i16_type.into(), i16_type.into()], false)
|
|
||||||
});
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I32, {
|
|
||||||
let fields = [i32_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
|
||||||
.fn_type(&[i32_type.into(), i32_type.into()], false)
|
|
||||||
});
|
|
||||||
|
|
||||||
add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I64, {
|
|
||||||
let fields = [i64_type.into(), i1_type.into()];
|
|
||||||
ctx.struct_type(&fields, false)
|
|
||||||
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LLVM_POW: IntrinsicName = float_intrinsic!("llvm.pow");
|
||||||
|
const LLVM_FABS: IntrinsicName = float_intrinsic!("llvm.fabs");
|
||||||
|
static LLVM_SQRT: IntrinsicName = float_intrinsic!("llvm.sqrt");
|
||||||
|
static LLVM_LOG: IntrinsicName = float_intrinsic!("llvm.log");
|
||||||
|
|
||||||
|
static LLVM_SIN: IntrinsicName = float_intrinsic!("llvm.sin");
|
||||||
|
static LLVM_COS: IntrinsicName = float_intrinsic!("llvm.cos");
|
||||||
|
static LLVM_CEILING: IntrinsicName = float_intrinsic!("llvm.ceil");
|
||||||
|
static LLVM_FLOOR: IntrinsicName = float_intrinsic!("llvm.floor");
|
||||||
|
|
||||||
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
||||||
static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
||||||
static LLVM_SQRT_F64: &str = "llvm.sqrt.f64";
|
|
||||||
static LLVM_LOG_F64: &str = "llvm.log.f64";
|
|
||||||
static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
|
static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
|
||||||
static LLVM_FABS_F64: &str = "llvm.fabs.f64";
|
|
||||||
static LLVM_SIN_F64: &str = "llvm.sin.f64";
|
|
||||||
static LLVM_COS_F64: &str = "llvm.cos.f64";
|
|
||||||
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
|
||||||
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
|
||||||
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
|
||||||
|
|
||||||
// static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress";
|
// static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress";
|
||||||
static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress.p0i8";
|
static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress.p0i8";
|
||||||
|
@ -591,23 +585,13 @@ static LLVM_STACK_SAVE: &str = "llvm.stacksave";
|
||||||
static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
|
static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
|
||||||
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
|
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
|
||||||
|
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I8: &str = "llvm.sadd.with.overflow.i8";
|
const LLVM_SADD_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.sadd.with.overflow");
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I16: &str = "llvm.sadd.with.overflow.i16";
|
const LLVM_SSUB_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.ssub.with.overflow");
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I32: &str = "llvm.sadd.with.overflow.i32";
|
const LLVM_SMUL_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.smul.with.overflow");
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I128: &str = "llvm.sadd.with.overflow.i128";
|
|
||||||
|
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I8: &str = "llvm.ssub.with.overflow.i8";
|
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I16: &str = "llvm.ssub.with.overflow.i16";
|
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I32: &str = "llvm.ssub.with.overflow.i32";
|
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64";
|
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I128: &str = "llvm.ssub.with.overflow.i128";
|
|
||||||
|
|
||||||
pub static LLVM_SMUL_WITH_OVERFLOW_I64: &str = "llvm.smul.with.overflow.i64";
|
|
||||||
|
|
||||||
fn add_intrinsic<'ctx>(
|
fn add_intrinsic<'ctx>(
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
intrinsic_name: &'static str,
|
intrinsic_name: &str,
|
||||||
fn_type: FunctionType<'ctx>,
|
fn_type: FunctionType<'ctx>,
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
add_func(
|
add_func(
|
||||||
|
@ -5161,8 +5145,14 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => {
|
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||||
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
|
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
|
||||||
}
|
}
|
||||||
Float128 | Float64 | Float32 => {
|
Float32 => {
|
||||||
build_float_unary_op(env, arg.into_float_value(), op)
|
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F32)
|
||||||
|
}
|
||||||
|
Float64 => {
|
||||||
|
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F64)
|
||||||
|
}
|
||||||
|
Float128 => {
|
||||||
|
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F128)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, arg_layout);
|
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, arg_layout);
|
||||||
|
@ -5896,6 +5886,31 @@ fn throw_on_overflow<'a, 'ctx, 'env>(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn intwidth_from_builtin(builtin: Builtin<'_>, ptr_bytes: u32) -> IntWidth {
|
||||||
|
use IntWidth::*;
|
||||||
|
|
||||||
|
match builtin {
|
||||||
|
Builtin::Int128 => I128,
|
||||||
|
Builtin::Int64 => I64,
|
||||||
|
Builtin::Int32 => I32,
|
||||||
|
Builtin::Int16 => I16,
|
||||||
|
Builtin::Int8 => I8,
|
||||||
|
Builtin::Usize => match ptr_bytes {
|
||||||
|
4 => I32,
|
||||||
|
8 => I64,
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intwidth_from_layout(layout: Layout<'_>, ptr_bytes: u32) -> IntWidth {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(builtin) => intwidth_from_builtin(builtin, ptr_bytes),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_int_binop<'a, 'ctx, 'env>(
|
fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
@ -5910,62 +5925,54 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
|
||||||
|
let int_width = intwidth_from_layout(*lhs_layout, env.ptr_bytes);
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
NumAdd => {
|
NumAdd => {
|
||||||
let intrinsic = match lhs_layout {
|
|
||||||
Layout::Builtin(Builtin::Int8) => LLVM_SADD_WITH_OVERFLOW_I8,
|
|
||||||
Layout::Builtin(Builtin::Int16) => LLVM_SADD_WITH_OVERFLOW_I16,
|
|
||||||
Layout::Builtin(Builtin::Int32) => LLVM_SADD_WITH_OVERFLOW_I32,
|
|
||||||
Layout::Builtin(Builtin::Int64) => LLVM_SADD_WITH_OVERFLOW_I64,
|
|
||||||
Layout::Builtin(Builtin::Int128) => LLVM_SADD_WITH_OVERFLOW_I128,
|
|
||||||
Layout::Builtin(Builtin::Usize) => match env.ptr_bytes {
|
|
||||||
4 => LLVM_SADD_WITH_OVERFLOW_I32,
|
|
||||||
8 => LLVM_SADD_WITH_OVERFLOW_I64,
|
|
||||||
other => panic!("invalid ptr_bytes {}", other),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = env
|
let result = env
|
||||||
.call_intrinsic(intrinsic, &[lhs.into(), rhs.into()])
|
.call_intrinsic(
|
||||||
|
&LLVM_SADD_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
)
|
||||||
.into_struct_value();
|
.into_struct_value();
|
||||||
|
|
||||||
throw_on_overflow(env, parent, result, "integer addition overflowed!")
|
throw_on_overflow(env, parent, result, "integer addition overflowed!")
|
||||||
}
|
}
|
||||||
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
|
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
|
||||||
NumAddChecked => env.call_intrinsic(LLVM_SADD_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
NumAddChecked => env.call_intrinsic(
|
||||||
|
&LLVM_SADD_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
),
|
||||||
NumSub => {
|
NumSub => {
|
||||||
let intrinsic = match lhs_layout {
|
|
||||||
Layout::Builtin(Builtin::Int8) => LLVM_SSUB_WITH_OVERFLOW_I8,
|
|
||||||
Layout::Builtin(Builtin::Int16) => LLVM_SSUB_WITH_OVERFLOW_I16,
|
|
||||||
Layout::Builtin(Builtin::Int32) => LLVM_SSUB_WITH_OVERFLOW_I32,
|
|
||||||
Layout::Builtin(Builtin::Int64) => LLVM_SSUB_WITH_OVERFLOW_I64,
|
|
||||||
Layout::Builtin(Builtin::Int128) => LLVM_SSUB_WITH_OVERFLOW_I128,
|
|
||||||
Layout::Builtin(Builtin::Usize) => match env.ptr_bytes {
|
|
||||||
4 => LLVM_SSUB_WITH_OVERFLOW_I32,
|
|
||||||
8 => LLVM_SSUB_WITH_OVERFLOW_I64,
|
|
||||||
other => panic!("invalid ptr_bytes {}", other),
|
|
||||||
},
|
|
||||||
_ => unreachable!("invalid layout {:?}", lhs_layout),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = env
|
let result = env
|
||||||
.call_intrinsic(intrinsic, &[lhs.into(), rhs.into()])
|
.call_intrinsic(
|
||||||
|
&LLVM_SSUB_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
)
|
||||||
.into_struct_value();
|
.into_struct_value();
|
||||||
|
|
||||||
throw_on_overflow(env, parent, result, "integer subtraction overflowed!")
|
throw_on_overflow(env, parent, result, "integer subtraction overflowed!")
|
||||||
}
|
}
|
||||||
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
||||||
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
NumSubChecked => env.call_intrinsic(
|
||||||
|
&LLVM_SSUB_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
),
|
||||||
NumMul => {
|
NumMul => {
|
||||||
let result = env
|
let result = env
|
||||||
.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()])
|
.call_intrinsic(
|
||||||
|
&LLVM_SMUL_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
)
|
||||||
.into_struct_value();
|
.into_struct_value();
|
||||||
|
|
||||||
throw_on_overflow(env, parent, result, "integer multiplication overflowed!")
|
throw_on_overflow(env, parent, result, "integer multiplication overflowed!")
|
||||||
}
|
}
|
||||||
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
||||||
NumMulChecked => env.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
NumMulChecked => env.call_intrinsic(
|
||||||
|
&LLVM_SMUL_WITH_OVERFLOW[int_width],
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
),
|
||||||
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
||||||
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
|
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
|
||||||
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
|
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
|
||||||
|
@ -6039,11 +6046,17 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
phi.as_basic_value()
|
phi.as_basic_value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NumPowInt => call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
&bitcode::NUM_POW_INT[int_width],
|
||||||
|
),
|
||||||
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
|
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
|
||||||
NumPowInt => call_bitcode_fn(env, &[lhs.into(), rhs.into()], bitcode::NUM_POW_INT),
|
NumDivCeilUnchecked => call_bitcode_fn(
|
||||||
NumDivCeilUnchecked => {
|
env,
|
||||||
call_bitcode_fn(env, &[lhs.into(), rhs.into()], bitcode::NUM_DIV_CEIL)
|
&[lhs.into(), rhs.into()],
|
||||||
}
|
&bitcode::NUM_DIV_CEIL[int_width],
|
||||||
|
),
|
||||||
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
|
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
|
||||||
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
|
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
|
||||||
NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(),
|
NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(),
|
||||||
|
@ -6085,6 +6098,17 @@ pub fn build_num_binop<'a, 'ctx, 'env>(
|
||||||
{
|
{
|
||||||
use roc_mono::layout::Builtin::*;
|
use roc_mono::layout::Builtin::*;
|
||||||
|
|
||||||
|
let float_binop = |float_width| {
|
||||||
|
build_float_binop(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
lhs_arg.into_float_value(),
|
||||||
|
rhs_arg.into_float_value(),
|
||||||
|
float_width,
|
||||||
|
op,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
match lhs_builtin {
|
match lhs_builtin {
|
||||||
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
||||||
env,
|
env,
|
||||||
|
@ -6095,15 +6119,11 @@ pub fn build_num_binop<'a, 'ctx, 'env>(
|
||||||
rhs_layout,
|
rhs_layout,
|
||||||
op,
|
op,
|
||||||
),
|
),
|
||||||
Float128 | Float64 | Float32 => build_float_binop(
|
|
||||||
env,
|
Float32 => float_binop(FloatWidth::F32),
|
||||||
parent,
|
Float64 => float_binop(FloatWidth::F64),
|
||||||
lhs_arg.into_float_value(),
|
Float128 => float_binop(FloatWidth::F128),
|
||||||
lhs_layout,
|
|
||||||
rhs_arg.into_float_value(),
|
|
||||||
rhs_layout,
|
|
||||||
op,
|
|
||||||
),
|
|
||||||
Decimal => {
|
Decimal => {
|
||||||
build_dec_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
|
build_dec_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
|
||||||
}
|
}
|
||||||
|
@ -6122,9 +6142,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
lhs: FloatValue<'ctx>,
|
lhs: FloatValue<'ctx>,
|
||||||
_lhs_layout: &Layout<'a>,
|
|
||||||
rhs: FloatValue<'ctx>,
|
rhs: FloatValue<'ctx>,
|
||||||
_rhs_layout: &Layout<'a>,
|
float_width: FloatWidth,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
use inkwell::FloatPredicate::*;
|
use inkwell::FloatPredicate::*;
|
||||||
|
@ -6140,7 +6159,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_add(lhs, rhs, "add_float");
|
let result = bd.build_float_add(lhs, rhs, "add_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
let then_block = context.append_basic_block(parent, "then_block");
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
@ -6161,7 +6181,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_add(lhs, rhs, "add_float");
|
let result = bd.build_float_add(lhs, rhs, "add_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
let is_infinite = bd.build_not(is_finite, "negate");
|
let is_infinite = bd.build_not(is_finite, "negate");
|
||||||
|
|
||||||
let struct_type = context.struct_type(
|
let struct_type = context.struct_type(
|
||||||
|
@ -6189,7 +6210,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_sub(lhs, rhs, "sub_float");
|
let result = bd.build_float_sub(lhs, rhs, "sub_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
let then_block = context.append_basic_block(parent, "then_block");
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
@ -6210,7 +6232,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_sub(lhs, rhs, "sub_float");
|
let result = bd.build_float_sub(lhs, rhs, "sub_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
let is_infinite = bd.build_not(is_finite, "negate");
|
let is_infinite = bd.build_not(is_finite, "negate");
|
||||||
|
|
||||||
let struct_type = context.struct_type(
|
let struct_type = context.struct_type(
|
||||||
|
@ -6238,7 +6261,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
let then_block = context.append_basic_block(parent, "then_block");
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
@ -6259,7 +6283,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
||||||
|
|
||||||
let is_finite =
|
let is_finite =
|
||||||
call_bitcode_fn(env, &[result.into()], bitcode::NUM_IS_FINITE).into_int_value();
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE[float_width])
|
||||||
|
.into_int_value();
|
||||||
let is_infinite = bd.build_not(is_finite, "negate");
|
let is_infinite = bd.build_not(is_finite, "negate");
|
||||||
|
|
||||||
let struct_type = context.struct_type(
|
let struct_type = context.struct_type(
|
||||||
|
@ -6286,7 +6311,7 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
||||||
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
|
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
|
||||||
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
||||||
NumPow => env.call_intrinsic(LLVM_POW_F64, &[lhs.into(), rhs.into()]),
|
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
|
@ -6528,6 +6553,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
arg: FloatValue<'ctx>,
|
arg: FloatValue<'ctx>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
|
float_width: FloatWidth,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
use roc_module::low_level::LowLevel::*;
|
use roc_module::low_level::LowLevel::*;
|
||||||
|
|
||||||
|
@ -6536,34 +6562,72 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
// TODO: Handle different sized floats
|
// TODO: Handle different sized floats
|
||||||
match op {
|
match op {
|
||||||
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
||||||
NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]),
|
NumAbs => env.call_intrinsic(&LLVM_FABS[float_width], &[arg.into()]),
|
||||||
NumSqrtUnchecked => env.call_intrinsic(LLVM_SQRT_F64, &[arg.into()]),
|
NumSqrtUnchecked => env.call_intrinsic(&LLVM_SQRT[float_width], &[arg.into()]),
|
||||||
NumLogUnchecked => env.call_intrinsic(LLVM_LOG_F64, &[arg.into()]),
|
NumLogUnchecked => env.call_intrinsic(&LLVM_LOG[float_width], &[arg.into()]),
|
||||||
NumRound => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_ROUND),
|
|
||||||
NumSin => env.call_intrinsic(LLVM_SIN_F64, &[arg.into()]),
|
|
||||||
NumCos => env.call_intrinsic(LLVM_COS_F64, &[arg.into()]),
|
|
||||||
NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */
|
NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */
|
||||||
NumCeiling => env.builder.build_cast(
|
NumCeiling => env.builder.build_cast(
|
||||||
InstructionOpcode::FPToSI,
|
InstructionOpcode::FPToSI,
|
||||||
env.call_intrinsic(LLVM_CEILING_F64, &[arg.into()]),
|
env.call_intrinsic(&LLVM_CEILING[float_width], &[arg.into()]),
|
||||||
env.context.i64_type(),
|
env.context.i64_type(),
|
||||||
"num_ceiling",
|
"num_ceiling",
|
||||||
),
|
),
|
||||||
NumFloor => env.builder.build_cast(
|
NumFloor => env.builder.build_cast(
|
||||||
InstructionOpcode::FPToSI,
|
InstructionOpcode::FPToSI,
|
||||||
env.call_intrinsic(LLVM_FLOOR_F64, &[arg.into()]),
|
env.call_intrinsic(&LLVM_FLOOR[float_width], &[arg.into()]),
|
||||||
env.context.i64_type(),
|
env.context.i64_type(),
|
||||||
"num_floor",
|
"num_floor",
|
||||||
),
|
),
|
||||||
NumIsFinite => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_IS_FINITE),
|
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
||||||
NumAtan => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_ATAN),
|
NumRound => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND[float_width]),
|
||||||
NumAcos => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_ACOS),
|
|
||||||
NumAsin => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_ASIN),
|
// trigonometry
|
||||||
|
NumSin => env.call_intrinsic(&LLVM_SIN[float_width], &[arg.into()]),
|
||||||
|
NumCos => env.call_intrinsic(&LLVM_COS[float_width], &[arg.into()]),
|
||||||
|
|
||||||
|
NumAtan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ATAN[float_width]),
|
||||||
|
NumAcos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ACOS[float_width]),
|
||||||
|
NumAsin => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ASIN[float_width]),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_bitcode_int_fn<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
fn_name: &str,
|
||||||
|
args: &[BasicValueEnum<'ctx>],
|
||||||
|
int_width: IntWidth,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
match int_width {
|
||||||
|
IntWidth::U8 => call_bitcode_fn(env, args, &format!("{}_u8", fn_name)),
|
||||||
|
IntWidth::U16 => call_bitcode_fn(env, args, &format!("{}_u16", fn_name)),
|
||||||
|
IntWidth::U32 => call_bitcode_fn(env, args, &format!("{}_u32", fn_name)),
|
||||||
|
IntWidth::U64 => call_bitcode_fn(env, args, &format!("{}_u64", fn_name)),
|
||||||
|
IntWidth::U128 => call_bitcode_fn(env, args, &format!("{}_u128", fn_name)),
|
||||||
|
IntWidth::I8 => call_bitcode_fn(env, args, &format!("{}_i8", fn_name)),
|
||||||
|
IntWidth::I16 => call_bitcode_fn(env, args, &format!("{}_i16", fn_name)),
|
||||||
|
IntWidth::I32 => call_bitcode_fn(env, args, &format!("{}_i32", fn_name)),
|
||||||
|
IntWidth::I64 => call_bitcode_fn(env, args, &format!("{}_i64", fn_name)),
|
||||||
|
IntWidth::I128 => call_bitcode_fn(env, args, &format!("{}_i128", fn_name)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_bitcode_float_fn<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
fn_name: &str,
|
||||||
|
args: &[BasicValueEnum<'ctx>],
|
||||||
|
float_width: FloatWidth,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
match float_width {
|
||||||
|
FloatWidth::F32 => call_bitcode_fn(env, args, &format!("{}_f32", fn_name)),
|
||||||
|
FloatWidth::F64 => call_bitcode_fn(env, args, &format!("{}_f64", fn_name)),
|
||||||
|
FloatWidth::F128 => todo!("suport 128-bit floats"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn define_global_str_literal_ptr<'a, 'ctx, 'env>(
|
fn define_global_str_literal_ptr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
message: &str,
|
message: &str,
|
||||||
|
|
|
@ -502,38 +502,6 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||||
env.builder.build_load(result_ptr, "load_result")
|
env.builder.build_load(result_ptr, "load_result")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(u8)]
|
|
||||||
enum IntWidth {
|
|
||||||
U8,
|
|
||||||
U16,
|
|
||||||
U32,
|
|
||||||
U64,
|
|
||||||
U128,
|
|
||||||
I8,
|
|
||||||
I16,
|
|
||||||
I32,
|
|
||||||
I64,
|
|
||||||
I128,
|
|
||||||
Usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<roc_mono::layout::Builtin<'_>> for IntWidth {
|
|
||||||
fn from(builtin: Builtin) -> Self {
|
|
||||||
use IntWidth::*;
|
|
||||||
|
|
||||||
match builtin {
|
|
||||||
Builtin::Int128 => I128,
|
|
||||||
Builtin::Int64 => I64,
|
|
||||||
Builtin::Int32 => I32,
|
|
||||||
Builtin::Int16 => I16,
|
|
||||||
Builtin::Int8 => I8,
|
|
||||||
Builtin::Usize => Usize,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List.range : Int a, Int a -> List (Int a)
|
/// List.range : Int a, Int a -> List (Int a)
|
||||||
pub fn list_range<'a, 'ctx, 'env>(
|
pub fn list_range<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -552,7 +520,10 @@ pub fn list_range<'a, 'ctx, 'env>(
|
||||||
let int_width = env
|
let int_width = env
|
||||||
.context
|
.context
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.const_int(IntWidth::from(builtin) as u64, false)
|
.const_int(
|
||||||
|
crate::llvm::build::intwidth_from_builtin(builtin, env.ptr_bytes) as u64,
|
||||||
|
false,
|
||||||
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
call_bitcode_fn(
|
call_bitcode_fn(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue