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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions