// Copyright 2025 The Khronos Group Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "gmock/gmock.h" #include "test/link/linker_fixture.h" namespace spvtools { namespace { using FunctionVariants = spvtest::LinkerTest; TEST_F(FunctionVariants, Dot4) { constexpr const char* const dot4_fp32 = R"( OpCapability Kernel OpCapability Addresses OpCapability Int8 OpCapability Int64 OpCapability Linkage %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %2 "dot4" %3 OpExecutionMode %2 ContractionOff OpSource OpenCL_C 102000 OpName %2 "dot4" OpName %3 "__spirv_BuiltInGlobalInvocationId" OpName %4 "entry" %5 = OpTypeFloat 32 %6 = OpTypePointer CrossWorkgroup %5 %7 = OpTypeInt 8 0 %8 = OpTypePointer CrossWorkgroup %7 %9 = OpTypeVoid %10 = OpTypeFunction %9 %6 %6 %8 %11 = OpTypeInt 64 0 %12 = OpTypeInt 32 0 %13 = OpTypeVector %11 3 %14 = OpTypePointer Input %13 %15 = OpTypeVector %5 4 %16 = OpConstant %11 30 %17 = OpConstant %11 32 %18 = OpConstant %12 2 %3 = OpVariable %14 Input %2 = OpFunction %9 None %10 %19 = OpFunctionParameter %6 %20 = OpFunctionParameter %6 %21 = OpFunctionParameter %8 %4 = OpLabel %22 = OpLoad %13 %3 Aligned 1 %23 = OpCompositeExtract %11 %22 0 %24 = OpUConvert %12 %23 %25 = OpShiftLeftLogical %12 %24 %18 %26 = OpSConvert %11 %25 %27 = OpExtInst %15 %1 vloadn %26 %19 4 %28 = OpExtInst %15 %1 vloadn %26 %20 4 %29 = OpDot %5 %27 %28 %30 = OpShiftLeftLogical %11 %23 %17 %31 = OpShiftRightArithmetic %11 %30 %16 %32 = OpInBoundsPtrAccessChain %8 %21 %31 %33 = OpBitcast %6 %32 OpStore %33 %29 Aligned 4 OpReturn OpFunctionEnd )"; constexpr const char* const dot4_fp16 = R"( OpCapability Kernel OpCapability Addresses OpCapability Int8 OpCapability Int64 OpCapability Float16 OpCapability Linkage %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %2 "dot4" %3 OpExecutionMode %2 ContractionOff OpSource OpenCL_C 102000 OpName %2 "dot4" OpName %3 "__spirv_BuiltInGlobalInvocationId" OpName %4 "entry" %5 = OpTypeFloat 16 %6 = OpTypePointer CrossWorkgroup %5 %7 = OpTypeInt 8 0 %8 = OpTypePointer CrossWorkgroup %7 %9 = OpTypeVoid %10 = OpTypeFunction %9 %6 %6 %8 %11 = OpTypeInt 64 0 %12 = OpTypeInt 32 0 %13 = OpTypeVector %11 3 %14 = OpTypePointer Input %13 %15 = OpTypeVector %5 4 %16 = OpConstant %11 31 %17 = OpConstant %11 32 %18 = OpConstant %12 2 %3 = OpVariable %14 Input %2 = OpFunction %9 None %10 %19 = OpFunctionParameter %6 %20 = OpFunctionParameter %6 %21 = OpFunctionParameter %8 %4 = OpLabel %22 = OpLoad %13 %3 Aligned 1 %23 = OpCompositeExtract %11 %22 0 %24 = OpUConvert %12 %23 %25 = OpShiftLeftLogical %12 %24 %18 %26 = OpSConvert %11 %25 %27 = OpExtInst %15 %1 vloadn %26 %19 4 %28 = OpExtInst %15 %1 vloadn %26 %20 4 %29 = OpDot %5 %27 %28 %30 = OpShiftLeftLogical %11 %23 %17 %31 = OpShiftRightArithmetic %11 %30 %16 %32 = OpInBoundsPtrAccessChain %8 %21 %31 %33 = OpBitcast %6 %32 OpStore %33 %29 Aligned 4 OpReturn OpFunctionEnd )"; // clang-format off const std::vector expected_lines = { "OpCapability FunctionVariantsINTEL", "OpCapability SpecConditionalINTEL", "OpCapability Kernel", "OpCapability Addresses", "OpCapability Int8", "OpCapability Int64", "OpCapability Linkage", "OpConditionalCapabilityINTEL %dot4_fp16_spv Float16", "OpExtension \"SPV_INTEL_function_variants\"", "OpConditionalEntryPointINTEL %dot4_fp32_spv Kernel %dot4 \"dot4\" %__spirv_BuiltInGlobalInvocationId", "OpConditionalEntryPointINTEL %dot4_fp16_spv Kernel %dot4_0 \"dot4\" %__spirv_BuiltInGlobalInvocationId_0", "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"", "OpDecorate %ulong_30 ConditionalINTEL %dot4_fp32_spv", "OpDecorate %ulong_32 ConditionalINTEL %dot4_fp32_spv", "OpDecorate %uint_2 ConditionalINTEL %dot4_fp32_spv", "OpDecorate %__spirv_BuiltInGlobalInvocationId ConditionalINTEL %dot4_fp32_spv", "OpDecorate %dot4 ConditionalINTEL %dot4_fp32_spv", "OpDecorate %ulong_31 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %ulong_32_0 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %uint_2_0 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %__spirv_BuiltInGlobalInvocationId_0 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %dot4_0 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %float ConditionalINTEL %dot4_fp32_spv", "OpDecorate %_ptr_CrossWorkgroup_float ConditionalINTEL %dot4_fp32_spv", "OpDecorate %18 ConditionalINTEL %dot4_fp32_spv", "OpDecorate %v4float ConditionalINTEL %dot4_fp32_spv", "OpDecorate %half ConditionalINTEL %dot4_fp16_spv", "OpDecorate %_ptr_CrossWorkgroup_half ConditionalINTEL %dot4_fp16_spv", "OpDecorate %22 ConditionalINTEL %dot4_fp16_spv", "OpDecorate %v4half ConditionalINTEL %dot4_fp16_spv", "%float = OpTypeFloat 32", "%_ptr_CrossWorkgroup_float = OpTypePointer CrossWorkgroup %float", "%18 = OpTypeFunction %void %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_uchar", "%v4float = OpTypeVector %float 4", "%ulong_30 = OpConstant %ulong 30", "%ulong_32 = OpConstant %ulong 32", "%uint_2 = OpConstant %uint 2", "%__spirv_BuiltInGlobalInvocationId = OpVariable %_ptr_Input_v3ulong Input", "%bool = OpTypeBool", "%32 = OpSpecConstantArchitectureINTEL %bool 2 3 174 0", "%33 = OpSpecConstantTargetINTEL %bool 7", "%34 = OpSpecConstantTargetINTEL %bool 8", "%35 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8", "%36 = OpSpecConstantOp %bool LogicalOr %33 %34", "%37 = OpSpecConstantOp %bool LogicalAnd %35 %32", "%38 = OpSpecConstantOp %bool LogicalAnd %37 %36", "%half = OpTypeFloat 16", "%_ptr_CrossWorkgroup_half = OpTypePointer CrossWorkgroup %half", "%22 = OpTypeFunction %void %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_uchar", "%v4half = OpTypeVector %half 4", "%ulong_31 = OpConstant %ulong 31", "%ulong_32_0 = OpConstant %ulong 32", "%uint_2_0 = OpConstant %uint 2", "%__spirv_BuiltInGlobalInvocationId_0 = OpVariable %_ptr_Input_v3ulong Input", "%39 = OpSpecConstantArchitectureINTEL %bool 2 3 174 4", "%40 = OpSpecConstantTargetINTEL %bool 7", "%41 = OpSpecConstantTargetINTEL %bool 8", "%42 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Float16 Int64 Int8", "%43 = OpSpecConstantOp %bool LogicalOr %40 %41", "%44 = OpSpecConstantOp %bool LogicalAnd %42 %39", "%dot4_fp16_spv = OpSpecConstantOp %bool LogicalAnd %44 %43", "%45 = OpSpecConstantOp %bool LogicalNot %dot4_fp16_spv", "%dot4_fp32_spv = OpSpecConstantOp %bool LogicalAnd %38 %45", "%dot4 = OpFunction %void None %18", "%56 = OpDot %float %54 %55", "%dot4_0 = OpFunction %void None %22", "%71 = OpDot %half %69 %70", }; // clang-format on const std::string targets_csv = "module,target,features\n" "dot4_fp32.spv,7,\n" "dot4_fp32.spv,8,\n" "dot4_fp16.spv,7,\n" "dot4_fp16.spv,8,\n"; const std::string architectures_csv = "module,category,family,op,architecture\n" "dot4_fp32.spv,2,3,174,0\n" "dot4_fp16.spv,2,3,174,4\n"; const std::vector sources = {dot4_fp32, dot4_fp16}; const std::vector in_files = {"dot4_fp32.spv", "dot4_fp16.spv"}; LinkerOptions options; options.SetInFiles(in_files); options.SetFnVarTargetsCsv(targets_csv); options.SetFnVarArchitecturesCsv(architectures_csv); options.SetHasFnVarCapabilities(true); options.SetCreateLibrary(true); options.SetVerifyIds(true); spvtest::Binary linked_binary; spv_result_t res = AssembleAndLink(sources, &linked_binary, options); EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); EXPECT_TRUE(Validate(linked_binary)); std::string linked_asm; res = Disassemble(linked_binary, &linked_asm); EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); for (const auto& expected : expected_lines) { EXPECT_THAT(linked_asm, testing::HasSubstr(expected)); } } TEST_F(FunctionVariants, FAddAsm) { constexpr const char* const foo_base = R"( OpCapability Kernel OpCapability Addresses OpCapability Int8 OpCapability Int64 OpCapability Linkage %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpSource OpenCL_CPP 100000 OpName %2 "foo" OpName %3 "add" OpName %4 "entry" OpName %5 "work" OpName %6 "call" OpName %7 "entry" OpDecorate %2 LinkageAttributes "foo" Export OpDecorate %5 LinkageAttributes "work" Export %8 = OpTypeFloat 32 %9 = OpTypePointer Function %8 %10 = OpTypeFunction %8 %9 %9 %9 %11 = OpTypeInt 8 0 %12 = OpTypePointer Function %11 %13 = OpTypeInt 64 0 %14 = OpConstant %13 28 %15 = OpConstant %13 24 %16 = OpConstant %13 20 %17 = OpConstant %13 16 %18 = OpConstant %13 12 %19 = OpConstant %13 8 %20 = OpConstant %13 4 %2 = OpFunction %8 DontInline %10 %21 = OpFunctionParameter %9 %22 = OpFunctionParameter %9 %23 = OpFunctionParameter %9 %4 = OpLabel %24 = OpLoad %8 %21 Aligned 4 %25 = OpLoad %8 %22 Aligned 4 %3 = OpFAdd %8 %24 %25 OpStore %23 %3 Aligned 4 OpReturnValue %3 OpFunctionEnd %5 = OpFunction %8 None %10 %26 = OpFunctionParameter %9 %27 = OpFunctionParameter %9 %28 = OpFunctionParameter %9 %7 = OpLabel %6 = OpFunctionCall %8 %2 %26 %27 %28 %29 = OpFDiv %8 %6 %6 %30 = OpFDiv %8 %29 %6 OpReturnValue %30 OpFunctionEnd )"; constexpr const char* const foo_asm = R"( OpCapability Kernel OpCapability Addresses OpCapability Linkage OpCapability AsmINTEL OpExtension "SPV_INTEL_inline_assembly" %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpSource OpenCL_CPP 100000 OpName %2 "a" OpName %3 "b" OpName %4 "c" OpName %5 "foo" OpName %6 "add" OpName %7 "entry" OpDecorate %2 FuncParamAttr NoWrite OpDecorate %2 FuncParamAttr NoAlias OpDecorate %3 FuncParamAttr NoWrite OpDecorate %3 FuncParamAttr NoAlias OpDecorate %4 FuncParamAttr NoAlias OpDecorate %5 LinkageAttributes "foo" Export OpDecorate %8 SideEffectsINTEL %9 = OpTypeFloat 32 %10 = OpTypePointer Function %9 %11 = OpTypeFunction %9 %10 %10 %10 %12 = OpTypeVoid %13 = OpTypeFunction %12 %14 = OpAsmTargetINTEL "spirv64-unknown-unknown" %8 = OpAsmINTEL %12 %13 %14 "nop1" "" %5 = OpFunction %9 None %11 %2 = OpFunctionParameter %10 %3 = OpFunctionParameter %10 %4 = OpFunctionParameter %10 %7 = OpLabel %15 = OpLoad %9 %2 Aligned 4 %16 = OpLoad %9 %3 Aligned 4 %6 = OpFSub %9 %15 %16 OpStore %4 %6 Aligned 4 %17 = OpAsmCallINTEL %12 %8 OpReturnValue %6 OpFunctionEnd )"; // same as foo_asm, just with OpFMul and a different assembly string in // OpAsmINTEL constexpr const char* const foo_asm2 = R"( OpCapability Kernel OpCapability Addresses OpCapability Linkage OpCapability AsmINTEL OpExtension "SPV_INTEL_inline_assembly" %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpSource OpenCL_CPP 100000 OpName %2 "a" OpName %3 "b" OpName %4 "c" OpName %5 "foo" OpName %6 "add" OpName %7 "entry" OpDecorate %2 FuncParamAttr NoWrite OpDecorate %2 FuncParamAttr NoAlias OpDecorate %3 FuncParamAttr NoWrite OpDecorate %3 FuncParamAttr NoAlias OpDecorate %4 FuncParamAttr NoAlias OpDecorate %5 LinkageAttributes "foo" Export OpDecorate %8 SideEffectsINTEL %9 = OpTypeFloat 32 %10 = OpTypePointer Function %9 %11 = OpTypeFunction %9 %10 %10 %10 %12 = OpTypeVoid %13 = OpTypeFunction %12 %14 = OpAsmTargetINTEL "spirv64-unknown-unknown" %8 = OpAsmINTEL %12 %13 %14 "nop2" "" %5 = OpFunction %9 None %11 %2 = OpFunctionParameter %10 %3 = OpFunctionParameter %10 %4 = OpFunctionParameter %10 %7 = OpLabel %15 = OpLoad %9 %2 Aligned 4 %16 = OpLoad %9 %3 Aligned 4 %6 = OpFMul %9 %15 %16 OpStore %4 %6 Aligned 4 %17 = OpAsmCallINTEL %12 %8 OpReturnValue %6 OpFunctionEnd )"; // clang-format off const std::vector expected_lines = { "OpCapability FunctionVariantsINTEL", "OpCapability SpecConditionalINTEL", "OpCapability Kernel", "OpCapability Addresses", "OpConditionalCapabilityINTEL %foo_spv Int8", "OpConditionalCapabilityINTEL %foo_spv Int64", "OpCapability Linkage", "OpConditionalCapabilityINTEL %2 AsmINTEL", "OpExtension \"SPV_INTEL_function_variants\"", "OpConditionalExtensionINTEL %2 \"SPV_INTEL_inline_assembly\"", "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"", "OpDecorate %foo LinkageAttributes \"foo\" Export", "OpDecorate %work LinkageAttributes \"work\" Export", "OpDecorate %ulong_28 ConditionalINTEL %foo_spv", "OpDecorate %ulong_24 ConditionalINTEL %foo_spv", "OpDecorate %ulong_20 ConditionalINTEL %foo_spv", "OpDecorate %ulong_16 ConditionalINTEL %foo_spv", "OpDecorate %ulong_12 ConditionalINTEL %foo_spv", "OpDecorate %ulong_8 ConditionalINTEL %foo_spv", "OpDecorate %ulong_4 ConditionalINTEL %foo_spv", "OpDecorate %foo ConditionalINTEL %foo_spv", "OpDecorate %foo_0 LinkageAttributes \"foo\" Export", "OpDecorate %32 ConditionalINTEL %foo_asm_spv", "OpDecorate %31 ConditionalINTEL %foo_asm_spv", "OpDecorate %foo_0 ConditionalINTEL %foo_asm_spv", "OpDecorate %foo_1 LinkageAttributes \"foo\" Export", "OpDecorate %34 ConditionalINTEL %foo_asm2_spv", "OpDecorate %33 ConditionalINTEL %foo_asm2_spv", "OpDecorate %foo_1 ConditionalINTEL %foo_asm2_spv", "OpDecorate %call ConditionalINTEL %foo_spv", "OpDecorate %35 ConditionalINTEL %foo_asm_spv", "OpDecorate %36 ConditionalINTEL %foo_asm2_spv", "OpDecorate %uchar ConditionalINTEL %foo_spv", "OpDecorate %_ptr_Function_uchar ConditionalINTEL %foo_spv", "OpDecorate %ulong ConditionalINTEL %foo_spv", "OpDecorate %void ConditionalINTEL %2", "OpDecorate %41 ConditionalINTEL %2", "%ulong_28 = OpConstant %ulong 28", "%ulong_24 = OpConstant %ulong 24", "%ulong_20 = OpConstant %ulong 20", "%ulong_16 = OpConstant %ulong 16", "%ulong_12 = OpConstant %ulong 12", "%ulong_8 = OpConstant %ulong 8", "%ulong_4 = OpConstant %ulong 4", "%bool = OpTypeBool", "%46 = OpSpecConstantTargetINTEL %bool 4", "%47 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8", "%48 = OpSpecConstantOp %bool LogicalAnd %47 %46", "%31 = OpAsmINTEL %void %41 %32 \"nop1\" \"\"", "%49 = OpSpecConstantArchitectureINTEL %bool 1 1 170 1", "%50 = OpSpecConstantTargetINTEL %bool 4 9 10", "%51 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL", "%52 = OpSpecConstantOp %bool LogicalAnd %51 %49", "%foo_asm_spv = OpSpecConstantOp %bool LogicalAnd %52 %50", "%33 = OpAsmINTEL %void %41 %34 \"nop2\" \"\"", "%53 = OpSpecConstantArchitectureINTEL %bool 1 7 174 1", "%54 = OpSpecConstantArchitectureINTEL %bool 1 7 178 3", "%55 = OpSpecConstantArchitectureINTEL %bool 1 8 170 1", "%56 = OpSpecConstantArchitectureINTEL %bool 1 9 174 1", "%57 = OpSpecConstantArchitectureINTEL %bool 1 9 178 3", "%58 = OpSpecConstantTargetINTEL %bool 5 2 4 5", "%59 = OpSpecConstantTargetINTEL %bool 6 2 4 5", "%60 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL", "%61 = OpSpecConstantOp %bool LogicalAnd %53 %54", "%62 = OpSpecConstantOp %bool LogicalAnd %56 %57", "%63 = OpSpecConstantOp %bool LogicalOr %61 %55", "%64 = OpSpecConstantOp %bool LogicalOr %63 %62", "%65 = OpSpecConstantOp %bool LogicalOr %58 %59", "%66 = OpSpecConstantOp %bool LogicalAnd %60 %64", "%foo_asm2_spv = OpSpecConstantOp %bool LogicalAnd %66 %65", "%67 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv", "%68 = OpSpecConstantOp %bool LogicalNot %67", "%foo_spv = OpSpecConstantOp %bool LogicalAnd %48 %68", "%2 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv", "%foo = OpFunction %float DontInline %44", "%add = OpFAdd %float %72 %73", "%work = OpFunction %float None %44", "%call = OpFunctionCall %float %foo %74 %75 %76", "%35 = OpFunctionCall %float %foo_0 %74 %75 %76", "%36 = OpFunctionCall %float %foo_1 %74 %75 %76", "%77 = OpConditionalCopyObjectINTEL %float %foo_spv %call %foo_asm_spv %35 %foo_asm2_spv %36", "%78 = OpFDiv %float %77 %77", "%79 = OpFDiv %float %78 %77", "OpReturnValue %79", "%foo_0 = OpFunction %float None %44", "%add_0 = OpFSub %float %80 %81", "%82 = OpAsmCallINTEL %void %31", "%foo_1 = OpFunction %float None %44", "%add_1 = OpFMul %float %83 %84", "%85 = OpAsmCallINTEL %void %33", }; // clang-format on const std::string targets_csv = "module,target,features\n" "foo.spv,04,\n" // test leading zeros "foo_asm.spv,4,9/0010\n" // test leading zeros "foo_asm2.spv,5,2/4/5\n" "foo_asm2.spv,6,2/4/5\n"; const std::string architectures_csv = "module,category,family,op,architecture\n" "foo_asm.spv,1,1,170,1\n" "foo_asm2.spv,1,7,174,1\n" "foo_asm2.spv,1,7,178,3\n" "foo_asm2.spv,1,8,170,1\n" "foo_asm2.spv,1,9,174,1\n" "foo_asm2.spv,1,9,178,3\n"; const std::vector sources = {foo_base, foo_asm, foo_asm2}; const std::vector in_files = {"foo.spv", "foo_asm.spv", "foo_asm2.spv"}; LinkerOptions options; options.SetInFiles(in_files); options.SetFnVarTargetsCsv(targets_csv); options.SetFnVarArchitecturesCsv(architectures_csv); options.SetHasFnVarCapabilities(true); options.SetCreateLibrary(true); options.SetVerifyIds(true); spvtest::Binary linked_binary; spv_result_t res = AssembleAndLink(sources, &linked_binary, options); EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); EXPECT_TRUE(Validate(linked_binary)); std::string linked_asm; res = Disassemble(linked_binary, &linked_asm); EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); for (const auto& expected : expected_lines) { EXPECT_THAT(linked_asm, testing::HasSubstr(expected)); } } TEST_F(FunctionVariants, InvalidNumber1) { const std::string targets_csv = "module,target,features\n" "foo.spv,-4,9/10\n"; const std::string architectures_csv = ""; const std::vector sources = {""}; const std::vector in_files = {"foo.spv"}; LinkerOptions options; options.SetInFiles(in_files); options.SetFnVarTargetsCsv(targets_csv); options.SetFnVarArchitecturesCsv(architectures_csv); options.SetHasFnVarCapabilities(true); options.SetCreateLibrary(true); options.SetVerifyIds(true); spvtest::Binary linked_binary; spv_result_t res = AssembleAndLink(sources, &linked_binary, options); EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting -4 to target."); } TEST_F(FunctionVariants, InvalidNumber2) { const std::string targets_csv = "module,target,features\n" "foo.spv,4,9/-10\n"; const std::string architectures_csv = ""; const std::vector sources = {""}; const std::vector in_files = {"foo.spv"}; LinkerOptions options; options.SetInFiles(in_files); options.SetFnVarTargetsCsv(targets_csv); options.SetFnVarArchitecturesCsv(architectures_csv); options.SetHasFnVarCapabilities(true); options.SetCreateLibrary(true); options.SetVerifyIds(true); spvtest::Binary linked_binary; spv_result_t res = AssembleAndLink(sources, &linked_binary, options); EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting -10 in 9/-10 to target feature."); } TEST_F(FunctionVariants, InvalidNumber3) { const std::string targets_csv = "module,target,features\n" "foo.spv,4.0,9/10\n"; const std::string architectures_csv = ""; const std::vector sources = {""}; const std::vector in_files = {"foo.spv"}; LinkerOptions options; options.SetInFiles(in_files); options.SetFnVarTargetsCsv(targets_csv); options.SetFnVarArchitecturesCsv(architectures_csv); options.SetHasFnVarCapabilities(true); options.SetCreateLibrary(true); options.SetVerifyIds(true); spvtest::Binary linked_binary; spv_result_t res = AssembleAndLink(sources, &linked_binary, options); EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting 4.0 to target."); } } // namespace } // namespace spvtools