llvm / llvm/circt

[ARC] arcilator FlattenModulesPass self-RAUW assertion crash with self-referential port connection

Open
#10,184 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.