Some of the test cases are failing as they introduce unstructured control flow. It would be good to catch this in MLIR, but I am not sure it is currently feasible. We could enforce the conditional branch to always be in `spirv.mlir.selection` however from my perspective it will break our downstream project as we rely on unstructured control flow, i.e., the control flow graph is structured but we do not use `spirv.mlir.selection` and `spirv.mlir.loop` as they do not currently support early exists. It seems that the support for early exists is slowly coming into MLIR so we probably can revise what is being enforced regarding control flow ops in the future.
384 lines
12 KiB
MLIR
384 lines
12 KiB
MLIR
// RUN: mlir-translate -no-implicit-module -split-input-file -test-spirv-roundtrip %s | FileCheck %s
|
|
|
|
// RUN: %if spirv-tools %{ rm -rf %t %}
|
|
// RUN: %if spirv-tools %{ mkdir %t %}
|
|
// RUN: %if spirv-tools %{ mlir-translate --no-implicit-module --serialize-spirv --split-input-file --spirv-save-validation-files-with-prefix=%t/module %s %}
|
|
// RUN: %if spirv-tools %{ spirv-val %t %}
|
|
|
|
// Test branch with one block argument
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @foo() -> () "None" {
|
|
// CHECK: %[[CST:.*]] = spirv.Constant 0
|
|
%zero = spirv.Constant 0 : i32
|
|
// CHECK-NEXT: spirv.Branch ^bb1(%[[CST]] : i32)
|
|
spirv.Branch ^bb1(%zero : i32)
|
|
// CHECK-NEXT: ^bb1(%{{.*}}: i32):
|
|
^bb1(%arg0: i32):
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test branch with multiple block arguments
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @foo() -> () "None" {
|
|
// CHECK: %[[ZERO:.*]] = spirv.Constant 0
|
|
%zero = spirv.Constant 0 : i32
|
|
// CHECK-NEXT: %[[ONE:.*]] = spirv.Constant 1
|
|
%one = spirv.Constant 1.0 : f32
|
|
// CHECK-NEXT: spirv.Branch ^bb1(%[[ZERO]], %[[ONE]] : i32, f32)
|
|
spirv.Branch ^bb1(%zero, %one : i32, f32)
|
|
|
|
// CHECK-NEXT: ^bb1(%{{.*}}: i32, %{{.*}}: f32): // pred: ^bb0
|
|
^bb1(%arg0: i32, %arg1: f32):
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test using block arguments within branch
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @foo() -> () "None" {
|
|
// CHECK: %[[CST0:.*]] = spirv.Constant 0
|
|
%zero = spirv.Constant 0 : i32
|
|
// CHECK-NEXT: spirv.Branch ^bb1(%[[CST0]] : i32)
|
|
spirv.Branch ^bb1(%zero : i32)
|
|
|
|
// CHECK-NEXT: ^bb1(%[[ARG:.*]]: i32):
|
|
^bb1(%arg0: i32):
|
|
// CHECK-NEXT: %[[ADD:.*]] = spirv.IAdd %[[ARG]], %[[ARG]] : i32
|
|
%0 = spirv.IAdd %arg0, %arg0 : i32
|
|
// CHECK-NEXT: %[[CST1:.*]] = spirv.Constant 0
|
|
// CHECK-NEXT: spirv.Branch ^bb2(%[[CST1]], %[[ADD]] : i32, i32)
|
|
spirv.Branch ^bb2(%zero, %0 : i32, i32)
|
|
|
|
// CHECK-NEXT: ^bb2(%{{.*}}: i32, %{{.*}}: i32):
|
|
^bb2(%arg1: i32, %arg2: i32):
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test block not following domination order
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @foo() -> () "None" {
|
|
// CHECK: spirv.Branch ^bb1
|
|
spirv.Branch ^bb1
|
|
|
|
// CHECK-NEXT: ^bb1:
|
|
// CHECK-NEXT: %[[ZERO:.*]] = spirv.Constant 0
|
|
// CHECK-NEXT: %[[ONE:.*]] = spirv.Constant 1
|
|
// CHECK-NEXT: spirv.Branch ^bb2(%[[ZERO]], %[[ONE]] : i32, f32)
|
|
|
|
// CHECK-NEXT: ^bb2(%{{.*}}: i32, %{{.*}}: f32):
|
|
^bb2(%arg0: i32, %arg1: f32):
|
|
// CHECK-NEXT: spirv.Return
|
|
spirv.Return
|
|
|
|
// This block is reordered to follow domination order.
|
|
^bb1:
|
|
%zero = spirv.Constant 0 : i32
|
|
%one = spirv.Constant 1.0 : f32
|
|
spirv.Branch ^bb2(%zero, %one : i32, f32)
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test multiple predecessors
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @foo() -> () "None" {
|
|
%var = spirv.Variable : !spirv.ptr<i32, Function>
|
|
|
|
// CHECK: spirv.mlir.selection
|
|
spirv.mlir.selection {
|
|
%true = spirv.Constant true
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^bb1, ^bb2
|
|
spirv.BranchConditional %true, ^true, ^false
|
|
|
|
// CHECK-NEXT: ^bb1:
|
|
^true:
|
|
// CHECK-NEXT: %[[ZERO:.*]] = spirv.Constant 0
|
|
%zero = spirv.Constant 0 : i32
|
|
// CHECK-NEXT: spirv.Branch ^bb3(%[[ZERO]] : i32)
|
|
spirv.Branch ^phi(%zero: i32)
|
|
|
|
// CHECK-NEXT: ^bb2:
|
|
^false:
|
|
// CHECK-NEXT: %[[ONE:.*]] = spirv.Constant 1
|
|
%one = spirv.Constant 1 : i32
|
|
// CHECK-NEXT: spirv.Branch ^bb3(%[[ONE]] : i32)
|
|
spirv.Branch ^phi(%one: i32)
|
|
|
|
// CHECK-NEXT: ^bb3(%[[ARG:.*]]: i32):
|
|
^phi(%arg: i32):
|
|
// CHECK-NEXT: spirv.Store "Function" %{{.*}}, %[[ARG]] : i32
|
|
spirv.Store "Function" %var, %arg : i32
|
|
// CHECK-NEXT: spirv.Return
|
|
spirv.Return
|
|
|
|
// CHECK-NEXT: ^bb4:
|
|
^merge:
|
|
// CHECK-NEXT: spirv.mlir.merge
|
|
spirv.mlir.merge
|
|
}
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test nested loops with block arguments
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.GlobalVariable @__builtin_var_NumWorkgroups__ built_in("NumWorkgroups") : !spirv.ptr<vector<3xi32>, Input>
|
|
spirv.GlobalVariable @__builtin_var_WorkgroupId__ built_in("WorkgroupId") : !spirv.ptr<vector<3xi32>, Input>
|
|
spirv.func @fmul_kernel() "None" {
|
|
%3 = spirv.Constant 12 : i32
|
|
%4 = spirv.Constant 32 : i32
|
|
%5 = spirv.Constant 4 : i32
|
|
%6 = spirv.mlir.addressof @__builtin_var_WorkgroupId__ : !spirv.ptr<vector<3xi32>, Input>
|
|
%7 = spirv.Load "Input" %6 : vector<3xi32>
|
|
%8 = spirv.CompositeExtract %7[0 : i32] : vector<3xi32>
|
|
%9 = spirv.mlir.addressof @__builtin_var_WorkgroupId__ : !spirv.ptr<vector<3xi32>, Input>
|
|
%10 = spirv.Load "Input" %9 : vector<3xi32>
|
|
%11 = spirv.CompositeExtract %10[1 : i32] : vector<3xi32>
|
|
%18 = spirv.mlir.addressof @__builtin_var_NumWorkgroups__ : !spirv.ptr<vector<3xi32>, Input>
|
|
%19 = spirv.Load "Input" %18 : vector<3xi32>
|
|
%20 = spirv.CompositeExtract %19[0 : i32] : vector<3xi32>
|
|
%21 = spirv.mlir.addressof @__builtin_var_NumWorkgroups__ : !spirv.ptr<vector<3xi32>, Input>
|
|
%22 = spirv.Load "Input" %21 : vector<3xi32>
|
|
%23 = spirv.CompositeExtract %22[1 : i32] : vector<3xi32>
|
|
%30 = spirv.IMul %11, %4 : i32
|
|
%31 = spirv.IMul %23, %4 : i32
|
|
|
|
// CHECK: spirv.Branch ^[[FN_BB:.*]](%{{.*}} : i32)
|
|
// CHECK: ^[[FN_BB]](%[[FN_BB_ARG:.*]]: i32):
|
|
// CHECK: spirv.mlir.loop {
|
|
spirv.mlir.loop {
|
|
// CHECK: spirv.Branch ^bb1(%[[FN_BB_ARG]] : i32)
|
|
spirv.Branch ^bb1(%30 : i32)
|
|
// CHECK: ^[[LP1_HDR:.*]](%[[LP1_HDR_ARG:.*]]: i32):
|
|
^bb1(%32: i32):
|
|
// CHECK: spirv.SLessThan
|
|
%33 = spirv.SLessThan %32, %3 : i32
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^[[LP1_BDY:.*]], ^[[LP1_MG:.*]]
|
|
spirv.BranchConditional %33, ^bb2, ^bb3
|
|
// CHECK: ^[[LP1_BDY]]:
|
|
^bb2:
|
|
// CHECK: %[[MUL:.*]] = spirv.IMul
|
|
%34 = spirv.IMul %8, %5 : i32
|
|
// CHECK: spirv.IMul
|
|
%35 = spirv.IMul %20, %5 : i32
|
|
// CHECK: spirv.Branch ^[[LP1_CNT:.*]](%[[MUL]] : i32)
|
|
// CHECK: ^[[LP1_CNT]](%[[LP1_CNT_ARG:.*]]: i32):
|
|
// CHECK: spirv.mlir.loop {
|
|
spirv.mlir.loop {
|
|
// CHECK: spirv.Branch ^[[LP2_HDR:.*]](%[[LP1_CNT_ARG]] : i32)
|
|
spirv.Branch ^bb1(%34 : i32)
|
|
// CHECK: ^[[LP2_HDR]](%[[LP2_HDR_ARG:.*]]: i32):
|
|
^bb1(%37: i32):
|
|
// CHECK: spirv.SLessThan %[[LP2_HDR_ARG]]
|
|
%38 = spirv.SLessThan %37, %5 : i32
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^[[LP2_BDY:.*]], ^[[LP2_MG:.*]]
|
|
spirv.BranchConditional %38, ^bb2, ^bb3
|
|
// CHECK: ^[[LP2_BDY]]:
|
|
^bb2:
|
|
// CHECK: %[[ADD1:.*]] = spirv.IAdd
|
|
%48 = spirv.IAdd %37, %35 : i32
|
|
// CHECK: spirv.Branch ^[[LP2_HDR]](%[[ADD1]] : i32)
|
|
spirv.Branch ^bb1(%48 : i32)
|
|
// CHECK: ^[[LP2_MG]]:
|
|
^bb3:
|
|
// CHECK: spirv.mlir.merge
|
|
spirv.mlir.merge
|
|
}
|
|
// CHECK: %[[ADD2:.*]] = spirv.IAdd %[[LP1_HDR_ARG]]
|
|
%36 = spirv.IAdd %32, %31 : i32
|
|
// CHECK: spirv.Branch ^[[LP1_HDR]](%[[ADD2]] : i32)
|
|
spirv.Branch ^bb1(%36 : i32)
|
|
// CHECK: ^[[LP1_MG]]:
|
|
^bb3:
|
|
// CHECK: spirv.mlir.merge
|
|
spirv.mlir.merge
|
|
}
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.EntryPoint "GLCompute" @fmul_kernel, @__builtin_var_WorkgroupId__, @__builtin_var_NumWorkgroups__
|
|
spirv.ExecutionMode @fmul_kernel "LocalSize", 32, 1, 1
|
|
}
|
|
|
|
// -----
|
|
|
|
// Test back-to-back loops with block arguments
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
spirv.func @fmul_kernel() "None" {
|
|
%cst4 = spirv.Constant 4 : i32
|
|
|
|
%val1 = spirv.Constant 43 : i32
|
|
%val2 = spirv.Constant 44 : i32
|
|
|
|
// CHECK: spirv.Constant 43
|
|
// CHECK-NEXT: spirv.Branch ^[[BB1:.+]](%{{.+}} : i32)
|
|
// CHECK-NEXT: ^[[BB1]](%{{.+}}: i32):
|
|
// CHECK-NEXT: spirv.mlir.loop
|
|
spirv.mlir.loop { // loop 1
|
|
spirv.Branch ^bb1(%val1 : i32)
|
|
^bb1(%loop1_bb_arg: i32):
|
|
%loop1_lt = spirv.SLessThan %loop1_bb_arg, %cst4 : i32
|
|
spirv.BranchConditional %loop1_lt, ^bb2, ^bb3
|
|
^bb2:
|
|
%loop1_add = spirv.IAdd %loop1_bb_arg, %cst4 : i32
|
|
spirv.Branch ^bb1(%loop1_add : i32)
|
|
^bb3:
|
|
spirv.mlir.merge
|
|
}
|
|
|
|
// CHECK: spirv.Constant 44
|
|
// CHECK-NEXT: spirv.Branch ^[[BB2:.+]](%{{.+}} : i32)
|
|
// CHECK-NEXT: ^[[BB2]](%{{.+}}: i32):
|
|
// CHECK-NEXT: spirv.mlir.loop
|
|
spirv.mlir.loop { // loop 2
|
|
spirv.Branch ^bb1(%val2 : i32)
|
|
^bb1(%loop2_bb_arg: i32):
|
|
%loop2_lt = spirv.SLessThan %loop2_bb_arg, %cst4 : i32
|
|
spirv.BranchConditional %loop2_lt, ^bb2, ^bb3
|
|
^bb2:
|
|
%loop2_add = spirv.IAdd %loop2_bb_arg, %cst4 : i32
|
|
spirv.Branch ^bb1(%loop2_add : i32)
|
|
^bb3:
|
|
spirv.mlir.merge
|
|
}
|
|
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.EntryPoint "GLCompute" @fmul_kernel
|
|
spirv.ExecutionMode @fmul_kernel "LocalSize", 32, 1, 1
|
|
}
|
|
|
|
// -----
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
// CHECK-LABEL: @cond_branch_true_argument
|
|
spirv.func @cond_branch_true_argument() -> () "None" {
|
|
%true = spirv.Constant true
|
|
%zero = spirv.Constant 0 : i32
|
|
%one = spirv.Constant 1 : i32
|
|
spirv.mlir.selection {
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^[[true1:.*]](%{{.*}}, %{{.*}} : i32, i32), ^[[false1:.*]]
|
|
spirv.BranchConditional %true, ^true1(%zero, %zero: i32, i32), ^false1
|
|
// CHECK: [[true1]](%{{.*}}: i32, %{{.*}}: i32)
|
|
^true1(%arg0: i32, %arg1: i32):
|
|
spirv.Return
|
|
// CHECK: [[false1]]:
|
|
^false1:
|
|
spirv.Return
|
|
^merge:
|
|
spirv.mlir.merge
|
|
}
|
|
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
// CHECK-LABEL: @cond_branch_false_argument
|
|
spirv.func @cond_branch_false_argument() -> () "None" {
|
|
%true = spirv.Constant true
|
|
%zero = spirv.Constant 0 : i32
|
|
%one = spirv.Constant 1 : i32
|
|
spirv.mlir.selection {
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^[[true1:.*]], ^[[false1:.*]](%{{.*}}, %{{.*}} : i32, i32)
|
|
spirv.BranchConditional %true, ^true1, ^false1(%zero, %zero: i32, i32)
|
|
// CHECK: [[true1]]:
|
|
^true1:
|
|
spirv.Return
|
|
// CHECK: [[false1]](%{{.*}}: i32, %{{.*}}: i32):
|
|
^false1(%arg0: i32, %arg1: i32):
|
|
spirv.Return
|
|
^merge:
|
|
spirv.mlir.merge
|
|
}
|
|
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|
|
|
|
// -----
|
|
|
|
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
|
|
// CHECK-LABEL: @cond_branch_true_and_false_argument
|
|
spirv.func @cond_branch_true_and_false_argument() -> () "None" {
|
|
%true = spirv.Constant true
|
|
%zero = spirv.Constant 0 : i32
|
|
%one = spirv.Constant 1 : i32
|
|
spirv.mlir.selection {
|
|
// CHECK: spirv.BranchConditional %{{.*}}, ^[[true1:.*]](%{{.*}} : i32), ^[[false1:.*]](%{{.*}}, %{{.*}} : i32, i32)
|
|
spirv.BranchConditional %true, ^true1(%one: i32), ^false1(%zero, %zero: i32, i32)
|
|
// CHECK: [[true1]](%{{.*}}: i32):
|
|
^true1(%arg0: i32):
|
|
spirv.Return
|
|
// CHECK: [[false1]](%{{.*}}: i32, %{{.*}}: i32):
|
|
^false1(%arg1: i32, %arg2: i32):
|
|
spirv.Return
|
|
^merge:
|
|
spirv.mlir.merge
|
|
}
|
|
|
|
spirv.Return
|
|
}
|
|
|
|
spirv.func @main() -> () "None" {
|
|
spirv.Return
|
|
}
|
|
spirv.EntryPoint "GLCompute" @main
|
|
}
|