@@ -724,6 +724,25 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
724724 return SPV_SUCCESS;
725725}
726726
727+ bool PerEntryExecutionMode (spv::ExecutionMode mode) {
728+ switch (mode) {
729+ // These execution modes can be specified multiple times per entry point.
730+ case spv::ExecutionMode::DenormPreserve:
731+ case spv::ExecutionMode::DenormFlushToZero:
732+ case spv::ExecutionMode::SignedZeroInfNanPreserve:
733+ case spv::ExecutionMode::RoundingModeRTE:
734+ case spv::ExecutionMode::RoundingModeRTZ:
735+ case spv::ExecutionMode::FPFastMathDefault:
736+ case spv::ExecutionMode::RoundingModeRTPINTEL:
737+ case spv::ExecutionMode::RoundingModeRTNINTEL:
738+ case spv::ExecutionMode::FloatingPointModeALTINTEL:
739+ case spv::ExecutionMode::FloatingPointModeIEEEINTEL:
740+ return false ;
741+ default :
742+ return true ;
743+ }
744+ }
745+
727746} // namespace
728747
729748spv_result_t ValidateFloatControls2 (ValidationState_t& _) {
@@ -808,5 +827,52 @@ spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
808827 return SPV_SUCCESS;
809828}
810829
830+ spv_result_t ValidateDuplicateExecutionModes (ValidationState_t& _) {
831+ using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t >;
832+ using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t , uint32_t >;
833+ std::set<PerEntryKey> seen_per_entry;
834+ std::set<PerOperandKey> seen_per_operand;
835+
836+ const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string {
837+ spv_operand_desc desc = nullptr ;
838+ if (_.grammar ().lookupOperand (SPV_OPERAND_TYPE_EXECUTION_MODE,
839+ static_cast <uint32_t >(mode),
840+ &desc) == SPV_SUCCESS) {
841+ return std::string (desc->name );
842+ }
843+ return " Unknown" ;
844+ };
845+
846+ for (const auto & inst : _.ordered_instructions ()) {
847+ if (inst.opcode () != spv::Op::OpExecutionMode &&
848+ inst.opcode () != spv::Op::OpExecutionModeId) {
849+ continue ;
850+ }
851+
852+ const auto entry = inst.GetOperandAs <uint32_t >(0 );
853+ const auto mode = inst.GetOperandAs <spv::ExecutionMode>(1 );
854+ if (PerEntryExecutionMode (mode)) {
855+ if (!seen_per_entry.insert (std::make_tuple (mode, entry)).second ) {
856+ return _.diag (SPV_ERROR_INVALID_ID, &inst)
857+ << lookupMode (mode)
858+ << " execution mode must not be specified multiple times per "
859+ " entry point" ;
860+ }
861+ } else {
862+ // Execution modes allowed multiple times all take a single operand.
863+ const auto operand = inst.GetOperandAs <uint32_t >(2 );
864+ if (!seen_per_operand.insert (std::make_tuple (mode, entry, operand))
865+ .second ) {
866+ return _.diag (SPV_ERROR_INVALID_ID, &inst)
867+ << lookupMode (mode)
868+ << " execution mode must not be specified multiple times for "
869+ " the same entry point and operands" ;
870+ }
871+ }
872+ }
873+
874+ return SPV_SUCCESS;
875+ }
876+
811877} // namespace val
812878} // namespace spvtools
0 commit comments