graphics_robust_access_pass.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  1. // Copyright (c) 2019 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. // This pass injects code in a graphics shader to implement guarantees
  15. // satisfying Vulkan's robustBufferAcces rules. Robust access rules permit
  16. // an out-of-bounds access to be redirected to an access of the same type
  17. // (load, store, etc.) but within the same root object.
  18. //
  19. // We assume baseline functionality in Vulkan, i.e. the module uses
  20. // logical addressing mode, without VK_KHR_variable_pointers.
  21. //
  22. // - Logical addressing mode implies:
  23. // - Each root pointer (a pointer that exists other than by the
  24. // execution of a shader instruction) is the result of an OpVariable.
  25. //
  26. // - Instructions that result in pointers are:
  27. // OpVariable
  28. // OpAccessChain
  29. // OpInBoundsAccessChain
  30. // OpFunctionParameter
  31. // OpImageTexelPointer
  32. // OpCopyObject
  33. //
  34. // - Instructions that use a pointer are:
  35. // OpLoad
  36. // OpStore
  37. // OpAccessChain
  38. // OpInBoundsAccessChain
  39. // OpFunctionCall
  40. // OpImageTexelPointer
  41. // OpCopyMemory
  42. // OpCopyObject
  43. // all OpAtomic* instructions
  44. //
  45. // We classify pointer-users into:
  46. // - Accesses:
  47. // - OpLoad
  48. // - OpStore
  49. // - OpAtomic*
  50. // - OpCopyMemory
  51. //
  52. // - Address calculations:
  53. // - OpAccessChain
  54. // - OpInBoundsAccessChain
  55. //
  56. // - Pass-through:
  57. // - OpFunctionCall
  58. // - OpFunctionParameter
  59. // - OpCopyObject
  60. //
  61. // The strategy is:
  62. //
  63. // - Handle only logical addressing mode. In particular, don't handle a module
  64. // if it uses one of the variable-pointers capabilities.
  65. //
  66. // - Don't handle modules using capability RuntimeDescriptorArrayEXT. So the
  67. // only runtime arrays are those that are the last member in a
  68. // Block-decorated struct. This allows us to feasibly/easily compute the
  69. // length of the runtime array. See below.
  70. //
  71. // - The memory locations accessed by OpLoad, OpStore, OpCopyMemory, and
  72. // OpAtomic* are determined by their pointer parameter or parameters.
  73. // Pointers are always (correctly) typed and so the address and number of
  74. // consecutive locations are fully determined by the pointer.
  75. //
  76. // - A pointer value orginates as one of few cases:
  77. //
  78. // - OpVariable for an interface object or an array of them: image,
  79. // buffer (UBO or SSBO), sampler, sampled-image, push-constant, input
  80. // variable, output variable. The execution environment is responsible for
  81. // allocating the correct amount of storage for these, and for ensuring
  82. // each resource bound to such a variable is big enough to contain the
  83. // SPIR-V pointee type of the variable.
  84. //
  85. // - OpVariable for a non-interface object. These are variables in
  86. // Workgroup, Private, and Function storage classes. The compiler ensures
  87. // the underlying allocation is big enough to store the entire SPIR-V
  88. // pointee type of the variable.
  89. //
  90. // - An OpFunctionParameter. This always maps to a pointer parameter to an
  91. // OpFunctionCall.
  92. //
  93. // - In logical addressing mode, these are severely limited:
  94. // "Any pointer operand to an OpFunctionCall must be:
  95. // - a memory object declaration, or
  96. // - a pointer to an element in an array that is a memory object
  97. // declaration, where the element type is OpTypeSampler or OpTypeImage"
  98. //
  99. // - This has an important simplifying consequence:
  100. //
  101. // - When looking for a pointer to the structure containing a runtime
  102. // array, you begin with a pointer to the runtime array and trace
  103. // backward in the function. You never have to trace back beyond
  104. // your function call boundary. So you can't take a partial access
  105. // chain into an SSBO, then pass that pointer into a function. So
  106. // we don't resort to using fat pointers to compute array length.
  107. // We can trace back to a pointer to the containing structure,
  108. // and use that in an OpArrayLength instruction. (The structure type
  109. // gives us the member index of the runtime array.)
  110. //
  111. // - Otherwise, the pointer type fully encodes the range of valid
  112. // addresses. In particular, the type of a pointer to an aggregate
  113. // value fully encodes the range of indices when indexing into
  114. // that aggregate.
  115. //
  116. // - The pointer is the result of an access chain instruction. We clamp
  117. // indices contributing to address calculations. As noted above, the
  118. // valid ranges are either bound by the length of a runtime array, or
  119. // by the type of the base pointer. The length of a runtime array is
  120. // the result of an OpArrayLength instruction acting on the pointer of
  121. // the containing structure as noted above.
  122. //
  123. // - Access chain indices are always treated as signed, so:
  124. // - Clamp the upper bound at the signed integer maximum.
  125. // - Use SClamp for all clamping.
  126. //
  127. // - TODO(dneto): OpImageTexelPointer:
  128. // - Clamp coordinate to the image size returned by OpImageQuerySize
  129. // - If multi-sampled, clamp the sample index to the count returned by
  130. // OpImageQuerySamples.
  131. // - If not multi-sampled, set the sample index to 0.
  132. //
  133. // - Rely on the external validator to check that pointers are only
  134. // used by the instructions as above.
  135. //
  136. // - Handles OpTypeRuntimeArray
  137. // Track pointer back to original resource (pointer to struct), so we can
  138. // query the runtime array size.
  139. //
  140. #include "graphics_robust_access_pass.h"
  141. #include <algorithm>
  142. #include <cstring>
  143. #include <functional>
  144. #include <initializer_list>
  145. #include <limits>
  146. #include <utility>
  147. #include "constants.h"
  148. #include "def_use_manager.h"
  149. #include "function.h"
  150. #include "ir_context.h"
  151. #include "module.h"
  152. #include "pass.h"
  153. #include "source/diagnostic.h"
  154. #include "source/util/make_unique.h"
  155. #include "spirv-tools/libspirv.h"
  156. #include "spirv/unified1/GLSL.std.450.h"
  157. #include "spirv/unified1/spirv.h"
  158. #include "type_manager.h"
  159. #include "types.h"
  160. namespace spvtools {
  161. namespace opt {
  162. using opt::Instruction;
  163. using opt::Operand;
  164. using spvtools::MakeUnique;
  165. GraphicsRobustAccessPass::GraphicsRobustAccessPass() : module_status_() {}
  166. Pass::Status GraphicsRobustAccessPass::Process() {
  167. module_status_ = PerModuleState();
  168. ProcessCurrentModule();
  169. auto result = module_status_.failed
  170. ? Status::Failure
  171. : (module_status_.modified ? Status::SuccessWithChange
  172. : Status::SuccessWithoutChange);
  173. return result;
  174. }
  175. spvtools::DiagnosticStream GraphicsRobustAccessPass::Fail() {
  176. module_status_.failed = true;
  177. // We don't really have a position, and we'll ignore the result.
  178. return std::move(
  179. spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY)
  180. << name() << ": ");
  181. }
  182. spv_result_t GraphicsRobustAccessPass::IsCompatibleModule() {
  183. auto* feature_mgr = context()->get_feature_mgr();
  184. if (!feature_mgr->HasCapability(SpvCapabilityShader))
  185. return Fail() << "Can only process Shader modules";
  186. if (feature_mgr->HasCapability(SpvCapabilityVariablePointers))
  187. return Fail() << "Can't process modules with VariablePointers capability";
  188. if (feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer))
  189. return Fail() << "Can't process modules with VariablePointersStorageBuffer "
  190. "capability";
  191. if (feature_mgr->HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
  192. // These have a RuntimeArray outside of Block-decorated struct. There
  193. // is no way to compute the array length from within SPIR-V.
  194. return Fail() << "Can't process modules with RuntimeDescriptorArrayEXT "
  195. "capability";
  196. }
  197. {
  198. auto* inst = context()->module()->GetMemoryModel();
  199. const auto addressing_model = inst->GetSingleWordOperand(0);
  200. if (addressing_model != SpvAddressingModelLogical)
  201. return Fail() << "Addressing model must be Logical. Found "
  202. << inst->PrettyPrint();
  203. }
  204. return SPV_SUCCESS;
  205. }
  206. spv_result_t GraphicsRobustAccessPass::ProcessCurrentModule() {
  207. auto err = IsCompatibleModule();
  208. if (err != SPV_SUCCESS) return err;
  209. ProcessFunction fn = [this](opt::Function* f) { return ProcessAFunction(f); };
  210. module_status_.modified |= context()->ProcessReachableCallTree(fn);
  211. // Need something here. It's the price we pay for easier failure paths.
  212. return SPV_SUCCESS;
  213. }
  214. bool GraphicsRobustAccessPass::ProcessAFunction(opt::Function* function) {
  215. // Ensure that all pointers computed inside a function are within bounds.
  216. // Find the access chains in this block before trying to modify them.
  217. std::vector<Instruction*> access_chains;
  218. std::vector<Instruction*> image_texel_pointers;
  219. for (auto& block : *function) {
  220. for (auto& inst : block) {
  221. switch (inst.opcode()) {
  222. case SpvOpAccessChain:
  223. case SpvOpInBoundsAccessChain:
  224. access_chains.push_back(&inst);
  225. break;
  226. case SpvOpImageTexelPointer:
  227. image_texel_pointers.push_back(&inst);
  228. break;
  229. default:
  230. break;
  231. }
  232. }
  233. }
  234. for (auto* inst : access_chains) {
  235. ClampIndicesForAccessChain(inst);
  236. if (module_status_.failed) return module_status_.modified;
  237. }
  238. for (auto* inst : image_texel_pointers) {
  239. if (SPV_SUCCESS != ClampCoordinateForImageTexelPointer(inst)) break;
  240. }
  241. return module_status_.modified;
  242. }
  243. void GraphicsRobustAccessPass::ClampIndicesForAccessChain(
  244. Instruction* access_chain) {
  245. Instruction& inst = *access_chain;
  246. auto* constant_mgr = context()->get_constant_mgr();
  247. auto* def_use_mgr = context()->get_def_use_mgr();
  248. auto* type_mgr = context()->get_type_mgr();
  249. const bool have_int64_cap =
  250. context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
  251. // Replaces one of the OpAccessChain index operands with a new value.
  252. // Updates def-use analysis.
  253. auto replace_index = [&inst, def_use_mgr](uint32_t operand_index,
  254. Instruction* new_value) {
  255. inst.SetOperand(operand_index, {new_value->result_id()});
  256. def_use_mgr->AnalyzeInstUse(&inst);
  257. return SPV_SUCCESS;
  258. };
  259. // Replaces one of the OpAccesssChain index operands with a clamped value.
  260. // Replace the operand at |operand_index| with the value computed from
  261. // signed_clamp(%old_value, %min_value, %max_value). It also analyzes
  262. // the new instruction and records that them module is modified.
  263. // Assumes %min_value is signed-less-or-equal than %max_value. (All callees
  264. // use 0 for %min_value).
  265. auto clamp_index = [&inst, type_mgr, this, &replace_index](
  266. uint32_t operand_index, Instruction* old_value,
  267. Instruction* min_value, Instruction* max_value) {
  268. auto* clamp_inst =
  269. MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst);
  270. return replace_index(operand_index, clamp_inst);
  271. };
  272. // Ensures the specified index of access chain |inst| has a value that is
  273. // at most |count| - 1. If the index is already a constant value less than
  274. // |count| then no change is made.
  275. auto clamp_to_literal_count =
  276. [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index,
  277. &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t {
  278. Instruction* index_inst =
  279. this->GetDef(inst.GetSingleWordOperand(operand_index));
  280. const auto* index_type =
  281. type_mgr->GetType(index_inst->type_id())->AsInteger();
  282. assert(index_type);
  283. const auto index_width = index_type->width();
  284. if (count <= 1) {
  285. // Replace the index with 0.
  286. return replace_index(operand_index, GetValueForType(0, index_type));
  287. }
  288. uint64_t maxval = count - 1;
  289. // Compute the bit width of a viable type to hold |maxval|.
  290. // Look for a bit width, up to 64 bits wide, to fit maxval.
  291. uint32_t maxval_width = index_width;
  292. while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
  293. maxval_width *= 2;
  294. }
  295. // Determine the type for |maxval|.
  296. uint32_t next_id = context()->module()->IdBound();
  297. analysis::Integer signed_type_for_query(maxval_width, true);
  298. auto* maxval_type =
  299. type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger();
  300. if (next_id != context()->module()->IdBound()) {
  301. module_status_.modified = true;
  302. }
  303. // Access chain indices are treated as signed, so limit the maximum value
  304. // of the index so it will always be positive for a signed clamp operation.
  305. maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1));
  306. if (index_width > 64) {
  307. return this->Fail() << "Can't handle indices wider than 64 bits, found "
  308. "constant index with "
  309. << index_width << " bits as index number "
  310. << operand_index << " of access chain "
  311. << inst.PrettyPrint();
  312. }
  313. // Split into two cases: the current index is a constant, or not.
  314. // If the index is a constant then |index_constant| will not be a null
  315. // pointer. (If index is an |OpConstantNull| then it |index_constant| will
  316. // not be a null pointer.) Since access chain indices must be scalar
  317. // integers, this can't be a spec constant.
  318. if (auto* index_constant = constant_mgr->GetConstantFromInst(index_inst)) {
  319. auto* int_index_constant = index_constant->AsIntConstant();
  320. int64_t value = 0;
  321. // OpAccessChain indices are treated as signed. So get the signed
  322. // constant value here.
  323. if (index_width <= 32) {
  324. value = int64_t(int_index_constant->GetS32BitValue());
  325. } else if (index_width <= 64) {
  326. value = int_index_constant->GetS64BitValue();
  327. }
  328. if (value < 0) {
  329. return replace_index(operand_index, GetValueForType(0, index_type));
  330. } else if (uint64_t(value) <= maxval) {
  331. // Nothing to do.
  332. return SPV_SUCCESS;
  333. } else {
  334. // Replace with maxval.
  335. assert(count > 0); // Already took care of this case above.
  336. return replace_index(operand_index,
  337. GetValueForType(maxval, maxval_type));
  338. }
  339. } else {
  340. // Generate a clamp instruction.
  341. assert(maxval >= 1);
  342. assert(index_width <= 64); // Otherwise, already returned above.
  343. if (index_width >= 64 && !have_int64_cap) {
  344. // An inconsistent module.
  345. return Fail() << "Access chain index is wider than 64 bits, but Int64 "
  346. "is not declared: "
  347. << index_inst->PrettyPrint();
  348. }
  349. // Widen the index value if necessary
  350. if (maxval_width > index_width) {
  351. // Find the wider type. We only need this case if a constant array
  352. // bound is too big.
  353. // From how we calculated maxval_width, widening won't require adding
  354. // the Int64 capability.
  355. assert(have_int64_cap || maxval_width <= 32);
  356. if (!have_int64_cap && maxval_width >= 64) {
  357. // Be defensive, but this shouldn't happen.
  358. return this->Fail()
  359. << "Clamping index would require adding Int64 capability. "
  360. << "Can't clamp 32-bit index " << operand_index
  361. << " of access chain " << inst.PrettyPrint();
  362. }
  363. index_inst = WidenInteger(index_type->IsSigned(), maxval_width,
  364. index_inst, &inst);
  365. }
  366. // Finally, clamp the index.
  367. return clamp_index(operand_index, index_inst,
  368. GetValueForType(0, maxval_type),
  369. GetValueForType(maxval, maxval_type));
  370. }
  371. return SPV_SUCCESS;
  372. };
  373. // Ensures the specified index of access chain |inst| has a value that is at
  374. // most the value of |count_inst| minus 1, where |count_inst| is treated as an
  375. // unsigned integer. This can log a failure.
  376. auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count,
  377. &clamp_index,
  378. &type_mgr](uint32_t operand_index,
  379. Instruction* count_inst) -> spv_result_t {
  380. Instruction* index_inst =
  381. this->GetDef(inst.GetSingleWordOperand(operand_index));
  382. const auto* index_type =
  383. type_mgr->GetType(index_inst->type_id())->AsInteger();
  384. const auto* count_type =
  385. type_mgr->GetType(count_inst->type_id())->AsInteger();
  386. assert(index_type);
  387. if (const auto* count_constant =
  388. constant_mgr->GetConstantFromInst(count_inst)) {
  389. uint64_t value = 0;
  390. const auto width = count_constant->type()->AsInteger()->width();
  391. if (width <= 32) {
  392. value = count_constant->AsIntConstant()->GetU32BitValue();
  393. } else if (width <= 64) {
  394. value = count_constant->AsIntConstant()->GetU64BitValue();
  395. } else {
  396. return this->Fail() << "Can't handle indices wider than 64 bits, found "
  397. "constant index with "
  398. << index_type->width() << "bits";
  399. }
  400. return clamp_to_literal_count(operand_index, value);
  401. } else {
  402. // Widen them to the same width.
  403. const auto index_width = index_type->width();
  404. const auto count_width = count_type->width();
  405. const auto target_width = std::max(index_width, count_width);
  406. // UConvert requires the result type to have 0 signedness. So enforce
  407. // that here.
  408. auto* wider_type = index_width < count_width ? count_type : index_type;
  409. if (index_type->width() < target_width) {
  410. // Access chain indices are treated as signed integers.
  411. index_inst = WidenInteger(true, target_width, index_inst, &inst);
  412. } else if (count_type->width() < target_width) {
  413. // Assume type sizes are treated as unsigned.
  414. count_inst = WidenInteger(false, target_width, count_inst, &inst);
  415. }
  416. // Compute count - 1.
  417. // It doesn't matter if 1 is signed or unsigned.
  418. auto* one = GetValueForType(1, wider_type);
  419. auto* count_minus_1 = InsertInst(
  420. &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(),
  421. {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
  422. {SPV_OPERAND_TYPE_ID, {one->result_id()}}});
  423. auto* zero = GetValueForType(0, wider_type);
  424. // Make sure we clamp to an upper bound that is at most the signed max
  425. // for the target type.
  426. const uint64_t max_signed_value =
  427. ((uint64_t(1) << (target_width - 1)) - 1);
  428. // Use unsigned-min to ensure that the result is always non-negative.
  429. // That ensures we satisfy the invariant for SClamp, where the "min"
  430. // argument we give it (zero), is no larger than the third argument.
  431. auto* upper_bound =
  432. MakeUMinInst(*type_mgr, count_minus_1,
  433. GetValueForType(max_signed_value, wider_type), &inst);
  434. // Now clamp the index to this upper bound.
  435. return clamp_index(operand_index, index_inst, zero, upper_bound);
  436. }
  437. return SPV_SUCCESS;
  438. };
  439. const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0));
  440. const Instruction* base_type = GetDef(base_inst->type_id());
  441. Instruction* pointee_type = GetDef(base_type->GetSingleWordInOperand(1));
  442. // Walk the indices from earliest to latest, replacing indices with a
  443. // clamped value, and updating the pointee_type. The order matters for
  444. // the case when we have to compute the length of a runtime array. In
  445. // that the algorithm relies on the fact that that the earlier indices
  446. // have already been clamped.
  447. const uint32_t num_operands = inst.NumOperands();
  448. for (uint32_t idx = 3; !module_status_.failed && idx < num_operands; ++idx) {
  449. const uint32_t index_id = inst.GetSingleWordOperand(idx);
  450. Instruction* index_inst = GetDef(index_id);
  451. switch (pointee_type->opcode()) {
  452. case SpvOpTypeMatrix: // Use column count
  453. case SpvOpTypeVector: // Use component count
  454. {
  455. const uint32_t count = pointee_type->GetSingleWordOperand(2);
  456. clamp_to_literal_count(idx, count);
  457. pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
  458. } break;
  459. case SpvOpTypeArray: {
  460. // The array length can be a spec constant, so go through the general
  461. // case.
  462. Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2));
  463. clamp_to_count(idx, array_len);
  464. pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
  465. } break;
  466. case SpvOpTypeStruct: {
  467. // SPIR-V requires the index to be an OpConstant.
  468. // We need to know the index literal value so we can compute the next
  469. // pointee type.
  470. if (index_inst->opcode() != SpvOpConstant ||
  471. !constant_mgr->GetConstantFromInst(index_inst)
  472. ->type()
  473. ->AsInteger()) {
  474. Fail() << "Member index into struct is not a constant integer: "
  475. << index_inst->PrettyPrint(
  476. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
  477. << "\nin access chain: "
  478. << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  479. return;
  480. }
  481. const auto num_members = pointee_type->NumInOperands();
  482. const auto* index_constant =
  483. constant_mgr->GetConstantFromInst(index_inst);
  484. // Get the sign-extended value, since access index is always treated as
  485. // signed.
  486. const auto index_value = index_constant->GetSignExtendedValue();
  487. if (index_value < 0 || index_value >= num_members) {
  488. Fail() << "Member index " << index_value
  489. << " is out of bounds for struct type: "
  490. << pointee_type->PrettyPrint(
  491. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
  492. << "\nin access chain: "
  493. << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  494. return;
  495. }
  496. pointee_type = GetDef(pointee_type->GetSingleWordInOperand(
  497. static_cast<uint32_t>(index_value)));
  498. // No need to clamp this index. We just checked that it's valid.
  499. } break;
  500. case SpvOpTypeRuntimeArray: {
  501. auto* array_len = MakeRuntimeArrayLengthInst(&inst, idx);
  502. if (!array_len) { // We've already signaled an error.
  503. return;
  504. }
  505. clamp_to_count(idx, array_len);
  506. if (module_status_.failed) return;
  507. pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
  508. } break;
  509. default:
  510. Fail() << " Unhandled pointee type for access chain "
  511. << pointee_type->PrettyPrint(
  512. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  513. }
  514. }
  515. }
  516. uint32_t GraphicsRobustAccessPass::GetGlslInsts() {
  517. if (module_status_.glsl_insts_id == 0) {
  518. // This string serves double-duty as raw data for a string and for a vector
  519. // of 32-bit words
  520. const char glsl[] = "GLSL.std.450\0\0\0\0";
  521. const size_t glsl_str_byte_len = 16;
  522. // Use an existing import if we can.
  523. for (auto& inst : context()->module()->ext_inst_imports()) {
  524. const auto& name_words = inst.GetInOperand(0).words;
  525. if (0 == std::strncmp(reinterpret_cast<const char*>(name_words.data()),
  526. glsl, glsl_str_byte_len)) {
  527. module_status_.glsl_insts_id = inst.result_id();
  528. }
  529. }
  530. if (module_status_.glsl_insts_id == 0) {
  531. // Make a new import instruction.
  532. module_status_.glsl_insts_id = TakeNextId();
  533. std::vector<uint32_t> words(glsl_str_byte_len / sizeof(uint32_t));
  534. std::memcpy(words.data(), glsl, glsl_str_byte_len);
  535. auto import_inst = MakeUnique<Instruction>(
  536. context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id,
  537. std::initializer_list<Operand>{
  538. Operand{SPV_OPERAND_TYPE_LITERAL_STRING, std::move(words)}});
  539. Instruction* inst = import_inst.get();
  540. context()->module()->AddExtInstImport(std::move(import_inst));
  541. module_status_.modified = true;
  542. context()->AnalyzeDefUse(inst);
  543. // Reanalyze the feature list, since we added an extended instruction
  544. // set improt.
  545. context()->get_feature_mgr()->Analyze(context()->module());
  546. }
  547. }
  548. return module_status_.glsl_insts_id;
  549. }
  550. opt::Instruction* opt::GraphicsRobustAccessPass::GetValueForType(
  551. uint64_t value, const analysis::Integer* type) {
  552. auto* mgr = context()->get_constant_mgr();
  553. assert(type->width() <= 64);
  554. std::vector<uint32_t> words;
  555. words.push_back(uint32_t(value));
  556. if (type->width() > 32) {
  557. words.push_back(uint32_t(value >> 32u));
  558. }
  559. const auto* constant = mgr->GetConstant(type, words);
  560. return mgr->GetDefiningInstruction(
  561. constant, context()->get_type_mgr()->GetTypeInstruction(type));
  562. }
  563. opt::Instruction* opt::GraphicsRobustAccessPass::WidenInteger(
  564. bool sign_extend, uint32_t bit_width, Instruction* value,
  565. Instruction* before_inst) {
  566. analysis::Integer unsigned_type_for_query(bit_width, false);
  567. auto* type_mgr = context()->get_type_mgr();
  568. auto* unsigned_type = type_mgr->GetRegisteredType(&unsigned_type_for_query);
  569. auto type_id = context()->get_type_mgr()->GetId(unsigned_type);
  570. auto conversion_id = TakeNextId();
  571. auto* conversion = InsertInst(
  572. before_inst, (sign_extend ? SpvOpSConvert : SpvOpUConvert), type_id,
  573. conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}});
  574. return conversion;
  575. }
  576. Instruction* GraphicsRobustAccessPass::MakeUMinInst(
  577. const analysis::TypeManager& tm, Instruction* x, Instruction* y,
  578. Instruction* where) {
  579. // Get IDs of instructions we'll be referencing. Evaluate them before calling
  580. // the function so we force a deterministic ordering in case both of them need
  581. // to take a new ID.
  582. const uint32_t glsl_insts_id = GetGlslInsts();
  583. uint32_t smin_id = TakeNextId();
  584. const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
  585. const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width();
  586. assert(xwidth == ywidth);
  587. (void)xwidth;
  588. (void)ywidth;
  589. auto* smin_inst = InsertInst(
  590. where, SpvOpExtInst, x->type_id(), smin_id,
  591. {
  592. {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
  593. {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}},
  594. {SPV_OPERAND_TYPE_ID, {x->result_id()}},
  595. {SPV_OPERAND_TYPE_ID, {y->result_id()}},
  596. });
  597. return smin_inst;
  598. }
  599. Instruction* GraphicsRobustAccessPass::MakeSClampInst(
  600. const analysis::TypeManager& tm, Instruction* x, Instruction* min,
  601. Instruction* max, Instruction* where) {
  602. // Get IDs of instructions we'll be referencing. Evaluate them before calling
  603. // the function so we force a deterministic ordering in case both of them need
  604. // to take a new ID.
  605. const uint32_t glsl_insts_id = GetGlslInsts();
  606. uint32_t clamp_id = TakeNextId();
  607. const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
  608. const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width();
  609. const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width();
  610. assert(xwidth == minwidth);
  611. assert(xwidth == maxwidth);
  612. (void)xwidth;
  613. (void)minwidth;
  614. (void)maxwidth;
  615. auto* clamp_inst = InsertInst(
  616. where, SpvOpExtInst, x->type_id(), clamp_id,
  617. {
  618. {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
  619. {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}},
  620. {SPV_OPERAND_TYPE_ID, {x->result_id()}},
  621. {SPV_OPERAND_TYPE_ID, {min->result_id()}},
  622. {SPV_OPERAND_TYPE_ID, {max->result_id()}},
  623. });
  624. return clamp_inst;
  625. }
  626. Instruction* GraphicsRobustAccessPass::MakeRuntimeArrayLengthInst(
  627. Instruction* access_chain, uint32_t operand_index) {
  628. // The Index parameter to the access chain at |operand_index| is indexing
  629. // *into* the runtime-array. To get the number of elements in the runtime
  630. // array we need a pointer to the Block-decorated struct that contains the
  631. // runtime array. So conceptually we have to go 2 steps backward in the
  632. // access chain. The two steps backward might forces us to traverse backward
  633. // across multiple dominating instructions.
  634. auto* type_mgr = context()->get_type_mgr();
  635. // How many access chain indices do we have to unwind to find the pointer
  636. // to the struct containing the runtime array?
  637. uint32_t steps_remaining = 2;
  638. // Find or create an instruction computing the pointer to the structure
  639. // containing the runtime array.
  640. // Walk backward through pointer address calculations until we either get
  641. // to exactly the right base pointer, or to an access chain instruction
  642. // that we can replicate but truncate to compute the address of the right
  643. // struct.
  644. Instruction* current_access_chain = access_chain;
  645. Instruction* pointer_to_containing_struct = nullptr;
  646. while (steps_remaining > 0) {
  647. switch (current_access_chain->opcode()) {
  648. case SpvOpCopyObject:
  649. // Whoops. Walk right through this one.
  650. current_access_chain =
  651. GetDef(current_access_chain->GetSingleWordInOperand(0));
  652. break;
  653. case SpvOpAccessChain:
  654. case SpvOpInBoundsAccessChain: {
  655. const int first_index_operand = 3;
  656. // How many indices in this access chain contribute to getting us
  657. // to an element in the runtime array?
  658. const auto num_contributing_indices =
  659. current_access_chain == access_chain
  660. ? operand_index - (first_index_operand - 1)
  661. : current_access_chain->NumInOperands() - 1 /* skip the base */;
  662. Instruction* base =
  663. GetDef(current_access_chain->GetSingleWordInOperand(0));
  664. if (num_contributing_indices == steps_remaining) {
  665. // The base pointer points to the structure.
  666. pointer_to_containing_struct = base;
  667. steps_remaining = 0;
  668. break;
  669. } else if (num_contributing_indices < steps_remaining) {
  670. // Peel off the index and keep going backward.
  671. steps_remaining -= num_contributing_indices;
  672. current_access_chain = base;
  673. } else {
  674. // This access chain has more indices than needed. Generate a new
  675. // access chain instruction, but truncating the list of indices.
  676. const int base_operand = 2;
  677. // We'll use the base pointer and the indices up to but not including
  678. // the one indexing into the runtime array.
  679. Instruction::OperandList ops;
  680. // Use the base pointer
  681. ops.push_back(current_access_chain->GetOperand(base_operand));
  682. const uint32_t num_indices_to_keep =
  683. num_contributing_indices - steps_remaining - 1;
  684. for (uint32_t i = 0; i <= num_indices_to_keep; i++) {
  685. ops.push_back(
  686. current_access_chain->GetOperand(first_index_operand + i));
  687. }
  688. // Compute the type of the result of the new access chain. Start at
  689. // the base and walk the indices in a forward direction.
  690. auto* constant_mgr = context()->get_constant_mgr();
  691. std::vector<uint32_t> indices_for_type;
  692. for (uint32_t i = 0; i < ops.size() - 1; i++) {
  693. uint32_t index_for_type_calculation = 0;
  694. Instruction* index =
  695. GetDef(current_access_chain->GetSingleWordOperand(
  696. first_index_operand + i));
  697. if (auto* index_constant =
  698. constant_mgr->GetConstantFromInst(index)) {
  699. // We only need 32 bits. For the type calculation, it's sufficient
  700. // to take the zero-extended value. It only matters for the struct
  701. // case, and struct member indices are unsigned.
  702. index_for_type_calculation =
  703. uint32_t(index_constant->GetZeroExtendedValue());
  704. } else {
  705. // Indexing into a variably-sized thing like an array. Use 0.
  706. index_for_type_calculation = 0;
  707. }
  708. indices_for_type.push_back(index_for_type_calculation);
  709. }
  710. auto* base_ptr_type = type_mgr->GetType(base->type_id())->AsPointer();
  711. auto* base_pointee_type = base_ptr_type->pointee_type();
  712. auto* new_access_chain_result_pointee_type =
  713. type_mgr->GetMemberType(base_pointee_type, indices_for_type);
  714. const uint32_t new_access_chain_type_id = type_mgr->FindPointerToType(
  715. type_mgr->GetId(new_access_chain_result_pointee_type),
  716. base_ptr_type->storage_class());
  717. // Create the instruction and insert it.
  718. const auto new_access_chain_id = TakeNextId();
  719. auto* new_access_chain =
  720. InsertInst(current_access_chain, current_access_chain->opcode(),
  721. new_access_chain_type_id, new_access_chain_id, ops);
  722. pointer_to_containing_struct = new_access_chain;
  723. steps_remaining = 0;
  724. break;
  725. }
  726. } break;
  727. default:
  728. Fail() << "Unhandled access chain in logical addressing mode passes "
  729. "through "
  730. << current_access_chain->PrettyPrint(
  731. SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET |
  732. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  733. return nullptr;
  734. }
  735. }
  736. assert(pointer_to_containing_struct);
  737. auto* pointee_type =
  738. type_mgr->GetType(pointer_to_containing_struct->type_id())
  739. ->AsPointer()
  740. ->pointee_type();
  741. auto* struct_type = pointee_type->AsStruct();
  742. const uint32_t member_index_of_runtime_array =
  743. uint32_t(struct_type->element_types().size() - 1);
  744. // Create the length-of-array instruction before the original access chain,
  745. // but after the generation of the pointer to the struct.
  746. const auto array_len_id = TakeNextId();
  747. analysis::Integer uint_type_for_query(32, false);
  748. auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query);
  749. auto* array_len = InsertInst(
  750. access_chain, SpvOpArrayLength, type_mgr->GetId(uint_type), array_len_id,
  751. {{SPV_OPERAND_TYPE_ID, {pointer_to_containing_struct->result_id()}},
  752. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index_of_runtime_array}}});
  753. return array_len;
  754. }
  755. spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
  756. opt::Instruction* image_texel_pointer) {
  757. // TODO(dneto): Write tests for this code.
  758. // TODO(dneto): Use signed-clamp
  759. (void)(image_texel_pointer);
  760. return SPV_SUCCESS;
  761. // Do not compile this code until it is ready to be used.
  762. #if 0
  763. // Example:
  764. // %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord
  765. // %sample
  766. //
  767. // We want to clamp %coord components between vector-0 and the result
  768. // of OpImageQuerySize acting on the underlying image. So insert:
  769. // %image = OpLoad %image_type %image_ptr
  770. // %query_size = OpImageQuerySize %query_size_type %image
  771. //
  772. // For a multi-sampled image, %sample is the sample index, and we need
  773. // to clamp it between zero and the number of samples in the image.
  774. // %sample_count = OpImageQuerySamples %uint %image
  775. // %max_sample_index = OpISub %uint %sample_count %uint_1
  776. // For non-multi-sampled images, the sample index must be constant zero.
  777. auto* def_use_mgr = context()->get_def_use_mgr();
  778. auto* type_mgr = context()->get_type_mgr();
  779. auto* constant_mgr = context()->get_constant_mgr();
  780. auto* image_ptr = GetDef(image_texel_pointer->GetSingleWordInOperand(0));
  781. auto* image_ptr_type = GetDef(image_ptr->type_id());
  782. auto image_type_id = image_ptr_type->GetSingleWordInOperand(1);
  783. auto* image_type = GetDef(image_type_id);
  784. auto* coord = GetDef(image_texel_pointer->GetSingleWordInOperand(1));
  785. auto* samples = GetDef(image_texel_pointer->GetSingleWordInOperand(2));
  786. // We will modify the module, at least by adding image query instructions.
  787. module_status_.modified = true;
  788. // Declare the ImageQuery capability if the module doesn't already have it.
  789. auto* feature_mgr = context()->get_feature_mgr();
  790. if (!feature_mgr->HasCapability(SpvCapabilityImageQuery)) {
  791. auto cap = MakeUnique<Instruction>(
  792. context(), SpvOpCapability, 0, 0,
  793. std::initializer_list<Operand>{
  794. {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityImageQuery}}});
  795. def_use_mgr->AnalyzeInstDefUse(cap.get());
  796. context()->AddCapability(std::move(cap));
  797. feature_mgr->Analyze(context()->module());
  798. }
  799. // OpImageTexelPointer is used to translate a coordinate and sample index
  800. // into an address for use with an atomic operation. That is, it may only
  801. // used with what Vulkan calls a "storage image"
  802. // (OpTypeImage parameter Sampled=2).
  803. // Note: A storage image never has a level-of-detail associated with it.
  804. // Constraints on the sample id:
  805. // - Only 2D images can be multi-sampled: OpTypeImage parameter MS=1
  806. // only if Dim=2D.
  807. // - Non-multi-sampled images (OpTypeImage parameter MS=0) must use
  808. // sample ID to a constant 0.
  809. // The coordinate is treated as unsigned, and should be clamped against the
  810. // image "size", returned by OpImageQuerySize. (Note: OpImageQuerySizeLod
  811. // is only usable with a sampled image, i.e. its image type has Sampled=1).
  812. // Determine the result type for the OpImageQuerySize.
  813. // For non-arrayed images:
  814. // non-Cube:
  815. // - Always the same as the coordinate type
  816. // Cube:
  817. // - Use all but the last component of the coordinate (which is the face
  818. // index from 0 to 5).
  819. // For arrayed images (in Vulkan the Dim is 1D, 2D, or Cube):
  820. // non-Cube:
  821. // - A vector with the components in the coordinate, and one more for
  822. // the layer index.
  823. // Cube:
  824. // - The same as the coordinate type: 3-element integer vector.
  825. // - The third component from the size query is the layer count.
  826. // - The third component in the texel pointer calculation is
  827. // 6 * layer + face, where 0 <= face < 6.
  828. // Cube: Use all but the last component of the coordinate (which is the face
  829. // index from 0 to 5).
  830. const auto dim = SpvDim(image_type->GetSingleWordInOperand(1));
  831. const bool arrayed = image_type->GetSingleWordInOperand(3) == 1;
  832. const bool multisampled = image_type->GetSingleWordInOperand(4) != 0;
  833. const auto query_num_components = [dim, arrayed, this]() -> int {
  834. const int arrayness_bonus = arrayed ? 1 : 0;
  835. int num_coords = 0;
  836. switch (dim) {
  837. case SpvDimBuffer:
  838. case SpvDim1D:
  839. num_coords = 1;
  840. break;
  841. case SpvDimCube:
  842. // For cube, we need bounds for x, y, but not face.
  843. case SpvDimRect:
  844. case SpvDim2D:
  845. num_coords = 2;
  846. break;
  847. case SpvDim3D:
  848. num_coords = 3;
  849. break;
  850. case SpvDimSubpassData:
  851. case SpvDimMax:
  852. return Fail() << "Invalid image dimension for OpImageTexelPointer: "
  853. << int(dim);
  854. break;
  855. }
  856. return num_coords + arrayness_bonus;
  857. }();
  858. const auto* coord_component_type = [type_mgr, coord]() {
  859. const analysis::Type* coord_type = type_mgr->GetType(coord->type_id());
  860. if (auto* vector_type = coord_type->AsVector()) {
  861. return vector_type->element_type()->AsInteger();
  862. }
  863. return coord_type->AsInteger();
  864. }();
  865. // For now, only handle 32-bit case for coordinates.
  866. if (!coord_component_type) {
  867. return Fail() << " Coordinates for OpImageTexelPointer are not integral: "
  868. << image_texel_pointer->PrettyPrint(
  869. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  870. }
  871. if (coord_component_type->width() != 32) {
  872. return Fail() << " Expected OpImageTexelPointer coordinate components to "
  873. "be 32-bits wide. They are "
  874. << coord_component_type->width() << " bits. "
  875. << image_texel_pointer->PrettyPrint(
  876. SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
  877. }
  878. const auto* query_size_type =
  879. [type_mgr, coord_component_type,
  880. query_num_components]() -> const analysis::Type* {
  881. if (query_num_components == 1) return coord_component_type;
  882. analysis::Vector proposed(coord_component_type, query_num_components);
  883. return type_mgr->GetRegisteredType(&proposed);
  884. }();
  885. const uint32_t image_id = TakeNextId();
  886. auto* image =
  887. InsertInst(image_texel_pointer, SpvOpLoad, image_type_id, image_id,
  888. {{SPV_OPERAND_TYPE_ID, {image_ptr->result_id()}}});
  889. const uint32_t query_size_id = TakeNextId();
  890. auto* query_size =
  891. InsertInst(image_texel_pointer, SpvOpImageQuerySize,
  892. type_mgr->GetTypeInstruction(query_size_type), query_size_id,
  893. {{SPV_OPERAND_TYPE_ID, {image->result_id()}}});
  894. auto* component_1 = constant_mgr->GetConstant(coord_component_type, {1});
  895. const uint32_t component_1_id =
  896. constant_mgr->GetDefiningInstruction(component_1)->result_id();
  897. auto* component_0 = constant_mgr->GetConstant(coord_component_type, {0});
  898. const uint32_t component_0_id =
  899. constant_mgr->GetDefiningInstruction(component_0)->result_id();
  900. // If the image is a cube array, then the last component of the queried
  901. // size is the layer count. In the query, we have to accomodate folding
  902. // in the face index ranging from 0 through 5. The inclusive upper bound
  903. // on the third coordinate therefore is multiplied by 6.
  904. auto* query_size_including_faces = query_size;
  905. if (arrayed && (dim == SpvDimCube)) {
  906. // Multiply the last coordinate by 6.
  907. auto* component_6 = constant_mgr->GetConstant(coord_component_type, {6});
  908. const uint32_t component_6_id =
  909. constant_mgr->GetDefiningInstruction(component_6)->result_id();
  910. assert(query_num_components == 3);
  911. auto* multiplicand = constant_mgr->GetConstant(
  912. query_size_type, {component_1_id, component_1_id, component_6_id});
  913. auto* multiplicand_inst =
  914. constant_mgr->GetDefiningInstruction(multiplicand);
  915. const auto query_size_including_faces_id = TakeNextId();
  916. query_size_including_faces = InsertInst(
  917. image_texel_pointer, SpvOpIMul,
  918. type_mgr->GetTypeInstruction(query_size_type),
  919. query_size_including_faces_id,
  920. {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}},
  921. {SPV_OPERAND_TYPE_ID, {multiplicand_inst->result_id()}}});
  922. }
  923. // Make a coordinate-type with all 1 components.
  924. auto* coordinate_1 =
  925. query_num_components == 1
  926. ? component_1
  927. : constant_mgr->GetConstant(
  928. query_size_type,
  929. std::vector<uint32_t>(query_num_components, component_1_id));
  930. // Make a coordinate-type with all 1 components.
  931. auto* coordinate_0 =
  932. query_num_components == 0
  933. ? component_0
  934. : constant_mgr->GetConstant(
  935. query_size_type,
  936. std::vector<uint32_t>(query_num_components, component_0_id));
  937. const uint32_t query_max_including_faces_id = TakeNextId();
  938. auto* query_max_including_faces = InsertInst(
  939. image_texel_pointer, SpvOpISub,
  940. type_mgr->GetTypeInstruction(query_size_type),
  941. query_max_including_faces_id,
  942. {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}},
  943. {SPV_OPERAND_TYPE_ID,
  944. {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}});
  945. // Clamp the coordinate
  946. auto* clamp_coord = MakeSClampInst(
  947. *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0),
  948. query_max_including_faces, image_texel_pointer);
  949. image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()});
  950. // Clamp the sample index
  951. if (multisampled) {
  952. // Get the sample count via OpImageQuerySamples
  953. const auto query_samples_id = TakeNextId();
  954. auto* query_samples = InsertInst(
  955. image_texel_pointer, SpvOpImageQuerySamples,
  956. constant_mgr->GetDefiningInstruction(component_0)->type_id(),
  957. query_samples_id, {{SPV_OPERAND_TYPE_ID, {image->result_id()}}});
  958. const auto max_samples_id = TakeNextId();
  959. auto* max_samples = InsertInst(image_texel_pointer, SpvOpImageQuerySamples,
  960. query_samples->type_id(), max_samples_id,
  961. {{SPV_OPERAND_TYPE_ID, {query_samples_id}},
  962. {SPV_OPERAND_TYPE_ID, {component_1_id}}});
  963. auto* clamp_samples = MakeSClampInst(
  964. *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0),
  965. max_samples, image_texel_pointer);
  966. image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
  967. } else {
  968. // Just replace it with 0. Don't even check what was there before.
  969. image_texel_pointer->SetInOperand(2, {component_0_id});
  970. }
  971. def_use_mgr->AnalyzeInstUse(image_texel_pointer);
  972. return SPV_SUCCESS;
  973. #endif
  974. }
  975. opt::Instruction* GraphicsRobustAccessPass::InsertInst(
  976. opt::Instruction* where_inst, SpvOp opcode, uint32_t type_id,
  977. uint32_t result_id, const Instruction::OperandList& operands) {
  978. module_status_.modified = true;
  979. auto* result = where_inst->InsertBefore(
  980. MakeUnique<Instruction>(context(), opcode, type_id, result_id, operands));
  981. context()->get_def_use_mgr()->AnalyzeInstDefUse(result);
  982. auto* basic_block = context()->get_instr_block(where_inst);
  983. context()->set_instr_block(result, basic_block);
  984. return result;
  985. }
  986. } // namespace opt
  987. } // namespace spvtools