Repository: KhronosGroup/SPIRV-Tools (language: python)

Issue:
spirv-opt crashes when optimizing function declarations
When spv input to spirv-opt contains functions without blocks (declarations), some optimization passes crash. It seems that they assume functions will have blocks.

```
 %normalFunc = OpFunction %int None %26
          %a = OpFunctionParameter %int
               OpFunctionEnd
```

```
; SPIR-V
; Version: 1.5
; Generator: Khronos; 40
; Bound: 36
; Schema: 0
               OpCapability Linkage
               OpCapability RayTracingKHR
               OpCapability Shader
               OpExtension "SPV_KHR_ray_tracing"
               OpMemoryModel Logical GLSL450
               OpEntryPoint AnyHitKHR %anyhit "main" %Constants %8
               OpSource Slang 1
               OpName %Payload "Payload"
               OpMemberName %Payload 0 "val"
               OpName %SLANG_ParameterGroup_Constants_std140 "SLANG_ParameterGroup_Constants_std140"
               OpMemberName %SLANG_ParameterGroup_Constants_std140 0 "x"
               OpMemberName %SLANG_ParameterGroup_Constants_std140 1 "y"
               OpName %Constants "Constants"
               OpName %a "a"
               OpName %normalFunc "normalFunc"
               OpName %a_0 "a"
               OpName %normalFuncUsesGeneric "normalFuncUsesGeneric"
               OpName %anyhit "anyhit"
               OpMemberDecorate %Payload 0 Offset 0
               OpDecorate %SLANG_ParameterGroup_Constants_std140 Block
               OpMemberDecorate %SLANG_ParameterGroup_Constants_std140 0 Offset 0
               OpMemberDecorate %SLANG_ParameterGroup_Constants_std140 1 Offset 4
               OpDecorate %Constants Binding 0
               OpDecorate %Constants DescriptorSet 0
               OpDecorate %normalFunc LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics10normalFuncp1pi_ii" Import
               OpDecorate %normalFuncUsesGeneric LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics21normalFuncUsesGenericp1pi_ii" Import
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
        %int = OpTypeInt 32 1
    %Payload = OpTypeStruct %int
%_ptr_IncomingRayPayloadKHR_Payload = OpTypePointer IncomingRayPayloadKHR %Payload
      %int_0 = OpConstant %int 0
%_ptr_IncomingRayPayloadKHR_int = OpTypePointer IncomingRayPayloadKHR %int
      %float = OpTypeFloat 32
%SLANG_ParameterGroup_Constants_std140 = OpTypeStruct %float %float
%_ptr_Uniform_SLANG_ParameterGroup_Constants_std140 = OpTypePointer Uniform %SLANG_ParameterGroup_Constants_std140
%_ptr_Uniform_float = OpTypePointer Uniform %float
      %int_1 = OpConstant %int 1
         %26 = OpTypeFunction %int %int
          %8 = OpVariable %_ptr_IncomingRayPayloadKHR_Payload IncomingRayPayloadKHR
  %Constants = OpVariable %_ptr_Uniform_SLANG_ParameterGroup_Constants_std140 Uniform
 %normalFunc = OpFunction %int None %26
          %a = OpFunctionParameter %int
               OpFunctionEnd
%normalFuncUsesGeneric = OpFunction %int None %26
        %a_0 = OpFunctionParameter %int
               OpFunctionEnd
     %anyhit = OpFunction %void None %3
          %4 = OpLabel
         %11 = OpAccessChain %_ptr_IncomingRayPayloadKHR_int %8 %int_0
         %17 = OpAccessChain %_ptr_Uniform_float %Constants %int_0
         %18 = OpLoad %float %17
         %20 = OpAccessChain %_ptr_Uniform_float %Constants %int_1
         %21 = OpLoad %float %20
         %22 = OpFMul %float %18 %21
         %23 = OpConvertFToS %int %22
         %24 = OpFunctionCall %int %normalFunc %23
         %28 = OpLoad %float %20
         %29 = OpConvertFToS %int %28
         %30 = OpFunctionCall %int %normalFuncUsesGeneric %29
         %33 = OpIAdd %int %24 %30
               OpStore %11 %33
               OpReturn
               OpFunctionEnd
```


Discussion / hints:

Fix patch:
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 7eb158d5e6..6e86c378b9 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -267,6 +267,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
 }
 
 bool AggressiveDCEPass::AggressiveDCE(Function* func) {
+  if (func->IsDeclaration()) return false;
   std::list<BasicBlock*> structured_order;
   cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
   live_local_vars_.clear();
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 80ec8da18e..65f45ec3b8 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -415,6 +415,7 @@ void MemPass::RemoveBlock(Function::iterator* bi) {
 }
 
 bool MemPass::RemoveUnreachableBlocks(Function* func) {
+  if (func->IsDeclaration()) return false;
   bool modified = false;
 
   // Mark reachable all blocks reachable from the function's entry block.


Test patch:
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index d837099fe9..a4353b1b88 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -8070,6 +8070,42 @@ TEST_F(AggressiveDCETest, StoringAPointer) {
   SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
 }
 
+TEST_F(AggressiveDCETest, FunctionDeclaration) {
+  // Ensure the optimizer can handle traversing over a function declaration
+  // 'myfunc' which has no blocks
+
+  const std::string text = R"(OpCapability Linkage
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %PSMain "main" %entryPointParam_PSMain
+OpExecutionMode %PSMain OriginUpperLeft
+OpSource Slang 1
+OpName %myfunc "myfunc"
+OpName %entryPointParam_PSMain "entryPointParam_PSMain"
+OpName %PSMain "PSMain"
+OpDecorate %myfunc LinkageAttributes "_S6myfuncp0pv4f" Import
+OpDecorate %entryPointParam_PSMain Location 0
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%8 = OpTypeFunction %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%entryPointParam_PSMain = OpVariable %_ptr_Output_v4float Output
+%myfunc = OpFunction %v4float None %8
+OpFunctionEnd
+%PSMain = OpFunction %void None %5
+%10 = OpLabel
+%11 = OpFunctionCall %v4float %myfunc
+OpStore %entryPointParam_PSMain %11
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<AggressiveDCEPass>(text, text, true, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools