split_combined_image_sampler_pass.cpp 25 KB

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