Repository navigation
feat(compile): composite emission seam, GELU legalization pass, @Lowering (#1342, part 2) - #1346
Merged
michalharakal merged 1 commit intoOct 14, 2026
Conversation
…ring (SKaiNET-developers#1342, part 2) The exact-GELU flag from part 1 becomes a per-target lowering decision. - LoweringSpec + ConversionContext.emitComposite (sk.ainet.compile.hlo.lowering): one record per fused-able op — composite name, the node parameters that become composite_attributes, a version, one decomposition builder. spec.lower(...) emits `stablehlo.composite "skainet.gelu" %x {..., decomposition = @fn, version = 1 : i32}` plus one `func.func private` per (op, attributes, type) when the target's OpGranularityPolicy keeps the op fused, and the same builder's lines inline otherwise. First consumer of OpGranularityPolicy.keepFused. - Module trailer: helper functions follow the entry function (emitModuleTrailer / claimSymbol), so MlirParser still finds @main first and the module-wide SSA rule holds (helper bodies use the shared counter). - Specs: GeluLowering.spec (skainet.gelu, attribute approximate) and ErfLowering.spec (skainet.erf); convertGelu and UnaryMathConverter's erf case lower through them. Portable emission is unchanged. - GeluApproximationLegalizationPass (skainet-compile-opt): targets without an erf lowering rewrite gelu(approximate = false) to the tanh form and record legalizedFrom / maxAbsError = 4.8e-4 on the node plus a GELU_APPROX_LEGALIZED diagnostic; erf nodes are left to fail loudly. Opt-in via TargetOptimizers.registerDagPasses. - @lowering annotation on TensorOps.gelu/erf; OperatorDocProcessor writes a `lowering` field into operators.json; the coverage matrix gains a Lowering column (composite · decompose · native). - Docs: "Fused ops and stablehlo.composite" in the HLO tutorial; CHANGELOG. Tests: CompositeEmissionTest (7), GeluApproximationLegalizationPassTest (3), OperatorDocProcessorTest.testLoweringFlowsIntoJson. API dumps regenerated.
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part 2 of #1342, on top of #1344: the op metadata becomes a per-target lowering decision.
LoweringSpec+ConversionContext.emitComposite(sk.ainet.compile.hlo.lowering): one record per fused-able op — composite name, the node parameters that becomecomposite_attributes, a version, and a single decomposition builder.spec.lower(...)emitsstablehlo.composite "skainet.gelu" %x {composite_attributes = {approximate = false}, decomposition = @skainet_gelu_approximate_false_2x3xf32, version = 1 : i32} : (T) -> Tplus onefunc.func privateper (op, attributes, type) whencontext.granularity?.keepFused(op)is true, and the same builder's lines inline otherwise. This is the first consumer ofOpGranularityPolicy, which was threaded through the converter but never read.ConversionContext.emitModuleTrailer,claimSymbolfor dedup), soMlirParserstill finds@mainfirst andMlirValidator's module-wide SSA rule holds because the helper bodies draw names from the shared counter. The text-levelStableHloOptimizerpasses tolerate the two-function module (tested).GeluLowering.spec(skainet.gelu, attributeapproximate) andErfLowering.spec(skainet.erf);convertGeluand theerfcase ofUnaryMathConvertergo throughspec.lower. Portable emission is byte-identical to feat(ops)!: exact (erf) GELU and erf op (#1342, part 1) #1344.GeluApproximationLegalizationPass(skainet-compile-opt): a target with no erf lowering rewritesgelu(approximate = false)→gelu(approximate = true)and recordslegalizedFrom/maxAbsError = 4.8e-4on the node plus aGELU_APPROX_LEGALIZEDdiagnostic;erfnodes are left to fail loudly. Opt-in per target viaTargetOptimizers.registerDagPasses.@Loweringannotation (sk.ainet.lang.ops) onTensorOps.gelu/erf;OperatorDocProcessorwrites aloweringfield intooperators.json(bothFunctionDocmodel copies default it to empty, so old JSON still deserializes); the coverage matrix gains a Lowering column (composite · decompose · native).stablehlo.composite" section in the HLO tutorial; CHANGELOG.Nothing registers a fused policy or the legalization pass in-repo yet — both are seams a target plugs into (
FusedOpAllowList("my-npu", setOf("gelu")),TargetOptimizers.registerDagPasses("my-npu") { … }), as the #1342 lowering-metadata section describes. Composite emission is currently unary-only (emitCompositerequires one operand); SDPA (#1343) will extend it.Test plan
CompositeEmissionTest(hlo): composite + private fn after@main,MlirParserentry =@main,MlirValidatorclean, inline when policy is null or decomposes gelu, dedup per (attributes, shape),skainet.erf, composite body ≡ inline decomposition modulo SSA names,convertWithOptimizationon a two-function moduleGeluApproximationLegalizationPassTest(opt): rewrite + recorded parameters + diagnostics + edges, no-op on tanh nodes, idempotent, registry opt-in throughdagPipelineForvs a portable targetOperatorDocProcessorTest.testLoweringFlowsIntoJsonActivationOperationsConverterTest/UnaryMatherf tests from feat(ops)!: exact (erf) GELU and erf op (#1342, part 1) #1344 unchangedapiDump,generateDocs(matrix column),scripts/pr-gate.sh --quickand full gate