Removed intermediate wire between instances and output operations (#1351)

* Implemented tiny optimisation where it removes intermediate wires between instances and outputs

* Removed unnecessary import for iostream

* Added some tests

* Added some more tests

* Fixed tests

* Implemented most of what Lattner suggested

* Fixed the bug with outputs being null

* Did nit picky stuff
This commit is contained in:
Laura Gallo 2021-07-06 16:05:13 -04:00 committed by GitHub
parent 06c5ae9e98
commit 5d22741472
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 85 deletions

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
/build*
/docker_build
tags
.vscode
compile_commands.json
.cache/
#*
*~
.DS_Store

View File

@ -1916,13 +1916,21 @@ LogicalResult StmtEmitter::visitStmt(OutputOp op) {
for (ModulePortInfo port : parent.getPorts()) {
if (!port.isOutput())
continue;
auto operand = op.getOperand(operandIndex);
if (operand.hasOneUse() &&
dyn_cast_or_null<InstanceOp>(operand.getDefiningOp())) {
++operandIndex;
continue;
}
ops.clear();
ops.insert(op);
indent();
if (isZeroBitType(port.type))
os << "// Zero width: ";
os << "assign " << names.getOutputName(port.argNum) << " = ";
emitExpression(op.getOperand(operandIndex), ops);
emitExpression(operand, ops);
os << ';';
emitLocationInfoAndNewLine(ops);
++operandIndex;
@ -2417,10 +2425,23 @@ LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
// Emit the value as an expression.
ops.clear();
// output ports were lowered to wire
if (elt.isOutput())
// Output ports that are not connected to single use output ports were
// lowered to wire.
OutputOp output;
if (!elt.isOutput()) {
emitExpression(portVal, ops);
} else if (portVal.hasOneUse() &&
(output = dyn_cast_or_null<OutputOp>(
portVal.getUses().begin()->getOwner()))) {
auto module = output->getParentOfType<HWModuleOp>();
auto name = getModuleResultNameAttr(
module, portVal.getUses().begin()->getOperandNumber());
os << name.getValue().str();
} else {
portVal = getWireForValue(portVal);
emitExpression(portVal, ops);
emitExpression(portVal, ops);
}
os << ')';
}
if (!isFirst) {
@ -2688,22 +2709,30 @@ static void lowerInstanceResults(InstanceOp op) {
if (onlyUseIsConnect(result))
continue;
nameTmp.resize(namePrefixSize);
if (port.name)
nameTmp += port.name.getValue().str();
else
nameTmp += std::to_string(nextResultNo - 1);
auto newWire = builder.create<WireOp>(result.getType(), nameTmp);
while (!result.use_empty()) {
auto newWireRead = builder.create<ReadInOutOp>(newWire);
bool isOneUseOutput = false;
if (result.hasOneUse()) {
OpOperand &use = *result.getUses().begin();
use.set(newWireRead);
newWireRead->moveBefore(use.getOwner());
isOneUseOutput = dyn_cast_or_null<OutputOp>(use.getOwner()) != nullptr;
}
auto connect = builder.create<ConnectOp>(newWire, result);
connect->moveAfter(op);
if (!isOneUseOutput) {
nameTmp.resize(namePrefixSize);
if (port.name)
nameTmp += port.name.getValue().str();
else
nameTmp += std::to_string(nextResultNo - 1);
auto newWire = builder.create<WireOp>(result.getType(), nameTmp);
while (!result.use_empty()) {
auto newWireRead = builder.create<ReadInOutOp>(newWire);
OpOperand &use = *result.getUses().begin();
use.set(newWireRead);
newWireRead->moveBefore(use.getOwner());
}
auto connect = builder.create<ConnectOp>(newWire, result);
connect->moveAfter(op);
}
}
}

View File

@ -205,50 +205,44 @@ hw.module @AB(%w: i1, %x: i1, %i2: i2, %i3: i0) -> (%y: i1, %z: i1, %p: i1, %p2:
hw.output %y, %x, %p, %p2 : i1, i1, i1, i1
}
// CHECK-LABEL: module AB(
// CHECK-NEXT: input w, x,
// CHECK-NEXT: input [1:0] i2,
// CHECK-NEXT: // input /*Zero Width*/ i3,
// CHECK-NEXT: output y, z, p, p2);
// CHECK-EMPTY:
// CHECK-LABEL: module AB(
// CHECK-NEXT: input w, x,
// CHECK-NEXT: input [1:0] i2,
// CHECK-NEXT: // input /*Zero Width*/ i3,
// CHECK-NEXT: output y, z, p, p2);
// CHECK-EMPTY:
// CHECK-NEXT: wire b1_b;
// CHECK-NEXT: wire a1_f;
// CHECK-EMPTY:
// CHECK-NEXT: AAA a1 (
// CHECK-NEXT: .d (w),
// CHECK-NEXT: .e (b1_b),
// CHECK-NEXT: .f (a1_f)
// CHECK-NEXT: );
// CHECK-NEXT: B b1 (
// CHECK-NEXT: .a (a1_f),
// CHECK-NEXT: .b (b1_b),
// CHECK-NEXT: .c (y)
// CHECK-NEXT: );
// CHECK-NEXT: FooModule #(
// CHECK-NEXT: .DEFAULT(64'd14000240888948784983),
// CHECK-NEXT: .DEPTH(3.242000e+01),
// CHECK-NEXT: .FORMAT("xyz_timeout=%d\n"),
// CHECK-NEXT: .WIDTH(8'd32)
// CHECK-NEXT: ) paramd (
// CHECK-NEXT: .a (w),
// CHECK-NEXT: //.b (i3),
// CHECK-NEXT: .out (p)
// CHECK-NEXT: );
// CHECK-NEXT: FooModule #(
// CHECK-NEXT: .DEFAULT(64'd1)
// CHECK-NEXT: ) paramd2 (
// CHECK-NEXT: .a (i2),
// CHECK-NEXT: .out (p2)
// CHECK-NEXT: );
// CHECK-NEXT: assign z = x;
// CHECK-NEXT: endmodule
// CHECK-NEXT: wire paramd2_out;
// CHECK-NEXT: wire paramd_out;
// CHECK-NEXT: wire b1_b;
// CHECK-NEXT: wire b1_c;
// CHECK-NEXT: wire a1_f;
// CHECK-EMPTY:
// CHECK-NEXT: A a1 (
// CHECK-NEXT: .d (w),
// CHECK-NEXT: .e (b1_b),
// CHECK-NEXT: .f (a1_f)
// CHECK-NEXT: )
// CHECK-NEXT: B b1 (
// CHECK-NEXT: .a (a1_f),
// CHECK-NEXT: .b (b1_b),
// CHECK-NEXT: .c (b1_c)
// CHECK-NEXT: )
// CHECK-NEXT: FooModule #(
// CHECK-NEXT: .DEFAULT(64'd14000240888948784983),
// CHECK-NEXT: .DEPTH(3.242000e+01),
// CHECK-NEXT: .FORMAT("xyz_timeout=%d\n"),
// CHECK-NEXT: .WIDTH(8'd32)
// CHECK-NEXT: ) paramd (
// CHECK-NEXT: .a (w),
// CHECK-NEXT: .b (i3),
// CHECK-NEXT: .out (paramd_out)
// CHECK-NEXT: );
// CHECK-NEXT: FooModule #(
// CHECK-NEXT: .DEFAULT(64'd1)
// CHECK-NEXT: ) paramd2 (
// CHECK-NEXT: .a (i2),
// CHECK-NEXT: .out (paramd2_out)
// CHECK-NEXT: );
// CHECK-NEXT: assign y = b1_c;
// CHECK-NEXT: assign z = x;
// CHECK-NEXT: assign p = paramd_out;
// CHECK-NEXT: assign p2 = paramd2_out;
// CHECK-NEXT: endmodule
hw.module @shl(%a: i1) -> (%b: i1) {
@ -447,21 +441,19 @@ hw.module @TestZero(%a: i4, %zeroBit: i0, %arrZero: !hw.array<3xi0>)
hw.module @TestZeroInstance(%aa: i4, %azeroBit: i0, %aarrZero: !hw.array<3xi0>)
-> (%r0: i4, %rZero: i0, %arrZero_0: !hw.array<3xi0>) {
// CHECK: TestZero iii ( // {{.*}}hw-dialect.mlir:{{.*}}:19
// CHECK: .a (aa),
// CHECK: //.zeroBit (azeroBit),
// CHECK: //.arrZero (aarrZero),
// CHECK: .r0 (iii_r0)
// CHECK: //.rZero (iii_rZero)
// CHECK: //.arrZero_0 (iii_arrZero_0)
// CHECK: );
// CHECK: TestZero iii (
// CHECK-NEXT: .a (aa),
// CHECK-NEXT: //.zeroBit (azeroBit),
// CHECK-NEXT: //.arrZero (aarrZero),
// CHECK-NEXT: .r0 (r0)
// CHECK-NEXT: //.rZero (rZero)
// CHECK-NEXT: //.arrZero_0 (arrZero_0)
// CHECK-NEXT: );
// CHECK-NEXT: endmodule
%o1, %o2, %o3 = hw.instance "iii" @TestZero(%aa, %azeroBit, %aarrZero)
: (i4, i0, !hw.array<3xi0>) -> (i4, i0, !hw.array<3xi0>)
// CHECK: assign r0 = iii_r0;
// CHECK: // Zero width: assign rZero = iii_rZero;
// CHECK: // Zero width: assign arrZero_0 = iii_arrZero_0;
hw.output %o1, %o2, %o3 : i4, i0, !hw.array<3xi0>
}
@ -638,21 +630,83 @@ hw.module.extern @ExternDestMod(%a: i1, %b: i2) -> (%c: i3, %d: i4)
hw.module @InternalDestMod(%a: i1, %b: i3) {}
// CHECK-LABEL module ABC
hw.module @ABC(%a: i1, %b: i2) -> (%c: i4) {
// CHECK: wire [2:0] whatever_c;
// CHECK: wire [3:0] whatever_d;
%0,%1 = hw.instance "whatever" sym @a1 @ExternDestMod(%a, %b) {doNotPrint=1}: (i1, i2) -> (i3, i4)
// CHECK: // ExternDestMod whatever (
// CHECK-NEXT: // .a (a),
// CHECK-NEXT: // .b (b),
// CHECK-NEXT: // .c (whatever_c),
// CHECK-NEXT: // .d (whatever_d)
// CHECK-NEXT: // );
hw.instance "yo" sym @b1 @InternalDestMod(%a, %0) {doNotPrint=1} : (i1, i3) -> ()
// CHECK-NEXT: // InternalDestMod yo (
// CHECK-NEXT: // .a (a),
// CHECK-NEXT: // .b (whatever_c)
// CHECK-NEXT: // );
hw.output %1 : i4
// CHECK-NEXT: assign c = whatever_d;
// CHECK-NEXT: endmodule
}
// CHECK: wire [2:0] whatever_c;
// CHECK-EMPTY:
// CHECK-NEXT: // ExternDestMod whatever (
// CHECK-NEXT: // .a (a),
// CHECK-NEXT: // .b (b),
// CHECK-NEXT: // .c (whatever_c),
// CHECK-NEXT: // .d (c)
// CHECK-NEXT: // );
// CHECK-NEXT: // InternalDestMod yo (
// CHECK-NEXT: // .a (a),
// CHECK-NEXT: // .b (whatever_c)
// CHECK-NEXT: // );
// CHECK-NEXT: endmodule
hw.module.extern @Uwu() -> (%uwu_output : i32)
hw.module.extern @Owo(%owo_in : i32) -> ()
// CHECK-LABEL: module Nya(
hw.module @Nya() -> (%nya_output : i32) {
%0 = hw.instance "uwu" @Uwu() : () -> (i32)
// CHECK: wire [31:0] uwu_uwu_output;
// CHECK-EMPTY:
// CHECK: Uwu uwu (
// CHECK: .uwu_output (uwu_uwu_output)
// CHECK: );
hw.instance "owo" @Owo(%0) : (i32) -> ()
// CHECK: Owo owo (
// CHECK: .owo_in (uwu_uwu_output)
// CHECK: );
hw.output %0 : i32
// CHECK: assign nya_output = uwu_uwu_output;
// CHECK: endmodule
}
// CHECK-LABEL: module Nya2(
hw.module @Nya2() -> (%nya2_output : i32) {
%0 = hw.instance "uwu" @Uwu() : () -> (i32)
// CHECK: Uwu uwu (
// CHECK: .uwu_output (nya2_output)
// CHECK: );
hw.output %0 : i32
// CHECK: endmodule
}
hw.module.extern @Ni() -> (%ni_output : i0)
hw.module.extern @San(%san_input : i0) -> ()
// CHECK-LABEL: module Ichi(
hw.module @Ichi() -> (%Ichi_output : i0) {
%0 = hw.instance "ni" @Ni() : () -> (i0)
// CHECK: Ni ni (
// CHECK: //.ni_output (Ichi_output));
hw.output %0 : i0
// CHECK: endmodule
}
// CHECK-LABEL: module Chi(
hw.module @Chi() -> (%Chi_output : i0) {
%0 = hw.instance "ni" @Ni() : () -> (i0)
// CHECK: Ni ni (
// CHECK: //.ni_output (ni_ni_output));
hw.instance "san" @San(%0) : (i0) -> ()
// CHECK: San san (
// CHECK: //.san_input (ni_ni_output));
// CHECK: // Zero width: assign Chi_output = ni_ni_output;
hw.output %0 : i0
// CHECK: endmodule
}