Description
Running the following command using branch 22.0.1 and master (commit cde1be0b) of Mesa:
amber -t spv1.3 -t 1.1 reduced-cross-edge-bug.amber
gives:
[ERROR] validation layer (../src/vulkan/runtime/vk_nir.c:59):
SPIR-V offset 0: SPIR-V parsing FAILED:
In file ../src/compiler/spirv/vtn_cfg.c:662
Invalid back or cross-edge in the CFG
0 bytes into the SPIR-V binary
SPIR-V parsing FAILED:
In file ../src/compiler/spirv/vtn_cfg.c:662
Invalid back or cross-edge in the CFG
0 bytes into the SPIR-V binary
[ERROR] validation layer (../src/vulkan/runtime/vk_shader_module.c:128):
spirv_to_nir failed (VK_ERROR_UNKNOWN)
[ERROR] validation layer (../src/intel/vulkan/anv_pipeline.c:1987):
VK_ERROR_UNKNOWN
reduced-cross-edge-bug.amber: Vulkan::Calling vkCreateComputePipelines Fail
Summary of Failures:
reduced-cross-edge-bug.amber
Summary: 0 pass, 1 fail
[ERROR] validation layer (Validation):
Validation Error: [ VUID-vkDestroyDevice-device-00378 ] Object 0: handle = 0x5613ab15de10, type = VK_OBJECT_TYPE_DEVICE; Object 1: handle = 0xe7f79a0000000005, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x71500fba | OBJ ERROR : For VkDevice 0x5613ab15de10[], VkPipelineLayout 0xe7f79a0000000005[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.204.1/linux/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-00378)
This is potentially related to this bug. A patch was eventually merged for that bug so this is still valid. I think the discussion of the bug highlights the value in the formal modelling of the control flow rules.
I've compiled and validated the reduced asm from the following amber code:
#!amber
SHADER compute compute_shader SPIRV-ASM
; Follow the path:
; 8 -> <9> -> <12> -> 10
;
; 2 CFG nodes have OpBranchConditional or OpSwitch as their terminators (denoted <n>): 9 and 12.
;
; To follow this path, we need to make these decisions each time we reach 9 or 12.
; This path was generated with the seed 2641702989343433340 and has length 4.
;
; We equip the shader with 2+1 storage buffers:
; - An input storage buffer with the directions for each node 9 or 12
; - An output storage buffer that records the blocks that are executed
; SPIR-V
; Version: 1.3
; Generator: Khronos Glslang Reference Front End; 8
; Bound: 15
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %7 "main"
OpExecutionMode %7 LocalSize 1 1 1
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeBool
%true = OpConstantTrue %3
%7 = OpFunction %1 None %2
%8 = OpLabel ; validCFG/StructurallyReachableBlock$6
OpBranch %9
%9 = OpLabel ; validCFG/LoopHeader$0
OpLoopMerge %10 %11 None
OpBranchConditional %true %12 %15
%12 = OpLabel ; validCFG/StructurallyReachableBlock$4
OpBranchConditional %true %15 %10
%10 = OpLabel ; validCFG/StructurallyReachableBlock$3
OpReturn
%15 = OpLabel ; validCFG/StructurallyReachableBlock$1
OpBranch %11
%11 = OpLabel ; validCFG/StructurallyReachableBlock$0
OpBranch %17
%17 = OpLabel ; validCFG/StructurallyReachableBlock$5
OpBranch %9
OpFunctionEnd
END
PIPELINE compute pipeline
ATTACH compute_shader
END
RUN pipeline 1 1 1
I've removed many blocks from the original CFG, so it would be good if someone (@afd @vili-1 @johnwickerson) could double check this before I submit it as a bug.
There is something in the original CFG file that caught my eye, but doesn't affect the bug, however could indicate an error in some part of our code. There is a block B0 which is labelled as wholly unreachable (i.e. uses Block$X
naming), but looks structurally reachable to me because of the path SRB6->LH0->B0
. Am I missing something? I've drawn out the CFG below. Note SRBX = StructurallyReachableBlock$X and BX = Block$X
.
Here is the skeleton asm:
; SPIR-V
; Version: 1.3
; Generator: Khronos Glslang Reference Front End; 8
; Bound: 15
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %7 "main"
OpExecutionMode %7 LocalSize 1 1 1
; Below, we declare various types and variables for storage buffers.
; These decorations tell SPIR-V that the types and variables relate to storage buffers
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeBool
%4 = OpTypeInt 32 0
%true = OpConstantTrue %3
%6 = OpConstant %4 0
%7 = OpFunction %1 None %2
%8 = OpLabel ; validCFG/StructurallyReachableBlock$6
OpBranch %9
%9 = OpLabel ; validCFG/LoopHeader$0
OpLoopMerge %10 %11 None
OpBranchConditional %true %12 %13
%12 = OpLabel ; validCFG/StructurallyReachableBlock$4
OpBranchConditional %true %14 %10
%13 = OpLabel ; validCFG/Block$0
OpBranch %14
%10 = OpLabel ; validCFG/StructurallyReachableBlock$3
OpReturn
%14 = OpLabel ; validCFG/SelectionHeader$0
OpSelectionMerge %15 None
OpSwitch %6 %15 1 %16
%16 = OpLabel ; validCFG/StructurallyReachableBlock$2
OpBranchConditional %true %11 %15
%15 = OpLabel ; validCFG/StructurallyReachableBlock$1
OpBranch %11
%11 = OpLabel ; validCFG/StructurallyReachableBlock$0
OpBranch %17
%17 = OpLabel ; validCFG/StructurallyReachableBlock$5
OpBranch %9
OpFunctionEnd
Activity