From 164c2dd825be7141bf06a1e170ceac7a1f766e63 Mon Sep 17 00:00:00 2001 From: Nimalan Date: Tue, 9 Apr 2024 20:07:48 +0530 Subject: [PATCH] Fix Calyx backend issue with emitting signed binops (#430) --- file-tests/should-futil/emit-signed-op.expect | 110 ++++++++++++++++++ file-tests/should-futil/emit-signed-op.fuse | 9 ++ src/main/scala/backends/calyx/Backend.scala | 8 +- src/main/scala/backends/calyx/Helpers.scala | 14 ++- 4 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 file-tests/should-futil/emit-signed-op.expect create mode 100644 file-tests/should-futil/emit-signed-op.fuse diff --git a/file-tests/should-futil/emit-signed-op.expect b/file-tests/should-futil/emit-signed-op.expect new file mode 100644 index 00000000..26f83c41 --- /dev/null +++ b/file-tests/should-futil/emit-signed-op.expect @@ -0,0 +1,110 @@ +import "primitives/core.futil"; +import "primitives/memories/seq.futil"; +import "primitives/binary_operators.futil"; +component main() -> () { + cells { + @external(1) a = seq_mem_d1(10,128,8); + a_read0_0 = std_reg(10); + a_read1_0 = std_reg(10); + add0 = std_sadd(10); + add1 = std_sadd(8); + and0 = std_and(10); + @external(1) b = seq_mem_d1(10,128,8); + b_read0_0 = std_reg(10); + b_read1_0 = std_reg(10); + @external(1) c = seq_mem_d1(10,128,8); + const0 = std_const(8,0); + const1 = std_const(8,1); + @external(1) d = seq_mem_d1(10,128,8); + i0 = std_reg(8); + } + wires { + group let0<"promotable"=1> { + i0.in = const0.out; + i0.write_en = 1'd1; + let0[done] = i0.done; + } + group let1<"promotable"=2> { + a_read0_0.in = a.read_data; + a_read0_0.write_en = a.done; + let1[done] = a_read0_0.done; + a.content_en = 1'd1; + a.addr0 = i0.out; + } + group let2<"promotable"=2> { + b_read0_0.in = b.read_data; + b_read0_0.write_en = b.done; + let2[done] = b_read0_0.done; + b.content_en = 1'd1; + b.addr0 = i0.out; + } + group let3<"promotable"=2> { + a_read1_0.in = a.read_data; + a_read1_0.write_en = a.done; + let3[done] = a_read1_0.done; + a.content_en = 1'd1; + a.addr0 = i0.out; + } + group let4<"promotable"=2> { + b_read1_0.in = b.read_data; + b_read1_0.write_en = b.done; + let4[done] = b_read1_0.done; + b.content_en = 1'd1; + b.addr0 = i0.out; + } + group upd0<"promotable"=1> { + c.content_en = 1'd1; + c.addr0 = i0.out; + c.write_en = 1'd1; + and0.left = a_read0_0.out; + and0.right = b_read0_0.out; + c.write_data = and0.out; + upd0[done] = c.done; + } + group upd1<"promotable"=1> { + d.content_en = 1'd1; + d.addr0 = i0.out; + d.write_en = 1'd1; + add0.left = a_read1_0.out; + add0.right = b_read1_0.out; + d.write_data = add0.out; + upd1[done] = d.done; + } + group upd2<"promotable"=1> { + i0.write_en = 1'd1; + add1.left = i0.out; + add1.right = const1.out; + i0.in = add1.out; + upd2[done] = i0.done; + } + } + control { + seq { + @pos(0) let0; + repeat 128 { + seq { + par { + @pos(1) let1; + @pos(2) let2; + } + par { + @pos(3) upd0; + @pos(4) let3; + @pos(5) let4; + } + @pos(6) upd1; + @pos(0) upd2; + } + } + } + } +} +metadata #{ + 0: for (let i:bit<8>=0..128) { + 1: c[i] := a[i] & b[i]; // This should not be emit signed Calyx primitive + 2: c[i] := a[i] & b[i]; // This should not be emit signed Calyx primitive + 3: c[i] := a[i] & b[i]; // This should not be emit signed Calyx primitive + 4: d[i] := a[i] + b[i]; // This should emit a signed Calyx primitive + 5: d[i] := a[i] + b[i]; // This should emit a signed Calyx primitive + 6: d[i] := a[i] + b[i]; // This should emit a signed Calyx primitive +}# diff --git a/file-tests/should-futil/emit-signed-op.fuse b/file-tests/should-futil/emit-signed-op.fuse new file mode 100644 index 00000000..11966821 --- /dev/null +++ b/file-tests/should-futil/emit-signed-op.fuse @@ -0,0 +1,9 @@ +decl a: bit<10>[128]; +decl b: bit<10>[128]; +decl c: bit<10>[128]; +decl d: bit<10>[128]; + +for (let i:bit<8>=0..128) { + c[i] := a[i] & b[i]; // This should not be emit signed Calyx primitive + d[i] := a[i] + b[i]; // This should emit a signed Calyx primitive +} diff --git a/src/main/scala/backends/calyx/Backend.scala b/src/main/scala/backends/calyx/Backend.scala index 58f65d21..2a580d2d 100644 --- a/src/main/scala/backends/calyx/Backend.scala +++ b/src/main/scala/backends/calyx/Backend.scala @@ -255,7 +255,7 @@ private class CalyxBackendHelper { /** `emitBinop` is a helper function to generate the structure for `e1 binop * e2`. The return type is described in `emitExpr`. */ - def emitBinop(compName: String, e1: Expr, e2: Expr)(implicit + def emitBinop(compName: String, op: BOp, e1: Expr, e2: Expr)(implicit store: Store ): EmitOutput = { val e1Out = emitExpr(e1) @@ -286,7 +286,7 @@ private class CalyxBackendHelper { bitsForType(e1.typ, e1.pos) match { case (e1Bits, None) => { - val isSigned = signed(e1.typ) + val isSigned = signed(e1.typ, op) val binOp = Stdlib.binop(s"$compName", e1Bits, isSigned) val comp = Cell(genName(compName), binOp, false, List()) val struct = List( @@ -309,7 +309,7 @@ private class CalyxBackendHelper { val (e2Bits, Some(intBit2)) = bitsForType(e2.typ, e2.pos): @unchecked val fracBit1 = e1Bits - intBit1 val fracBit2 = e2Bits - intBit2 - val isSigned = signed(e1.typ) + val isSigned = signed(e1.typ, op) assertOrThrow( fracBit1 == fracBit2 && intBit1 == intBit2, NotImplemented( @@ -487,7 +487,7 @@ private class CalyxBackendHelper { "out_remainder", None ) - case _ => emitBinop(compName, e1, e2) + case _ => emitBinop(compName, op, e1, e2) } } case EVar(id) => diff --git a/src/main/scala/backends/calyx/Helpers.scala b/src/main/scala/backends/calyx/Helpers.scala index 5660bf54..776aac55 100644 --- a/src/main/scala/backends/calyx/Helpers.scala +++ b/src/main/scala/backends/calyx/Helpers.scala @@ -47,9 +47,11 @@ object Helpers: /** Returns true if the given int or fixed point is signed */ - def signed(typ: Option[Type]) = - typ match - case Some(TSizedInt(_, un)) => un == false - case Some(TFixed(_, _, un)) => un == false - case _ => false - + def signed(typ: Option[Type], op: BOp) = + op match + case _@NumOp(_, _) => + typ match + case Some(TSizedInt(_, un)) => un == false + case Some(TFixed(_, _, un)) => un == false + case _ => false + case _ => false // bit