--- /dev/null
+// Copyright (c) 2016 Google 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 "set_spec_constant_default_value_pass.h"
+
+#include <cstring>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "spirv-tools/libspirv.h"
+#include "util/parse_number.h"
+
+#include "def_use_manager.h"
+#include "type_manager.h"
+#include "types.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+using spvutils::NumberType;
+using spvutils::EncodeNumberStatus;
+using spvutils::ParseAndEncodeNumber;
+
+// Given a numeric value in a null-terminated c string and the expected type of
+// the value, parses the string and encodes it in a vector of words. If the
+// value is a scalar integer or floating point value, encodes the value in
+// SPIR-V encoding format. If the value is 'false' or 'true', returns a vector
+// with single word with value 0 or 1 respectively. Returns the vector
+// containing the encoded value on success. Otherwise returns an empty vector.
+std::vector<uint32_t> ParseDefaultValueStr(const char* text,
+ const analysis::Type* type) {
+ std::vector<uint32_t> result;
+ if (!strcmp(text, "true")) {
+ result.push_back(1u);
+ } else if (!strcmp(text, "false")) {
+ result.push_back(0u);
+ } else {
+ NumberType number_type = {32, SPV_NUMBER_UNSIGNED_INT};
+ if (const auto* IT = type->AsInteger()) {
+ number_type.bitwidth = IT->width();
+ number_type.kind =
+ IT->IsSigned() ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
+ } else if (const auto* FT = type->AsFloat()) {
+ number_type.bitwidth = FT->width();
+ number_type.kind = SPV_NUMBER_FLOATING;
+ }
+ EncodeNumberStatus rc = ParseAndEncodeNumber(
+ text, number_type, [&result](uint32_t word) { result.push_back(word); },
+ nullptr);
+ // Clear the result vector on failure.
+ if (rc != EncodeNumberStatus::kSuccess) {
+ result.clear();
+ }
+ }
+ return result;
+}
+
+// Returns true if the given instruction's result id could have a SpecId
+// decoration.
+bool CanHaveSpecIdDecoration(const ir::Instruction& inst) {
+ switch (inst.opcode()) {
+ case SpvOp::SpvOpSpecConstant:
+ case SpvOp::SpvOpSpecConstantFalse:
+ case SpvOp::SpvOpSpecConstantTrue:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Given a decoration group defining instruction that is decorated with SpecId
+// decoration, finds the spec constant defining instruction which is the real
+// target of the SpecId decoration. Returns the spec constant defining
+// instruction if such an instruction is found, otherwise returns a nullptr.
+ir::Instruction* GetSpecIdTargetFromDecorationGroup(
+ const ir::Instruction& decoration_group_defining_inst,
+ analysis::DefUseManager* def_use_mgr) {
+ // Find the OpGroupDecorate instruction which consumes the given decoration
+ // group. Note that the given decoration group has SpecId decoration, which
+ // is unique for different spec constants. So the decoration group cannot be
+ // consumed by different OpGroupDecorate instructions. Therefore we only need
+ // the first OpGroupDecoration instruction that uses the given decoration
+ // group.
+ ir::Instruction* group_decorate_inst = nullptr;
+ for (const auto& u :
+ *def_use_mgr->GetUses(decoration_group_defining_inst.result_id())) {
+ if (u.inst->opcode() == SpvOp::SpvOpGroupDecorate) {
+ group_decorate_inst = u.inst;
+ break;
+ }
+ }
+ if (!group_decorate_inst) return nullptr;
+
+ // Scan through the target ids of the OpGroupDecorate instruction. There
+ // should be only one spec constant target consumes the SpecId decoration.
+ // If multiple target ids are presented in the OpGroupDecorate instruction,
+ // they must be the same one that defined by an eligible spec constant
+ // instruction. If the OpGroupDecorate instruction has different target ids
+ // or a target id is not defined by an eligible spec cosntant instruction,
+ // returns a nullptr.
+ ir::Instruction* target_inst = nullptr;
+ for (uint32_t i = 1; i < group_decorate_inst->NumInOperands(); i++) {
+ // All the operands of a OpGroupDecorate instruction should be of type
+ // SPV_OPERAND_TYPE_ID.
+ uint32_t candidate_id = group_decorate_inst->GetSingleWordInOperand(i);
+ ir::Instruction* candidate_inst = def_use_mgr->GetDef(candidate_id);
+
+ if (!candidate_inst) {
+ continue;
+ }
+
+ if (!target_inst) {
+ // If the spec constant target has not been found yet, check if the
+ // candidate instruction is the target.
+ if (CanHaveSpecIdDecoration(*candidate_inst)) {
+ target_inst = candidate_inst;
+ } else {
+ // Spec id decoration should not be applied on other instructions.
+ // TODO(qining): Emit an error message in the invalid case once the
+ // error handling is done.
+ return nullptr;
+ }
+ } else {
+ // If the spec constant target has been found, check if the candidate
+ // instruction is the same one as the target. The module is invalid if
+ // the candidate instruction is different with the found target.
+ // TODO(qining): Emit an error messaage in the invalid case once the
+ // error handling is done.
+ if (candidate_inst != target_inst) return nullptr;
+ }
+ }
+ return target_inst;
+}
+};
+
+bool SetSpecConstantDefaultValuePass::Process(ir::Module* module) {
+ // The operand index of decoration target in an OpDecorate instruction.
+ const uint32_t kTargetIdOperandIndex = 0;
+ // The operand index of the decoration literal in an OpDecorate instruction.
+ const uint32_t kDecorationOperandIndex = 1;
+ // The operand index of Spec id literal value in an OpDecorate SpecId
+ // instruction.
+ const uint32_t kSpecIdLiteralOperandIndex = 2;
+ // The number of operands in an OpDecorate SpecId instruction.
+ const uint32_t kOpDecorateSpecIdNumOperands = 3;
+ // The in-operand index of the default value in a OpSpecConstant instruction.
+ const uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
+
+ bool modified = false;
+ analysis::DefUseManager def_use_mgr(module);
+ analysis::TypeManager type_mgr(*module);
+ // Scan through all the annotation instructions to find 'OpDecorate SpecId'
+ // instructions. Then extract the decoration target of those instructions.
+ // The decoration targets should be spec constant defining instructions with
+ // opcode: OpSpecConstant{|True|False}. The spec id of those spec constants
+ // will be used to look up their new default values in the mapping from
+ // spec id to new default value strings. Once a new default value string
+ // is found for a spec id, the string will be parsed according to the target
+ // spec constant type. The parsed value will be used to replace the original
+ // default value of the target spec constant.
+ for (ir::Instruction& inst : module->annotations()) {
+ // Only process 'OpDecorate SpecId' instructions
+ if (inst.opcode() != SpvOp::SpvOpDecorate) continue;
+ if (inst.NumOperands() != kOpDecorateSpecIdNumOperands) continue;
+ if (inst.GetSingleWordInOperand(kDecorationOperandIndex) !=
+ uint32_t(SpvDecoration::SpvDecorationSpecId)) {
+ continue;
+ }
+
+ // 'inst' is an OpDecorate SpecId instruction.
+ uint32_t spec_id = inst.GetSingleWordOperand(kSpecIdLiteralOperandIndex);
+ uint32_t target_id = inst.GetSingleWordOperand(kTargetIdOperandIndex);
+
+ // Find the spec constant defining instruction. Note that the
+ // target_id might be a decoration group id.
+ ir::Instruction* spec_inst = nullptr;
+ if (ir::Instruction* target_inst = def_use_mgr.GetDef(target_id)) {
+ if (target_inst->opcode() == SpvOp::SpvOpDecorationGroup) {
+ spec_inst =
+ GetSpecIdTargetFromDecorationGroup(*target_inst, &def_use_mgr);
+ } else {
+ spec_inst = target_inst;
+ }
+ } else {
+ continue;
+ }
+ if (!spec_inst) continue;
+
+ // Search for the new default value for this spec id.
+ auto iter = spec_id_to_value_.find(spec_id);
+ if (iter == spec_id_to_value_.end()) continue;
+
+ // Gets the string of the default value and parses it to bit pattern
+ // with the type of the spec constant.
+ const std::string& default_value_str = iter->second;
+ std::vector<uint32_t> bit_pattern = ParseDefaultValueStr(
+ default_value_str.c_str(), type_mgr.GetType(spec_inst->type_id()));
+ if (bit_pattern.empty()) continue;
+
+ // Update the operand bit patterns of the spec constant defining
+ // instruction.
+ switch (spec_inst->opcode()) {
+ case SpvOp::SpvOpSpecConstant:
+ // If the new value is the same with the original value, no
+ // need to do anything. Otherwise update the operand words.
+ if (spec_inst->GetInOperand(kOpSpecConstantLiteralInOperandIndex)
+ .words != bit_pattern) {
+ spec_inst->SetInOperand(kOpSpecConstantLiteralInOperandIndex,
+ std::move(bit_pattern));
+ modified = true;
+ }
+ break;
+ case SpvOp::SpvOpSpecConstantTrue:
+ // If the new value is also 'true', no need to change anything.
+ // Otherwise, set the opcode to OpSpecConstantFalse;
+ if (!static_cast<bool>(bit_pattern.front())) {
+ spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantFalse);
+ modified = true;
+ }
+ break;
+ case SpvOp::SpvOpSpecConstantFalse:
+ // If the new value is also 'false', no need to change anything.
+ // Otherwise, set the opcode to OpSpecConstantTrue;
+ if (static_cast<bool>(bit_pattern.front())) {
+ spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantTrue);
+ modified = true;
+ }
+ break;
+ default:
+ break;
+ }
+ // No need to update the DefUse manager, as this pass does not change any
+ // ids.
+ }
+ return modified;
+}
+
+} // namespace opt
+} // namespace spvtools
--- /dev/null
+// Copyright (c) 2016 Google 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 "pass_fixture.h"
+
+namespace {
+using namespace spvtools;
+
+using SpecIdToValueStrMap =
+ opt::SetSpecConstantDefaultValuePass::SpecIdToValueStrMap;
+
+struct SetSpecConstantDefaultValueTestCase {
+ const char* code;
+ SpecIdToValueStrMap default_values;
+ const char* expected;
+};
+
+using SetSpecConstantDefaultValueParamTest =
+ PassTest<::testing::TestWithParam<SetSpecConstantDefaultValueTestCase>>;
+
+TEST_P(SetSpecConstantDefaultValueParamTest, TestCase) {
+ const auto& tc = GetParam();
+ SinglePassRunAndCheck<opt::SetSpecConstantDefaultValuePass>(
+ tc.code, tc.expected, /* skip_nop = */ false, tc.default_values);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ValidCases, SetSpecConstantDefaultValueParamTest,
+ ::testing::ValuesIn(std::vector<SetSpecConstantDefaultValueTestCase>{
+ // 0. Empty.
+ {"", SpecIdToValueStrMap{}, ""},
+ // 1. Empty with non-empty values to set.
+ {"", SpecIdToValueStrMap{{1, "100"}, {2, "200"}}, ""},
+ // 2. Bool type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%bool = OpTypeBool\n"
+ "%1 = OpSpecConstantTrue %bool\n"
+ "%2 = OpSpecConstantFalse %bool\n",
+ // default values
+ SpecIdToValueStrMap{{100, "false"}, {101, "true"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%bool = OpTypeBool\n"
+ "%1 = OpSpecConstantFalse %bool\n"
+ "%2 = OpSpecConstantTrue %bool\n",
+ },
+ // 3. 32-bit int type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int 10\n"
+ "%2 = OpSpecConstant %int 11\n"
+ "%3 = OpSpecConstant %int 11\n",
+ // default values
+ SpecIdToValueStrMap{
+ {100, "2147483647"}, {101, "0xffffffff"}, {102, "-42"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int 2147483647\n"
+ "%2 = OpSpecConstant %int -1\n"
+ "%3 = OpSpecConstant %int -42\n",
+ },
+ // 4. 64-bit uint type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%ulong = OpTypeInt 64 0\n"
+ "%1 = OpSpecConstant %ulong 10\n"
+ "%2 = OpSpecConstant %ulong 11\n",
+ // default values
+ SpecIdToValueStrMap{{100, "18446744073709551614"}, {101, "0x100"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%ulong = OpTypeInt 64 0\n"
+ "%1 = OpSpecConstant %ulong 18446744073709551614\n"
+ "%2 = OpSpecConstant %ulong 256\n",
+ },
+ // 5. 32-bit float type.
+ {
+ // code
+ "OpDecorate %1 SpecId 101\n"
+ "OpDecorate %2 SpecId 102\n"
+ "%float = OpTypeFloat 32\n"
+ "%1 = OpSpecConstant %float 200\n"
+ "%2 = OpSpecConstant %float 201\n",
+ // default values
+ SpecIdToValueStrMap{{101, "-0x1.fffffep+128"}, {102, "2.5"}},
+ // expected
+ "OpDecorate %1 SpecId 101\n"
+ "OpDecorate %2 SpecId 102\n"
+ "%float = OpTypeFloat 32\n"
+ "%1 = OpSpecConstant %float -0x1.fffffep+128\n"
+ "%2 = OpSpecConstant %float 2.5\n",
+ },
+ // 6. 64-bit float type.
+ {
+ // code
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 3.14159265358979\n"
+ "%2 = OpSpecConstant %double 0.142857\n",
+ // default values
+ SpecIdToValueStrMap{{201, "0x1.fffffffffffffp+1024"},
+ {202, "-32.5"}},
+ // expected
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n"
+ "%2 = OpSpecConstant %double -32.5\n",
+ },
+ // 7. SpecId not found, expect no modification.
+ {
+ // code
+ "OpDecorate %1 SpecId 201\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 3.14159265358979\n",
+ // default values
+ SpecIdToValueStrMap{{8888, "0.0"}},
+ // expected
+ "OpDecorate %1 SpecId 201\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 3.14159265358979\n",
+ },
+ // 8. Multiple types of spec constants.
+ {
+ // code
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "OpDecorate %3 SpecId 203\n"
+ "%bool = OpTypeBool\n"
+ "%int = OpTypeInt 32 1\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 3.14159265358979\n"
+ "%2 = OpSpecConstant %int 1024\n"
+ "%3 = OpSpecConstantTrue %bool\n",
+ // default values
+ SpecIdToValueStrMap{
+ {201, "0x1.fffffffffffffp+1024"}, {202, "2048"}, {203, "false"},
+ },
+ // expected
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "OpDecorate %3 SpecId 203\n"
+ "%bool = OpTypeBool\n"
+ "%int = OpTypeInt 32 1\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n"
+ "%2 = OpSpecConstant %int 2048\n"
+ "%3 = OpSpecConstantFalse %bool\n",
+ },
+ // 9. Ignore other decorations.
+ {
+ // code
+ "OpDecorate %1 ArrayStride 4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{4, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 ArrayStride 4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int 100\n",
+ },
+ // 10. Distinguish from other decorations.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %1 ArrayStride 4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{4, "0x7fffffff"}, {100, "0xffffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %1 ArrayStride 4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpSpecConstant %int -1\n",
+ },
+ // 11. Decorate through decoration group.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 2147483647\n",
+ },
+ // 12. Ignore other decorations in decoration group.
+ {
+ // code
+ "OpDecorate %1 ArrayStride 4\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{4, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 ArrayStride 4\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n",
+ },
+ // 13. Distinguish from other decorations in decoration group.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %1 ArrayStride 4\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}, {4, "0x00000001"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %1 ArrayStride 4\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 2147483647\n",
+ },
+ // 14. Unchanged bool default value
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%bool = OpTypeBool\n"
+ "%1 = OpSpecConstantTrue %bool\n"
+ "%2 = OpSpecConstantFalse %bool\n",
+ // default values
+ SpecIdToValueStrMap{{100, "true"}, {101, "false"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%bool = OpTypeBool\n"
+ "%1 = OpSpecConstantTrue %bool\n"
+ "%2 = OpSpecConstantFalse %bool\n",
+ },
+ // 15. Unchanged int default values
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%int = OpTypeInt 32 1\n"
+ "%ulong = OpTypeInt 64 0\n"
+ "%1 = OpSpecConstant %int 10\n"
+ "%2 = OpSpecConstant %ulong 11\n",
+ // default values
+ SpecIdToValueStrMap{{100, "10"}, {101, "11"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "%int = OpTypeInt 32 1\n"
+ "%ulong = OpTypeInt 64 0\n"
+ "%1 = OpSpecConstant %int 10\n"
+ "%2 = OpSpecConstant %ulong 11\n",
+ },
+ // 16. Unchanged float default values
+ {
+ // code
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "%float = OpTypeFloat 32\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %float 3.14159\n"
+ "%2 = OpSpecConstant %double 0.142857\n",
+ // default values
+ SpecIdToValueStrMap{{201, "3.14159"}, {202, "0.142857"}},
+ // expected
+ "OpDecorate %1 SpecId 201\n"
+ "OpDecorate %2 SpecId 202\n"
+ "%float = OpTypeFloat 32\n"
+ "%double = OpTypeFloat 64\n"
+ "%1 = OpSpecConstant %float 3.14159\n"
+ "%2 = OpSpecConstant %double 0.142857\n",
+ },
+ // 17. OpGroupDecorate may have multiple target ids defined by the same
+ // eligible spec constant
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2 %2 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0xffffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2 %2 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int -1\n",
+ },
+ }));
+
+INSTANTIATE_TEST_CASE_P(
+ InvalidCases, SetSpecConstantDefaultValueParamTest,
+ ::testing::ValuesIn(std::vector<SetSpecConstantDefaultValueTestCase>{
+ // 0. Do not crash when decoration group is not used.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "%int = OpTypeInt 32 1\n"
+ "%3 = OpSpecConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "%int = OpTypeInt 32 1\n"
+ "%3 = OpSpecConstant %int 100\n",
+ },
+ // 1. Do not crash when target does not exist.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n",
+ },
+ // 2. Do nothing when SpecId decoration is not attached to a
+ // non-spec-contant instruction.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpConstant %int 101\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n"
+ "%1 = OpConstant %int 101\n",
+ },
+ // 3. Do nothing when SpecId decoration is not attached to a
+ // OpSpecConstant{|True|False} instruction.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n"
+ "%3 = OpSpecConstant %int 101\n"
+ "%1 = OpSpecConstantOp %int IAdd %3 %3\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0x7fffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%int = OpTypeInt 32 1\n"
+ "%3 = OpSpecConstant %int 101\n"
+ "%1 = OpSpecConstantOp %int IAdd %3 %3\n",
+ },
+ // 4. Do not crash and do nothing when SpecId decoration is applied to
+ // multiple spec constants.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2 %3 %4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n"
+ "%3 = OpSpecConstant %int 200\n"
+ "%4 = OpSpecConstant %int 300\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0xffffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2 %3 %4\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpSpecConstant %int 100\n"
+ "%3 = OpSpecConstant %int 200\n"
+ "%4 = OpSpecConstant %int 300\n",
+ },
+ // 5. Do not crash and do nothing when SpecId decoration is attached to
+ // non-spec-constants (invalid case).
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpConstant %int 100\n",
+ // default values
+ SpecIdToValueStrMap{{100, "0xffffffff"}},
+ // expected
+ "OpDecorate %1 SpecId 100\n"
+ "%1 = OpDecorationGroup\n"
+ "OpGroupDecorate %1 %2\n"
+ "%int = OpTypeInt 32 1\n"
+ "%2 = OpConstant %int 100\n",
+ },
+ }));
+
+} // anonymous namespace