Skip to content

Accept :hooks compiler opt in EMLX.__compile__/4 - #133

Merged
polvalente merged 3 commits into
elixir-nx:mainfrom
marceloreichert:fix/compiler-hooks-opt
Aug 12, 2026
Merged

Accept :hooks compiler opt in EMLX.__compile__/4#133
polvalente merged 3 commits into
elixir-nx:mainfrom
marceloreichert:fix/compiler-hooks-opt

Conversation

@marceloreichert

Copy link
Copy Markdown
Contributor

nx's Nx.Defn.compile/3/jit/2 now always forward a :hooks option (defaulting to %{}) to the selected compiler, even when no hooks are used. EMLX's strict Keyword.validate! allowlist didn't include it, so any compile under a current nx (e.g. bench/svd_bench.exs, which pins nx main) failed immediately with "unknown keys [:hooks]".

An empty hooks map is now accepted and ignored. A non-empty one raises a clear error instead: EMLX lowers :hook/:io_call expr nodes natively (each with its own inline default callback), but doesn't wire through the named-override map carried by this compiler opt.

nx's `Nx.Defn.compile/3`/`jit/2` now always forward a `:hooks` option
(defaulting to `%{}`) to the selected compiler, even when no hooks are
used. EMLX's strict Keyword.validate! allowlist didn't include it, so
any compile under a current nx (e.g. bench/svd_bench.exs, which pins
nx main) failed immediately with "unknown keys [:hooks]".

An empty hooks map is now accepted and ignored. A non-empty one raises
a clear error instead: EMLX lowers :hook/:io_call expr nodes natively
(each with its own inline default callback), but doesn't wire through
the named-override map carried by this compiler opt.
Comment thread emlx/lib/emlx.ex Outdated
Comment on lines +1741 to +1752
# Known EMLX-specific compiler opts. `:command_queue` is injected by
# `__partitions_options__/1` but may also be passed directly by callers
# that manage their own queues (equivalent to a manual `with_queue`).
@valid_compiler_keys [:device, :max_concurrency, :command_queue]
#
# `:hooks` isn't EMLX-specific — `Nx.Defn.compile/3`/`jit/2` always forward
# it (defaulting to `%{}`) to whichever compiler is selected (see
# `Nx.Defn.prepare_options/1`), so every `Nx.Defn.Compiler` callback must
# accept it even if unused. `:hook`/`:io_call` *expr nodes* (each carrying
# their own inline default callback) do lower natively — see
# `EMLX.Native.Expr`'s `:hook`/`:io_call` clause — but the *named-override*
# map this option carries (swapping in a different callback per hook name
# at compile time) isn't wired through, so it's only accepted when empty.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove these comments

Comment thread emlx/lib/emlx.ex
Comment on lines +1785 to +1795
case Keyword.get(opts, :hooks, %{}) do
empty when empty == %{} ->
:ok

hooks ->
raise ArgumentError,
"EMLX does not support the :hooks named-override map (got callbacks for " <>
"#{inspect(Map.keys(hooks))}) — :hook/:io_call expr nodes lower natively " <>
"with their own inline default callback, but swapping in a different " <>
"callback per hook name via compiler opts isn't wired through"
end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should add support for them. Can you either implement this (bigger ask) or open a related issue?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can open an issue for this now. I can pick it up later.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!

nx's `Nx.Defn.compile/3`/`jit/2` now always forward a `:hooks` option
(defaulting to `%{}`) to the selected compiler, even when no hooks are
used. EMLX's strict Keyword.validate! allowlist didn't include it, so
any compile under a current nx (e.g. bench/svd_bench.exs, which pins
nx main) failed immediately with "unknown keys [:hooks]".

An empty hooks map is now accepted and ignored. A non-empty one raises
a clear error instead: EMLX lowers :hook/:io_call expr nodes natively
(each with its own inline default callback), but doesn't wire through
the named-override map carried by this compiler opt.
@polvalente
polvalente merged commit 09dcaae into elixir-nx:main Aug 12, 2026
10 checks passed
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