llvm / circt

Circuit IR Compilers and Tools
https://circt.org
Other
1.63k stars 283 forks source link

[ExportVerilog] Assertion Firing for icmp -> sampled #4485

Closed seldridge closed 1 year ago

seldridge commented 1 year ago

I stumbled across the following assertion failure due to prepare for emission not generating a wire for an icmp operation.

Consider:

module {
  hw.module @Top() {
    %c0_i4 = hw.constant 0 : i4
    %v = sv.reg  {sv.namehint = "value"} : !hw.inout<i4>
    %0 = sv.read_inout %v : !hw.inout<i4>
    %1 = comb.icmp eq %0, %c0_i4 {sv.namehint = "foo"} : i4
    %2 = sv.system.sampled %1 : i1
    hw.output
  }
}

Compiling this with circt-opt Tmp.mlir -export-verilog produces a nice crash:

# circt-opt Tmp.mlir -export-verilog
Assertion failed: ((!isExpr || isExpressionEmittedInline(&op, moduleEmitter.state.options)) && "If 'op' is a verilog expression, the expression must be inlinable. " "Otherwise, it is a bug of PrepareForEmission"), function collectNames, file ExportVerilog.cpp, line 2895.
PLEASE submit a bug report to https://github.com/llvm/circt and include the crash backtrace.
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  circt-opt                0x0000000102eb930d llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 61
1  circt-opt                0x0000000102eb988b PrintStackTraceSignalHandler(void*) + 27
2  circt-opt                0x0000000102eb7696 llvm::sys::RunSignalHandlers() + 134
3  circt-opt                0x0000000102ebb5bf SignalHandler(int) + 223
4  libsystem_platform.dylib 0x00007ff81f38fdfd _sigtramp + 29
5  circt-opt                0x0000000104f1b3cd std::__1::__optional_storage_base<mlir::RegisteredOperationName, false>::__optional_storage_base<mlir::RegisteredOperationName>(std::__1::in_place_t, mlir::RegisteredOperationName&&) + 29
6  libsystem_c.dylib        0x00007ff81f2c5d24 abort + 123
7  libsystem_c.dylib        0x00007ff81f2c50cb err + 0
8  circt-opt                0x0000000103213bb3 (anonymous namespace)::NameCollector::collectNames(mlir::Block&) + 947
9  circt-opt                0x0000000103213576 (anonymous namespace)::StmtEmitter::collectNamesAndCalculateDeclarationWidths(mlir::Block&) + 54
10 circt-opt                0x0000000103213483 (anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31::operator()() const + 115
11 circt-opt                0x0000000103213405 decltype(static_cast<(anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31>(fp)()) std::__1::__invoke<(anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31>((anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31&&) + 21
12 circt-opt                0x000000010321332d std::__1::invoke_result<(anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31>::type std::__1::invoke<(anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31>((anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31&&) + 29
13 circt-opt                0x00000001032132b6 auto circt::pretty::TokenStream<circt::pretty::PrettyPrinter>::scopedBox<circt::pretty::PP, (anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31>(circt::pretty::PP&&, (anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&)::$_31&&, circt::pretty::Token) + 102
14 circt-opt                0x0000000103203cc4 (anonymous namespace)::StmtEmitter::emitStatementBlock(mlir::Block&) + 116
15 circt-opt                0x00000001032027b1 (anonymous namespace)::ModuleEmitter::emitHWModule(circt::hw::HWModuleOp) + 2897
16 circt-opt                0x0000000103201c14 auto emitOperation((anonymous namespace)::VerilogEmitterState&, mlir::Operation*)::$_101::operator()<circt::hw::HWModuleOp>(circt::hw::HWModuleOp) const + 68
17 circt-opt                0x000000010320186b llvm::TypeSwitch<mlir::Operation*, void>& llvm::TypeSwitch<mlir::Operation*, void>::Case<circt::hw::HWModuleOp, emitOperation((anonymous namespace)::VerilogEmitterState&, mlir::Operation*)::$_101>(emitOperation((anonymous namespace)::VerilogEmitterState&, mlir::Operation*)::$_101&&) + 107
18 circt-opt                0x00000001031fa415 emitOperation((anonymous namespace)::VerilogEmitterState&, mlir::Operation*) + 53
19 circt-opt                0x0000000103277586 circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46::operator()(circt::ExportVerilog::StringOrOpToEmit&) const + 278
20 circt-opt                0x0000000103277380 auto void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)::operator()<circt::ExportVerilog::StringOrOpToEmit&>(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&) const + 48
21 circt-opt                0x0000000103279259 mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()::operator()() const + 169
22 circt-opt                0x00000001032791a5 decltype(static_cast<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>>(fp)()) std::__1::__invoke<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()&>(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&) + 21
23 circt-opt                0x000000010327915d void std::__1::__invoke_void_return_wrapper<void, true>::__call<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()&>(mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()&) + 29
24 circt-opt                0x000000010327912d std::__1::__function::__alloc_func<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(), std::__1::allocator<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()>, void ()>::operator()() + 29
25 circt-opt                0x0000000103278179 std::__1::__function::__func<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(), std::__1::allocator<mlir::LogicalResult mlir::failableParallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, void mlir::parallelForEach<std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'(std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>&&)>(mlir::MLIRContext*, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, std::__1::__wrap_iter<circt::ExportVerilog::StringOrOpToEmit*>, circt::ExportVerilog::SharedEmitterState::emitOps(std::__1::vector<circt::ExportVerilog::StringOrOpToEmit, std::__1::allocator<circt::ExportVerilog::StringOrOpToEmit>>&, llvm::raw_ostream&, bool)::$_46&&)::'lambda'()>, void ()>::operator()() + 25
26 circt-opt                0x0000000104fea852 std::__1::__function::__value_func<void ()>::operator()() const + 50
27 circt-opt                0x0000000104fea815 std::__1::function<void ()>::operator()() const + 21
28 circt-opt                0x0000000104fea7dd llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()::operator()() const + 29
29 circt-opt                0x0000000104fea7a5 decltype(static_cast<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()&>(fp)()) std::__1::__invoke<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()&>(llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()&) + 21
30 circt-opt                0x0000000104fea75d void std::__1::__invoke_void_return_wrapper<void, true>::__call<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()&>(llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()&) + 29
31 circt-opt                0x0000000104fea72d std::__1::__function::__alloc_func<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'(), std::__1::allocator<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()>, void ()>::operator()() + 29
32 circt-opt                0x0000000104fe9519 std::__1::__function::__func<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'(), std::__1::allocator<llvm::ThreadPool::createTaskAndFuture(std::__1::function<void ()>)::'lambda'()>, void ()>::operator()() + 25
33 circt-opt                0x0000000104fea852 std::__1::__function::__value_func<void ()>::operator()() const + 50
34 circt-opt                0x0000000104fea815 std::__1::function<void ()>::operator()() const + 21
35 circt-opt                0x0000000102e0cc92 llvm::ThreadPool::processTasks(llvm::ThreadPoolTaskGroup*) + 546
36 circt-opt                0x0000000102e10373 llvm::ThreadPool::grow(int)::$_0::operator()() const + 51
37 circt-opt                0x0000000102e10305 void llvm::thread::Apply<llvm::ThreadPool::grow(int)::$_0>(std::__1::tuple<llvm::ThreadPool::grow(int)::$_0>&, std::__1::integer_sequence<unsigned long>) + 37
38 circt-opt                0x0000000102e102c3 void llvm::thread::GenericThreadProxy<std::__1::tuple<llvm::ThreadPool::grow(int)::$_0>>(void*) + 51
39 circt-opt                0x0000000102e0fff5 void* llvm::thread::ThreadProxy<std::__1::tuple<llvm::ThreadPool::grow(int)::$_0>>(void*) + 21
40 libsystem_pthread.dylib  0x00007ff81f37a4e1 _pthread_start + 125
41 libsystem_pthread.dylib  0x00007ff81f375f6b thread_start + 15
seldridge commented 1 year ago

Some observations after thinking about this:

  1. This sv.system.sampled op is coming from an assertion that gets extracted.
  2. The sv.system.sampled op does not have the trait Pure which will block it from getting CSE'd or removed meaning that it hangs around after SVExtractTestCode runs. (It probably should have this?)
  3. PrepareForEmission still needs to do the right thing here, even if sv.system.sampled isn't removed, i.e., when optimizations are disenabled. Currently, prepare is removing the sv.system.sampled op as it is inlinable and has no uses. The icmp is then left in a situation where prepare should have created a wire for it, but no such wire was created. ExportVerilog then asserts.
uenoku commented 1 year ago

Thanks for a small reproducer! That's exactly what I was thinking of: https://github.com/llvm/circt/pull/4487, https://github.com/llvm/circt/pull/4486

I agree that sampled needs to be pure, and isExpressionEmittedInline should return true for dead expressions.