[ARC] arcilator FlattenModulesPass self-RAUW assertion crash with self-referential port connection
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 2.2k
- Forks
- 524
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 46
Description
## [ARC] CIRCT arcilator FlattenModulesPass self-RAUW assertion crash with self-referential port connection
**Tool**: CIRCT arcilator (circt-verilog + arcilator pipeline)
**Severity**: Crash
### Description
A SystemVerilog module with a nested module definition and a self-referential port connection `uut(x, x)` causes CIRCT arcilator to crash with an MLIR assertion "cannot RAUW a value with itself". During FlattenModulesPass, when inlining a nested module whose output is connected to its own input via the same instance port, the `PrefixingInliner::handleTerminator` attempts to replace a value with itself (self-RAUW), triggering the assertion in `mlir::IRObjectWithUseList::replaceAllUsesWith`.
### Minimal Reproducible Example
```systemverilog
module top();
m u(a, a);
module m(input p, output q);
assign q = p;
endmodule
endmodule
```
### Reproduction Steps
```bash
circt-verilog --ir-hw minimal_testcase.sv -o t.mlir && arcilator t.mlir --state-file state.json -o arc.ll
```
### Actual Output
```
$ arcilator t.mlir --state-file state.json -o arc.ll
arcilator: circt-1.144.0-src/llvm/llvm/../mlir/include/mlir/IR/UseDefLists.h:213: void mlir::IRObjectWithUseList::replaceAllUsesWith(ValueT &&) [OperandType = mlir::OpOperand, ValueT = mlir::Value &]: Assertion `(!newValue || this != OperandType::getUseList(newValue)) && "cannot RAUW a value with itself"' failed.
PLEASE submit a bug report to https://github.com/llvm/circt and include the crash backtrace.
Stack dump:
0. Program arguments: arcilator t.mlir --state-file state.json -o arc.ll
#0 0x00005c9c41058ce8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x1839ce8)
#1 0x00005c9c41055bb1 llvm::sys::RunSignalHandlers() (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x1836bb1)
#2 0x00005c9c41059b01 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
#3 0x00007528107c0330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
#4 0x0000752810819b2c pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x9eb2c)
#5 0x00007528107c027e raise (/lib/x86_64-linux-gnu/libc.so.6+0x4527e)
#6 0x00007528107a38ff abort (/lib/x86_64-linux-gnu/libc.so.6+0x288ff)
#7 0x00007528107a381b (/lib/x86_64-linux-gnu/libc.so.6+0x2881b)
#8 0x00007528107b6517 (/lib/x86_64-linux-gnu/libc.so.6+0x3b517)
#9 0x00005c9c415d60b6 (anonymous namespace)::PrefixingInliner::handleTerminator(mlir::Operation*, mlir::ValueRange) const FlattenModules.cpp:0:0
#10 0x00005c9c43b597de inlineRegionImpl(mlir::InlinerInterface&, llvm::function_ref, mlir::Region*, mlir::Block*, llvm::ilist_iterator, false, false>, mlir::IRMapping&, mlir::ValueRange, mlir::TypeRange, std::optional, bool, mlir::CallOpInterface) InliningUtils.cpp:0:0
#11 0x00005c9c43b59d9a mlir::inlineRegion(mlir::InlinerInterface&, llvm::function_ref, mlir::Region*, mlir::Block*, llvm::ilist_iterator, false, false>, mlir::ValueRange, mlir::ValueRange, std::optional, bool) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x433ad9a)
#12 0x00005c9c43b59b06 mlir::inlineRegion(mlir::InlinerInterface&, llvm::function_ref, mlir::Region*, mlir::Operation*, mlir::ValueRange, mlir::ValueRange, std::optional, bool) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x433ab06)
#13 0x00005c9c415d1acf (anonymous namespace)::FlattenModulesPass::runOnOperation() FlattenModules.cpp:0:0
#14 0x00005c9c43bd80f2 mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x43b90f2)
#15 0x00005c9c43bd8d66 mlir::detail::OpToOpPassAdaptor::runPipeline(mlir::OpPassManager&, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int, mlir::PassInstrumentor*, mlir::PassInstrumentation::PipelineParentInfo const*) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x43b9d66)
#16 0x00005c9c43be0102 mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x43c1102)
#17 0x00005c9c43bdf74d mlir::PassManager::run(mlir::Operation*) (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x43c074d)
#18 0x00005c9c40fc328d processBuffer(mlir::MLIRContext&, mlir::TimingScope&, llvm::SourceMgr&, std::optional>>&) arcilator.cpp:0:0
#19 0x00005c9c40fc240f processInputSplit(mlir::MLIRContext&, mlir::TimingScope&, std::unique_ptr>, std::optional>>&) arcilator.cpp:0:0
#20 0x00005c9c40fbf80a executeArcilator(mlir::MLIRContext&) arcilator.cpp:0:0
#21 0x00005c9c40fbeeac main (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x179feac)
#22 0x00007528107a51ca (/lib/x86_64-linux-gnu/libc.so.6+0x2a1ca)
#23 0x00007528107a528b __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28b)
#24 0x00005c9c40fbba25 _start (/edazz/target/circt-1.144.0-bin/bin/arcilator+0x179ca25)
[1] 707763 IOT instruction (core dumped) arcilator t.mlir --state-file state.json -o arc.ll
```
Exit code: 134 (SIGABRT)
### Expected Output
arcilator should either compile successfully or emit a meaningful error message about the self-referential port connection, but it must not crash with an assertion violation.
### Cross-Tool Comparison
| Tool | Result | Exit Code | Notes |
|------|--------|-----------|-------|
| CIRCT arcilator | Crash (SIGABRT) | 134 | Assertion failure: self-RAUW in FlattenModulesPass |
| Verilator | Compilation error | 1 | "module decls within module decls" - nested modules unsupported |
| Icarus Verilog | Compilation error | 2 | "Unknown module type: test_module" - nested modules unsupported |
CIRCT is the only tool that crashes; Verilator and Iverilog report expected errors about unsupported nested module definitions.
### Root Cause (Preliminary)
The crash chain begins when `circt-verilog` lowers the SV source to HW MLIR, generating a self-referential instance result for `uut(x,x)`:
```
%uut.port_b = hw.instance "uut" @test_module(port_a: %uut.port_b: i1) -> (port_b: i1)
```
When `FlattenModulesPass` inlines `test_module` into `top`, `PrefixingInliner::handleTerminator` (FlattenModules.cpp:89-94) processes the `hw.output %port_a` terminator by calling `from.replaceAllUsesWith(to)` where `from` is the inlined block argument `%port_a` and `to` is `%uut.port_b` (the instance result that depends on `%port_a` as its own operand). This creates a cyclic dependency where a value would replace itself, triggering the MLIR assertion.
### Standard Compliance Assessment
- **Classification**: `tool_bug`
- **IEEE References**:
- IEEE 1800-2017 Section 23.4 (page 720) - "Nested modules": Explicitly permits module definitions to be nested inside other modules, which is the pattern used in the testcase (`test_module` defined inside `top`).
- IEEE 1800-2017 Section 23.2.2 - "Port connection semantics": The pattern of connecting the same signal to both input and output ports of an instance is valid SystemVerilog; the output port directly driven by the input creates a self-connecting wire.
- **Justification**: The testcase uses valid IEEE 1800 SystemVerilog constructs. CIRCT's FlattenModulesPass crashes when processing the self-referential port connection `uut(x,x)` during module inlining. The MLIR RAUW assertion fires because the pass generates a cyclic value mapping. A robust compiler must handle valid input without crashing; emitting a proper error message is the correct response to unusual-but-legal constructs.
- **Confidence**: High
### Related Issues
- #3600: **"[FIRRTL] Crash using an instance input ports to drive another instance"** - Closed. Same "cannot RAUW a value with itself" assertion crash pattern. Root cause: self-connected instance port `x.a <= x.a`. Fixed in LowerToHW (FIRRTL dialect) in April 2023. The current bug has an identical crash pattern but occurs in `FlattenModulesPass::PrefixingInliner` (HW dialect), suggesting the prior fix did not cover this code path.
- #4780: **"[FIRRTL] Missed comb cycle, crash in canonicalizer"** - Closed. Same assertion crash in `sv.wire` canonicalizer on a self-connecting wire `rwDataOut <= rwDataOut`. Fixed May 2024.
- #9483: **"[HW] hw-flatten-modules doesn't work with parameters"** - Open. Related FlattenModules issue but not about self-RAUW.
- #6967: **"[HW] Inner symbols dropped by InlineModules"** - Open. Different issue about inner symbol handling.
### Suggested Fix Direction
1. **In `FlattenModules.cpp` `PrefixingInliner::handleTerminator`**: Add a check before `from.replaceAllUsesWith(to)` to detect when `from` and `to` are the same value or when `to` depends on `from` (cycle detection). If a cycle is detected, skip the replacement or emit a meaningful error instead of crashing.
2. **Upstream fix in `circt-verilog`**: Detect self-referential instance connections during SV-to-HW lowering (where an instance's operand is derived from the same instance's result) and emit a proper error rather than generating MLIR that causes a downstream assertion failure.
3. **Defensive check**: The inliner could check `from != to` before calling RAUW, or verify whether `to` is an operand of the instance being inlined, which would indicate a cyclic dependency.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in FlattenModules.cpp at PrefixingInliner::handleTerminator, then reproduce the failure with the provided nested-module SystemVerilog example and the circt-verilog/arcilator commands. Trace the hw.output value mapping around replaceAllUsesWith and add a regression test for uut(x, x). Done means arcilator no longer aborts with the self-RAUW assertion and instead compiles or reports a meaningful error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100