Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/math-extra.ilo
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Extra transcendental builtins: tan, log10, log2, atan2 (radians).

-- Phase angle of a 2D vector (atan2 takes y then x, C/Python convention).
phase y:n x:n>n;atan2 y x

-- Decibels: 20 * log10(amplitude_ratio).
db ratio:n>n;l=log10 ratio;*20 l

-- Bit depth needed to represent n distinct values: ceil(log2 n).
bits n:n>n;l=log2 n;cel l

-- Slope of a line at angle theta (radians).
slope theta:n>n;tan theta

-- run: phase 1 1
-- out: 0.7853981633974483
-- run: db 100
-- out: 40
-- run: bits 256
-- out: 8
-- run: slope 0
-- out: 0
21 changes: 17 additions & 4 deletions src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub enum Builtin {
Exp,
Sin,
Cos,
Tan,
Log10,
Log2,
Atan2,
Sum,
Avg,

Expand Down Expand Up @@ -104,6 +108,10 @@ impl Builtin {
"exp" => Some(Builtin::Exp),
"sin" => Some(Builtin::Sin),
"cos" => Some(Builtin::Cos),
"tan" => Some(Builtin::Tan),
"log10" => Some(Builtin::Log10),
"log2" => Some(Builtin::Log2),
"atan2" => Some(Builtin::Atan2),
"sum" => Some(Builtin::Sum),
"avg" => Some(Builtin::Avg),
"len" => Some(Builtin::Len),
Expand Down Expand Up @@ -169,6 +177,10 @@ impl Builtin {
Builtin::Exp => "exp",
Builtin::Sin => "sin",
Builtin::Cos => "cos",
Builtin::Tan => "tan",
Builtin::Log10 => "log10",
Builtin::Log2 => "log2",
Builtin::Atan2 => "atan2",
Builtin::Sum => "sum",
Builtin::Avg => "avg",
Builtin::Len => "len",
Expand Down Expand Up @@ -228,10 +240,11 @@ mod tests {
fn round_trip_all_builtins() {
let all = [
"str", "num", "abs", "flr", "cel", "rou", "min", "max", "mod", "pow", "sqrt", "log",
"exp", "sin", "cos", "sum", "avg", "len", "hd", "at", "tl", "rev", "srt", "slc", "unq",
"flat", "has", "spl", "cat", "map", "flt", "fld", "grp", "rnd", "now", "rd", "rdl",
"rdb", "wr", "wrl", "prnt", "env", "trm", "fmt", "rgx", "jpth", "jdmp", "jpar", "get",
"post", "mmap", "mget", "mset", "mhas", "mkeys", "mvals", "mdel",
"exp", "sin", "cos", "tan", "log10", "log2", "atan2", "sum", "avg", "len", "hd", "at",
"tl", "rev", "srt", "slc", "unq", "flat", "has", "spl", "cat", "map", "flt", "fld",
"grp", "rnd", "now", "rd", "rdl", "rdb", "wr", "wrl", "prnt", "env", "trm", "fmt",
"rgx", "jpth", "jdmp", "jpar", "get", "post", "mmap", "mget", "mset", "mhas", "mkeys",
"mvals", "mdel",
];
for name in &all {
let b = Builtin::from_name(name).unwrap_or_else(|| panic!("missing builtin: {name}"));
Expand Down
25 changes: 23 additions & 2 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,16 @@ fn call_function(env: &mut Env, name: &str, args: Vec<Value>) -> Result<Value> {
}
if matches!(
builtin,
Some(Builtin::Sqrt | Builtin::Log | Builtin::Exp | Builtin::Sin | Builtin::Cos)
Some(
Builtin::Sqrt
| Builtin::Log
| Builtin::Exp
| Builtin::Sin
| Builtin::Cos
| Builtin::Tan
| Builtin::Log10
| Builtin::Log2
)
) && args.len() == 1
{
return match &args[0] {
Expand All @@ -532,7 +541,10 @@ fn call_function(env: &mut Env, name: &str, args: Vec<Value>) -> Result<Value> {
Some(Builtin::Log) => n.ln(),
Some(Builtin::Exp) => n.exp(),
Some(Builtin::Sin) => n.sin(),
_ => n.cos(),
Some(Builtin::Cos) => n.cos(),
Some(Builtin::Tan) => n.tan(),
Some(Builtin::Log10) => n.log10(),
_ => n.log2(),
};
Ok(Value::Number(result))
}
Expand All @@ -551,6 +563,15 @@ fn call_function(env: &mut Env, name: &str, args: Vec<Value>) -> Result<Value> {
)),
};
}
if builtin == Some(Builtin::Atan2) && args.len() == 2 {
return match (&args[0], &args[1]) {
(Value::Number(y), Value::Number(x)) => Ok(Value::Number(y.atan2(*x))),
_ => Err(RuntimeError::new(
"ILO-R009",
"atan2 requires two numbers".to_string(),
)),
};
}
if builtin == Some(Builtin::Now) && args.is_empty() {
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
Expand Down
9 changes: 7 additions & 2 deletions src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ const BUILTINS: &[(&str, &[&str], &str)] = &[
("exp", &["n"], "n"),
("sin", &["n"], "n"),
("cos", &["n"], "n"),
("tan", &["n"], "n"),
("log10", &["n"], "n"),
("log2", &["n"], "n"),
("atan2", &["n", "n"], "n"),
("get", &["t"], "R t t"),
("get", &["t", "M t t"], "R t t"),
("post", &["t", "t"], "R t t"),
Expand Down Expand Up @@ -408,7 +412,8 @@ fn builtin_check_args(
}
(Ty::Result(Box::new(Ty::Number), Box::new(Ty::Text)), errors)
}
"abs" | "flr" | "cel" | "rou" | "sqrt" | "log" | "exp" | "sin" | "cos" => {
"abs" | "flr" | "cel" | "rou" | "sqrt" | "log" | "exp" | "sin" | "cos" | "tan"
| "log10" | "log2" => {
if let Some(arg) = arg_types.first()
&& !compatible(arg, &Ty::Number)
{
Expand All @@ -423,7 +428,7 @@ fn builtin_check_args(
}
(Ty::Number, errors)
}
"min" | "max" | "mod" | "pow" => {
"min" | "max" | "mod" | "pow" | "atan2" => {
for (i, arg) in arg_types.iter().enumerate() {
if !compatible(arg, &Ty::Number) {
errors.push(VerifyError {
Expand Down
29 changes: 26 additions & 3 deletions src/vm/compile_cranelift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ struct HelperFuncs {
exp: FuncId,
sin: FuncId,
cos: FuncId,
tan: FuncId,
log10: FuncId,
log2: FuncId,
atan2: FuncId,
rnd0: FuncId,
rnd2: FuncId,
now: FuncId,
Expand Down Expand Up @@ -177,6 +181,10 @@ fn declare_all_helpers(module: &mut ObjectModule) -> HelperFuncs {
exp: declare_helper(module, "jit_exp", 1, 1),
sin: declare_helper(module, "jit_sin", 1, 1),
cos: declare_helper(module, "jit_cos", 1, 1),
tan: declare_helper(module, "jit_tan", 1, 1),
log10: declare_helper(module, "jit_log10", 1, 1),
log2: declare_helper(module, "jit_log2", 1, 1),
atan2: declare_helper(module, "jit_atan2", 2, 1),
rnd0: declare_helper(module, "jit_rnd0", 0, 1),
rnd2: declare_helper(module, "jit_rnd2", 2, 1),
now: declare_helper(module, "jit_now", 0, 1),
Expand Down Expand Up @@ -883,7 +891,7 @@ fn compile_function_body(
OP_ADD_NN | OP_SUB_NN | OP_MUL_NN | OP_DIV_NN | OP_ADDK_N | OP_SUBK_N
| OP_MULK_N | OP_DIVK_N | OP_LEN | OP_ABS | OP_MIN | OP_MAX | OP_FLR | OP_CEL
| OP_ROU | OP_RND0 | OP_RND2 | OP_NOW | OP_MOD | OP_POW | OP_SQRT | OP_LOG
| OP_EXP | OP_SIN | OP_COS => {
| OP_EXP | OP_SIN | OP_COS | OP_TAN | OP_LOG10 | OP_LOG2 | OP_ATAN2 => {
num_write[a] = true;
}
// LOADK: numeric only when the constant itself is a number.
Expand Down Expand Up @@ -1767,14 +1775,29 @@ fn compile_function_body(
builder.def_var(f64_vars[a_idx], rf);
}
}
OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS => {
OP_ATAN2 => {
let bv = builder.use_var(vars[b_idx]);
let cv = builder.use_var(vars[c_idx]);
let fref = get_func_ref(&mut builder, module, helpers.atan2);
let call_inst = builder.ins().call(fref, &[bv, cv]);
let result = builder.inst_results(call_inst)[0];
builder.def_var(vars[a_idx], result);
if a_idx < reg_count && reg_always_num[a_idx] {
let rf = builder.ins().bitcast(F64, mf, result);
builder.def_var(f64_vars[a_idx], rf);
}
}
OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS | OP_TAN | OP_LOG10 | OP_LOG2 => {
let bv = builder.use_var(vars[b_idx]);
let fid = match op {
OP_SQRT => helpers.sqrt,
OP_LOG => helpers.log,
OP_EXP => helpers.exp,
OP_SIN => helpers.sin,
_ => helpers.cos,
OP_COS => helpers.cos,
OP_TAN => helpers.tan,
OP_LOG10 => helpers.log10,
_ => helpers.log2,
};
let fref = get_func_ref(&mut builder, module, fid);
let call_inst = builder.ins().call(fref, &[bv]);
Expand Down
35 changes: 32 additions & 3 deletions src/vm/jit_cranelift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ struct HelperFuncs {
exp: FuncId,
sin: FuncId,
cos: FuncId,
tan: FuncId,
log10: FuncId,
log2: FuncId,
atan2: FuncId,
rnd0: FuncId,
rnd2: FuncId,
now: FuncId,
Expand Down Expand Up @@ -172,6 +176,10 @@ fn register_helpers(builder: &mut JITBuilder) {
("jit_exp", jit_exp as *const u8),
("jit_sin", jit_sin as *const u8),
("jit_cos", jit_cos as *const u8),
("jit_tan", jit_tan as *const u8),
("jit_log10", jit_log10 as *const u8),
("jit_log2", jit_log2 as *const u8),
("jit_atan2", jit_atan2 as *const u8),
("jit_rnd0", jit_rnd0 as *const u8),
("jit_rnd2", jit_rnd2 as *const u8),
("jit_now", jit_now as *const u8),
Expand Down Expand Up @@ -270,6 +278,10 @@ fn declare_all_helpers(module: &mut JITModule) -> HelperFuncs {
exp: declare_helper(module, "jit_exp", 1, 1),
sin: declare_helper(module, "jit_sin", 1, 1),
cos: declare_helper(module, "jit_cos", 1, 1),
tan: declare_helper(module, "jit_tan", 1, 1),
log10: declare_helper(module, "jit_log10", 1, 1),
log2: declare_helper(module, "jit_log2", 1, 1),
atan2: declare_helper(module, "jit_atan2", 2, 1),
rnd0: declare_helper(module, "jit_rnd0", 0, 1),
rnd2: declare_helper(module, "jit_rnd2", 2, 1),
now: declare_helper(module, "jit_now", 0, 1),
Expand Down Expand Up @@ -851,7 +863,8 @@ fn compile_function_body(
| OP_ADDK_N | OP_SUBK_N | OP_MULK_N | OP_DIVK_N
| OP_LEN | OP_ABS | OP_MIN | OP_MAX
| OP_FLR | OP_CEL | OP_ROU | OP_RND0 | OP_RND2 | OP_NOW
| OP_MOD | OP_POW | OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS => {
| OP_MOD | OP_POW | OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS
| OP_TAN | OP_LOG10 | OP_LOG2 | OP_ATAN2 => {
num_write[a] = true;
}
// LOADK: numeric only when the constant itself is a number.
Expand Down Expand Up @@ -1799,14 +1812,30 @@ fn compile_function_body(
builder.def_var(f64_vars[a_idx], rf);
}
}
OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS => {
OP_ATAN2 => {
let bv = builder.use_var(vars[b_idx]);
let cv = builder.use_var(vars[c_idx]);
let fref = get_func_ref(&mut builder, module, helpers.atan2);
let call_inst = builder.ins().call(fref, &[bv, cv]);
let result = builder.inst_results(call_inst)[0];
builder.def_var(vars[a_idx], result);
if a_idx < reg_count && reg_always_num[a_idx] {
let mf = cranelift_codegen::ir::MemFlags::new();
let rf = builder.ins().bitcast(F64, mf, result);
builder.def_var(f64_vars[a_idx], rf);
}
}
OP_SQRT | OP_LOG | OP_EXP | OP_SIN | OP_COS | OP_TAN | OP_LOG10 | OP_LOG2 => {
let bv = builder.use_var(vars[b_idx]);
let fref = match op {
OP_SQRT => helpers.sqrt,
OP_LOG => helpers.log,
OP_EXP => helpers.exp,
OP_SIN => helpers.sin,
_ => helpers.cos,
OP_COS => helpers.cos,
OP_TAN => helpers.tan,
OP_LOG10 => helpers.log10,
_ => helpers.log2,
};
let fref = get_func_ref(&mut builder, module, fref);
let call_inst = builder.ins().call(fref, &[bv]);
Expand Down
Loading
Loading