Igor Wodiany 7219b1e8a9
[mlir][spirv] Enable validation of selection and phi tests (#166794)
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.
2025-11-07 09:29:44 +00:00

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
}