mirror of https://github.com/llvm/circt.git
620 lines
28 KiB
MLIR
620 lines
28 KiB
MLIR
// RUN: circt-opt -pass-pipeline='builtin.module(firrtl-imdeadcodeelim)' --split-input-file -verify-diagnostics %s | FileCheck %s
|
|
firrtl.circuit "top" {
|
|
// In `dead_module`, %source is connected to %dest through several dead operations such as
|
|
// node, wire, reg or rgereset. %dest is also dead at any instantiation, so check that
|
|
// all operations are removed by IMDeadCodeElim pass.
|
|
// CHECK-NOT: @dead_module
|
|
firrtl.module private @dead_module(in %source: !firrtl.uint<1>, out %dest: !firrtl.uint<1>,
|
|
in %clock:!firrtl.clock, in %reset:!firrtl.uint<1>) {
|
|
%dead_node = firrtl.node %source: !firrtl.uint<1>
|
|
|
|
%dead_wire = firrtl.wire : !firrtl.uint<1>
|
|
firrtl.matchingconnect %dead_wire, %dead_node : !firrtl.uint<1>
|
|
|
|
%dead_reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<1>
|
|
firrtl.matchingconnect %dead_reg, %dead_wire : !firrtl.uint<1>
|
|
|
|
%dead_reg_reset = firrtl.regreset %clock, %reset, %dead_reg : !firrtl.clock, !firrtl.uint<1>, !firrtl.uint<1>, !firrtl.uint<1>
|
|
firrtl.matchingconnect %dead_reg_reset, %dead_reg : !firrtl.uint<1>
|
|
|
|
%not = firrtl.not %dead_reg_reset : (!firrtl.uint<1>) -> !firrtl.uint<1>
|
|
firrtl.matchingconnect %dest, %not : !firrtl.uint<1>
|
|
}
|
|
|
|
// `%dontTouch` port has a symbol so it shouldn't be removed. `%sym_wire` also has a
|
|
// symbol so check that `%source` is preserved too.
|
|
// CHECK-LABEL: firrtl.module private @dontTouch(in %dontTouch: !firrtl.uint<1> sym @sym, in %source: !firrtl.uint<1>) {
|
|
firrtl.module private @dontTouch(in %dontTouch: !firrtl.uint<1> sym @sym, in %source: !firrtl.uint<1>, in %dead: !firrtl.uint<1>) {
|
|
// CHECK-NEXT: %sym_wire = firrtl.wire sym @sym2 : !firrtl.uint<1>
|
|
// CHECK-NEXT: firrtl.matchingconnect %sym_wire, %source : !firrtl.uint<1>
|
|
// CHECK-NEXT: }
|
|
%sym_wire = firrtl.wire sym @sym2 : !firrtl.uint<1>
|
|
firrtl.matchingconnect %sym_wire, %source : !firrtl.uint<1>
|
|
|
|
}
|
|
|
|
// CHECK-LABEL: firrtl.module private @mem(in %source: !firrtl.uint<1>) {
|
|
firrtl.module private @mem(in %source: !firrtl.uint<1>) {
|
|
// CHECK-NEXT: %ReadMemory_read0 = firrtl.mem Undefined {annotations = [{class = "firrtl.transforms.DontTouchAnnotation"}], depth = 16 : i64, name = "ReadMemory", portNames = ["read0"], readLatency = 1 : i32, writeLatency = 1 : i32} : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: sint<8>>
|
|
%mem = firrtl.mem Undefined {annotations = [{class = "firrtl.transforms.DontTouchAnnotation"}], depth = 16 : i64, name = "ReadMemory", portNames = ["read0"], readLatency = 1 : i32, writeLatency = 1 : i32} : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: sint<8>>
|
|
// CHECK-NEXT: %0 = firrtl.subfield %ReadMemory_read0[addr] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: sint<8>>
|
|
// CHECK-NEXT: firrtl.connect %0, %source : !firrtl.uint<4>, !firrtl.uint<1>
|
|
// CHECK-NEXT: }
|
|
%0 = firrtl.subfield %mem[addr] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: sint<8>>
|
|
firrtl.connect %0, %source : !firrtl.uint<4>, !firrtl.uint<1>
|
|
}
|
|
|
|
// Ports of public modules should not be modified.
|
|
// CHECK-LABEL: firrtl.module @top(in %source: !firrtl.uint<1>, out %dest: !firrtl.uint<1>, in %clock: !firrtl.clock, in %reset: !firrtl.uint<1>) {
|
|
firrtl.module @top(in %source: !firrtl.uint<1>, out %dest: !firrtl.uint<1>,
|
|
in %clock:!firrtl.clock, in %reset:!firrtl.uint<1>) {
|
|
// CHECK-NEXT: %tmp = firrtl.node %source
|
|
// CHECK-NEXT: firrtl.matchingconnect %dest, %tmp
|
|
%tmp = firrtl.node %source: !firrtl.uint<1>
|
|
firrtl.matchingconnect %dest, %tmp : !firrtl.uint<1>
|
|
|
|
// CHECK-NOT: @dead_module
|
|
%source1, %dest1, %clock1, %reset1 = firrtl.instance dead_module @dead_module(in source: !firrtl.uint<1>, out dest: !firrtl.uint<1>, in clock:!firrtl.clock, in reset:!firrtl.uint<1>)
|
|
firrtl.matchingconnect %source1, %source : !firrtl.uint<1>
|
|
firrtl.matchingconnect %clock1, %clock : !firrtl.clock
|
|
firrtl.matchingconnect %reset1, %reset : !firrtl.uint<1>
|
|
|
|
// Check that ports with dontTouch are not removed.
|
|
// CHECK-NEXT: %testDontTouch_dontTouch, %testDontTouch_source = firrtl.instance testDontTouch @dontTouch(in dontTouch: !firrtl.uint<1>, in source: !firrtl.uint<1>)
|
|
// CHECK-NEXT: firrtl.matchingconnect %testDontTouch_dontTouch, %source
|
|
// CHECK-NEXT: firrtl.matchingconnect %testDontTouch_source, %source
|
|
%testDontTouch_dontTouch, %testDontTouch_source, %dead = firrtl.instance testDontTouch @dontTouch(in dontTouch: !firrtl.uint<1>, in source: !firrtl.uint<1>, in dead:!firrtl.uint<1>)
|
|
firrtl.matchingconnect %testDontTouch_dontTouch, %source : !firrtl.uint<1>
|
|
firrtl.matchingconnect %testDontTouch_source, %source : !firrtl.uint<1>
|
|
firrtl.matchingconnect %dead, %source : !firrtl.uint<1>
|
|
|
|
// CHECK-NEXT: %mem_source = firrtl.instance mem @mem(in source: !firrtl.uint<1>)
|
|
// CHECK-NEXT: firrtl.matchingconnect %mem_source, %source : !firrtl.uint<1>
|
|
%mem_source = firrtl.instance mem @mem(in source: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %mem_source, %source : !firrtl.uint<1>
|
|
// CHECK-NEXT: }
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// Check that it's possible to analyze complex dependency across different modules.
|
|
firrtl.circuit "top" {
|
|
// CHECK-NOT: @Child1
|
|
firrtl.module private @Child1(in %input: !firrtl.uint<1>, out %output: !firrtl.uint<1>) {
|
|
firrtl.matchingconnect %output, %input : !firrtl.uint<1>
|
|
}
|
|
// CHECK-NOT: @Child2
|
|
firrtl.module private @Child2(in %input: !firrtl.uint<1>, in %clock: !firrtl.clock, out %output: !firrtl.uint<1>) {
|
|
%r = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<1>
|
|
firrtl.matchingconnect %r, %input : !firrtl.uint<1>
|
|
firrtl.matchingconnect %output, %r : !firrtl.uint<1>
|
|
}
|
|
|
|
// CHECK-LABEL: firrtl.module @top(in %clock: !firrtl.clock, in %input: !firrtl.uint<1>) {
|
|
// CHECK-NEXT: }
|
|
// expected-warning @below {{module `top` is empty but cannot be removed because the module is public}}
|
|
firrtl.module @top(in %clock: !firrtl.clock, in %input: !firrtl.uint<1>) {
|
|
%tile_input, %tile_output = firrtl.instance tile @Child1(in input: !firrtl.uint<1>, out output: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %tile_input, %input : !firrtl.uint<1>
|
|
%named = firrtl.node %tile_output : !firrtl.uint<1>
|
|
%bar_input, %bar_clock, %bar_output = firrtl.instance bar @Child2(in input: !firrtl.uint<1>, in clock: !firrtl.clock, out output: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %bar_clock, %clock : !firrtl.clock
|
|
firrtl.matchingconnect %bar_input, %named : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: "UnusedOutput"
|
|
firrtl.circuit "UnusedOutput" {
|
|
// CHECK: firrtl.module {{.+}}@SingleDriver
|
|
// CHECK-NOT: out %c
|
|
firrtl.module private @SingleDriver(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>, out %c: !firrtl.uint<1>) {
|
|
// CHECK-NEXT: %[[c_wire:.+]] = firrtl.wire
|
|
// CHECK-NEXT: firrtl.matchingconnect %b, %[[c_wire]]
|
|
firrtl.matchingconnect %b, %c : !firrtl.uint<1>
|
|
// CHECK-NEXT: %[[not_a:.+]] = firrtl.not %a
|
|
%0 = firrtl.not %a : (!firrtl.uint<1>) -> !firrtl.uint<1>
|
|
// CHECK-NEXT: firrtl.matchingconnect %[[c_wire]], %[[not_a]]
|
|
firrtl.matchingconnect %c, %0 : !firrtl.uint<1>
|
|
}
|
|
// CHECK-LABEL: @UnusedOutput
|
|
firrtl.module @UnusedOutput(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) {
|
|
// CHECK: %singleDriver_a, %singleDriver_b = firrtl.instance singleDriver
|
|
%singleDriver_a, %singleDriver_b, %singleDriver_c = firrtl.instance singleDriver @SingleDriver(in a: !firrtl.uint<1>, out b: !firrtl.uint<1>, out c: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %singleDriver_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %b, %singleDriver_b : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// Ensure that the "output_file" attribute isn't destroyed by IMDeadCodeElim.
|
|
// This matters for interactions between Grand Central (which sets these) and
|
|
// IMDeadCodeElim which may clone modules with stripped ports.
|
|
//
|
|
// CHECK-LABEL: "PreserveOutputFile"
|
|
firrtl.circuit "PreserveOutputFile" {
|
|
// CHECK-NEXT: firrtl.module {{.+}}@Sub
|
|
// CHECK-NOT: %a
|
|
// CHECK-SAME: output_file
|
|
// expected-warning @+1{{module `Sub` is empty but cannot be removed because the module has ports "b" are referenced by name or dontTouched}}
|
|
firrtl.module private @Sub(in %a: !firrtl.uint<1>, in %b: !firrtl.uint<1> sym @sym) attributes {output_file = #hw.output_file<"hello">} {}
|
|
// CHECK: firrtl.module @PreserveOutputFile
|
|
firrtl.module @PreserveOutputFile() {
|
|
// CHECK-NEXT: firrtl.instance sub
|
|
// CHECK-SAME: output_file
|
|
firrtl.instance sub {output_file = #hw.output_file<"hello">} @Sub(in a: !firrtl.uint<1>, in b: !firrtl.uint<1>)
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: "DeleteEmptyModule"
|
|
firrtl.circuit "DeleteEmptyModule" {
|
|
// CHECK: firrtl.module private @empty
|
|
// expected-warning @+1{{module `empty` is empty but cannot be removed because the module has annotations [{class = "foo"}]}}
|
|
firrtl.module private @empty() attributes {annotations = [{class = "foo"}]} {}
|
|
// CHECK-NOT: firrtl.module private @Sub
|
|
firrtl.module private @Sub(in %a: !firrtl.uint<1>) {}
|
|
// CHECK: firrtl.module @DeleteEmptyModule
|
|
firrtl.module @DeleteEmptyModule() {
|
|
// CHECK-NOT: firrtl.instance sub1
|
|
firrtl.instance sub1 sym @Foo @Sub(in a: !firrtl.uint<1>)
|
|
// CHECK-NOT: sub2
|
|
firrtl.instance sub2 @Sub(in a: !firrtl.uint<1>)
|
|
// CHECK: empty
|
|
firrtl.instance empty @empty()
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: "ForwardConstant"
|
|
firrtl.circuit "ForwardConstant" {
|
|
// CHECK-NOT: Zero
|
|
firrtl.module private @Zero(out %zero: !firrtl.uint<1>) {
|
|
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
|
firrtl.matchingconnect %zero, %c0_ui1 : !firrtl.uint<1>
|
|
}
|
|
// CHECK-LABEL: @ForwardConstant
|
|
firrtl.module @ForwardConstant(out %zero: !firrtl.uint<1>) {
|
|
// CHECK: %c0_ui1 = firrtl.constant 0
|
|
%sub_zero = firrtl.instance sub @Zero(out zero: !firrtl.uint<1>)
|
|
// CHECK-NEXT: firrtl.matchingconnect %zero, %c0_ui1
|
|
firrtl.matchingconnect %zero, %sub_zero : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test handling of ref ports and ops.
|
|
|
|
// CHECK-LABEL: "RefPorts"
|
|
firrtl.circuit "RefPorts" {
|
|
// CHECK-NOT: @dead_ref_send
|
|
firrtl.module private @dead_ref_send(in %source: !firrtl.uint<1>, out %dest: !firrtl.probe<uint<1>>) {
|
|
%ref = firrtl.ref.send %source: !firrtl.uint<1>
|
|
firrtl.ref.define %dest, %ref : !firrtl.probe<uint<1>>
|
|
}
|
|
|
|
// CHECK-LABEL: @dead_ref_port
|
|
// CHECK-NOT: firrtl.ref
|
|
firrtl.module private @dead_ref_port(in %source: !firrtl.uint<1>, out %dest: !firrtl.uint<1>, out %ref_dest: !firrtl.probe<uint<1>>) {
|
|
%ref_not = firrtl.ref.send %source: !firrtl.uint<1>
|
|
firrtl.ref.define %ref_dest, %ref_not : !firrtl.probe<uint<1>>
|
|
firrtl.matchingconnect %dest, %source : !firrtl.uint<1>
|
|
}
|
|
|
|
// CHECK: @live_ref
|
|
firrtl.module private @live_ref(in %source: !firrtl.uint<1>, out %dest: !firrtl.probe<uint<1>>) {
|
|
%ref_source = firrtl.ref.send %source: !firrtl.uint<1>
|
|
firrtl.ref.define %dest, %ref_source : !firrtl.probe<uint<1>>
|
|
}
|
|
|
|
// CHECK-LABEL: @RefPorts
|
|
firrtl.module @RefPorts(in %source : !firrtl.uint<1>, out %dest : !firrtl.uint<1>) {
|
|
// Delete send's that aren't resolved, and check deletion of modules with ref ops + ports.
|
|
// CHECK-NOT: @dead_ref_send
|
|
%source1, %dest1 = firrtl.instance dead_ref_send @dead_ref_send(in source: !firrtl.uint<1>, out dest: !firrtl.probe<uint<1>>)
|
|
firrtl.matchingconnect %source1, %source : !firrtl.uint<1>
|
|
|
|
// Check that an unused resolve doesn't keep send alive, and test ref port removal.
|
|
// CHECK: @dead_ref_port
|
|
// CHECK-NOT: firrtl.ref
|
|
%source2, %dest2, %ref_dest2 = firrtl.instance dead_ref_port @dead_ref_port(in source: !firrtl.uint<1>, out dest: !firrtl.uint<1>, out ref_dest: !firrtl.probe<uint<1>>)
|
|
firrtl.matchingconnect %source2, %source : !firrtl.uint<1>
|
|
%unused = firrtl.ref.resolve %ref_dest2 : !firrtl.probe<uint<1>>
|
|
firrtl.matchingconnect %dest, %dest2 : !firrtl.uint<1>
|
|
|
|
// Check not deleted if live.
|
|
// CHECK: @live_ref
|
|
%source3, %dest3 = firrtl.instance live_ref @live_ref(in source: !firrtl.uint<1>, out dest: !firrtl.probe<uint<1>>)
|
|
firrtl.matchingconnect %source3, %source : !firrtl.uint<1>
|
|
// CHECK: firrtl.ref.resolve
|
|
%dest3_resolved = firrtl.ref.resolve %dest3 : !firrtl.probe<uint<1>>
|
|
firrtl.matchingconnect %dest, %dest3_resolved : !firrtl.uint<1>
|
|
|
|
// Check dead resolve is deleted.
|
|
// CHECK-NOT: dead_instance
|
|
%source4, %dest4 = firrtl.instance dead_instance @live_ref(in source: !firrtl.uint<1>, out dest: !firrtl.probe<uint<1>>)
|
|
firrtl.matchingconnect %source4, %source : !firrtl.uint<1>
|
|
// CHECK-NOT: firrtl.ref.resolve
|
|
%unused5 = firrtl.ref.resolve %dest4 : !firrtl.probe<uint<1>>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test the removal of memories in dead cycles
|
|
|
|
firrtl.circuit "MemoryInDeadCycle" {
|
|
// CHECK-LABEL: firrtl.module public @MemoryInDeadCycle
|
|
// expected-warning @below {{module `MemoryInDeadCycle` is empty but cannot be removed because the module is public}}
|
|
firrtl.module public @MemoryInDeadCycle(in %clock: !firrtl.clock, in %addr: !firrtl.uint<4>) {
|
|
|
|
// CHECK-NOT: firrtl.mem
|
|
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
|
%Memory_r = firrtl.mem Undefined
|
|
{
|
|
depth = 12 : i64,
|
|
name = "Memory",
|
|
portNames = ["read"],
|
|
readLatency = 0 : i32,
|
|
writeLatency = 1 : i32
|
|
} : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<42>>
|
|
|
|
%r_addr = firrtl.subfield %Memory_r[addr] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<42>>
|
|
firrtl.connect %r_addr, %addr : !firrtl.uint<4>, !firrtl.uint<4>
|
|
%r_en = firrtl.subfield %Memory_r[en] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<42>>
|
|
firrtl.connect %r_en, %c1_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
|
|
%r_clk = firrtl.subfield %Memory_r[clk] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<42>>
|
|
firrtl.connect %r_clk, %clock : !firrtl.clock, !firrtl.clock
|
|
|
|
// CHECK-NOT: firrtl.mem
|
|
%Memory_w = firrtl.mem Undefined
|
|
{
|
|
depth = 12 : i64,
|
|
name = "Memory",
|
|
portNames = ["w"],
|
|
readLatency = 0 : i32,
|
|
writeLatency = 1 : i32
|
|
} : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
|
|
%w_addr = firrtl.subfield %Memory_w[addr] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
firrtl.connect %w_addr, %addr : !firrtl.uint<4>, !firrtl.uint<4>
|
|
%w_en = firrtl.subfield %Memory_w[en] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
firrtl.connect %w_en, %c1_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
|
|
%w_clk = firrtl.subfield %Memory_w[clk] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
firrtl.connect %w_clk, %clock : !firrtl.clock, !firrtl.clock
|
|
%w_mask = firrtl.subfield %Memory_w[mask] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
firrtl.connect %w_mask, %c1_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
|
|
|
|
%w_data = firrtl.subfield %Memory_w[data] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data: uint<42>, mask: uint<1>>
|
|
%r_data = firrtl.subfield %Memory_r[data] : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<42>>
|
|
firrtl.connect %w_data, %r_data : !firrtl.uint<42>, !firrtl.uint<42>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// CHECK-LABEL: firrtl.circuit "DeadInputPort"
|
|
firrtl.circuit "DeadInputPort" {
|
|
// CHECK-NOT: firrtl.module private @Bar
|
|
firrtl.module private @Bar(in %a: !firrtl.uint<1>) {
|
|
}
|
|
|
|
// CHECK-LABEL: firrtl.module @DeadInputPort
|
|
firrtl.module @DeadInputPort(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) {
|
|
// CHECK-NEXT: %0 = firrtl.wire
|
|
// CHECK-NEXT: firrtl.matchingconnect %0, %a
|
|
// CHECK-NEXT: firrtl.matchingconnect %b, %0
|
|
%bar_a = firrtl.instance bar @Bar(in a: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %bar_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %b, %bar_a : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
firrtl.circuit "DeleteInstance" {
|
|
// CHECK-NOT: @InvalidValue
|
|
firrtl.module private @InvalidValue() {
|
|
%invalid_ui289 = firrtl.invalidvalue : !firrtl.uint<289>
|
|
}
|
|
firrtl.module private @SideEffect1(in %a: !firrtl.uint<1>, in %clock: !firrtl.clock) {
|
|
firrtl.printf %clock, %a, "foo" : !firrtl.clock, !firrtl.uint<1>
|
|
}
|
|
firrtl.module private @SideEffect2(in %a: !firrtl.uint<1>, in %clock: !firrtl.clock) {
|
|
%s1_a, %s1_clock = firrtl.instance s1 @SideEffect1(in a: !firrtl.uint<1>, in clock: !firrtl.clock)
|
|
firrtl.matchingconnect %s1_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %s1_clock, %clock : !firrtl.clock
|
|
}
|
|
firrtl.module private @PassThrough(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) {
|
|
firrtl.matchingconnect %b, %a : !firrtl.uint<1>
|
|
}
|
|
// CHECK-LABEL: DeleteInstance
|
|
firrtl.module @DeleteInstance(in %a: !firrtl.uint<1>, in %clock: !firrtl.clock, out %b: !firrtl.uint<1>) {
|
|
// CHECK-NOT: inv
|
|
firrtl.instance inv @InvalidValue()
|
|
// CHECK-NOT: p1
|
|
// CHECK: instance p2 @PassThrough
|
|
// CHECK-NEXT: instance s @SideEffect2
|
|
%p1_a, %p1_b = firrtl.instance p1 @PassThrough(in a: !firrtl.uint<1>, out b: !firrtl.uint<1>)
|
|
%p2_a, %p2_b = firrtl.instance p2 @PassThrough(in a: !firrtl.uint<1>, out b: !firrtl.uint<1>)
|
|
%s_a, %s_clock = firrtl.instance s @SideEffect2(in a: !firrtl.uint<1>, in clock: !firrtl.clock)
|
|
// CHECK-NEXT: firrtl.matchingconnect %s_a, %a : !firrtl.uint<1>
|
|
// CHECK-NEXT: firrtl.matchingconnect %s_clock, %clock : !firrtl.clock
|
|
// CHECK-NEXT: firrtl.matchingconnect %p2_a, %a : !firrtl.uint<1>
|
|
// CHECK-NEXT: firrtl.matchingconnect %b, %p2_b : !firrtl.uint<1>
|
|
firrtl.matchingconnect %s_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %s_clock, %clock : !firrtl.clock
|
|
firrtl.matchingconnect %p1_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %p2_a, %a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %b, %p2_b : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
firrtl.circuit "Top" {
|
|
// CHECK-NOT: @nla_1
|
|
// CHECK: @nla_2
|
|
hw.hierpath private @nla_1 [@Foo1::@dead, @EncodingModule]
|
|
hw.hierpath private @nla_2 [@Foo2::@live, @EncodingModule]
|
|
// CHECK-LABEL: private @EncodingModule
|
|
// CHECK-NOT: @nla_1
|
|
// CHECK-SAME: @nla_2
|
|
firrtl.module private @EncodingModule(in %in: !firrtl.uint<1>, out %a: !firrtl.uint<1> [{circt.nonlocal = @nla_1, class = "freechips.rocketchip.objectmodel.OMIRTracker", id = 0 : i64, type = "OMReferenceTarget"}, {circt.nonlocal = @nla_2, class = "freechips.rocketchip.objectmodel.OMIRTracker", id = 1 : i64, type = "OMReferenceTarget"}]) {
|
|
firrtl.matchingconnect %a, %in : !firrtl.uint<1>
|
|
}
|
|
// CHECK-NOT: @Foo1
|
|
firrtl.module private @Foo1(in %in: !firrtl.uint<1>) {
|
|
%c_in, %c_a = firrtl.instance c sym @dead @EncodingModule(in in: !firrtl.uint<1>, out a: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %c_in, %in : !firrtl.uint<1>
|
|
}
|
|
// CHECK-LABEL: @Foo2
|
|
firrtl.module private @Foo2(in %in: !firrtl.uint<1>, out %a: !firrtl.uint<1>) {
|
|
%c_in, %c_a = firrtl.instance c sym @live @EncodingModule(in in: !firrtl.uint<1>, out a: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %a, %c_a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %c_in, %in : !firrtl.uint<1>
|
|
}
|
|
// CHECK-LABEL: @Top
|
|
// CHECK-NOT: @Foo1
|
|
// CHECK-NOT: firrtl.matchingconnect %foo1_in, %in
|
|
// CHECK: @Foo2
|
|
firrtl.module @Top(in %in: !firrtl.uint<1>, out %a: !firrtl.uint<1>) {
|
|
%foo1_in = firrtl.instance foo1 @Foo1(in in: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %foo1_in, %in : !firrtl.uint<1>
|
|
%foo2_in, %foo2_a = firrtl.instance foo2 @Foo2(in in: !firrtl.uint<1>, out a: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %a, %foo2_a : !firrtl.uint<1>
|
|
firrtl.matchingconnect %foo2_in, %in : !firrtl.uint<1>
|
|
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
firrtl.circuit "Top" {
|
|
// CHECK: hw.hierpath private @nla_1
|
|
hw.hierpath private @nla_1 [@Top::@foo1, @Bar::@w]
|
|
// CHECK-NEXT: hw.hierpath private @nla_2
|
|
hw.hierpath private @nla_2 [@Top::@foo1, @Bar]
|
|
// CHECK-NEXT: sv.verbatim "foo" {some = [@nla_2]}
|
|
sv.verbatim "foo" {some = [@nla_2]}
|
|
// CHECK-LABEL: firrtl.module private @Bar
|
|
// CHECK: %in1{{.*}}sym @w
|
|
// CHECK-SAME: %in2
|
|
// CHECK-NOT: %in3
|
|
// expected-warning @+1 {{module `Bar` is empty but cannot be removed because the module has ports "in1", "in2" are referenced by name or dontTouched}}
|
|
firrtl.module private @Bar(in %in1 : !firrtl.uint<1> sym @w, in %in2: !firrtl.uint<1> [{class = "foo"}], in %in3: !firrtl.uint<1>) {}
|
|
// CHECK-LABEL: firrtl.module private @Baz
|
|
// expected-warning @+1 {{module `Baz` is empty but cannot be removed because an instance is referenced by nam}}
|
|
firrtl.module private @Baz() {}
|
|
|
|
// CHECK-LABEL: firrtl.module @Top
|
|
firrtl.module @Top(in %in: !firrtl.uint<1>) {
|
|
%c_in1, %c_in2, %c_in3 = firrtl.instance c sym @foo1 @Bar(in in1: !firrtl.uint<1>, in in2: !firrtl.uint<1>, in in3: !firrtl.uint<1>)
|
|
firrtl.matchingconnect %c_in1, %in : !firrtl.uint<1>
|
|
firrtl.matchingconnect %c_in2, %in : !firrtl.uint<1>
|
|
firrtl.matchingconnect %c_in3, %in : !firrtl.uint<1>
|
|
// CHECK: sv.verbatim "foo" {some = #hw.innerNameRef<@Top::@baz1>}
|
|
sv.verbatim "foo" {some = #hw.innerNameRef<@Top::@baz1>}
|
|
// Don't remove the instance if there is an unknown use of inner reference.
|
|
// CHECK: baz1
|
|
// expected-note @+1 {{these are instances with symbols}}
|
|
firrtl.instance baz1 sym @baz1 @Baz()
|
|
// Remove a dead instance otherwise.
|
|
// CHECK-NOT: baz2
|
|
firrtl.instance baz2 sym @baz2 @Baz()
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// This tests that dead modules do not keep dead instance output ports alive.
|
|
|
|
firrtl.circuit "Test" {
|
|
firrtl.extmodule @ExtModule(out out : !firrtl.uint<1>)
|
|
|
|
// %out is only used by a dead module. It should be removed from the port list.
|
|
// CHECK: firrtl.module private @Blah() {
|
|
firrtl.module private @Blah(out %out : !firrtl.uint<1>) {
|
|
%extmodule_out = firrtl.instance extmodule @ExtModule(out out : !firrtl.uint<1>)
|
|
firrtl.matchingconnect %out, %extmodule_out : !firrtl.uint<1>
|
|
}
|
|
firrtl.module @Test() attributes {convention = #firrtl<convention scalarized>} {
|
|
// CHECK: firrtl.instance blah interesting_name @Blah()
|
|
%blah_out = firrtl.instance blah interesting_name @Blah(out out : !firrtl.uint<1>)
|
|
}
|
|
// This module is dead (unreachable from the toplevel) so the module should be removed.
|
|
// CHECK-NOT: firrtl.module private @Other
|
|
firrtl.module private @Other(out %out : !firrtl.uint<1>) {
|
|
%blah_out = firrtl.instance blah interesting_name @Blah(out out : !firrtl.uint<1>)
|
|
firrtl.matchingconnect %out, %blah_out : !firrtl.uint<1>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test that empty classes and objects are kept alive.
|
|
|
|
firrtl.circuit "Test" {
|
|
// CHECK: firrtl.class private @Empty()
|
|
firrtl.class private @Empty() {}
|
|
|
|
// CHECK: firrtl.class private @UnusedAndEmpty()
|
|
firrtl.class private @UnusedAndEmpty() {}
|
|
|
|
// CHECK: firrtl.module @Test()
|
|
firrtl.module @Test() {
|
|
// CHECK: %obj = firrtl.object @Empty()
|
|
%obj = firrtl.object @Empty()
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test that instances of classes are kept alive.
|
|
|
|
firrtl.circuit "Test" {
|
|
// Both the input and the output of this class are ignored, but preserved by
|
|
// IMDCE.
|
|
// CHECK: firrtl.class private @Class(in %in: !firrtl.integer, out %out: !firrtl.integer)
|
|
firrtl.class private @Class(in %in: !firrtl.integer, out %out: !firrtl.integer) {
|
|
// CHECK: %0 = firrtl.integer 123
|
|
// CHECK: firrtl.propassign %out, %0 : !firrtl.integer
|
|
%0 = firrtl.integer 123
|
|
firrtl.propassign %out, %0 : !firrtl.integer
|
|
}
|
|
|
|
// The write to %o's "in" port is preserved by IMDCE, even though the input
|
|
// is unused by the class.
|
|
// CHECK: firrtl.module @Test() attributes {convention = #firrtl<convention scalarized>}
|
|
firrtl.module @Test() attributes {convention = #firrtl<convention scalarized>} {
|
|
// CHECK: %0 = firrtl.integer 456
|
|
// CHECK: %o = firrtl.object @Class(in in: !firrtl.integer, out out: !firrtl.integer)
|
|
// CHECK: %1 = firrtl.object.subfield %o[in] : !firrtl.class<@Class(in in: !firrtl.integer, out out: !firrtl.integer)>
|
|
// CHECK: firrtl.propassign %1, %0 : !firrtl.integer
|
|
%0 = firrtl.integer 456
|
|
%o = firrtl.object @Class(in in: !firrtl.integer, out out: !firrtl.integer)
|
|
%1 = firrtl.object.subfield %o[in] : !firrtl.class<@Class(in in: !firrtl.integer, out out: !firrtl.integer)>
|
|
firrtl.propassign %1, %0 : !firrtl.integer
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test that instances of extclasses are kept alive.
|
|
|
|
module {
|
|
firrtl.circuit "Test" {
|
|
// CHECK: firrtl.extclass private @Class(out out_str: !firrtl.string, in in_str: !firrtl.string)
|
|
firrtl.extclass private @Class(out out_str: !firrtl.string, in in_str: !firrtl.string)
|
|
|
|
// CHECK: firrtl.module @Test(out %out_str: !firrtl.string) attributes {convention = #firrtl<convention scalarized>}
|
|
firrtl.module @Test(out %out_str: !firrtl.string) attributes {convention = #firrtl<convention scalarized>} {
|
|
// CHECK: %0 = firrtl.string "whatever"
|
|
// CHECK: %obj = firrtl.object @Class(out out_str: !firrtl.string, in in_str: !firrtl.string)
|
|
// CHECK: %1 = firrtl.object.subfield %obj[out_str] : !firrtl.class<@Class(out out_str: !firrtl.string, in in_str: !firrtl.string)>
|
|
// CHECK: %2 = firrtl.object.subfield %obj[in_str] : !firrtl.class<@Class(out out_str: !firrtl.string, in in_str: !firrtl.string)>
|
|
// CHECK: firrtl.propassign %2, %0 : !firrtl.string
|
|
// CHECK: firrtl.propassign %out_str, %1 : !firrtl.string
|
|
%0 = firrtl.string "whatever"
|
|
%obj = firrtl.object @Class(out out_str: !firrtl.string, in in_str: !firrtl.string)
|
|
%1 = firrtl.object.subfield %obj[out_str] : !firrtl.class<@Class(out out_str: !firrtl.string, in in_str: !firrtl.string)>
|
|
%2 = firrtl.object.subfield %obj[in_str] : !firrtl.class<@Class(out out_str: !firrtl.string, in in_str: !firrtl.string)>
|
|
firrtl.propassign %2, %0 : !firrtl.string
|
|
firrtl.propassign %out_str, %1 : !firrtl.string
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test that a live use of a forceable declaration keeps it alive.
|
|
// https://github.com/llvm/circt/issues/5898
|
|
|
|
// CHECK-LABEL: circuit "Issue5898"
|
|
firrtl.circuit "Issue5898" {
|
|
firrtl.module @Issue5898(in %x: !firrtl.uint<5>, out %p: !firrtl.rwprobe<uint<5>>) {
|
|
// CHECK: connect
|
|
%w, %w_ref = firrtl.wire forceable : !firrtl.uint<5>, !firrtl.rwprobe<uint<5>>
|
|
firrtl.matchingconnect %w, %x : !firrtl.uint<5>
|
|
firrtl.ref.define %p, %w_ref : !firrtl.rwprobe<uint<5>>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test that annotations keep declarations alive.
|
|
// CHECK-LABEL: "AnnoAlive"
|
|
firrtl.circuit "AnnoAlive" {
|
|
firrtl.module @AnnoAlive() {
|
|
// CHECK: firrtl.wire
|
|
firrtl.wire {annotations = [{class = "circt.test"}]} : !firrtl.uint
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// Test warning about not being able to remove dead public modules.
|
|
|
|
// CHECK-LABEL: "DeadPublic"
|
|
firrtl.circuit "DeadPublic" {
|
|
// CHECK: module @PublicDeadChild
|
|
// expected-warning @below {{module `PublicDeadChild` is empty but cannot be removed because the module is public}}
|
|
firrtl.module @PublicDeadChild() {}
|
|
// CHECK: module @DeadPublic
|
|
firrtl.module @DeadPublic() {
|
|
firrtl.instance pdc @PublicDeadChild()
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// OMIR annotation should not block removal.
|
|
// - See: https://github.com/llvm/circt/issues/6199
|
|
//
|
|
// CHECK-LABEL: firrtl.circuit "OMIRRemoval"
|
|
firrtl.circuit "OMIRRemoval" {
|
|
firrtl.module @OMIRRemoval() {
|
|
// CHECK-NOT: %tmp_0
|
|
%tmp_0 = firrtl.wire {
|
|
annotations = [
|
|
{
|
|
class = "freechips.rocketchip.objectmodel.OMIRTracker",
|
|
id = 0 : i64,
|
|
type = "OMReferenceTarget"
|
|
}
|
|
]} : !firrtl.uint<1>
|
|
|
|
// CHECK-NOT: %tmp_1
|
|
%tmp_1 = firrtl.wire {
|
|
annotations = [
|
|
{
|
|
class = "freechips.rocketchip.objectmodel.OMIRTracker",
|
|
id = 1 : i64,
|
|
type = "OMMemberReferenceTarget"
|
|
}
|
|
]} : !firrtl.uint<2>
|
|
|
|
// CHECK-NOT: %tmp_2
|
|
%tmp_2 = firrtl.wire {
|
|
annotations = [
|
|
{
|
|
class = "freechips.rocketchip.objectmodel.OMIRTracker",
|
|
id = 3 : i64,
|
|
type = "OMMemberInstanceTarget"
|
|
}
|
|
]} : !firrtl.uint<3>
|
|
|
|
// Adding one additional annotation will block removal.
|
|
//
|
|
// CHECK: %tmp_3
|
|
%tmp_3 = firrtl.wire {
|
|
annotations = [
|
|
{
|
|
class = "freechips.rocketchip.objectmodel.OMIRTracker",
|
|
id = 4 : i64,
|
|
type = "OMMemberInstanceTarget"
|
|
},
|
|
{
|
|
class = "circt.test"
|
|
}
|
|
]} : !firrtl.uint<4>
|
|
}
|
|
}
|