llvm / llvm/circt

[Calyx] Segfault in SCFToCalyx when func.call exists inside loop body

Open
#9,529 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
2.2k
Forks
524
Avg merge
3d 2h
Merged PRs (30d)
46

Description

## Description

`lower-scf-to-calyx` pass crashes with a segmentation fault when processing a function containing `func.call` operations inside a loop body. The crash occurs in `BuildControl::buildCFGControl` when constructing `mlir::SuccessorRange` with an invalid Block pointer.

When an `affine.for` loop containing `func.call` is lowered through `--lower-affine` and `--scf-for-to-while`, the resulting SCF while loop triggers a null pointer access during Calyx control flow construction.

**Crash Type**: Segmentation Fault (SIGSEGV)
**Dialect**: Calyx
**Failing Pass**: `lower-scf-to-calyx` (SCFToCalyx)

## Steps to Reproduce

1. Save the test case below as `test.mlir`
2. Run:
```bash
mlir-opt --lower-affine --scf-for-to-while test.mlir | \
circt-opt --pass-pipeline='builtin.module(lower-scf-to-calyx{top-level-function=f})'
```

## Test Case

```mlir
// Minimal test case: func.call inside affine.for triggers SCFToCalyx segfault

func.func private @ext()

func.func @f() {
affine.for %i = 0 to 1 {
func.call @ext() : () -> ()
}
return
}
```

## Error Output

```
PLEASE submit a bug report to https://github.com/llvm/circt and include the crash backtrace.
Stack dump:
0. Program arguments: /opt/firtool-1.139.0/bin/circt-opt --pass-pipeline=builtin.module(lower-scf-to-calyx{top-level-function=f})
#0 0x00007fc013a018a8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/firtool-1.139.0/bin/../lib/libLLVMSupport.so+0x2008a8)
#1 0x00007fc0139ff2f5 llvm::sys::RunSignalHandlers() (/opt/firtool-1.139.0/bin/../lib/libLLVMSupport.so+0x1fe2f5)
#2 0x00007fc013a02631 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
#3 0x00007fc01350f330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
#4 0x00007fc013c4a4f3 mlir::SuccessorRange::SuccessorRange(mlir::Block*) (/opt/firtool-1.139.0/bin/../lib/libMLIRIR.so+0x8f4f3)
#5 0x00007fc0199004f7 circt::scftocalyx::BuildControl::buildCFGControl(llvm::DenseSet>, mlir::PatternRewriter&, mlir::Block*, mlir::Block*, mlir::Block*) const (/opt/firtool-1.139.0/bin/../lib/libCIRCTSCFToCalyx.so+0x644f7)
#6 0x00007fc019900256 circt::scftocalyx::BuildControl::partiallyLowerFuncToComp(mlir::func::FuncOp, mlir::PatternRewriter&) const (/opt/firtool-1.139.0/bin/../lib/libCIRCTSCFToCalyx.so+0x64256)
#7 0x00007fc0187e298d circt::calyx::FuncOpPartialLoweringPattern::partiallyLower(mlir::func::FuncOp, mlir::PatternRewriter&) const (/opt/firtool-1.139.0/bin/../lib/libCIRCTCalyxTransforms.so+0x1c98d)
#8 0x00007fc0198f5047 circt::calyx::PartialLoweringPattern::matchAndRewrite(mlir::func::FuncOp, mlir::PatternRewriter&) const (/opt/firtool-1.139.0/bin/../lib/libCIRCTSCFToCalyx.so+0x59047)
#9 0x00007fc015f6b8ed void llvm::function_ref::callback_fn, llvm::function_ref, llvm::function_ref)::$_0>(long) PatternApplicator.cpp:0:0
#10 0x00007fc015f68774 mlir::PatternApplicator::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&, llvm::function_ref, llvm::function_ref, llvm::function_ref) (/opt/firtool-1.139.0/bin/../lib/libMLIRRewrite.so+0x7774)
#11 0x00007fc015fc04a7 (anonymous namespace)::GreedyPatternRewriteDriver::processWorklist() GreedyPatternRewriteDriver.cpp:0:0
#12 0x00007fc015fbdfd9 mlir::applyPatternsGreedily(mlir::Region&, mlir::FrozenRewritePatternSet const&, mlir::GreedyRewriteConfig, bool*) (/opt/firtool-1.139.0/bin/../lib/libMLIRTransformUtils.so+0x46fd9)
#13 0x00007fc0198ef341 circt::(anonymous namespace)::SCFToCalyxPass::runOnOperation() SCFToCalyx.cpp:0:0
#14 0x00007fc015d5b2a5 mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) (/opt/firtool-1.139.0/bin/../lib/libMLIRPass.so+0x172a5)
#15 0x00007fc015d5e7a9 mlir::PassManager::run(mlir::Operation*) (/opt/firtool-1.139.0/bin/../lib/libMLIRPass.so+0x1a7a9)
#16 0x00007fc0196b906a performActions(llvm::raw_ostream&, std::shared_ptr const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) MlirOptMain.cpp:0:0
#17 0x00007fc0196b81be llvm::LogicalResult llvm::function_ref>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>::callback_fn>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::unique_ptr>, llvm::MemoryBufferRef const&, llvm::raw_ostream&) MlirOptMain.cpp:0:0
#18 0x00007fc013b90257 mlir::splitAndProcessBuffer(std::unique_ptr>, llvm::function_ref>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) (/opt/firtool-1.139.0/bin/../lib/libMLIRSupport.so+0x27257)
#19 0x00007fc0196ae4e8 mlir::MlirOptMain(llvm::raw_ostream&, std::unique_ptr>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) (/opt/firtool-1.139.0/bin/../lib/libMLIROptLib.so+0xa4e8)
#20 0x00007fc0196ae7d6 mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) (/opt/firtool-1.139.0/bin/../lib/libMLIROptLib.so+0xa7d6)
#21 0x00007fc0196aea0d mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) (/opt/firtool-1.139.0/bin/../lib/libMLIROptLib.so+0xaa0d)
#22 0x0000560f2bbc5e10 main (/opt/firtool-1.139.0/bin/circt-opt+0xde10)
#23 0x00007fc0134f41ca __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#24 0x00007fc0134f428b call_init ./csu/../csu/libc-start.c:128:20
#25 0x00007fc0134f428b __libc_start_main ./csu/../csu/libc-start.c:347:5
#26 0x0000560f2bbc56a5 _start (/opt/firtool-1.139.0/bin/circt-opt+0xd6a5)
```

## Root Cause Analysis

**Cause**: SCFToCalyx does not support `func.call` operations inside loop bodies

**Mechanism**: `buildCFGControl` encounters `func.call` which introduces unexpected CFG edges or Block successor structure, leading to null pointer access in `SuccessorRange` constructor.

## Environment

- **CIRCT Version**: firtool-1.139.0
- **OS**: Linux
- **Architecture**: x86_64

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with SCFToCalyx.cpp, especially BuildControl::buildCFGControl and the SCFToCalyx pass entry point. Reproduce the crash using the supplied test.mlir and the documented mlir-opt/circt-opt pipeline, then trace how func.call inside the loop reaches an invalid block. Done means the same input no longer segfaults and the regression is covered by an appropriate test.

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
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.