Skip to content

Undefined behavior introduced by reuse_at transformation in HeteroCL #185

@wyanzhao

Description

@wyanzhao

We are currently performing formal verification on HeteroCL, and we have discovered an undefined behavior introduced by the reuse_at transformation. The input code is as follows:

func.func @blur_x(%A: memref<10x10xf32>, %B: memref<8x10xf32>)
{
    %s = hcl.create_op_handle "s"
    %li = hcl.create_loop_handle %s, "i"
    %lj = hcl.create_loop_handle %s, "j"
    affine.for %i = 0 to 8 {
        affine.for %j = 0 to 10 {
            %tmp = affine.load %A[%i, %j] : memref<10x10xf32>
            %tmp1 = affine.load %A[%i+1, %j] : memref<10x10xf32>
            %tmp2 = affine.load %A[%i+2, %j] : memref<10x10xf32>
            %sum = arith.addf %tmp, %tmp1: f32
            %sum1 = arith.addf %sum, %tmp2: f32
            affine.store %sum1, %B[%i, %j] : memref<8x10xf32>
        } { loop_name = "j" }
    } { loop_name = "i", op_name = "s" }
    %buf = hcl.reuse_at(%A: memref<10x10xf32>, %li) -> memref<3x10xf32>
    return
}

After running hcl-opt -opt, the output code is:

  func.func @blur_x(%arg0: memref<10x10xf32>, %arg1: memref<8x10xf32>) {
    %0 = memref.alloc() {name = "s_reuse_0"} : memref<3x10xf32>
    affine.for %arg2 = 0 to 10 {
      affine.for %arg3 = 0 to 10 {
        %1 = affine.load %0[1, %arg3] : memref<3x10xf32>
        affine.store %1, %0[0, %arg3] : memref<3x10xf32>
        %2 = affine.load %0[2, %arg3] : memref<3x10xf32>
        affine.store %2, %0[1, %arg3] : memref<3x10xf32>
        %3 = affine.load %arg0[%arg2, %arg3] : memref<10x10xf32>
        affine.store %3, %0[2, %arg3] : memref<3x10xf32>
      } {spatial}
      affine.if #set0(%arg2) {
        affine.for %arg3 = 0 to 10 {
          %1 = affine.load %0[0, %arg3] : memref<3x10xf32>
          %2 = affine.load %0[1, %arg3] : memref<3x10xf32>
          %3 = affine.load %0[2, %arg3] : memref<3x10xf32>
          %4 = arith.addf %1, %2 : f32
          %5 = arith.addf %4, %3 : f32
          affine.store %5, %arg1[%arg2 - 2, %arg3] : memref<8x10xf32>
        } {loop_name = "j"}
      }
    } {loop_name = "i", op_name = "s"}
    return
  }

The issue occurs when HeteroCL allocates a new memory block (%0 = memref.alloc() {name = "s_reuse_0"} : memref<3x10xf32>) and reads from it without initializing it first (%1 = affine.load %0[1, %arg3] : memref<3x10xf32>.

In MLIR, reading uninitialized memory is considered undefined behavior. The input code only has undefined behavior of reading from A and B when they are uninitialized, which implies that the reuse_at transformation introduces new undefined behavior does not exist in the input code.

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