Skip to content

--//:enable_yosys=0 build is broken. #3336

Description

@z0cal

Disclaimer
the second and third patches produced with Claude Code, and a also use Claude Code to write some of this issue; I reproduced the failures and verified the full build and test suite locally.

The enable_yosys=0 configuration currently fails to build, and once it builds, three tests that need Yosys are not excluded from it.
There are three independent problems. All are below with repros and patches.

1. Missing includes in BooleanPipelineRegistration.cpp

bazel build --//:enable_yosys=0 //tools:heir-opt
lib/Pipelines/BooleanPipelineRegistration.cpp:160:17: error: no type named 'oneShotBufferize' in namespace 'mlir::heir'
lib/Pipelines/BooleanPipelineRegistration.cpp:179:28: error: use of undeclared identifier 'createRemoveUnusedMemRef'
lib/Pipelines/BooleanPipelineRegistration.cpp:201:14: error: use of undeclared identifier 'createRemoveUnusedMemRef'

The #else (no-Yosys) branch of mlirToCGGIPipeline uses oneShotBufferize (declared in lib/Pipelines/PipelineRegistration.h) and createRemoveUnusedMemRef (generated from lib/Transforms/UnusedMemRef/UnusedMemRef.h). Both includes were removed in 16e8134 ("e2e tests of debug.validate lowerings & OpenFHE emitter improvements", 2026-06-10), which looks like an include cleanup that only considered the Yosys-enabled preprocessor branch. The Bazel deps in lib/Pipelines/BUILD are already correct; only the #include lines are missing.

--- a/lib/Pipelines/BooleanPipelineRegistration.cpp
+++ b/lib/Pipelines/BooleanPipelineRegistration.cpp
@@
 #include "lib/Dialect/Secret/Transforms/DistributeGeneric.h"
+#include "lib/Pipelines/PipelineRegistration.h"
 #include "lib/Transforms/BooleanVectorizer/BooleanVectorizer.h"
@@
 #include "lib/Transforms/TensorLinalgToAffineLoops/TensorLinalgToAffineLoops.h"
+#include "lib/Transforms/UnusedMemRef/UnusedMemRef.h"

2. heir-translate --emit-tfhe-rust-hl crashes on affine.yield with no operands

With the above applied, bazel build --//:enable_yosys=0 --build_tag_filters=-yosys //...:all fails in //tests/Examples/tfhe_rust_hl/cpu:add_round_key_test.heir_translate_rs:

heir-translate: llvm/include/llvm/ADT/ArrayRef.h:439:
  T &llvm::MutableArrayRef<mlir::OpOperand>::operator[](size_t) const [T = mlir::OpOperand]:
  Assertion `Index < this->size() && "Invalid index!"' failed.

Minimal repro (heir-translate --emit-tfhe-rust-hl):

module {
  func.func @f(%arg0: !tfhe_rust.server_key, %arg1: memref<16x!tfhe_rust.eui8> {secret.secret}) -> memref<16x!tfhe_rust.eui8> {
    %alloc = memref.alloc() : memref<16x!tfhe_rust.eui8>
    affine.for %i = 0 to 16 {
      %0 = memref.load %arg1[%i] : memref<16x!tfhe_rust.eui8>
      memref.store %0, %alloc[%i] {lwe_annotation = "LWE"} : memref<16x!tfhe_rust.eui8>
    }
    return %alloc : memref<16x!tfhe_rust.eui8>
  }
}

TfheRustHLEmitter::printOperation(affine::AffineYieldOp) guards on getNumResults(), but affine.yield is a terminator and never has results, so the guard is vacuous. It then unconditionally reads op->getOperand(0), which is out of range for a loop with no iter_args.

The sibling emitters are all fine: TfheRustBoolEmitter has the same vacuous guard but never dereferences an operand, OpenFhePkeEmitter iterates over getNumOperands(), and LattigoEmitter matches operands against the parent's iter_args.

This only shows up with Yosys disabled because the #else pipeline bufferizes to memref + affine.for, whereas the Yosys path stays on tensors and never reaches this emitter with an iter_args-free loop.

Patch below mirrors what printOperation(func::ReturnOp) already does in the same file; the tuple case matches the tuple accumulator that the affine.for emitter builds for multiple iter_args.

--- a/lib/Target/TfheRustHL/TfheRustHLEmitter.cpp
+++ b/lib/Target/TfheRustHL/TfheRustHLEmitter.cpp
@@ LogicalResult TfheRustHLEmitter::printOperation(affine::AffineYieldOp op) {
-  if (op->getNumResults() != 0) {
-    return op.emitOpError() << "AffineYieldOp has non-zero number of results";
-  }
-
-  os << variableNames->getNameForValue(op->getOperand(0)) << "\n";
+  // A loop with no iter_args yields nothing.
+  if (op->getNumOperands() == 0) {
+    return success();
+  }
+
+  if (op->getNumOperands() == 1) {
+    os << variableNames->getNameForValue(op->getOperand(0)) << "\n";
+    return success();
+  }
+
+  // Multiple iter_args are folded as a tuple.
+  os << "("
+     << commaSeparatedValues(
+            op->getOperands(),
+            [&](Value value) { return variableNames->getNameForValue(value); })
+     << ")\n";
 
   return success();
 }

3. Three Yosys-only lit tests are not excluded by --test_tag_filters=-yosys

With the two patches above, bazel build --//:enable_yosys=0 --build_tag_filters=-yosys //...:all succeeds, but bazel test with --test_tag_filters=-yosys still runs three tests that require Yosys:

  • //tests/Regression:issue_1086.mlir.test — its RUN line invokes --yosys-optimizer, which does not exist in this build (heir-opt: Unknown command line argument '--yosys-optimizer').
  • //tests/Examples/jaxite:add_one_lut3.mlir.test and :pmap_add_one_lut3.mlir.testheir-opt --mlir-to-cggi --scheme-to-jaxite hits Unsupported cleartext bitwidth in jaxite, UNREACHABLE executed at lib/Dialect/CGGI/Conversions/CGGIToJaxite/CGGIToJaxite.cpp:51, because the no-Yosys --mlir-to-cggi does not produce the boolean form the Jaxite conversion expects.

In tests/Examples/jaxite/BUILD the e2e target already carries tags = ["yosys"], but the glob_lit_tests in the same file globs the same .mlir files with no tags_override, so the lit variant escapes the filter. There is no REQUIRES: yosys lit feature (tests/lit.cfg.py puts Yosys on PATH unconditionally), so the Bazel tag is the only mechanism available.

--- a/tests/Examples/jaxite/BUILD
+++ b/tests/Examples/jaxite/BUILD
@@ glob_lit_tests(
     exclude = ["fully_connected.jaxite.mlir"],
+    tags_override = {
+        "add_one_lut3.mlir": ["yosys"],
+        "pmap_add_one_lut3.mlir": ["yosys"],
+    },
     test_file_exts = ["mlir"],
 )
--- a/tests/Regression/BUILD
+++ b/tests/Regression/BUILD
@@     tags_override = {
+        "issue_1086.mlir": ["yosys"],
         "issue_2466.mlir": ["nofastbuild"],
     },

Result

With all three patches:

  • bazel build --//:enable_yosys=0 --build_tag_filters=-yosys //...:all succeeds (10643 actions).
  • bazel test --//:enable_yosys=0 --build_tag_filters=-yosys --test_tag_filters=-yosys //...:all reports 991 of 991 tests passing.

The four tfhe_rust_hl e2e tests are included in that and pass, so the Rust emitted after patch 2 compiles and runs against tfhe-rs — not just "the emitter stops aborting".

One caveat on my numbers: I initially saw //tests/Examples/openfhe/ckks/batch_matmul:batch_matmul_test time out at 60s, but it passes in ~40s when not competing with other tests, so that was local contention at --jobs=6 rather than a real failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions