[CIRCT-VERILOG][ARC] arcilator fails to legalize llhd.process from always_comb with for loop
- Dominant language
- C++
- Stars
- 2.2k
- Forks
- 524
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 46
Description
## [CIRCT-VERILOG][ARC] arcilator fails to legalize llhd.process from always_comb with for loop
**Tool**: circt (CIRCT 5dc62fe)
**Severity**: Compilation Error
### Description
An `always_comb` block containing a zero-iteration for loop (`for (int i = 0; i < 0; i++);`) causes CIRCT to fail during the arcilator lowering phase. The `llhd.process` operation generated from the `always_comb` block cannot be legalized, as the LowerProcessesPass fails to convert it to `llhd.CombinationalOp`, leaving it marked as illegal when ConvertToArcs runs.
### Minimal Reproducible Example
```systemverilog
module top(output logic [3:0] out);
always_comb begin out = 4'b0;
for (int i = 0; i < 0; i++);
end endmodule
```
### Reproduction Steps
```bash
circt-verilog --ir-hw minimal_testcase.sv | arcilator
```
### Actual Output
```
circt-verilog.mlir:7:5: error: failed to legalize operation 'llhd.process' that was explicitly marked illegal: "llhd.process"() ({...}) : () -> ()
llhd.process {
^
circt-verilog.mlir:7:5: note: see current operation:
"llhd.process"() ({
"cf.br"(%2)[^bb2] : (i32) -> ()
^bb1: // pred: ^bb4
"cf.br"(%2)[^bb2] : (i32) -> ()
^bb2(%4: i32): // 3 preds: ^bb0, ^bb1, ^bb3
%5 = "comb.icmp"(%4, %1) <{predicate = 2 : i64}> : (i32, i32) -> i1
"cf.cond_br"(%5)[^bb3, ^bb4] <{operandSegmentSizes = array}> : (i1) -> ()
^bb3: // pred: ^bb2
%6 = "comb.add"(%4, %0) : (i32, i32) -> i32
"cf.br"(%6)[^bb2] : (i32) -> ()
^bb4: // pred: ^bb2
"llhd.wait"(%3)[^bb1] <{operandSegmentSizes = array}> : (i4) -> ()
}) : () -> ()
circt-verilog.mlir:1:1: error: conversion to arcs failed
module {
^
```
### Expected Output
The code should compile without error. The zero-iteration for loop is valid SystemVerilog and should not cause compilation failure.
### Cross-Tool Comparison
- **verilator**: PASS (exit_code 0, no errors)
- **iverilog**: PASS (exit_code 0, non-fatal warning only)
- **circt**: FAILS (trigger_tool)
### Root Cause (Preliminary)
The `always_comb` block is correctly lowered to `llhd::ProcessOp` by MooreToCore conversion. However, LowerProcessesPass.matchControlFlow() returns `false` for the control flow graph created by the zero-iteration for loop (where block bb2 has 3 predecessors), causing the process to remain as `llhd::ProcessOp`. When arcilator runs ConvertToArcsPass, the entire LLHD dialect is marked illegal, but only `llhd::CombinationalOp` and `llhd::YieldOp` have conversion patterns registered. The unhandled `llhd::ProcessOp` fails with "explicitly marked illegal" error.
The crash occurs at ConvertToArcs.cpp:585 where `target.addIllegalDialect()` marks the entire LLHD dialect as illegal.
### Standard Compliance Assessment
- **Classification**: tool_bug
- **IEEE References**: IEEE 1800-2017 Section 9.2.2.2 (always_comb procedure)
- **Justification**: The SystemVerilog code `always_comb begin ... for (int i = 0; i < 0; i++); ... end` is syntactically and semantically valid per IEEE 1800. The zero-iteration for loop is legal SV behavior. CIRCT should correctly process this construct without failing. The failure is in the compiler's handling of the generated IR, not in the input code itself.
Assisted-by: Claude Code:claude-opus-4-6
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the failure with `circt-verilog --ir-hw minimal_testcase.sv | arcilator`, then inspect `LowerProcessesPass.matchControlFlow()` and `ConvertToArcs.cpp` around line 585. Determine why this zero-iteration loop leaves an `llhd.process` unconverted; done means the supplied SystemVerilog example compiles successfully through arcilator without the legalization error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100