Skip to content

[SCF to Calyx] Generates external memory outside top level component #10036

@Abhilekhgautam

Description

@Abhilekhgautam

The semantics of Calyx doesn't allow external memory outside top-level component. However, the SCF To calyx pass doesn't seem to abide with this.

func.func @test(%arg0 : memref<3xf32>){
    %alloc = memref.alloc() : memref<3xf32>

    %c0 = arith.constant 0: index
    %c3 = arith.constant 3: index
    %step = arith.constant 1: index

    %cst_54 = arith.constant 54.5675 : f32

    scf.for %i = %c0 to %c3 step %step {
       memref.store %cst_54, %alloc[%i] : memref<3xf32>
    }


    return
}

func.func @main() {
    %alloc = memref.alloc() : memref<3xf32>

    %c0 = arith.constant 0: index
    %c3 = arith.constant 3: index
    %step = arith.constant 1: index

    %cst_45 = arith.constant 45.5675 : f32

    scf.for %i = %c0 to %c3 step %step {
       memref.store %cst_45, %alloc[%i] : memref<3xf32>
    } 

    func.call @test(%alloc): (memref<3xf32>) -> ()

    return
  }

If we use --pass-pipeline="builtin.module(lower-scf-to-calyx{top-level-function=main})" with circt-opt. It generates a test component corresponding to the test function with external attribute set to true for the memory:

module attributes {calyx.entrypoint = "main"} {
  calyx.component @test(%clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%done: i1 {done}) {
    %c1_i32 = hw.constant 1 : i32
    %true = hw.constant true
    %c0_i32 = hw.constant 0 : i32
    %cst = calyx.constant @cst_0 <5.456750e+01 : f32> : i32
    %std_slice_0.in, %std_slice_0.out = calyx.std_slice @std_slice_0 : i32, i2
    %std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add @std_add_0 : i32, i32, i32
    %mem_0.addr0, %mem_0.clk, %mem_0.reset, %mem_0.content_en, %mem_0.write_en, %mem_0.write_data, %mem_0.read_data, %mem_0.done = calyx.seq_mem @mem_0 <[3] x 32> [2] {external = true} : i2, i1, i1, i1, i1, i32, i32, i1
    ...

See Complete Output at Compiler Explorer

I checked the code at Conversion/SCFToCalyx/SCFToCalyx.cpp which adds external attribute to every memref.AllocaOp without checking whether it is top level component or not:

memoryOp->setAttr("external", IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));

I went on to change the code to add an check for top level component:

if (componentOp.hasAttr("toplevel")){
    memoryOp->setAttr("external", IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));
}

But I ran into another issue now. It's in the emitter.

There seems to be an assumption that memories in calyx are either of the two - external, ref.

The CalyxEmitter while emitting native calyx checks for the existence of the external attribute in the memory. If it is not external it assumes that it is a ref which it isn't (in this case) because it is just a memory local to the component.

Should we just set external = false (for non top-level component and non ref memory) and in the emitter check if external is set to false ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions