Check that if A calls B, B is defined before A for WebGPU (#2169)
Fixes #2067
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 5236e14..abc2f31 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -279,7 +279,15 @@
<< "A FunctionCall must happen within a function body.";
}
- vstate->AddFunctionCallTarget(inst->GetOperandAs<uint32_t>(2));
+ const auto called_id = inst->GetOperandAs<uint32_t>(2);
+ if (spvIsWebGPUEnv(context.target_env) &&
+ !vstate->IsFunctionCallDefined(called_id)) {
+ return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction)
+ << "For WebGPU, functions need to be defined before being "
+ "called.";
+ }
+
+ vstate->AddFunctionCallTarget(called_id);
}
if (vstate->in_function_body()) {
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 50f5812..cbd9a34 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -280,6 +280,9 @@
return (function_call_targets_.find(id) != function_call_targets_.end());
}
+ bool IsFunctionCallDefined(const uint32_t id) {
+ return (id_to_function_.find(id) != id_to_function_.end());
+ }
/// Registers the capability and its dependent capabilities
void RegisterCapability(SpvCapability cap);
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index e67c840..e502d8c 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -648,6 +648,57 @@
HasSubstr("ModuleProcessed cannot appear in a function declaration"));
}
+TEST_F(ValidateLayout, WebGPUCallerBeforeCalleeBad) {
+ char str[] = R"(
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%1 = OpLabel
+%2 = OpFunctionCall %void %callee
+ OpReturn
+ OpFunctionEnd
+%callee = OpFunction %void None %voidfn
+%3 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("For WebGPU, functions need to be defined before being "
+ "called.\n %5 = OpFunctionCall %void %6\n"));
+}
+
+TEST_F(ValidateLayout, WebGPUCalleeBeforeCallerGood) {
+ char str[] = R"(
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%callee = OpFunction %void None %voidfn
+%3 = OpLabel
+ OpReturn
+ OpFunctionEnd
+%main = OpFunction %void None %voidfn
+%1 = OpLabel
+%2 = OpFunctionCall %void %callee
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
// TODO(umar): Test optional instructions
} // namespace