1 // Copyright (c) 2025 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/split_combined_image_sampler_pass.h"
16
17 #include <algorithm>
18 #include <cassert>
19 #include <memory>
20
21 #include "source/opt/instruction.h"
22 #include "source/opt/ir_builder.h"
23 #include "source/opt/ir_context.h"
24 #include "source/opt/type_manager.h"
25 #include "source/opt/types.h"
26 #include "source/util/make_unique.h"
27 #include "source/util/string_utils.h"
28 #include "spirv/unified1/spirv.h"
29
30 namespace spvtools {
31 namespace opt {
32
33 #define CHECK(cond) \
34 { \
35 if ((cond) != SPV_SUCCESS) return Pass::Status::Failure; \
36 }
37
38 #define CHECK_STATUS(cond) \
39 { \
40 if (auto c = (cond); c != SPV_SUCCESS) return c; \
41 }
42
GetPreservedAnalyses()43 IRContext::Analysis SplitCombinedImageSamplerPass::GetPreservedAnalyses() {
44 return
45 // def use manager is updated
46 IRContext::kAnalysisDefUse
47
48 // decorations are updated
49 | IRContext::kAnalysisDecorations
50
51 // control flow is not changed
52 | IRContext::kAnalysisCFG //
53 | IRContext::kAnalysisLoopAnalysis //
54 | IRContext::kAnalysisStructuredCFG
55
56 // type manager is updated
57 | IRContext::kAnalysisTypes;
58 }
59
Process()60 Pass::Status SplitCombinedImageSamplerPass::Process() {
61 def_use_mgr_ = context()->get_def_use_mgr();
62 type_mgr_ = context()->get_type_mgr();
63
64 FindCombinedTextureSamplers();
65 if (combined_types_to_remove_.empty() && !sampled_image_used_as_param_) {
66 return Ok();
67 }
68
69 CHECK(RemapFunctions());
70 CHECK(RemapVars());
71 CHECK(RemoveDeadTypes());
72
73 def_use_mgr_ = nullptr;
74 type_mgr_ = nullptr;
75
76 return Ok();
77 }
78
Fail()79 spvtools::DiagnosticStream SplitCombinedImageSamplerPass::Fail() {
80 return std::move(
81 spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY)
82 << "split-combined-image-sampler: ");
83 }
84
FindCombinedTextureSamplers()85 void SplitCombinedImageSamplerPass::FindCombinedTextureSamplers() {
86 for (auto& inst : context()->types_values()) {
87 RegisterGlobal(inst.result_id());
88 switch (inst.opcode()) {
89 case spv::Op::OpTypeSampler:
90 // Modules can't have duplicate sampler types.
91 assert(!sampler_type_);
92 sampler_type_ = &inst;
93 break;
94
95 case spv::Op::OpTypeSampledImage:
96 if (!first_sampled_image_type_) {
97 first_sampled_image_type_ = &inst;
98 }
99 combined_types_.insert(inst.result_id());
100 def_use_mgr_->WhileEachUser(inst.result_id(), [&](Instruction* i) {
101 sampled_image_used_as_param_ |=
102 i->opcode() == spv::Op::OpTypeFunction;
103 return !sampled_image_used_as_param_;
104 });
105 break;
106
107 case spv::Op::OpTypeArray:
108 case spv::Op::OpTypeRuntimeArray: {
109 auto pointee_id = inst.GetSingleWordInOperand(0);
110 if (combined_types_.find(pointee_id) != combined_types_.end()) {
111 combined_types_.insert(inst.result_id());
112 combined_types_to_remove_.push_back(inst.result_id());
113 }
114 } break;
115
116 case spv::Op::OpTypePointer: {
117 auto sc =
118 static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0));
119 if (sc == spv::StorageClass::UniformConstant) {
120 auto pointee_id = inst.GetSingleWordInOperand(1);
121 if (combined_types_.find(pointee_id) != combined_types_.end()) {
122 combined_types_.insert(inst.result_id());
123 combined_types_to_remove_.push_back(inst.result_id());
124 }
125 }
126 } break;
127
128 case spv::Op::OpVariable:
129 if (combined_types_.find(inst.type_id()) != combined_types_.end()) {
130 ordered_vars_.push_back(&inst);
131 }
132 break;
133
134 default:
135 break;
136 }
137 }
138 }
139
GetSamplerType()140 Instruction* SplitCombinedImageSamplerPass::GetSamplerType() {
141 if (!sampler_type_) {
142 analysis::Sampler s;
143 uint32_t sampler_type_id = type_mgr_->GetTypeInstruction(&s);
144 sampler_type_ = def_use_mgr_->GetDef(sampler_type_id);
145 assert(first_sampled_image_type_);
146 sampler_type_->InsertBefore(first_sampled_image_type_);
147 RegisterNewGlobal(sampler_type_->result_id());
148 }
149 return sampler_type_;
150 }
151
RemapVars()152 spv_result_t SplitCombinedImageSamplerPass::RemapVars() {
153 for (Instruction* var : ordered_vars_) {
154 CHECK_STATUS(RemapVar(var));
155 }
156 return SPV_SUCCESS;
157 }
158
SplitType(Instruction & combined_kind_type)159 std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
160 Instruction& combined_kind_type) {
161 if (auto where = type_remap_.find(combined_kind_type.result_id());
162 where != type_remap_.end()) {
163 auto& type_remap = where->second;
164 return {type_remap.image_kind_type, type_remap.sampler_kind_type};
165 }
166
167 switch (combined_kind_type.opcode()) {
168 case spv::Op::OpTypeSampledImage: {
169 auto* image_type =
170 def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(0));
171 auto* sampler_type = GetSamplerType();
172 type_remap_[combined_kind_type.result_id()] = {&combined_kind_type,
173 image_type, sampler_type};
174 return {image_type, sampler_type};
175 break;
176 }
177 case spv::Op::OpTypePointer: {
178 auto sc = static_cast<spv::StorageClass>(
179 combined_kind_type.GetSingleWordInOperand(0));
180 if (sc == spv::StorageClass::UniformConstant) {
181 auto* pointee =
182 def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(1));
183 auto [image_pointee, sampler_pointee] = SplitType(*pointee);
184 // These would be null if the pointee is an image type or a sampler
185 // type. Don't decompose them. Currently this method does not check the
186 // assumption that it is being only called on combined types. So code
187 // this defensively.
188 if (image_pointee && sampler_pointee) {
189 auto* ptr_image = MakeUniformConstantPointer(image_pointee);
190 auto* ptr_sampler = MakeUniformConstantPointer(sampler_pointee);
191 type_remap_[combined_kind_type.result_id()] = {
192 &combined_kind_type, ptr_image, ptr_sampler};
193 return {ptr_image, ptr_sampler};
194 }
195 }
196 break;
197 }
198 case spv::Op::OpTypeArray: {
199 const auto* array_ty =
200 type_mgr_->GetType(combined_kind_type.result_id())->AsArray();
201 assert(array_ty);
202 const auto* sampled_image_ty = array_ty->element_type()->AsSampledImage();
203 assert(sampled_image_ty);
204
205 const analysis::Type* image_ty = sampled_image_ty->image_type();
206 assert(image_ty);
207 analysis::Array array_image_ty(image_ty, array_ty->length_info());
208 const uint32_t array_image_ty_id =
209 type_mgr_->GetTypeInstruction(&array_image_ty);
210 auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
211 if (!IsKnownGlobal(array_image_ty_id)) {
212 array_image_ty_inst->InsertBefore(&combined_kind_type);
213 RegisterNewGlobal(array_image_ty_id);
214 // GetTypeInstruction also updated the def-use manager.
215 }
216
217 analysis::Array sampler_array_ty(
218 type_mgr_->GetType(GetSamplerType()->result_id()),
219 array_ty->length_info());
220 const uint32_t array_sampler_ty_id =
221 type_mgr_->GetTypeInstruction(&sampler_array_ty);
222 auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
223 if (!IsKnownGlobal(array_sampler_ty_id)) {
224 array_sampler_ty_inst->InsertBefore(&combined_kind_type);
225 RegisterNewGlobal(array_sampler_ty_id);
226 // GetTypeInstruction also updated the def-use manager.
227 }
228 return {array_image_ty_inst, array_sampler_ty_inst};
229 }
230 case spv::Op::OpTypeRuntimeArray: {
231 // This is like the sized-array case, but there is no length parameter.
232 auto* array_ty =
233 type_mgr_->GetType(combined_kind_type.result_id())->AsRuntimeArray();
234 assert(array_ty);
235 auto* sampled_image_ty = array_ty->element_type()->AsSampledImage();
236 assert(sampled_image_ty);
237
238 const analysis::Type* image_ty = sampled_image_ty->image_type();
239 assert(image_ty);
240 analysis::RuntimeArray array_image_ty(image_ty);
241 const uint32_t array_image_ty_id =
242 type_mgr_->GetTypeInstruction(&array_image_ty);
243 auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
244 if (!IsKnownGlobal(array_image_ty_id)) {
245 array_image_ty_inst->InsertBefore(&combined_kind_type);
246 RegisterNewGlobal(array_image_ty_id);
247 // GetTypeInstruction also updated the def-use manager.
248 }
249
250 analysis::RuntimeArray sampler_array_ty(
251 type_mgr_->GetType(GetSamplerType()->result_id()));
252 const uint32_t array_sampler_ty_id =
253 type_mgr_->GetTypeInstruction(&sampler_array_ty);
254 auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
255 if (!IsKnownGlobal(array_sampler_ty_id)) {
256 array_sampler_ty_inst->InsertBefore(&combined_kind_type);
257 RegisterNewGlobal(array_sampler_ty_id);
258 // GetTypeInstruction also updated the def-use manager.
259 }
260 return {array_image_ty_inst, array_sampler_ty_inst};
261 }
262 default:
263 break;
264 }
265 return {nullptr, nullptr};
266 }
267
RemapVar(Instruction * combined_var)268 spv_result_t SplitCombinedImageSamplerPass::RemapVar(
269 Instruction* combined_var) {
270 InstructionBuilder builder(context(), combined_var,
271 IRContext::kAnalysisDefUse);
272
273 // Create an image variable, and a sampler variable.
274 auto* combined_var_type = def_use_mgr_->GetDef(combined_var->type_id());
275 auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type);
276 assert(ptr_image_ty);
277 assert(ptr_sampler_ty);
278 Instruction* sampler_var = builder.AddVariable(
279 ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant);
280 Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(),
281 SpvStorageClassUniformConstant);
282 modified_ = true;
283 return RemapUses(combined_var, image_var, sampler_var);
284 }
285
RemapUses(Instruction * combined,Instruction * image_part,Instruction * sampler_part)286 spv_result_t SplitCombinedImageSamplerPass::RemapUses(
287 Instruction* combined, Instruction* image_part, Instruction* sampler_part) {
288 // The instructions to delete.
289 std::unordered_set<Instruction*> dead_insts;
290 // The insertion point should be updated before using this builder.
291 // We needed *something* here.
292 InstructionBuilder builder(context(), combined, IRContext::kAnalysisDefUse);
293
294 // This code must maintain the SPIR-V "Data rule" about sampled image values:
295 // > All OpSampledImage instructions, or instructions that load an image or
296 // > sampler reference, must be in the same block in which their Result <id>
297 // > are consumed.
298 //
299 // When the code below inserts OpSampledImage instructions, it is always
300 // either:
301 // - in the same block as the previous OpSampledImage instruction it is
302 // replacing, or
303 // - in the same block as the instruction using sampled image value it is
304 // replacing.
305 //
306 // Assuming that rule is already honoured by the module, these updates will
307 // continue to honour the rule.
308
309 // Represents a single use of a value to be remapped.
310 struct RemapUse {
311 uint32_t used_id; // The ID that is being used.
312 Instruction* user;
313 uint32_t index;
314 Instruction* image_part; // The image part of the replacement.
315 Instruction* sampler_part; // The sampler part of the replacement.
316 };
317 // The work list of uses to be remapped.
318 std::vector<RemapUse> uses;
319
320 // Adds remap records for each use of a value to be remapped.
321 // Also schedules the original value for deletion.
322 auto add_remap = [this, &dead_insts, &uses](Instruction* combined_arg,
323 Instruction* image_part_arg,
324 Instruction* sampler_part_arg) {
325 const uint32_t used_combined_id = combined_arg->result_id();
326
327 def_use_mgr_->ForEachUse(
328 combined_arg, [&](Instruction* user, uint32_t use_index) {
329 uses.push_back({used_combined_id, user, use_index, image_part_arg,
330 sampler_part_arg});
331 });
332 dead_insts.insert(combined_arg);
333 };
334
335 add_remap(combined, image_part, sampler_part);
336
337 // Use index-based iteration because we can add to the work list as we go
338 // along, and reallocation would invalidate ordinary iterators.
339 for (size_t use_index = 0; use_index < uses.size(); ++use_index) {
340 auto& use = uses[use_index];
341 switch (use.user->opcode()) {
342 case spv::Op::OpCopyObject: {
343 // Append the uses of this OpCopyObject to the work list.
344 add_remap(use.user, image_part, sampler_part);
345 break;
346 }
347 case spv::Op::OpLoad: {
348 assert(use.index == 2 && "variable used as non-pointer index on load");
349 Instruction* load = use.user;
350
351 // Assume the loaded value is a sampled image.
352 assert(def_use_mgr_->GetDef(load->type_id())->opcode() ==
353 spv::Op::OpTypeSampledImage);
354
355 // Create loads for the image part and sampler part.
356 builder.SetInsertPoint(load);
357 auto* image = builder.AddLoad(PointeeTypeId(use.image_part),
358 use.image_part->result_id());
359 auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part),
360 use.sampler_part->result_id());
361
362 // Move decorations, such as RelaxedPrecision.
363 auto* deco_mgr = context()->get_decoration_mgr();
364 deco_mgr->CloneDecorations(load->result_id(), image->result_id());
365 deco_mgr->CloneDecorations(load->result_id(), sampler->result_id());
366 deco_mgr->RemoveDecorationsFrom(load->result_id());
367
368 // Create a sampled image from the loads of the two parts.
369 auto* sampled_image = builder.AddSampledImage(
370 load->type_id(), image->result_id(), sampler->result_id());
371 // Replace the original sampled image value with the new one.
372 std::unordered_set<Instruction*> users;
373 def_use_mgr_->ForEachUse(
374 load, [&users, sampled_image](Instruction* user, uint32_t index) {
375 user->SetOperand(index, {sampled_image->result_id()});
376 users.insert(user);
377 });
378 for (auto* user : users) {
379 def_use_mgr_->AnalyzeInstUse(user);
380 }
381 dead_insts.insert(load);
382 break;
383 }
384 case spv::Op::OpDecorate: {
385 assert(use.index == 0 && "variable used as non-target index");
386 builder.SetInsertPoint(use.user);
387 spv::Decoration deco{use.user->GetSingleWordInOperand(1)};
388 std::vector<uint32_t> literals;
389 for (uint32_t i = 2; i < use.user->NumInOperands(); i++) {
390 literals.push_back(use.user->GetSingleWordInOperand(i));
391 }
392 builder.AddDecoration(use.image_part->result_id(), deco, literals);
393 builder.AddDecoration(use.sampler_part->result_id(), deco, literals);
394 // KillInst will delete names and decorations, so don't schedule a
395 // deletion of this instruction.
396 break;
397 }
398 case spv::Op::OpEntryPoint: {
399 // The entry point lists variables in the shader interface, i.e.
400 // module-scope variables referenced by the static call tree rooted
401 // at the entry point. (It can be a proper superset). Before SPIR-V
402 // 1.4, only Input and Output variables are listed; in 1.4 and later,
403 // module-scope variables in all storage classes are listed.
404 // If a combined image+sampler is listed by the entry point, then
405 // the separated image and sampler variables should be.
406 assert(use.index >= 3 &&
407 "variable used in OpEntryPoint but not as an interface ID");
408 use.user->SetOperand(use.index, {use.image_part->result_id()});
409 use.user->InsertOperand(
410 use.user->NumOperands(),
411 {SPV_OPERAND_TYPE_ID, {use.sampler_part->result_id()}});
412 def_use_mgr_->AnalyzeInstUse(use.user);
413 break;
414 }
415 case spv::Op::OpName: {
416 // Synthesize new names from the old.
417 const auto name = use.user->GetOperand(1).AsString();
418 AddOpName(use.image_part->result_id(), name + "_image");
419 AddOpName(use.sampler_part->result_id(), name + "_sampler");
420
421 // KillInst will delete names and decorations, so don't schedule a
422 // deletion of this instruction.
423 break;
424 }
425 case spv::Op::OpFunctionCall: {
426 // Replace each combined arg with two args: the image part, then the
427 // sampler part.
428 // The combined value could have been used twice in the argument list.
429 // Moving things around now will invalidate the 'use' list above.
430 // So don't trust the use index value.
431 auto& call = *use.user;
432 // The insert API only takes absolute arg IDs, not "in" arg IDs.
433 const auto first_arg_operand_index = 3; // Skip the callee ID
434 for (uint32_t i = first_arg_operand_index; i < call.NumOperands();
435 ++i) {
436 if (use.used_id == call.GetSingleWordOperand(i)) {
437 call.SetOperand(i, {use.sampler_part->result_id()});
438 call.InsertOperand(
439 i, {SPV_OPERAND_TYPE_ID, {use.image_part->result_id()}});
440 ++i;
441 }
442 }
443 def_use_mgr_->AnalyzeInstUse(&call);
444 break;
445 }
446 case spv::Op::OpAccessChain:
447 case spv::Op::OpInBoundsAccessChain: {
448 auto* original_access_chain = use.user;
449 builder.SetInsertPoint(original_access_chain);
450 // It can only be the base pointer
451 assert(use.index == 2);
452
453 // Replace the original access chain with access chains for the image
454 // part and the sampler part.
455 std::vector<uint32_t> indices;
456 for (uint32_t i = 3; i < original_access_chain->NumOperands(); i++) {
457 indices.push_back(original_access_chain->GetSingleWordOperand(i));
458 }
459
460 auto [result_image_part_ty, result_sampler_part_ty] =
461 SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id()));
462 auto* result_image_part = builder.AddOpcodeAccessChain(
463 use.user->opcode(), result_image_part_ty->result_id(),
464 use.image_part->result_id(), indices);
465 auto* result_sampler_part = builder.AddOpcodeAccessChain(
466 use.user->opcode(), result_sampler_part_ty->result_id(),
467 use.sampler_part->result_id(), indices);
468
469 // Remap uses of the original access chain.
470 add_remap(original_access_chain, result_image_part,
471 result_sampler_part);
472 break;
473 }
474 default: {
475 uint32_t used_type_id = def_use_mgr_->GetDef(use.used_id)->type_id();
476 auto* used_type = def_use_mgr_->GetDef(used_type_id);
477 if (used_type->opcode() == spv::Op::OpTypeSampledImage) {
478 // This value being used is a sampled image value. But it's
479 // being replaced, so recreate it here.
480 // Example: used by OpImage, OpImageSampleExplicitLod, etc.
481 builder.SetInsertPoint(use.user);
482 auto* sampled_image =
483 builder.AddSampledImage(used_type_id, use.image_part->result_id(),
484 use.sampler_part->result_id());
485 use.user->SetOperand(use.index, {sampled_image->result_id()});
486 def_use_mgr_->AnalyzeInstUse(use.user);
487 break;
488 }
489 return Fail() << "unhandled user: " << *use.user;
490 }
491 }
492 }
493
494 for (auto* inst : dead_insts) {
495 KillInst(inst);
496 }
497
498 return SPV_SUCCESS;
499 }
500
RemapFunctions()501 spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() {
502 // Remap function types. A combined type can appear as a parameter, but not as
503 // the return type.
504 {
505 std::unordered_set<Instruction*> dead_insts;
506 for (auto& inst : context()->types_values()) {
507 if (inst.opcode() != spv::Op::OpTypeFunction) {
508 continue;
509 }
510 analysis::Function* f_ty =
511 type_mgr_->GetType(inst.result_id())->AsFunction();
512 std::vector<const analysis::Type*> new_params;
513 for (const auto* param_ty : f_ty->param_types()) {
514 const auto param_ty_id = type_mgr_->GetId(param_ty);
515 if (combined_types_.find(param_ty_id) != combined_types_.end()) {
516 auto* param_type = def_use_mgr_->GetDef(param_ty_id);
517 auto [image_type, sampler_type] = SplitType(*param_type);
518 assert(image_type);
519 assert(sampler_type);
520 // The image and sampler types must already exist, so there is no
521 // need to move them to the right spot.
522 new_params.push_back(type_mgr_->GetType(image_type->result_id()));
523 new_params.push_back(type_mgr_->GetType(sampler_type->result_id()));
524 } else {
525 new_params.push_back(param_ty);
526 }
527 }
528 if (new_params.size() != f_ty->param_types().size()) {
529 // Replace this type.
530 analysis::Function new_f_ty(f_ty->return_type(), new_params);
531 const uint32_t new_f_ty_id = type_mgr_->GetTypeInstruction(&new_f_ty);
532 std::unordered_set<Instruction*> users;
533 def_use_mgr_->ForEachUse(
534 &inst,
535 [&users, new_f_ty_id](Instruction* user, uint32_t use_index) {
536 user->SetOperand(use_index, {new_f_ty_id});
537 users.insert(user);
538 });
539 for (auto* user : users) {
540 def_use_mgr_->AnalyzeInstUse(user);
541 }
542 dead_insts.insert(&inst);
543 }
544 }
545 for (auto* inst : dead_insts) {
546 KillInst(inst);
547 }
548 }
549
550 // Rewite OpFunctionParameter in function definitions.
551 for (Function& fn : *context()->module()) {
552 // Rewrite the function parameters and record their replacements.
553 struct Replacement {
554 Instruction* combined;
555 Instruction* image;
556 Instruction* sampler;
557 };
558 std::vector<Replacement> replacements;
559
560 Function::RewriteParamFn rewriter =
561 [&](std::unique_ptr<Instruction>&& param,
562 std::back_insert_iterator<Function::ParamList>& appender) {
563 if (combined_types_.count(param->type_id()) == 0) {
564 appender = std::move(param);
565 return;
566 }
567
568 // Replace this parameter with two new parameters.
569 auto* combined_inst = param.release();
570 auto* combined_type = def_use_mgr_->GetDef(combined_inst->type_id());
571 auto [image_type, sampler_type] = SplitType(*combined_type);
572 auto image_param = MakeUnique<Instruction>(
573 context(), spv::Op::OpFunctionParameter, image_type->result_id(),
574 context()->TakeNextId(), Instruction::OperandList{});
575 auto sampler_param = MakeUnique<Instruction>(
576 context(), spv::Op::OpFunctionParameter,
577 sampler_type->result_id(), context()->TakeNextId(),
578 Instruction::OperandList{});
579 replacements.push_back(
580 {combined_inst, image_param.get(), sampler_param.get()});
581 appender = std::move(image_param);
582 appender = std::move(sampler_param);
583 };
584 fn.RewriteParams(rewriter);
585
586 for (auto& r : replacements) {
587 modified_ = true;
588 def_use_mgr_->AnalyzeInstDefUse(r.image);
589 def_use_mgr_->AnalyzeInstDefUse(r.sampler);
590 CHECK_STATUS(RemapUses(r.combined, r.image, r.sampler));
591 }
592 }
593 return SPV_SUCCESS;
594 }
595
MakeUniformConstantPointer(Instruction * pointee)596 Instruction* SplitCombinedImageSamplerPass::MakeUniformConstantPointer(
597 Instruction* pointee) {
598 uint32_t ptr_id = type_mgr_->FindPointerToType(
599 pointee->result_id(), spv::StorageClass::UniformConstant);
600 auto* ptr = def_use_mgr_->GetDef(ptr_id);
601 if (!IsKnownGlobal(ptr_id)) {
602 // The pointer type was created at the end. Put it right after the
603 // pointee.
604 ptr->InsertBefore(pointee);
605 pointee->InsertBefore(ptr);
606 RegisterNewGlobal(ptr_id);
607 // FindPointerToType also updated the def-use manager.
608 }
609 return ptr;
610 }
611
AddOpName(uint32_t id,const std::string & name)612 void SplitCombinedImageSamplerPass::AddOpName(uint32_t id,
613 const std::string& name) {
614 std::unique_ptr<Instruction> opname{new Instruction{
615 context(),
616 spv::Op::OpName,
617 0u,
618 0u,
619 {{SPV_OPERAND_TYPE_ID, {id}},
620 {SPV_OPERAND_TYPE_LITERAL_STRING,
621 utils::MakeVector<spvtools::opt::Operand::OperandData>(name)}}}};
622
623 context()->AddDebug2Inst(std::move(opname));
624 }
625
RemoveDeadTypes()626 spv_result_t SplitCombinedImageSamplerPass::RemoveDeadTypes() {
627 for (auto dead_type_id : combined_types_to_remove_) {
628 if (auto* ty = def_use_mgr_->GetDef(dead_type_id)) {
629 KillInst(ty);
630 }
631 }
632 return SPV_SUCCESS;
633 }
634
KillInst(Instruction * inst)635 void SplitCombinedImageSamplerPass::KillInst(Instruction* inst) {
636 // IRContext::KillInst will remove associated debug instructions and
637 // decorations. It will delete the object only if it is already in a list.
638 const bool was_in_list = inst->IsInAList();
639 context()->KillInst(inst);
640 if (!was_in_list) {
641 // Avoid leaking
642 delete inst;
643 }
644 modified_ = true;
645 }
646
647 } // namespace opt
648 } // namespace spvtools
649