Add support for SPV_KHR_float_controls2 (#5543)

* Test asm/dis for SPV_KHR_float_controls2
* SPV_KHR_float_controls2 validation

---------

Co-authored-by: David Neto <dneto@google.com>
This commit is contained in:
alan-baker 2024-01-25 10:22:09 -05:00 committed by GitHub
parent de3d5acc04
commit ef2f432364
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 961 additions and 23 deletions

View File

@ -141,6 +141,10 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) {
}
}
if (auto error = ValidateFloatControls2(_)) {
return error;
}
return SPV_SUCCESS;
}

View File

@ -82,6 +82,18 @@ spv_result_t ValidateAdjacency(ValidationState_t& _);
/// @return SPV_SUCCESS if no errors are found.
spv_result_t ValidateInterfaces(ValidationState_t& _);
/// @brief Validates entry point call tree requirements of
/// SPV_KHR_float_controls2
///
/// Checks that no entry point using FPFastMathDefault uses:
/// * FPFastMathMode Fast
/// * NoContraction
///
/// @param[in] _ the validation state of the module
///
/// @return SPV_SUCCESS if no errors are found.
spv_result_t ValidateFloatControls2(ValidationState_t& _);
/// @brief Validates memory instructions
///
/// @param[in] _ the validation state of the module

View File

@ -267,6 +267,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
}
}
if (decoration == spv::Decoration::FPFastMathMode) {
if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "FPFastMathMode and NoContraction cannot decorate the same "
"target";
}
auto mask = inst->GetOperandAs<spv::FPFastMathModeMask>(2);
if ((mask & spv::FPFastMathModeMask::AllowTransform) !=
spv::FPFastMathModeMask::MaskNone &&
((mask & (spv::FPFastMathModeMask::AllowContract |
spv::FPFastMathModeMask::AllowReassoc)) !=
(spv::FPFastMathModeMask::AllowContract |
spv::FPFastMathModeMask::AllowReassoc))) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "AllowReassoc and AllowContract must be specified when "
"AllowTransform is specified";
}
}
// This is checked from both sides since we register decorations as we go.
if (decoration == spv::Decoration::NoContraction) {
if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "FPFastMathMode and NoContraction cannot decorate the same "
"target";
}
}
if (DecorationTakesIdParameters(decoration)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Decorations taking ID parameters may not be used with "

View File

@ -470,7 +470,8 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
}
_.set_addressing_model(inst->GetOperandAs<spv::AddressingModel>(0));
_.set_memory_model(inst->GetOperandAs<spv::MemoryModel>(1));
} else if (opcode == spv::Op::OpExecutionMode) {
} else if (opcode == spv::Op::OpExecutionMode ||
opcode == spv::Op::OpExecutionModeId) {
const uint32_t entry_point = inst->word(1);
_.RegisterExecutionModeForEntryPoint(entry_point,
spv::ExecutionMode(inst->word(2)));

View File

@ -340,29 +340,92 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1);
if (inst->opcode() == spv::Op::OpExecutionModeId) {
bool valid_mode = false;
switch (mode) {
case spv::ExecutionMode::SubgroupsPerWorkgroupId:
case spv::ExecutionMode::LocalSizeHintId:
case spv::ExecutionMode::LocalSizeId:
case spv::ExecutionMode::FPFastMathDefault:
valid_mode = true;
break;
default:
valid_mode = false;
break;
}
if (!valid_mode) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpExecutionModeId is only valid when the Mode operand is an "
"execution mode that takes Extra Operands that are id "
"operands.";
}
size_t operand_count = inst->operands().size();
for (size_t i = 2; i < operand_count; ++i) {
const auto operand_id = inst->GetOperandAs<uint32_t>(2);
const auto operand_id = inst->GetOperandAs<uint32_t>(i);
const auto* operand_inst = _.FindDef(operand_id);
if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
mode == spv::ExecutionMode::LocalSizeHintId ||
mode == spv::ExecutionMode::LocalSizeId) {
if (!spvOpcodeIsConstant(operand_inst->opcode())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For OpExecutionModeId all Extra Operand ids must be "
"constant "
"instructions.";
}
} else {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpExecutionModeId is only valid when the Mode operand is an "
"execution mode that takes Extra Operands that are id "
"operands.";
switch (mode) {
case spv::ExecutionMode::SubgroupsPerWorkgroupId:
case spv::ExecutionMode::LocalSizeHintId:
case spv::ExecutionMode::LocalSizeId:
if (!spvOpcodeIsConstant(operand_inst->opcode())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For OpExecutionModeId all Extra Operand ids must be "
"constant instructions.";
}
break;
case spv::ExecutionMode::FPFastMathDefault:
if (i == 2) {
if (!_.IsFloatScalarType(operand_id)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Target Type operand must be a floating-point "
"scalar type";
}
} else {
bool is_int32 = false;
bool is_const = false;
uint32_t value = 0;
std::tie(is_int32, is_const, value) =
_.EvalInt32IfConst(operand_id);
if (is_int32 && is_const) {
// Valid values include up to 0x00040000 (AllowTransform).
uint32_t invalid_mask = 0xfff80000;
if ((invalid_mask & value) != 0) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Fast Math Default operand is an invalid bitmask "
"value";
}
if (value &
static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Fast Math Default operand must not include Fast";
}
const auto reassoc_contract =
spv::FPFastMathModeMask::AllowContract |
spv::FPFastMathModeMask::AllowReassoc;
if ((value & static_cast<uint32_t>(
spv::FPFastMathModeMask::AllowTransform)) != 0 &&
((value & static_cast<uint32_t>(reassoc_contract)) !=
static_cast<uint32_t>(reassoc_contract))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Fast Math Default operand must include "
"AllowContract and AllowReassoc when AllowTransform "
"is specified";
}
} else {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Fast Math Default operand must be a "
"non-specialization constant";
}
}
break;
default:
break;
}
}
} else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
mode == spv::ExecutionMode::LocalSizeHintId ||
mode == spv::ExecutionMode::LocalSizeId) {
mode == spv::ExecutionMode::LocalSizeId ||
mode == spv::ExecutionMode::FPFastMathDefault) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "OpExecutionMode is only valid when the Mode operand is an "
"execution mode that takes no Extra Operands, or takes Extra "
@ -579,6 +642,20 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
break;
}
if (mode == spv::ExecutionMode::FPFastMathDefault) {
const auto* modes = _.GetExecutionModes(entry_point_id);
if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "FPFastMathDefault and ContractionOff execution modes cannot "
"be applied to the same entry point";
}
if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "FPFastMathDefault and SignedZeroInfNanPreserve execution "
"modes cannot be applied to the same entry point";
}
}
if (spvIsVulkanEnv(_.context()->target_env)) {
if (mode == spv::ExecutionMode::OriginLowerLeft) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@ -636,6 +713,70 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
} // namespace
spv_result_t ValidateFloatControls2(ValidationState_t& _) {
std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
for (auto entry_point : _.entry_points()) {
const auto* exec_modes = _.GetExecutionModes(entry_point);
if (exec_modes &&
exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
fp_fast_math_default_entry_points.insert(entry_point);
}
}
std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
for (const auto& inst : _.ordered_instructions()) {
if (inst.opcode() != spv::Op::OpDecorate) {
continue;
}
const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
const auto target_id = inst.GetOperandAs<uint32_t>(0);
const auto target = _.FindDef(target_id);
if (decoration == spv::Decoration::NoContraction) {
worklist.push_back(std::make_pair(target, decoration));
} else if (decoration == spv::Decoration::FPFastMathMode) {
auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
if ((mask & spv::FPFastMathModeMask::Fast) !=
spv::FPFastMathModeMask::MaskNone) {
worklist.push_back(std::make_pair(target, decoration));
}
}
}
std::unordered_set<const Instruction*> visited;
while (!worklist.empty()) {
const auto inst = worklist.back().first;
const auto decoration = worklist.back().second;
worklist.pop_back();
if (!visited.insert(inst).second) {
continue;
}
const auto function = inst->function();
if (function) {
const auto& entry_points = _.FunctionEntryPoints(function->id());
for (auto entry_point : entry_points) {
if (fp_fast_math_default_entry_points.count(entry_point)) {
const std::string dec = decoration == spv::Decoration::NoContraction
? "NoContraction"
: "FPFastMathMode Fast";
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< dec
<< " cannot be used by an entry point with the "
"FPFastMathDefault execution mode";
}
}
} else {
for (const auto& pair : inst->uses()) {
worklist.push_back(std::make_pair(pair.first, decoration));
}
}
}
return SPV_SUCCESS;
}
spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
switch (inst->opcode()) {
case spv::Op::OpEntryPoint:

View File

@ -21,6 +21,9 @@
#include "source/assembly_grammar.h"
#include "source/enum_set.h"
#include "source/operand.h"
#include "source/spirv_target_env.h"
#include "source/table.h"
#include "spirv-tools/libspirv.h"
#include "test/unit_spirv.h"
namespace spvtools {
@ -58,15 +61,17 @@ struct EnumCapabilityCase {
uint32_t value;
CapabilitySet expected_capabilities;
};
// Emits an EnumCapabilityCase to the ostream, returning the ostream.
inline std::ostream& operator<<(std::ostream& out,
const EnumCapabilityCase& ecc) {
out << "EnumCapabilityCase{ " << spvOperandTypeStr(ecc.type) << "("
<< unsigned(ecc.type) << "), " << ecc.value << ", "
<< ecc.expected_capabilities << "}";
// Emits an EnumCapabilityCase to the given output stream. This is used
// to emit failure cases when they occur, which helps debug tests.
inline std::ostream& operator<<(std::ostream& out, EnumCapabilityCase e) {
out << "{" << spvOperandTypeStr(e.type) << " " << e.value << " "
<< e.expected_capabilities << " }";
return out;
}
using EnvEnumCapabilityCase = std::tuple<spv_target_env, EnumCapabilityCase>;
// Test fixture for testing EnumCapabilityCases.
using EnumCapabilityTest =
TestWithParam<std::tuple<spv_target_env, EnumCapabilityCase>>;

View File

@ -1264,5 +1264,41 @@ INSTANTIATE_TEST_SUITE_P(
{1, (uint32_t)spv::ExecutionMode::MaximallyReconvergesKHR})},
})));
// SPV_KHR_float_controls2
INSTANTIATE_TEST_SUITE_P(
SPV_KHR_float_controls2, ExtensionRoundTripTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0,
SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
ValuesIn(std::vector<AssemblyCase>{
{"OpExtension \"SPV_KHR_float_controls2\"\n",
MakeInstruction(spv::Op::OpExtension,
MakeVector("SPV_KHR_float_controls2"))},
{"OpCapability FloatControls2\n",
MakeInstruction(spv::Op::OpCapability,
{(uint32_t)spv::Capability::FloatControls2})},
{"OpExecutionMode %1 FPFastMathDefault %2 %3\n",
// The operands are: target type, flags constant
MakeInstruction(
spv::Op::OpExecutionMode,
{1, (uint32_t)spv::ExecutionMode::FPFastMathDefault, 2, 3})},
{"OpDecorate %1 FPFastMathMode AllowContract\n",
MakeInstruction(
spv::Op::OpDecorate,
{1, (uint32_t)spv::Decoration::FPFastMathMode,
(uint32_t)spv::FPFastMathModeMask::AllowContract})},
{"OpDecorate %1 FPFastMathMode AllowReassoc\n",
MakeInstruction(
spv::Op::OpDecorate,
{1, (uint32_t)spv::Decoration::FPFastMathMode,
(uint32_t)spv::FPFastMathModeMask::AllowReassoc})},
{"OpDecorate %1 FPFastMathMode AllowTransform\n",
MakeInstruction(
spv::Op::OpDecorate,
{1, (uint32_t)spv::Decoration::FPFastMathMode,
(uint32_t)spv::FPFastMathModeMask::AllowTransform})},
})));
} // namespace
} // namespace spvtools

View File

@ -65,6 +65,171 @@ OpDecorate %var BuiltIn WorkgroupSize
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(DecorationTest, FPFastMathModeInvalidMask) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode !524288
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid floating-point fast math mode operand"));
}
TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingAllowContract) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode AllowTransform|AllowReassoc
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("AllowReassoc and AllowContract must be specified when "
"AllowTransform is specified"));
}
TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingAllowReassoc) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode AllowTransform|AllowContract
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("AllowReassoc and AllowContract must be specified when "
"AllowTransform is specified"));
}
TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingContractAndReassoc) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode AllowTransform
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("AllowReassoc and AllowContract must be specified when "
"AllowTransform is specified"));
}
TEST_F(DecorationTest, FPFastMathModeAndNoContraction) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode None
OpDecorate %add NoContraction
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"FPFastMathMode and NoContraction cannot decorate the same target"));
}
TEST_F(DecorationTest, FPFastMathModeAndNoContraction2) {
const std::string text = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add NoContraction
OpDecorate %add FPFastMathMode None
%void = OpTypeVoid
%float = OpTypeFloat 32
%undef = OpUndef %float
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%add = OpFAdd %float %undef %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"FPFastMathMode and NoContraction cannot decorate the same target"));
}
using MemberOnlyDecorations = spvtest::ValidateBase<std::string>;
TEST_P(MemberOnlyDecorations, MemberDecoration) {

View File

@ -1328,6 +1328,552 @@ OpFunctionEnd
"SPV_KHR_maximal_reconvergence "));
}
TEST_F(ValidateMode, FPFastMathDefaultNotExecutionModeId) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionMode %main FPFastMathDefault %int_0 %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpExecutionMode is only valid when the Mode operand "
"is an execution mode that takes no Extra Operands, or "
"takes Extra Operands that are not id operands"));
}
TEST_F(ValidateMode, FPFastMathDefaultNotAType) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %int_0 %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"The Target Type operand must be a floating-point scalar type"));
}
TEST_F(ValidateMode, FPFastMathDefaultNotAFloatType) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %int %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"The Target Type operand must be a floating-point scalar type"));
}
TEST_F(ValidateMode, FPFastMathDefaultNotAFloatScalarType) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float2 %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%float = OpTypeFloat 32
%float2 = OpTypeVector %float 2
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"The Target Type operand must be a floating-point scalar type"));
}
TEST_F(ValidateMode, FPFastMathDefaultSpecConstant) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpSpecConstant %int 0
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("The Fast Math Default operand must be a "
"non-specialization constant"));
}
TEST_F(ValidateMode, FPFastMathDefaultInvalidMask) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 524288
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The Fast Math Default operand is an invalid bitmask value"));
}
TEST_F(ValidateMode, FPFastMathDefaultContainsFast) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 16
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("The Fast Math Default operand must not include Fast"));
}
TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowReassoc) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 327680
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The Fast Math Default operand must include AllowContract and "
"AllowReassoc when AllowTransform is specified"));
}
TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowContract) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 393216
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The Fast Math Default operand must include AllowContract and "
"AllowReassoc when AllowTransform is specified"));
}
TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingContractAndReassoc) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 262144
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The Fast Math Default operand must include AllowContract and "
"AllowReassoc when AllowTransform is specified"));
}
TEST_F(ValidateMode, FPFastMathDefaultSignedZeroInfNanPreserve) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpCapability SignedZeroInfNanPreserve
OpExtension "SPV_KHR_float_controls2"
OpExtension "SPV_KHR_float_controls"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main SignedZeroInfNanPreserve 32
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("FPFastMathDefault and SignedZeroInfNanPreserve execution "
"modes cannot be applied to the same entry point"));
}
TEST_F(ValidateMode, FPFastMathDefaultConractionOff) {
const std::string spirv = R"(
OpCapability Kernel
OpCapability Addresses
OpCapability FloatControls2
OpCapability SignedZeroInfNanPreserve
OpExtension "SPV_KHR_float_controls2"
OpExtension "SPV_KHR_float_controls"
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main ContractionOff
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("FPFastMathDefault and ContractionOff execution modes "
"cannot be applied to the same entry point"));
}
TEST_F(ValidateMode, FPFastMathDefaultNoContractionNotInCallTree) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add NoContraction
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %zero %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add NoContraction
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%call = OpFunctionCall %void %func
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %zero %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NoContraction cannot be used by an entry point with "
"the FPFastMathDefault execution mode"));
}
TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree2) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Kernel
OpCapability Addresses
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpDecorate %const NoContraction
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%const = OpSpecConstantOp %float FAdd %zero %zero
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%call = OpFunctionCall %void %func
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %const %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NoContraction cannot be used by an entry point with "
"the FPFastMathDefault execution mode"));
}
TEST_F(ValidateMode, FPFastMathDefaultFastMathFastNotInCallTree) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode Fast
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %zero %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree) {
const std::string spirv = R"(
OpCapability Shader
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %add FPFastMathMode Fast
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%call = OpFunctionCall %void %func
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %zero %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("FPFastMathMode Fast cannot be used by an entry point "
"with the FPFastMathDefault execution mode"));
}
TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree2) {
const std::string spirv = R"(
OpCapability Kernel
OpCapability Addresses
OpCapability FloatControls2
OpExtension "SPV_KHR_float_controls2"
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %main "main"
OpExecutionModeId %main FPFastMathDefault %float %constant
OpDecorate %const FPFastMathMode Fast
%void = OpTypeVoid
%int = OpTypeInt 32 0
%constant = OpConstant %int 0
%float = OpTypeFloat 32
%zero = OpConstant %float 0
%const = OpSpecConstantOp %float FAdd %zero %zero
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%call = OpFunctionCall %void %func
OpReturn
OpFunctionEnd
%func = OpFunction %void None %void_fn
%func_entry = OpLabel
%add = OpFAdd %float %const %zero
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("FPFastMathMode Fast cannot be used by an entry point "
"with the FPFastMathDefault execution mode"));
}
} // namespace
} // namespace val
} // namespace spvtools