Implement NumPy math functions #339

Merged
sb10q merged 10 commits from issue-149 into master 2023-11-01 20:02:08 +08:00
8 changed files with 1183 additions and 273 deletions

View File

@ -21,7 +21,7 @@
passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang packages.x86_64-linux.clang-unwrapped pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ])) ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
checkPhase =
''
echo "Checking nac3standalone demos..."
@ -94,7 +94,7 @@
})
];
buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.jsonschema nac3artiq-instrumented ]))
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema nac3artiq-instrumented ]))
pkgs.llvmPackages_14.llvm.out
];
phases = [ "buildPhase" "installPhase" ];
@ -151,7 +151,7 @@
rustc
# runtime dependencies
lld_14 # for running kernels on the host
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ]))
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
# development tools
cargo-insta
clippy

View File

@ -137,4 +137,12 @@ int32_t __nac3_list_slice_assign_var_size(
return dest_arr_len - (dest_end - dest_ind) - 1;
}
return dest_arr_len;
}
int32_t __nac3_isinf(double x) {
return __builtin_isinf(x);
}
int32_t __nac3_isnan(double x) {
return __builtin_isnan(x);
}

View File

@ -7,7 +7,7 @@ use inkwell::{
memory_buffer::MemoryBuffer,
module::Module,
types::BasicTypeEnum,
values::{IntValue, PointerValue},
values::{FloatValue, IntValue, PointerValue},
AddressSpace, IntPredicate,
};
use nac3parser::ast::Expr;
@ -432,3 +432,43 @@ pub fn list_slice_assignment<'ctx, 'a>(
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(cont_bb);
}
/// Generates a call to `isinf` in IR. Returns an `i1` representing the result.
pub fn call_isinf<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &CodeGenContext<'ctx, 'a>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
ctx.module.add_function("__nac3_isinf", fn_type, None)
});
let ret = ctx.builder
.build_call(intrinsic_fn, &[v.into()], "isinf")
.try_as_basic_value()
.unwrap_left()
.into_int_value();
generator.bool_to_i1(ctx, ret)
}
/// Generates a call to `isnan` in IR. Returns an `i1` representing the result.
pub fn call_isnan<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &CodeGenContext<'ctx, 'a>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
ctx.module.add_function("__nac3_isnan", fn_type, None)
});
let ret = ctx.builder
.build_call(intrinsic_fn, &[v.into()], "isnan")
.try_as_basic_value()
.unwrap_left()
.into_int_value();
generator.bool_to_i1(ctx, ret)
}

File diff suppressed because it is too large Load Diff

View File

@ -82,51 +82,55 @@ pub struct FunInstance {
#[derive(Debug, Clone)]
pub enum TopLevelDef {
Class {
// name for error messages and symbols
/// Name for error messages and symbols.
name: StrRef,
// object ID used for TypeEnum
/// Object ID used for [TypeEnum].
object_id: DefinitionId,
/// type variables bounded to the class.
type_vars: Vec<Type>,
// class fields
// name, type, is mutable
/// Class fields.
///
/// Name and type is mutable.
fields: Vec<(StrRef, Type, bool)>,
// class methods, pointing to the corresponding function definition.
/// Class methods, pointing to the corresponding function definition.
methods: Vec<(StrRef, Type, DefinitionId)>,
// ancestor classes, including itself.
/// Ancestor classes, including itself.
ancestors: Vec<TypeAnnotation>,
// symbol resolver of the module defined the class, none if it is built-in type
/// Symbol resolver of the module defined the class; [None] if it is built-in type.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
// constructor type
/// Constructor type.
constructor: Option<Type>,
// definition location
/// Definition location.
loc: Option<Location>,
},
Function {
// prefix for symbol, should be unique globally
/// Prefix for symbol, should be unique globally.
name: String,
// simple name, the same as in method/function definition
/// Simple name, the same as in method/function definition.
simple_name: StrRef,
// function signature.
/// Function signature.
signature: Type,
// instantiated type variable IDs
/// Instantiated type variable IDs.
var_id: Vec<u32>,
/// Function instance to symbol mapping
/// Key: string representation of type variable values, sorted by variable ID in ascending
///
/// * Key: String representation of type variable values, sorted by variable ID in ascending
/// order, including type variables associated with the class.
/// Value: function symbol name.
/// * Value: Function symbol name.
instance_to_symbol: HashMap<String, String>,
/// Function instances to annotated AST mapping
/// Key: string representation of type variable values, sorted by variable ID in ascending
///
/// * Key: String representation of type variable values, sorted by variable ID in ascending
/// order, including type variables associated with the class. Excluding rigid type
/// variables.
/// rigid type variables that would be substituted when the function is instantiated.
///
/// Rigid type variables that would be substituted when the function is instantiated.
instance_to_stmt: HashMap<String, FunInstance>,
// symbol resolver of the module defined the class
/// Symbol resolver of the module defined the class.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
// custom codegen callback
/// Custom code generation callback.
codegen_callback: Option<Arc<GenCall>>,
// definition location
/// Definition location.
loc: Option<Location>,
},
}

View File

@ -1,3 +1,4 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@ -12,6 +13,14 @@
#error "Unsupported platform - Platform is not 32-bit or 64-bit"
#endif
double dbl_nan(void) {
return NAN;
}
double dbl_inf(void) {
return INFINITY;
}
void output_bool(bool x) {
puts(x ? "True" : "False");
}
@ -33,7 +42,11 @@ void output_uint64(uint64_t x) {
}
void output_float64(double x) {
printf("%f\n", x);
if (isnan(x)) {
puts("nan");
} else {
printf("%f\n", x);
}
}
void output_asciiart(int32_t x) {

View File

@ -3,7 +3,9 @@
import sys
import importlib.util
import importlib.machinery
import numpy as np
import pathlib
import scipy
from numpy import int32, int64, uint32, uint64
from typing import TypeVar, Generic
@ -42,6 +44,12 @@ def Some(v: T) -> Option[T]:
none = Option(None)
def patch(module):
def dbl_nan():
return np.nan
def dbl_inf():
return np.inf
def output_asciiart(x):
if x < 0:
sys.stdout.write("\n")
@ -56,7 +64,11 @@ def patch(module):
def extern(fun):
name = fun.__name__
if name == "output_asciiart":
if name == "dbl_nan":
return dbl_nan
elif name == "dbl_inf":
return dbl_inf
elif name == "output_asciiart":
return output_asciiart
elif name == "output_float64":
return output_float
@ -86,6 +98,50 @@ def patch(module):
module.Some = Some
module.none = none
# NumPy Math functions
module.isnan = np.isnan
module.isinf = np.isinf
module.sin = np.sin
module.cos = np.cos
module.exp = np.exp
module.exp2 = np.exp2
module.log = np.log
module.log10 = np.log10
module.log2 = np.log2
module.fabs = np.fabs
module.floor = np.floor
module.ceil = np.ceil
module.trunc = np.trunc
module.sqrt = np.sqrt
module.rint = np.rint
module.tan = np.tan
module.arcsin = np.arcsin
module.arccos = np.arccos
module.arctan = np.arctan
module.sinh = np.sinh
module.cosh = np.cosh
module.tanh = np.tanh
module.arcsinh = np.arcsinh
module.arccosh = np.arccosh
module.arctanh = np.arctanh
module.expm1 = np.expm1
module.cbrt = np.cbrt
module.arctan2 = np.arctan2
module.copysign = np.copysign
module.fmax = np.fmax
module.fmin = np.fmin
module.ldexp = np.ldexp
module.hypot = np.hypot
module.nextafter = np.nextafter
# SciPy Math Functions
module.erf = scipy.special.erf
module.erfc = scipy.special.erfc
module.gamma = scipy.special.gamma
module.gammaln = scipy.special.gammaln
module.j0 = scipy.special.j0
module.j1 = scipy.special.j1
def file_import(filename, prefix="file_import_"):
filename = pathlib.Path(filename)

View File

@ -0,0 +1,236 @@
@extern
def output_bool(x: bool):
...
@extern
def output_float64(x: float):
...
@extern
def dbl_nan() -> float:
...
@extern
def dbl_inf() -> float:
...
def dbl_pi() -> float:
return 3.1415926535897932384626433
def dbl_e() -> float:
return 2.71828182845904523536028747135266249775724709369995
def test_isnan():
for x in [dbl_nan(), 0.0, dbl_inf()]:
output_bool(isnan(x))
def test_isinf():
for x in [dbl_inf(), -dbl_inf(), 0.0, dbl_nan()]:
output_bool(isinf(x))
def test_sin():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sin(x))
def test_cos():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(cos(x))
def test_exp():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(exp(x))
def test_exp2():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(exp2(x))
def test_log():
e = dbl_e()
for x in [1.0, e, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(log(x))
def test_log10():
for x in [1.0, 10.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(log10(x))
def test_log2():
for x in [1.0, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(log2(x))
def test_fabs():
for x in [-1.0, 0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(fabs(x))
def test_floor():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(floor(x))
def test_ceil():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(ceil(x))
def test_trunc():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(trunc(x))
def test_sqrt():
for x in [1.0, 2.0, 4.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sqrt(x))
def test_rint():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(rint(x))
def test_tan():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(tan(x))
def test_arcsin():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arcsin(x))
def test_arccos():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arccos(x))
def test_arctan():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arctan(x))
def test_sinh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sinh(x))
def test_cosh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(cosh(x))
def test_tanh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(tanh(x))
def test_arcsinh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arcsinh(x))
def test_arccosh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arccosh(x))
def test_arctanh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arctanh(x))
def test_expm1():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(expm1(x))
def test_cbrt():
for x in [1.0, 8.0, 27.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(expm1(x))
def test_erf():
for x in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(erf(x))
def test_erfc():
for x in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(erfc(x))
def test_gamma():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(gamma(x))
def test_gammaln():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(gammaln(x))
def test_j0():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(j0(x))
def test_j1():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]:
output_float64(j1(x))
def test_arctan2():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(arctan2(x1, x2))
def test_copysign():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(copysign(x1, x2))
def test_fmax():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(fmax(x1, x2))
def test_fmin():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(fmin(x1, x2))
def test_ldexp():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2, -1, 0, 1, 2]:
output_float64(ldexp(x1, x2))
def test_hypot():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(hypot(x1, x2))
def test_nextafter():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(nextafter(x1, x2))
def run() -> int32:
test_isnan()
test_isinf()
test_sin()
test_cos()
test_exp()
test_exp2()
test_log()
test_log10()
test_log2()
test_fabs()
test_floor()
test_ceil()
test_trunc()
test_sqrt()
test_rint()
test_tan()
test_arcsin()
test_arccos()
test_arctan()
test_sinh()
test_cosh()
test_tanh()
test_arcsinh()
test_arccosh()
test_arctanh()
test_expm1()
test_cbrt()
test_erf()
test_erfc()
test_gamma()
test_gammaln()
test_j0()
test_j1()
test_arctan2()
test_copysign()
test_fmax()
test_fmin()
test_ldexp()
test_hypot()
test_nextafter()
return 0