Add undef for inlined void function (#3720)

It is possible that the result of a void function call is used.  In case
it is used, we need something that still defines its id after inlining.
We use an undef for that purpose.

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/3704
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index ef94d0d..6021a7c 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -619,6 +619,14 @@
     assert(resId != 0);
     AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr,
             call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope());
+  } else {
+    // Even though it is very unlikely, it is possible that the result id of
+    // the void-function call is used, so we need to generate an instruction
+    // with that result id.
+    std::unique_ptr<Instruction> undef_inst(
+        new Instruction(context(), SpvOpUndef, call_inst_itr->type_id(),
+                        call_inst_itr->result_id(), {}));
+    context()->AddGlobalValue(std::move(undef_inst));
   }
 
   // Move instructions of original caller block after call instruction.
diff --git a/test/opt/inline_opaque_test.cpp b/test/opt/inline_opaque_test.cpp
index b8d2dfa..47e0533 100644
--- a/test/opt/inline_opaque_test.cpp
+++ b/test/opt/inline_opaque_test.cpp
@@ -90,7 +90,8 @@
 )";
 
   const std::string after =
-      R"(%main = OpFunction %void None %12
+      R"(%34 = OpUndef %void
+%main = OpFunction %void None %12
 %28 = OpLabel
 %s0 = OpVariable %_ptr_Function_S_t Function
 %param = OpVariable %_ptr_Function_S_t Function
@@ -289,7 +290,8 @@
 )";
 
   const std::string after =
-      R"(%main2 = OpFunction %void None %13
+      R"(%35 = OpUndef %void
+%main2 = OpFunction %void None %13
 %29 = OpLabel
 %s0 = OpVariable %_ptr_Function_S_t Function
 %param = OpVariable %_ptr_Function_S_t Function
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index 572e7b0..951721b 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -381,6 +381,7 @@
 
   const std::vector<const char*> after = {
       // clang-format off
+         "%26 = OpUndef %void",
        "%main = OpFunction %void None %11",
          "%23 = OpLabel",
           "%b = OpVariable %_ptr_Function_v4float Function",
@@ -1503,11 +1504,11 @@
 %bool = OpTypeBool
 %true = OpConstantTrue %bool
 %void = OpTypeVoid
+%5 = OpTypeFunction %void
 )";
 
   const std::string nonEntryFuncs =
-      R"(%5 = OpTypeFunction %void
-%6 = OpFunction %void None %5
+      R"(%6 = OpFunction %void None %5
 %7 = OpLabel
 OpBranch %8
 %8 = OpLabel
@@ -1542,9 +1543,11 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
+  const std::string undef = "%11 = OpUndef %void\n";
+
+  SinglePassRunAndCheck<InlineExhaustivePass>(
+      predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after,
+      false, true);
 }
 
 TEST_F(InlineTest, MultiBlockLoopHeaderCallsMultiBlockCallee) {
@@ -1619,9 +1622,10 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
+  const std::string undef = "%20 = OpUndef %void\n";
+  SinglePassRunAndCheck<InlineExhaustivePass>(
+      predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after,
+      false, true);
 }
 
 TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge) {
@@ -1707,10 +1711,10 @@
 OpReturn
 OpFunctionEnd
 )";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
+  const std::string undef = "%15 = OpUndef %void\n";
+  SinglePassRunAndCheck<InlineExhaustivePass>(
+      predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after,
+      false, true);
 }
 
 TEST_F(InlineTest,
@@ -1789,9 +1793,10 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
+  const std::string undef = "%20 = OpUndef %void\n";
+  SinglePassRunAndCheck<InlineExhaustivePass>(
+      predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after,
+      false, true);
 }
 
 TEST_F(InlineTest, NonInlinableCalleeWithSingleReturn) {
@@ -2164,6 +2169,7 @@
 OpName %foo_entry "foo_entry"
 %void = OpTypeVoid
 %void_fn = OpTypeFunction %void
+%3 = OpUndef %void
 %foo = OpFunction %void None %void_fn
 %foo_entry = OpLabel
 OpReturn
@@ -2437,6 +2443,7 @@
 %3 = OpTypeFunction %void
 %bool = OpTypeBool
 %true = OpConstantTrue %bool
+%16 = OpUndef %void
 %main = OpFunction %void None %3
 %5 = OpLabel
 OpKill
@@ -2534,6 +2541,7 @@
 %3 = OpTypeFunction %void
 %bool = OpTypeBool
 %true = OpConstantTrue %bool
+%16 = OpUndef %void
 %main = OpFunction %void None %3
 %5 = OpLabel
 OpTerminateInvocation
@@ -2761,6 +2769,7 @@
 %uint_0 = OpConstant %uint 0
 %false = OpConstantFalse %bool
 %_ptr_Function_bool = OpTypePointer Function %bool
+%11 = OpUndef %void
 %foo_ = OpFunction %void None %4
 %7 = OpLabel
 %18 = OpVariable %_ptr_Function_bool Function %false
@@ -3849,6 +3858,35 @@
   SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
 }
 
+TEST_F(InlineTest, UsingVoidFunctionResult) {
+  const std::string text = R"(
+; CHECK: [[undef:%\w+]] = OpUndef %void
+; CHECK: OpFunction
+; CHECK: OpCopyObject %void [[undef]]
+; CHECK: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpFunctionCall %2 %6
+          %9 = OpCopyObject %2 %8
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Empty modules