1// Copyright (c) 2018 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "source/opt/strip_reflect_info_pass.h"
16
17#include <cstring>
18#include <vector>
19
20#include "source/opt/instruction.h"
21#include "source/opt/ir_context.h"
22
23namespace spvtools {
24namespace opt {
25
26Pass::Status StripReflectInfoPass::Process() {
27 bool modified = false;
28
29 std::vector<Instruction*> to_remove;
30
31 bool other_uses_for_decorate_string = false;
32 for (auto& inst : context()->module()->annotations()) {
33 switch (inst.opcode()) {
34 case SpvOpDecorateStringGOOGLE:
35 if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE) {
36 to_remove.push_back(&inst);
37 } else {
38 other_uses_for_decorate_string = true;
39 }
40 break;
41
42 case SpvOpMemberDecorateStringGOOGLE:
43 if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE) {
44 to_remove.push_back(&inst);
45 } else {
46 other_uses_for_decorate_string = true;
47 }
48 break;
49
50 case SpvOpDecorateId:
51 if (inst.GetSingleWordInOperand(1) ==
52 SpvDecorationHlslCounterBufferGOOGLE) {
53 to_remove.push_back(&inst);
54 }
55 break;
56
57 default:
58 break;
59 }
60 }
61
62 for (auto& inst : context()->module()->extensions()) {
63 const char* ext_name =
64 reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
65 if (0 == std::strcmp(ext_name, "SPV_GOOGLE_hlsl_functionality1")) {
66 to_remove.push_back(&inst);
67 } else if (!other_uses_for_decorate_string &&
68 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) {
69 to_remove.push_back(&inst);
70 } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
71 to_remove.push_back(&inst);
72 }
73 }
74
75 // clear all debug data now if it hasn't been cleared already, to remove any
76 // remaining OpString that may have been referenced by non-semantic extinsts
77 for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
78 for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
79 for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
80 for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg);
81
82 // remove any extended inst imports that are non semantic
83 std::unordered_set<uint32_t> non_semantic_sets;
84 for (auto& inst : context()->module()->ext_inst_imports()) {
85 assert(inst.opcode() == SpvOpExtInstImport &&
86 "Expecting an import of an extension's instruction set.");
87 const char* extension_name =
88 reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
89 if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
90 non_semantic_sets.insert(inst.result_id());
91 to_remove.push_back(&inst);
92 }
93 }
94
95 // if we removed some non-semantic sets, then iterate over the instructions in
96 // the module to remove any OpExtInst that referenced those sets
97 if (!non_semantic_sets.empty()) {
98 context()->module()->ForEachInst(
99 [&non_semantic_sets, &to_remove](Instruction* inst) {
100 if (inst->opcode() == SpvOpExtInst) {
101 if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) !=
102 non_semantic_sets.end()) {
103 to_remove.push_back(inst);
104 }
105 }
106 });
107 }
108
109 // OpName must come first, since they may refer to other debug instructions.
110 // If they are after the instructions that refer to, then they will be killed
111 // when that instruction is killed, which will lead to a double kill.
112 std::sort(to_remove.begin(), to_remove.end(),
113 [](Instruction* lhs, Instruction* rhs) -> bool {
114 if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
115 return true;
116 return false;
117 });
118
119 for (auto* inst : to_remove) {
120 modified = true;
121 context()->KillInst(inst);
122 }
123
124 return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
125}
126
127} // namespace opt
128} // namespace spvtools
129