circt icon indicating copy to clipboard operation
circt copied to clipboard

[PrepareForEmission] Temporaries are not spilled in a procedural region

Open uenoku opened this issue 3 years ago • 2 comments

The following IR is derived from a test case of https://github.com/llvm/circt/pull/3116 which workarounded verilator hangs.

hw.module @Verilator3405() -> () {
    %state = sv.reg : !hw.inout<i2>
    sv.initial {
      %state_read = sv.read_inout %state : !hw.inout<i2> 
      %read = comb.extract %state_read from 0 : (i2) -> i1
      %lhs = comb.or %read, %read, %read, %read, %read, %read, %read, %read, %read, %read, %read, %read : i1
      %rhs = comb.or %read, %read, %read, %read, %read, %read, %read, %read, %read, %read, %read, %read  : i1
      %out = comb.concat %lhs, %rhs : i1, i1
      sv.passign %state, %out : i2
    }
  }

With circt-opt -export-verilog -lowering-options=disallowLocalVariables, we get an expected result:

module Verilator3405(); 
  wire       _GEN; 
  wire       _GEN_0;
  reg  [1:0] state;  

  assign _GEN_0 = state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] |
                state[0] | state[0] | state[0] | state[0];
  assign _GEN = state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] |
                state[0] | state[0] | state[0] | state[0]; 
  initial       
    state <= {_GEN_0, _GEN};
endmodule

%lhs and %rhs are spilled to temporary wires. On the other hand with circt-opt -export-verilog, the result is:

module Verilator3405();
  reg [1:0] state; 

  initial 
    state <= {state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] |
                                state[0] | state[0] | state[0] | state[0], state[0] | state[0] | state[0] | state[0] |
                                state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0]}; 
endmodule

I think it should be:

  initial begin
    automatic logic _GEN_0 = state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] |
                state[0] | state[0] | state[0] | state[0];
    automatic logic _GEN = state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] | state[0] |
                state[0] | state[0] | state[0] | state[0]; 
       
    state <= {_GEN_0, _GEN};
 end

uenoku avatar Jun 29 '22 09:06 uenoku

So the issue here is not necessarily a logical error, but that the spill can't (and doesn't) occur?

I believe that stopped happening even earlier than #3116, when the large expression spilling was removed from ExportVerilog in https://github.com/llvm/circt/commit/586f3dc63dc39fcb8d0ec27667af00cc65a4f332.

We're relying on Prepare to spill what needs to be spilled, but Prepare is an IR transform, and there is no automatic logic IR. ExportVerilog was free to print automatic logic before, but we are removing all spilling from ExportVerilog. Is that the root of the issue?

If I understand this correctly, I think your point on Discourse is the right one, we should declare something like an sv.automatic_logic op, which Prepare can use to restore the ability to spill in procedural regions.

mikeurbach avatar Jun 29 '22 17:06 mikeurbach

We're relying on Prepare to spill what needs to be spilled, but Prepare is an IR transform, and there is no automatic logic IR. ExportVerilog was free to print automatic logic before, but we are removing all spilling from ExportVerilog. Is that the root of the issue?

Yes, exactly!

If I understand this correctly, I think your point on Discourse is the right one, we should declare something like an sv.automatic_logic op, which Prepare can use to restore the ability to spill in procedural regions.

I think sv.automatic_logic makes sense to me. Another idea issv.internal.temporary op which is lowered to wires if op is in non-procedural regions, or lowered to automatic logic in procedural regions.

uenoku avatar Jun 29 '22 17:06 uenoku