diff --git a/source/val/validate.cpp b/source/val/validate.cpp index b145b19d8..52009fc3d 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -281,6 +281,12 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( << "Invalid SPIR-V magic number."; } + if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "WebGPU requires SPIR-V to be little endian."; + } + spv_header_t header; if (spvBinaryHeaderGet(binary.get(), endian, &header)) { return DiagnosticStream(position, context.consumer, "", diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp index 48ea21db2..c55f9276d 100644 --- a/test/val/val_webgpu_test.cpp +++ b/test/val/val_webgpu_test.cpp @@ -276,6 +276,63 @@ TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) { "\"SPV_KHR_8bit_storage\"\n")); } +spv_binary GenerateTrivialBinary(bool need_little_endian) { + // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all + // the required boilerplate and a trivial entry point function. + static const uint8_t binary_bytes[] = { + // clang-format off + 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00, + 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, + 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, + 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 + // clang-format on + }; + static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t); + std::unique_ptr result(new spv_binary_t); + if (!result) return nullptr; + + result->wordCount = word_count; + result->code = new uint32_t[word_count]; + if (!result->code) return nullptr; + + if (need_little_endian) { + memcpy(result->code, binary_bytes, sizeof(binary_bytes)); + } else { + uint8_t* code_bytes = reinterpret_cast(result->code); + for (size_t word = 0; word < word_count; ++word) { + code_bytes[4 * word] = binary_bytes[4 * word + 3]; + code_bytes[4 * word + 1] = binary_bytes[4 * word + 2]; + code_bytes[4 * word + 2] = binary_bytes[4 * word + 1]; + code_bytes[4 * word + 3] = binary_bytes[4 * word]; + } + } + + return result.release(); +} + +TEST_F(ValidateWebGPU, LittleEndianGood) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, BigEndianBad) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(false); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU requires SPIR-V to be little endian.")); +} + } // namespace } // namespace val } // namespace spvtools