Import and transform textual Program IR
Example source: examples/13_compile_textual_ir.py
Example 13 starts from textual mixed-level IR. It parses and prints the Program, verifies a stable textual round trip, inserts a caller-defined analysis pass into the Compile pipeline, and inspects the transformed CKKS operations.
Run the example
python examples/13_compile_textual_ir.pyNo cryptographic runtime or device is required because the example stops at the transformed Program.
Textual Program
The input module contains one function with:
- an encrypted semantic value;
- a public message gain;
- a cyclic roll;
- encrypted addition;
- encrypted-by-public multiplication.
The module also carries FHElium schema and dialect versions. fhelium.ir.parse(...) loads the registered FHElium and upstream xDSL operations while preserving the mixed abstraction levels represented in the text.
Stable serialization
The example performs:
imported = ir.parse(text, source_name="inline-compile-example.mlir")
serialized = imported.to_text()
round_tripped = ir.parse(serialized)2
3
It then checks that printing the second Program reproduces the first serialized form. This verifies structural round-trip stability for the selected Program; it does not prove numerical equivalence or executability.
Caller-defined analysis pass
RecordDialectInventoryPass implements the public pass protocol:
from fhelium import compile as fh_compile
@dataclass(frozen=True)
class RecordDialectInventoryPass:
name: str = "record-dialect-inventory"
def run(self, compilation):
program = compilation.program
shared_data = compilation.workspace
...
shared_data["textual-ir/dialect-counts"] = counts
return fh_compile.PassResult.unchanged(...)2
3
4
5
6
7
8
9
10
11
12
The pass reads the current Program, counts operation namespaces, publishes its result through the request's schema-free CompileWorkspace, and returns an unchanged PassResult.
The example places this pass before dead-value elimination:
pipeline = fh_compile.Pipeline(
(
RecordDialectInventoryPass(),
fh_compile.EliminateDeadValuesPass(),
fh_compile.LowerSemanticToLogicalPass(),
fh_compile.InsertPlaintextPreparationPass(),
fh_compile.InsertMultiplyNttTransitionsPass(),
fh_compile.LowerLogicalToCkksPass(),
fh_compile.InsertRelinearizationPass(),
fh_compile.InsertRescalePass(),
)
)2
3
4
5
6
7
8
9
10
11
12
Pipeline composition preserves the listed execution order. The analysis pass receives the same workspace as every subsequent Compile pass.
Partial lowering
The remaining pipeline lowers semantic roll, addition, and mixed multiplication through logical operations into CKKS operations. The resulting Program contains the representation transitions and plaintext preparation selected by those passes.
The example also derives evaluation-key requirements from the transformed IR. This operation-depth analysis remains separate from key creation and runtime resource binding.
Why use textual IR
Textual Program IR is useful when a compiler developer needs to:
- inspect a transformation independently of source capture;
- construct a minimal reproduction for a pass;
- exchange a source-independent Program;
- preserve unknown extension operations for another consumer;
- compare structural output before and after a transformation.
The parser accepts permissive mixed-level Programs. It does not turn structural acceptance into a CKKS correctness or distributed-safety proof.
Source
#!/usr/bin/env python3
"""Parse, inspect, transform, and serialize a textual mixed-level Program.
Textual IR is the same Program representation accepted by Compile. This
example performs a stable parse/print round trip, inserts one caller-defined
analysis pass into the standard compile pipeline, and stops before execution.
"""
from __future__ import annotations
from collections import Counter
from dataclasses import dataclass
from common import print_table
from fhelium import compile as fh_compile
from fhelium import ir
_PROGRAM_TEXT = r'''
builtin.module attributes {
fhelium.schema_version = "1",
fhelium.dialect_version = "0.2"
} {
func.func @main(
%x: !fhelium_semantic.secret<{role = "encrypted"}>,
%gain: !fhelium_semantic.public<{role = "message"}>
) -> !fhelium_semantic.secret<{role = "encrypted"}> {
%rotated = "fhelium_semantic.roll"(%x) {shift = 2 : i64, dimension = -1 : i64}
: (!fhelium_semantic.secret<{role = "encrypted"}>)
-> !fhelium_semantic.secret<{role = "encrypted"}>
%mixed = "fhelium_semantic.add"(%x, %rotated)
: (!fhelium_semantic.secret<{role = "encrypted"}>,
!fhelium_semantic.secret<{role = "encrypted"}>)
-> !fhelium_semantic.secret<{role = "encrypted"}>
%result = "fhelium_semantic.multiply"(%mixed, %gain)
: (!fhelium_semantic.secret<{role = "encrypted"}>,
!fhelium_semantic.public<{role = "message"}>)
-> !fhelium_semantic.secret<{role = "encrypted"}>
func.return %result : !fhelium_semantic.secret<{role = "encrypted"}>
}
}
'''
@dataclass(frozen=True)
class RecordDialectInventoryPass:
"""Publish operation counts by dialect without rewriting the Program."""
name: str = "record-dialect-inventory"
def run(self, compilation: fh_compile.Compilation) -> fh_compile.PassResult:
program = compilation.program
shared_data = compilation.workspace
counts = Counter(
operation.name.split(".", maxsplit=1)[0]
for operation in program.walk()
)
shared_data["textual-ir/dialect-counts"] = dict(sorted(counts.items()))
return fh_compile.PassResult.unchanged(
program,
matched=sum(counts.values()),
diagnostics=(f"observed {len(counts)} dialect namespaces",),
)
def main() -> None:
imported = ir.parse(
_PROGRAM_TEXT, source_name="inline-compile-example.mlir"
)
serialized = imported.to_text()
round_tripped = ir.parse(
serialized,
source_name="round-tripped-compile-example.mlir",
)
if round_tripped.to_text() != serialized:
raise RuntimeError("textual Program round trip was not stable")
workspace = fh_compile.CompileWorkspace()
pipeline = fh_compile.Pipeline(
(
RecordDialectInventoryPass(),
fh_compile.EliminateDeadValuesPass(),
fh_compile.LowerSemanticToLogicalPass(),
fh_compile.InsertPlaintextPreparationPass(),
fh_compile.InsertMultiplyNttTransitionsPass(),
fh_compile.LowerLogicalToCkksPass(),
fh_compile.InsertRelinearizationPass(),
fh_compile.InsertRescalePass(),
)
)
compiled = pipeline.run(fh_compile.Compilation(round_tripped, workspace))
requirements = ir.analyze_evaluation_key_requirements(compiled.program)
print_table(
["textual Program", "value"],
[
["stable round trip", True],
["dialect counts", workspace["textual-ir/dialect-counts"]],
["rotation steps", sorted(requirements.rotation_steps)],
["relinearization key", requirements.requires_relinearization],
],
)
print()
print_table(
["pass", "matched", "transformed", "diagnostics"],
[
[
report.name,
report.stats.matched,
report.stats.transformed,
"; ".join(report.diagnostics) or "none",
]
for report in compiled.reports
],
)
print()
print("--- stable imported Program ---")
print(serialized)
print()
print("--- compiled Program ---")
print(ir.format_program(compiled.program))
if __name__ == "__main__":
main()2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Next steps
- Compose and execute built-in Compile passes creates the same Program representation from a callable and lowers it for Backend execution.
- Customize a Compile pass and pipeline demonstrates a rewriting pass.
- Neutral IR Programs defines Program structure and serialization.