Files
clang-p2996/mlir/test/Target/SPIRV/loop.mlir
Lei Zhang 5e55a20119 [mlir][spirv] Serialize selection with separate header block
The previous "optimization" that tries to reuse existing block for
selection header block can be problematic for deserialization
because it effectively pulls in previous ops in the selection op's
enclosing block into the selection op's header. When deserializing,
those ops will be placed in the selection op's region. If any of
the previous ops has usage after the section op, it will break. That
is, the following IR cannot round trip:

```mlir
^bb:
  %def = ...
  spv.mlir.selection { ... }
  %use = spv.SomeOp %def
```

This commit removes the "optimization" to always create new blocks
for the selection header.

Along the way, also made error reporting better in deserialization
by turning asserts into proper errors and add check of uses outside
of sinked structured control flow region blocks.

Reviewed By: Hardcode84

Differential Revision: https://reviews.llvm.org/D115582
2021-12-13 10:42:26 -05:00

291 lines
9.4 KiB
MLIR

// RUN: mlir-translate -split-input-file -test-spirv-roundtrip %s | FileCheck %s
// Single loop
spv.module Logical GLSL450 requires #spv.vce<v1.0, [Shader], []> {
// for (int i = 0; i < count; ++i) {}
// CHECK-LABEL: @loop
spv.func @loop(%count : i32) -> () "None" {
%zero = spv.Constant 0: i32
%one = spv.Constant 1: i32
%var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
// CHECK: spv.Branch ^bb1
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: spv.mlir.loop
spv.mlir.loop {
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb1:
^header:
// CHECK-NEXT: spv.Load
%val0 = spv.Load "Function" %var : i32
// CHECK-NEXT: spv.SLessThan
%cmp = spv.SLessThan %val0, %count : i32
// CHECK-NEXT: spv.BranchConditional %{{.*}} [1, 1], ^bb2, ^bb4
spv.BranchConditional %cmp [1, 1], ^body, ^merge
// CHECK-NEXT: ^bb2:
^body:
// Do nothing
// CHECK-NEXT: spv.Branch ^bb3
spv.Branch ^continue
// CHECK-NEXT: ^bb3:
^continue:
// CHECK-NEXT: spv.Load
%val1 = spv.Load "Function" %var : i32
// CHECK-NEXT: spv.Constant 1
// CHECK-NEXT: spv.IAdd
%add = spv.IAdd %val1, %one : i32
// CHECK-NEXT: spv.Store
spv.Store "Function" %var, %add : i32
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb4:
// CHECK-NEXT: spv.mlir.merge
^merge:
spv.mlir.merge
}
spv.Return
}
spv.func @main() -> () "None" {
spv.Return
}
spv.EntryPoint "GLCompute" @main
}
// -----
// Single loop with block arguments
spv.module Logical GLSL450 requires #spv.vce<v1.0, [Shader], []> {
spv.GlobalVariable @GV1 bind(0, 0) : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>
spv.GlobalVariable @GV2 bind(0, 1) : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>
// CHECK-LABEL: @loop_kernel
spv.func @loop_kernel() "None" {
%0 = spv.mlir.addressof @GV1 : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>
%1 = spv.Constant 0 : i32
%2 = spv.AccessChain %0[%1] : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>, i32
%3 = spv.mlir.addressof @GV2 : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>
%5 = spv.AccessChain %3[%1] : !spv.ptr<!spv.struct<(!spv.array<10 x f32, stride=4> [0])>, StorageBuffer>, i32
%6 = spv.Constant 4 : i32
%7 = spv.Constant 42 : i32
%8 = spv.Constant 2 : i32
// CHECK: spv.Branch ^bb1(%{{.*}} : i32)
// CHECK-NEXT: ^bb1(%[[OUTARG:.*]]: i32):
// CHECK-NEXT: spv.mlir.loop {
spv.mlir.loop {
// CHECK-NEXT: spv.Branch ^bb1(%[[OUTARG]] : i32)
spv.Branch ^header(%6 : i32)
// CHECK-NEXT: ^bb1(%[[HEADARG:.*]]: i32):
^header(%9: i32):
%10 = spv.SLessThan %9, %7 : i32
// CHECK: spv.BranchConditional %{{.*}}, ^bb2, ^bb3
spv.BranchConditional %10, ^body, ^merge
// CHECK-NEXT: ^bb2: // pred: ^bb1
^body:
%11 = spv.AccessChain %2[%9] : !spv.ptr<!spv.array<10 x f32, stride=4>, StorageBuffer>, i32
%12 = spv.Load "StorageBuffer" %11 : f32
%13 = spv.AccessChain %5[%9] : !spv.ptr<!spv.array<10 x f32, stride=4>, StorageBuffer>, i32
spv.Store "StorageBuffer" %13, %12 : f32
// CHECK: %[[ADD:.*]] = spv.IAdd
%14 = spv.IAdd %9, %8 : i32
// CHECK-NEXT: spv.Branch ^bb1(%[[ADD]] : i32)
spv.Branch ^header(%14 : i32)
// CHECK-NEXT: ^bb3:
^merge:
// CHECK-NEXT: spv.mlir.merge
spv.mlir.merge
}
spv.Return
}
spv.EntryPoint "GLCompute" @loop_kernel
spv.ExecutionMode @loop_kernel "LocalSize", 1, 1, 1
}
// -----
// Nested loop
spv.module Logical GLSL450 requires #spv.vce<v1.0, [Shader], []> {
// for (int i = 0; i < count; ++i) {
// for (int j = 0; j < count; ++j) { }
// }
// CHECK-LABEL: @loop
spv.func @loop(%count : i32) -> () "None" {
%zero = spv.Constant 0: i32
%one = spv.Constant 1: i32
%ivar = spv.Variable init(%zero) : !spv.ptr<i32, Function>
%jvar = spv.Variable init(%zero) : !spv.ptr<i32, Function>
// CHECK: spv.Branch ^bb1
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: spv.mlir.loop control(Unroll)
spv.mlir.loop control(Unroll) {
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb1:
^header:
// CHECK-NEXT: spv.Load
%ival0 = spv.Load "Function" %ivar : i32
// CHECK-NEXT: spv.SLessThan
%icmp = spv.SLessThan %ival0, %count : i32
// CHECK-NEXT: spv.BranchConditional %{{.*}}, ^bb2, ^bb5
spv.BranchConditional %icmp, ^body, ^merge
// CHECK-NEXT: ^bb2:
^body:
// CHECK-NEXT: spv.Constant 0
// CHECK-NEXT: spv.Store
spv.Store "Function" %jvar, %zero : i32
// CHECK-NEXT: spv.Branch ^bb3
// CHECK-NEXT: ^bb3:
// CHECK-NEXT: spv.mlir.loop control(DontUnroll)
spv.mlir.loop control(DontUnroll) {
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb1:
^header:
// CHECK-NEXT: spv.Load
%jval0 = spv.Load "Function" %jvar : i32
// CHECK-NEXT: spv.SLessThan
%jcmp = spv.SLessThan %jval0, %count : i32
// CHECK-NEXT: spv.BranchConditional %{{.*}}, ^bb2, ^bb4
spv.BranchConditional %jcmp, ^body, ^merge
// CHECK-NEXT: ^bb2:
^body:
// Do nothing
// CHECK-NEXT: spv.Branch ^bb3
spv.Branch ^continue
// CHECK-NEXT: ^bb3:
^continue:
// CHECK-NEXT: spv.Load
%jval1 = spv.Load "Function" %jvar : i32
// CHECK-NEXT: spv.Constant 1
// CHECK-NEXT: spv.IAdd
%add = spv.IAdd %jval1, %one : i32
// CHECK-NEXT: spv.Store
spv.Store "Function" %jvar, %add : i32
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb4:
^merge:
// CHECK-NEXT: spv.mlir.merge
spv.mlir.merge
} // end inner loop
// CHECK: spv.Branch ^bb4
spv.Branch ^continue
// CHECK-NEXT: ^bb4:
^continue:
// CHECK-NEXT: spv.Load
%ival1 = spv.Load "Function" %ivar : i32
// CHECK-NEXT: spv.Constant 1
// CHECK-NEXT: spv.IAdd
%add = spv.IAdd %ival1, %one : i32
// CHECK-NEXT: spv.Store
spv.Store "Function" %ivar, %add : i32
// CHECK-NEXT: spv.Branch ^bb1
spv.Branch ^header
// CHECK-NEXT: ^bb5:
// CHECK-NEXT: spv.mlir.merge
^merge:
spv.mlir.merge
} // end outer loop
spv.Return
}
spv.func @main() -> () "None" {
spv.Return
}
spv.EntryPoint "GLCompute" @main
}
// -----
// Loop with selection in its header
spv.module Physical64 OpenCL requires #spv.vce<v1.0, [Kernel, Linkage, Addresses, Int64], []> {
// CHECK-LABEL: @kernel
// CHECK-SAME: (%[[INPUT0:.+]]: i64)
spv.func @kernel(%input: i64) "None" {
// CHECK-NEXT: %[[VAR:.+]] = spv.Variable : !spv.ptr<i1, Function>
// CHECK-NEXT: spv.Branch ^[[BB0:.+]](%[[INPUT0]] : i64)
// CHECK-NEXT: ^[[BB0]](%[[INPUT1:.+]]: i64):
%cst0_i64 = spv.Constant 0 : i64
%true = spv.Constant true
%false = spv.Constant false
// CHECK-NEXT: spv.mlir.loop {
spv.mlir.loop {
// CHECK-NEXT: spv.Branch ^[[LOOP_HEADER:.+]](%[[INPUT1]] : i64)
spv.Branch ^loop_header(%input : i64)
// CHECK-NEXT: ^[[LOOP_HEADER]](%[[ARG1:.+]]: i64):
^loop_header(%arg1: i64):
// CHECK-NEXT: spv.Branch ^[[LOOP_BODY:.+]]
// CHECK-NEXT: ^[[LOOP_BODY]]:
// CHECK-NEXT: %[[C0:.+]] = spv.Constant 0 : i64
%gt = spv.SGreaterThan %arg1, %cst0_i64 : i64
// CHECK-NEXT: %[[GT:.+]] = spv.SGreaterThan %[[ARG1]], %[[C0]] : i64
// CHECK-NEXT: spv.Branch ^[[BB1:.+]]
// CHECK-NEXT: ^[[BB1]]:
%var = spv.Variable : !spv.ptr<i1, Function>
// CHECK-NEXT: spv.mlir.selection {
spv.mlir.selection {
// CHECK-NEXT: spv.BranchConditional %[[GT]], ^[[THEN:.+]], ^[[ELSE:.+]]
spv.BranchConditional %gt, ^then, ^else
// CHECK-NEXT: ^[[THEN]]:
^then:
// CHECK-NEXT: %true = spv.Constant true
// CHECK-NEXT: spv.Store "Function" %[[VAR]], %true : i1
spv.Store "Function" %var, %true : i1
// CHECK-NEXT: spv.Branch ^[[SELECTION_MERGE:.+]]
spv.Branch ^selection_merge
// CHECK-NEXT: ^[[ELSE]]:
^else:
// CHECK-NEXT: %false = spv.Constant false
// CHECK-NEXT: spv.Store "Function" %[[VAR]], %false : i1
spv.Store "Function" %var, %false : i1
// CHECK-NEXT: spv.Branch ^[[SELECTION_MERGE]]
spv.Branch ^selection_merge
// CHECK-NEXT: ^[[SELECTION_MERGE]]:
^selection_merge:
// CHECK-NEXT: spv.mlir.merge
spv.mlir.merge
// CHECK-NEXT: }
}
// CHECK-NEXT: %[[LOAD:.+]] = spv.Load "Function" %[[VAR]] : i1
%load = spv.Load "Function" %var : i1
// CHECK-NEXT: spv.BranchConditional %[[LOAD]], ^[[CONTINUE:.+]](%[[ARG1]] : i64), ^[[LOOP_MERGE:.+]]
spv.BranchConditional %load, ^continue(%arg1 : i64), ^loop_merge
// CHECK-NEXT: ^[[CONTINUE]](%[[ARG2:.+]]: i64):
^continue(%arg2: i64):
// CHECK-NEXT: %[[C0:.+]] = spv.Constant 0 : i64
// CHECK-NEXT: %[[LT:.+]] = spv.SLessThan %[[ARG2]], %[[C0]] : i64
%lt = spv.SLessThan %arg2, %cst0_i64 : i64
// CHECK-NEXT: spv.Store "Function" %[[VAR]], %[[LT]] : i1
spv.Store "Function" %var, %lt : i1
// CHECK-NEXT: spv.Branch ^[[LOOP_HEADER]](%[[ARG2]] : i64)
spv.Branch ^loop_header(%arg2 : i64)
// CHECK-NEXT: ^[[LOOP_MERGE]]:
^loop_merge:
// CHECK-NEXT: spv.mlir.merge
spv.mlir.merge
// CHECK-NEXT: }
}
// CHECK-NEXT: spv.Return
spv.Return
}
}