Skip to content

Add an OpenFHE dialect interpreter#2314

Merged
copybara-service[bot] merged 1 commit intogoogle:mainfrom
j2kun:openfhe-interp
Oct 22, 2025
Merged

Add an OpenFHE dialect interpreter#2314
copybara-service[bot] merged 1 commit intogoogle:mainfrom
j2kun:openfhe-interp

Conversation

@j2kun
Copy link
Collaborator

@j2kun j2kun commented Oct 16, 2025

This PR adds the ability to run OpenFHE dialect code in an interpreted mode, rather than generating C++ code that is then compiled by clang.

This is intended to support cases in the codebase where clang compile times are very long due to having fully unrolled FHE kernels. We have unrolled FHE kernels because noise analysis and ciphertext management passes do not support loops yet, and the plan to add support for loops is based on the HALO compiler paper (see #289).

To use the interpreter, load the openfhe_interpreter_test bazel macro, and use the generated mlir-opt filename from that macro in your test file to load the openfhe dialect IR as a ModuleOp, which is passed to an Interpreter object.

Then a call to

interpreter.interpret("<func_name>", <func_args>)

must have as its second argument a vector of TypedCppValue objects whose values correspond to the same ordered arguments of the function being interpreted. The call to interpret returns a vector of results as TypedCppValue. These can be chained together as in the compiled case to produce a full execution of the encrypt/run/decrypt flow.

Note: This PR was written with a decent amount of assistance from an LLM. Though it has unit tests and runs the Halevi-Shoup matvec test case correctly, it was not tested on BGV/BFV (just the one CKKS example) and so it should be expected to have some rough edges.

@j2kun j2kun requested a review from asraa October 16, 2025 16:19
@j2kun
Copy link
Collaborator Author

j2kun commented Oct 16, 2025

CC @eymay I'd be curious to see if this is suitable for the large examples you were working with. I suspect the resulting program latency will suffer slightly by interpreting it instead of compiling it, and I'd be curious to hear how much worse it is for your examples.

@j2kun
Copy link
Collaborator Author

j2kun commented Oct 17, 2025

One thing that can be improved here is to identify that an SSA value has had its last use executed, and remove it from the environment to free up memory. Otherwise we're just keeping all SSA values in RAM for the entirety of the program.

@asraa asraa self-assigned this Oct 21, 2025
This PR adds the ability to run OpenFHE dialect code in an interpreted
mode, rather than generating C++ code that is then compiled by clang.

This is intended to support cases in the codebase where clang compile
times are very long due to having fully unrolled FHE kernels. We have
unrolled FHE kernels because noise analysis and ciphertext management
passes do not support loops yet, and the plan to add support for loops
is based on the HALO compiler paper (see google#289).

To use the interpreter, load the `openfhe_interpreter_test` bazel macro,
and use the generated mlir-opt filename from that macro in your test
file to load the openfhe dialect IR as a ModuleOp, which is passed
to an Interpreter object.

Then a call to

```
interpreter.interpret("<func_name>", <func_args>)
```

must have as its second argument a vector of `TypedCppValue` objects
whose values correspond to the same ordered arguments of the function
being interpreted. The call to `interpret` returns a vector of
results as `TypedCppValue`. These can be chained together as
in the compiled case to produce a full execution of the
encrypt/run/decrypt flow.
@j2kun j2kun added the pull_ready Indicates whether a PR is ready to pull. The copybara worker will import for internal testing label Oct 21, 2025
@copybara-service copybara-service bot merged commit 8e66ca3 into google:main Oct 22, 2025
8 checks passed
@eymay
Copy link
Contributor

eymay commented Oct 24, 2025

CC @eymay I'd be curious to see if this is suitable for the large examples you were working with. I suspect the resulting program latency will suffer slightly by interpreting it instead of compiling it, and I'd be curious to hear how much worse it is for your examples.

Hi, I tried the IRs I had but because they are from an older version of HEIR it wasn't directly compatible and backporting the interpreter turned out to be more work than I imagined. So I ended up copying the matvec op from the example multiple times to get a stress test and instead compared its compiled and interpreted executions. Surprisingly I ended up with 1.5x slowdown in interpreted mode with a benchmark that has 20 matvecs. The main bottleneck is not the OpenFHE calls but rather the cleartext computations such as scf.for taking almost half of the execution.

The solution to it would probably involve compiling most of the cleartext computations that do not involve FHE operations and let the interpreter do the FHE operations. JIT compiling the cleartext can be an option. Instead of interpreting cleartext lines, they can be translated and compiled just-in-time to functions that are run by the interpreter. Then it is also possible to cache these functions in case there are repetitions of these cleartext lines which should make the compilation overhead a one-time cost for the repeated cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pull_ready Indicates whether a PR is ready to pull. The copybara worker will import for internal testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants