diff --git a/integration_tests/MATH_INTRINSICS/math_intrinsics.va b/integration_tests/MATH_INTRINSICS/math_intrinsics.va new file mode 100644 index 00000000..a95b7771 --- /dev/null +++ b/integration_tests/MATH_INTRINSICS/math_intrinsics.va @@ -0,0 +1,46 @@ +`include "constants.vams" +`include "disciplines.vams" + +// Exercises every math builtin that `Builder::intrinsic` routes to an LLVM +// intrinsic or a libm symbol. Most of them were called by no other model in +// the suite, so a missing or mis-declared entry in `CodegenCx::intrinsic` +// stayed invisible until a user hit it: `ceil` aborted codegen outright and +// `hypot` emitted a module that failed the LLVM verifier. +// +// Every argument is derived from a branch voltage, otherwise constant folding +// would evaluate the call before it ever reaches codegen and the coverage +// would be lost. +module math_intrinsics(A, C); + inout A, C; + electrical A, C; + + branch (A, C) br; + + (*desc = "Output scale", units = "A/V"*) parameter real scale = 1.0 from (0:inf); + + real x, unit, upper, acc; + integer ix; + + analog begin + x = V(br); + + // |unit| < 1 keeps asin/acos/atanh inside their domain + unit = x / sqrt(1.0 + x * x); + // upper >= 1 keeps ln/log/acosh inside theirs + upper = 1.0 + x * x; + + acc = sqrt(upper) + exp(-upper) + ln(upper) + log(upper); + acc = acc + floor(x) + ceil(x); + acc = acc + sin(x) + cos(x) + tan(unit); + acc = acc + asin(unit) + acos(unit) + atan(x); + acc = acc + sinh(unit) + cosh(unit) + tanh(x); + acc = acc + asinh(x) + acosh(upper) + atanh(unit); + acc = acc + hypot(x, 1.0) + atan2(x, 1.0) + pow(upper, unit); + + // real -> integer cast and $clog2 also resolve through the intrinsic path + ix = upper * 8.0; + acc = acc + $clog2(ix); + + I(br) <+ scale * acc; + end +endmodule diff --git a/openvaf/hir_ty/src/builtin.rs b/openvaf/hir_ty/src/builtin.rs index 51f5abef..8eaa57e1 100644 --- a/openvaf/hir_ty/src/builtin.rs +++ b/openvaf/hir_ty/src/builtin.rs @@ -187,7 +187,7 @@ bultins! { const fn REAL_INFO() -> Real; const fn REAL_MATH_1(Val(Real)) -> Real; const fn REAL_MATH_2(Val(Real),Val(Real)) -> Real; - const fn INT_MATH_2(Val(Integer),Val(Integer)) -> Integer; + const fn INT_MATH_1(Val(Integer)) -> Integer; VT = const { @@ -431,7 +431,7 @@ copied_builtins! { FLOOR = REAL_MATH_1 LN = REAL_MATH_1 LOG = REAL_MATH_1 - CLOG2 = INT_MATH_2 + CLOG2 = INT_MATH_1 LOG10 = REAL_MATH_1 CEIL = REAL_MATH_1 LIMEXP = REAL_MATH_1 diff --git a/openvaf/mir_llvm/src/builder.rs b/openvaf/mir_llvm/src/builder.rs index 6d8d3de2..582bb13a 100644 --- a/openvaf/mir_llvm/src/builder.rs +++ b/openvaf/mir_llvm/src/builder.rs @@ -908,7 +908,8 @@ impl<'ll> Builder<'_, '_, 'll> { Opcode::Log => NonNull::from(self.intrinsic(args, "llvm.log10.f64")).as_ptr(), Opcode::Clog2 => { let leading_zeros = - NonNull::from(self.intrinsic(&[args[0], true.into()], "llvm.ctlz")).as_ptr(); + NonNull::from(self.intrinsic(&[args[0], true.into()], "llvm.ctlz.i32")) + .as_ptr(); let total_bits = NonNull::from(self.cx.const_int(32)).as_ptr(); llvm_sys::core::LLVMBuildSub(self.llbuilder, total_bits, leading_zeros, UNNAMED) } diff --git a/openvaf/mir_llvm/src/intrinsics.rs b/openvaf/mir_llvm/src/intrinsics.rs index f04fd44a..2da1cbb0 100644 --- a/openvaf/mir_llvm/src/intrinsics.rs +++ b/openvaf/mir_llvm/src/intrinsics.rs @@ -37,7 +37,8 @@ impl<'a, 'll> CodegenCx<'a, 'll> { ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ctlz", fn(t_i32, t_bool) -> t_i32); + ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); + ifn!("llvm.ctlz.i32", fn(t_i32, t_bool) -> t_i32); // not technically intrinsics but part of the C standard library // TODO link custom mathematical functions @@ -56,7 +57,7 @@ impl<'a, 'll> CodegenCx<'a, 'll> { if name == "hypot" { let name = if self.target.options.is_like_windows { "_hypot" } else { "hypot" }; - return Some(self.insert_intrinsic(name, &[t_f64], t_f64, false)); + return Some(self.insert_intrinsic(name, &[t_f64, t_f64], t_f64, false)); } ifn!("strcmp", fn(t_str, t_str) -> t_i32); diff --git a/openvaf/test_data/osdi/math_intrinsics.snap b/openvaf/test_data/osdi/math_intrinsics.snap new file mode 100644 index 00000000..671c538a --- /dev/null +++ b/openvaf/test_data/osdi/math_intrinsics.snap @@ -0,0 +1,14 @@ +param "$mfactor" +units = "", desc = "Multiplier (Verilog-A $mfactor)", flags = ParameterFlags(PARA_KIND_INST) +param "scale" +units = "A/V", desc = "Output scale", flags = ParameterFlags(0x0) + +2 terminals +node "A" units = "V", runits = "A" +node "C" units = "V", runits = "A" +jacobian (A, A) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (A, C) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (C, A) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (C, C) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +0 states +has bound_step false