Skip to content

Add expm1/ln1p math builtins (VAMS-2023) - #20

Merged
Kreijstal merged 3 commits into
OpenVAF:mobfrom
sai-v-ch:vams2023-expm1-ln1p
Jul 29, 2026
Merged

Add expm1/ln1p math builtins (VAMS-2023)#20
Kreijstal merged 3 commits into
OpenVAF:mobfrom
sai-v-ch:vams2023-expm1-ln1p

Conversation

@sai-v-ch

Copy link
Copy Markdown
Collaborator

Implements expm1() and ln1p() from Verilog-AMS VAMS-2023 (Mantis 7780, table in 4.3.1). This is the first slice of the VAMS-2023 alignment from #19 — smallest self-contained piece first so the approach can be checked before I do the larger items.

To answer your question from #19 directly: no, I don't have a VAMS-2023 implementation to diff against. So instead of trusting a reference, I pinned every behaviour to something checkable — the closed-form derivatives, f64::exp_m1/f64::ln_1p for the constant-folding path, and the LRM's own accuracy statements. Details below.

Why these aren't just wrappers

The LRM adds them for accuracy, and the difference is total rather than marginal:

expression result
ln(1 + 1e-16) 0.0
ln1p(1e-16) 1e-16

The in-tree DIODE model already computes is*(limexp(vd/(n*vt)) - 1), which is exactly the subtraction expm1 exists to avoid.

What's wired up

New MIR unary opcodes Expm1/Ln1p, lowered to the libm expm1/log1p symbols since LLVM has no intrinsics for either. Both the plain and the $-prefixed spelling are accepted, matching the LRM table.

Derivatives:

d/dx expm1(x) = exp(x)
d/dx ln1p(x)  = 1/(1+x)

The expm1 rule emits a fresh exp(x) rather than reusing the already computed expm1(x) + 1. That shortcut looks cheaper but is wrong: for x = -40, expm1(x) rounds to exactly -1.0, so expm1(x) + 1 cancels to 0, while the true exp(-40) is 4.25e-18.

Two things worth your call

1. I did not add these to the reserved keyword set, even though VAMS-2023 reserves them. HiSIMSOTB (in-tree, hisimsotb.va:1124) declares its own analog function real expm1, and other models written against older revisions do the same. Reserving the names turns those into hard errors.

I first tried routing them through kw_comp so they'd produce the existing warn-by-default vams_keyword_compat lint. That works and keeps the models compiling, but the lint's note reads "will likely never be used in the implemented language subset", which is now false — so kw_comp is the wrong bucket. I left the names unreserved instead, which keeps this PR purely additive: a user-defined function still shadows the builtin, and no existing model changes behaviour. Happy to add a properly worded lint in a follow-up if you'd rather flag it.

2. Builder::intrinsic panics for unregistered symbols. CodegenCx::intrinsic has to declare the libm name or codegen aborts with intrinsic not found. I hit this, and no in-tree model would have caught it, because HiSIMSOTB shadows expm1 and nothing else used the new builtins. That's why this PR also adds an integration model — otherwise the LLVM path stays uncovered in CI.

Testing

Verified locally against LLVM 18.1.8:

  • cargo test -p osdi — 27/27, including the new VAMS2023_MATH model compiled through LLVM/OSDI
  • cargo test -p openvaf --test integration (RUN_DEV_TESTS=1 RUN_SLOW_TESTS=1) — 71/71, which compiles the new model to a shared library, dlopens it and processes its parameters
  • full frontend suite with RUN_DEV_TESTS=1 — all 26 pre-existing compact models unchanged
  • cargo fmt -- --check clean

New tests:

  • numeric autodiff checks up to third order plus chain-rule cases, comparing the interpreted result against the closed form
  • MIR snapshots for both spellings
  • a MIR snapshot proving a user-defined expm1 still shadows the builtin (it lowers to the inlined exp(x) - 1.0 body, not the opcode)
  • a const-folding precision check: ln1p(1e-16) folds back to 1e-16 while the naive ln(1+x) in the same function folds to 0

One pre-existing failure is unrelated and also fails on a clean mob checkout: sourcegen osdi::gen_osdi_structs panics at sourcegen/src/osdi.rs:200.

sai-v-ch and others added 3 commits July 28, 2026 22:13
Verilog-AMS VAMS-2023 (Mantis 7780) adds `expm1` and `ln1p` alongside the
existing math builtins. Both are wired up the same way as `exp`/`ln`: as new
MIR unary opcodes, lowered to the libm `expm1`/`log1p` symbols since LLVM has
no intrinsics for them. Both the plain and `$`-prefixed spellings are accepted.

Derivatives:
    d/dx expm1(x) = exp(x)
    d/dx ln1p(x)  = 1/(1+x)

The derivative of expm1 emits `exp(x)` rather than reusing the already
computed `expm1(x) + 1`, which would cancel to zero for large negative x.

The two names are deliberately *not* added to the reserved keyword set even
though VAMS-2023 reserves them: HiSIMSOTB and other compact models written
against older revisions declare their own `expm1` function, and those
declarations must keep shadowing the builtin.

Tests:
  - numeric autodiff checks up to third order plus chain rule cases
  - MIR snapshots for both spellings
  - MIR snapshot proving a user-defined `expm1` still shadows the builtin
  - const folding, including a precision check that ln1p(1e-16) stays 1e-16
    where the naive ln(1+x) folds to 0
`Builder::intrinsic` panics with "intrinsic not found" unless the symbol is
declared in `CodegenCx::intrinsic`, so codegen would have aborted on the first
model that actually called expm1()/ln1p().

No in-tree model used the new builtins (HiSIMSOTB declares its own expm1), so
the OSDI/LLVM harness could not have caught this. Added an integration model
that uses both, which the osdi and openvaf integration harnesses compile
through LLVM.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@Kreijstal
Kreijstal merged commit 7c1841d into OpenVAF:mob Jul 29, 2026
8 of 11 checks passed
@sai-v-ch
sai-v-ch deleted the vams2023-expm1-ln1p branch August 1, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants