propagateNoContraction.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. //
  2. // Copyright (C) 2015-2016 Google, Inc.
  3. //
  4. // All rights reserved.
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions
  8. // are met:
  9. //
  10. // Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. //
  13. // Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following
  15. // disclaimer in the documentation and/or other materials provided
  16. // with the distribution.
  17. //
  18. // Neither the name of Google Inc. nor the names of its
  19. // contributors may be used to endorse or promote products derived
  20. // from this software without specific prior written permission.
  21. //
  22. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. // POSSIBILITY OF SUCH DAMAGE.
  34. //
  35. // Visit the nodes in the glslang intermediate tree representation to
  36. // propagate the 'noContraction' qualifier.
  37. //
  38. #ifndef GLSLANG_WEB
  39. #include "propagateNoContraction.h"
  40. #include <cstdlib>
  41. #include <string>
  42. #include <tuple>
  43. #include <unordered_map>
  44. #include <unordered_set>
  45. #include "localintermediate.h"
  46. namespace {
  47. // Use a string to hold the access chain information, as in most cases the
  48. // access chain is short and may contain only one element, which is the symbol
  49. // ID.
  50. // Example: struct {float a; float b;} s;
  51. // Object s.a will be represented with: <symbol ID of s>/0
  52. // Object s.b will be represented with: <symbol ID of s>/1
  53. // Object s will be represented with: <symbol ID of s>
  54. // For members of vector, matrix and arrays, they will be represented with the
  55. // same symbol ID of their container symbol objects. This is because their
  56. // preciseness is always the same as their container symbol objects.
  57. typedef std::string ObjectAccessChain;
  58. // The delimiter used in the ObjectAccessChain string to separate symbol ID and
  59. // different level of struct indices.
  60. const char ObjectAccesschainDelimiter = '/';
  61. // Mapping from Symbol IDs of symbol nodes, to their defining operation
  62. // nodes.
  63. typedef std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator*> NodeMapping;
  64. // Mapping from object nodes to their access chain info string.
  65. typedef std::unordered_map<glslang::TIntermTyped*, ObjectAccessChain> AccessChainMapping;
  66. // Set of object IDs.
  67. typedef std::unordered_set<ObjectAccessChain> ObjectAccesschainSet;
  68. // Set of return branch nodes.
  69. typedef std::unordered_set<glslang::TIntermBranch*> ReturnBranchNodeSet;
  70. // A helper function to tell whether a node is 'noContraction'. Returns true if
  71. // the node has 'noContraction' qualifier, otherwise false.
  72. bool isPreciseObjectNode(glslang::TIntermTyped* node)
  73. {
  74. return node->getType().getQualifier().isNoContraction();
  75. }
  76. // Returns true if the opcode is a dereferencing one.
  77. bool isDereferenceOperation(glslang::TOperator op)
  78. {
  79. switch (op) {
  80. case glslang::EOpIndexDirect:
  81. case glslang::EOpIndexDirectStruct:
  82. case glslang::EOpIndexIndirect:
  83. case glslang::EOpVectorSwizzle:
  84. case glslang::EOpMatrixSwizzle:
  85. return true;
  86. default:
  87. return false;
  88. }
  89. }
  90. // Returns true if the opcode leads to an assignment operation.
  91. bool isAssignOperation(glslang::TOperator op)
  92. {
  93. switch (op) {
  94. case glslang::EOpAssign:
  95. case glslang::EOpAddAssign:
  96. case glslang::EOpSubAssign:
  97. case glslang::EOpMulAssign:
  98. case glslang::EOpVectorTimesMatrixAssign:
  99. case glslang::EOpVectorTimesScalarAssign:
  100. case glslang::EOpMatrixTimesScalarAssign:
  101. case glslang::EOpMatrixTimesMatrixAssign:
  102. case glslang::EOpDivAssign:
  103. case glslang::EOpModAssign:
  104. case glslang::EOpAndAssign:
  105. case glslang::EOpLeftShiftAssign:
  106. case glslang::EOpRightShiftAssign:
  107. case glslang::EOpInclusiveOrAssign:
  108. case glslang::EOpExclusiveOrAssign:
  109. case glslang::EOpPostIncrement:
  110. case glslang::EOpPostDecrement:
  111. case glslang::EOpPreIncrement:
  112. case glslang::EOpPreDecrement:
  113. return true;
  114. default:
  115. return false;
  116. }
  117. }
  118. // A helper function to get the unsigned int from a given constant union node.
  119. // Note the node should only hold a uint scalar.
  120. unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node)
  121. {
  122. assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar());
  123. unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst();
  124. return struct_dereference_index;
  125. }
  126. // A helper function to generate symbol_label.
  127. ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node)
  128. {
  129. ObjectAccessChain symbol_id =
  130. std::to_string(node->getId()) + "(" + node->getName().c_str() + ")";
  131. return symbol_id;
  132. }
  133. // Returns true if the operation is an arithmetic operation and valid for
  134. // the 'NoContraction' decoration.
  135. bool isArithmeticOperation(glslang::TOperator op)
  136. {
  137. switch (op) {
  138. case glslang::EOpAddAssign:
  139. case glslang::EOpSubAssign:
  140. case glslang::EOpMulAssign:
  141. case glslang::EOpVectorTimesMatrixAssign:
  142. case glslang::EOpVectorTimesScalarAssign:
  143. case glslang::EOpMatrixTimesScalarAssign:
  144. case glslang::EOpMatrixTimesMatrixAssign:
  145. case glslang::EOpDivAssign:
  146. case glslang::EOpModAssign:
  147. case glslang::EOpNegative:
  148. case glslang::EOpAdd:
  149. case glslang::EOpSub:
  150. case glslang::EOpMul:
  151. case glslang::EOpDiv:
  152. case glslang::EOpMod:
  153. case glslang::EOpVectorTimesScalar:
  154. case glslang::EOpVectorTimesMatrix:
  155. case glslang::EOpMatrixTimesVector:
  156. case glslang::EOpMatrixTimesScalar:
  157. case glslang::EOpMatrixTimesMatrix:
  158. case glslang::EOpDot:
  159. case glslang::EOpPostIncrement:
  160. case glslang::EOpPostDecrement:
  161. case glslang::EOpPreIncrement:
  162. case glslang::EOpPreDecrement:
  163. return true;
  164. default:
  165. return false;
  166. }
  167. }
  168. // A helper class to help manage the populating_initial_no_contraction_ flag.
  169. template <typename T> class StateSettingGuard {
  170. public:
  171. StateSettingGuard(T* state_ptr, T new_state_value)
  172. : state_ptr_(state_ptr), previous_state_(*state_ptr)
  173. {
  174. *state_ptr = new_state_value;
  175. }
  176. StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {}
  177. void setState(T new_state_value) { *state_ptr_ = new_state_value; }
  178. ~StateSettingGuard() { *state_ptr_ = previous_state_; }
  179. private:
  180. T* state_ptr_;
  181. T previous_state_;
  182. };
  183. // A helper function to get the front element from a given ObjectAccessChain
  184. ObjectAccessChain getFrontElement(const ObjectAccessChain& chain)
  185. {
  186. size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
  187. return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter);
  188. }
  189. // A helper function to get the access chain starting from the second element.
  190. ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain)
  191. {
  192. size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
  193. return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1);
  194. }
  195. // A helper function to get the access chain after removing a given prefix.
  196. ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain,
  197. const ObjectAccessChain& prefix)
  198. {
  199. size_t pos = chain.find(prefix);
  200. if (pos != 0)
  201. return chain;
  202. return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter));
  203. }
  204. //
  205. // A traverser which traverses the whole AST and populates:
  206. // 1) A mapping from symbol nodes' IDs to their defining operation nodes.
  207. // 2) A set of access chains of the initial precise object nodes.
  208. //
  209. class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser {
  210. public:
  211. TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping,
  212. AccessChainMapping* accesschain_mapping,
  213. ObjectAccesschainSet* precise_objects,
  214. ReturnBranchNodeSet* precise_return_nodes);
  215. bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override;
  216. bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override;
  217. void visitSymbol(glslang::TIntermSymbol*) override;
  218. bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override;
  219. bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override;
  220. protected:
  221. TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&);
  222. // The mapping from symbol node IDs to their defining nodes. This should be
  223. // populated along traversing the AST.
  224. NodeMapping& symbol_definition_mapping_;
  225. // The set of symbol node IDs for precise symbol nodes, the ones marked as
  226. // 'noContraction'.
  227. ObjectAccesschainSet& precise_objects_;
  228. // The set of precise return nodes.
  229. ReturnBranchNodeSet& precise_return_nodes_;
  230. // A temporary cache of the symbol node whose defining node is to be found
  231. // currently along traversing the AST.
  232. ObjectAccessChain current_object_;
  233. // A map from object node to its access chain. This traverser stores
  234. // the built access chains into this map for each object node it has
  235. // visited.
  236. AccessChainMapping& accesschain_mapping_;
  237. // The pointer to the Function Definition node, so we can get the
  238. // preciseness of the return expression from it when we traverse the
  239. // return branch node.
  240. glslang::TIntermAggregate* current_function_definition_node_;
  241. };
  242. TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser(
  243. NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping,
  244. ObjectAccesschainSet* precise_objects,
  245. std::unordered_set<glslang::TIntermBranch*>* precise_return_nodes)
  246. : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping),
  247. precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes),
  248. current_object_(), accesschain_mapping_(*accesschain_mapping),
  249. current_function_definition_node_(nullptr) {}
  250. // Visits a symbol node, set the current_object_ to the
  251. // current node symbol ID, and record a mapping from this node to the current
  252. // current_object_, which is the just obtained symbol
  253. // ID.
  254. void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node)
  255. {
  256. current_object_ = generateSymbolLabel(node);
  257. accesschain_mapping_[node] = current_object_;
  258. }
  259. // Visits an aggregate node, traverses all of its children.
  260. bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit,
  261. glslang::TIntermAggregate* node)
  262. {
  263. // This aggregate node might be a function definition node, in which case we need to
  264. // cache this node, so we can get the preciseness information of the return value
  265. // of this function later.
  266. StateSettingGuard<glslang::TIntermAggregate*> current_function_definition_node_setting_guard(
  267. &current_function_definition_node_);
  268. if (node->getOp() == glslang::EOpFunction) {
  269. // This is function definition node, we need to cache this node so that we can
  270. // get the preciseness of the return value later.
  271. current_function_definition_node_setting_guard.setState(node);
  272. }
  273. // Traverse the items in the sequence.
  274. glslang::TIntermSequence& seq = node->getSequence();
  275. for (int i = 0; i < (int)seq.size(); ++i) {
  276. current_object_.clear();
  277. seq[i]->traverse(this);
  278. }
  279. return false;
  280. }
  281. bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit,
  282. glslang::TIntermBranch* node)
  283. {
  284. if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() &&
  285. current_function_definition_node_ &&
  286. current_function_definition_node_->getType().getQualifier().noContraction) {
  287. // This node is a return node with an expression, and its function has a
  288. // precise return value. We need to find the involved objects in its
  289. // expression and add them to the set of initial precise objects.
  290. precise_return_nodes_.insert(node);
  291. node->getExpression()->traverse(this);
  292. }
  293. return false;
  294. }
  295. // Visits a unary node. This might be an implicit assignment like i++, i--. etc.
  296. bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */,
  297. glslang::TIntermUnary* node)
  298. {
  299. current_object_.clear();
  300. node->getOperand()->traverse(this);
  301. if (isAssignOperation(node->getOp())) {
  302. // We should always be able to get an access chain of the operand node.
  303. assert(!current_object_.empty());
  304. // If the operand node object is 'precise', we collect its access chain
  305. // for the initial set of 'precise' objects.
  306. if (isPreciseObjectNode(node->getOperand())) {
  307. // The operand node is an 'precise' object node, add its
  308. // access chain to the set of 'precise' objects. This is to collect
  309. // the initial set of 'precise' objects.
  310. precise_objects_.insert(current_object_);
  311. }
  312. // Gets the symbol ID from the object's access chain.
  313. ObjectAccessChain id_symbol = getFrontElement(current_object_);
  314. // Add a mapping from the symbol ID to this assignment operation node.
  315. symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
  316. }
  317. // A unary node is not a dereference node, so we clear the access chain which
  318. // is under construction.
  319. current_object_.clear();
  320. return false;
  321. }
  322. // Visits a binary node and updates the mapping from symbol IDs to the definition
  323. // nodes. Also collects the access chains for the initial precise objects.
  324. bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */,
  325. glslang::TIntermBinary* node)
  326. {
  327. // Traverses the left node to build the access chain info for the object.
  328. current_object_.clear();
  329. node->getLeft()->traverse(this);
  330. if (isAssignOperation(node->getOp())) {
  331. // We should always be able to get an access chain for the left node.
  332. assert(!current_object_.empty());
  333. // If the left node object is 'precise', it is an initial precise object
  334. // specified in the shader source. Adds it to the initial work list to
  335. // process later.
  336. if (isPreciseObjectNode(node->getLeft())) {
  337. // The left node is an 'precise' object node, add its access chain to
  338. // the set of 'precise' objects. This is to collect the initial set
  339. // of 'precise' objects.
  340. precise_objects_.insert(current_object_);
  341. }
  342. // Gets the symbol ID from the object access chain, which should be the
  343. // first element recorded in the access chain.
  344. ObjectAccessChain id_symbol = getFrontElement(current_object_);
  345. // Adds a mapping from the symbol ID to this assignment operation node.
  346. symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
  347. // Traverses the right node, there may be other 'assignment'
  348. // operations in the right.
  349. current_object_.clear();
  350. node->getRight()->traverse(this);
  351. } else if (isDereferenceOperation(node->getOp())) {
  352. // The left node (parent node) is a struct type object. We need to
  353. // record the access chain information of the current node into its
  354. // object id.
  355. if (node->getOp() == glslang::EOpIndexDirectStruct) {
  356. unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight());
  357. current_object_.push_back(ObjectAccesschainDelimiter);
  358. current_object_.append(std::to_string(struct_dereference_index));
  359. }
  360. accesschain_mapping_[node] = current_object_;
  361. // For a dereference node, there is no need to traverse the right child
  362. // node as the right node should always be an integer type object.
  363. } else {
  364. // For other binary nodes, still traverse the right node.
  365. current_object_.clear();
  366. node->getRight()->traverse(this);
  367. }
  368. return false;
  369. }
  370. // Traverses the AST and returns a tuple of four members:
  371. // 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols.
  372. // 2) a mapping from object nodes in the AST to the access chains of these objects.
  373. // 3) a set of access chains of precise objects.
  374. // 4) a set of return nodes with precise expressions.
  375. std::tuple<NodeMapping, AccessChainMapping, ObjectAccesschainSet, ReturnBranchNodeSet>
  376. getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate)
  377. {
  378. auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(),
  379. ReturnBranchNodeSet());
  380. TIntermNode* root = intermediate.getTreeRoot();
  381. if (root == 0)
  382. return result_tuple;
  383. NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple);
  384. AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple);
  385. ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple);
  386. ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple);
  387. // Traverses the AST and populate the results.
  388. TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping,
  389. &precise_objects, &precise_return_nodes);
  390. root->traverse(&collector);
  391. return result_tuple;
  392. }
  393. //
  394. // A traverser that determine whether the left node (or operand node for unary
  395. // node) of an assignment node is 'precise', containing 'precise' or not,
  396. // according to the access chain a given precise object which share the same
  397. // symbol as the left node.
  398. //
  399. // Post-orderly traverses the left node subtree of an binary assignment node and:
  400. //
  401. // 1) Propagates the 'precise' from the left object nodes to this object node.
  402. //
  403. // 2) Builds object access chain along the traversal, and also compares with
  404. // the access chain of the given 'precise' object along with the traversal to
  405. // tell if the node to be defined is 'precise' or not.
  406. //
  407. class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser {
  408. enum DecisionStatus {
  409. // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects.
  410. Mixed = 0,
  411. // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'.
  412. Precise = 1,
  413. // The object node to be assigned to is not a 'precise' object.
  414. NotPreicse = 2,
  415. };
  416. public:
  417. TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping)
  418. : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping),
  419. precise_object_(nullptr) {}
  420. // Checks the preciseness of a given assignment node with a precise object
  421. // represented as access chain. The precise object shares the same symbol
  422. // with the assignee of the given assignment node. Return a tuple of two:
  423. //
  424. // 1) The preciseness of the assignee node of this assignment node. True
  425. // if the assignee contains 'precise' objects or is 'precise', false if
  426. // the assignee is not 'precise' according to the access chain of the given
  427. // precise object.
  428. //
  429. // 2) The incremental access chain from the assignee node to its nested
  430. // 'precise' object, according to the access chain of the given precise
  431. // object. This incremental access chain can be empty, which means the
  432. // assignee is 'precise'. Otherwise it shows the path to the nested
  433. // precise object.
  434. std::tuple<bool, ObjectAccessChain>
  435. getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node,
  436. const ObjectAccessChain& precise_object)
  437. {
  438. assert(isAssignOperation(node->getOp()));
  439. precise_object_ = &precise_object;
  440. ObjectAccessChain assignee_object;
  441. if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) {
  442. // This is a binary assignment node, we need to check the
  443. // preciseness of the left node.
  444. assert(accesschain_mapping_.count(BN->getLeft()));
  445. // The left node (assignee node) is an object node, traverse the
  446. // node to let the 'precise' of nesting objects being transfered to
  447. // nested objects.
  448. BN->getLeft()->traverse(this);
  449. // After traversing the left node, if the left node is 'precise',
  450. // we can conclude this assignment should propagate 'precise'.
  451. if (isPreciseObjectNode(BN->getLeft())) {
  452. return make_tuple(true, ObjectAccessChain());
  453. }
  454. // If the preciseness of the left node (assignee node) can not
  455. // be determined by now, we need to compare the access chain string
  456. // of the assignee object with the given precise object.
  457. assignee_object = accesschain_mapping_.at(BN->getLeft());
  458. } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) {
  459. // This is a unary assignment node, we need to check the
  460. // preciseness of the operand node. For unary assignment node, the
  461. // operand node should always be an object node.
  462. assert(accesschain_mapping_.count(UN->getOperand()));
  463. // Traverse the operand node to let the 'precise' being propagated
  464. // from lower nodes to upper nodes.
  465. UN->getOperand()->traverse(this);
  466. // After traversing the operand node, if the operand node is
  467. // 'precise', this assignment should propagate 'precise'.
  468. if (isPreciseObjectNode(UN->getOperand())) {
  469. return make_tuple(true, ObjectAccessChain());
  470. }
  471. // If the preciseness of the operand node (assignee node) can not
  472. // be determined by now, we need to compare the access chain string
  473. // of the assignee object with the given precise object.
  474. assignee_object = accesschain_mapping_.at(UN->getOperand());
  475. } else {
  476. // Not a binary or unary node, should not happen.
  477. assert(false);
  478. }
  479. // Compare the access chain string of the assignee node with the given
  480. // precise object to determine if this assignment should propagate
  481. // 'precise'.
  482. if (assignee_object.find(precise_object) == 0) {
  483. // The access chain string of the given precise object is a prefix
  484. // of assignee's access chain string. The assignee should be
  485. // 'precise'.
  486. return make_tuple(true, ObjectAccessChain());
  487. } else if (precise_object.find(assignee_object) == 0) {
  488. // The assignee's access chain string is a prefix of the given
  489. // precise object, the assignee object contains 'precise' object,
  490. // and we need to pass the remained access chain to the object nodes
  491. // in the right.
  492. return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object));
  493. } else {
  494. // The access chain strings do not match, the assignee object can
  495. // not be labeled as 'precise' according to the given precise
  496. // object.
  497. return make_tuple(false, ObjectAccessChain());
  498. }
  499. }
  500. protected:
  501. TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&);
  502. bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override;
  503. void visitSymbol(glslang::TIntermSymbol* node) override;
  504. // A map from object nodes to their access chain string (used as object ID).
  505. const AccessChainMapping& accesschain_mapping_;
  506. // A given precise object, represented in it access chain string. This
  507. // precise object is used to be compared with the assignee node to tell if
  508. // the assignee node is 'precise', contains 'precise' object or not
  509. // 'precise'.
  510. const ObjectAccessChain* precise_object_;
  511. };
  512. // Visits a binary node. If the node is an object node, it must be a dereference
  513. // node. In such cases, if the left node is 'precise', this node should also be
  514. // 'precise'.
  515. bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit,
  516. glslang::TIntermBinary* node)
  517. {
  518. // Traverses the left so that we transfer the 'precise' from nesting object
  519. // to its nested object.
  520. node->getLeft()->traverse(this);
  521. // If this binary node is an object node, we should have it in the
  522. // accesschain_mapping_.
  523. if (accesschain_mapping_.count(node)) {
  524. // A binary object node must be a dereference node.
  525. assert(isDereferenceOperation(node->getOp()));
  526. // If the left node is 'precise', this node should also be precise,
  527. // otherwise, compare with the given precise_object_. If the
  528. // access chain of this node matches with the given precise_object_,
  529. // this node should be marked as 'precise'.
  530. if (isPreciseObjectNode(node->getLeft())) {
  531. node->getWritableType().getQualifier().noContraction = true;
  532. } else if (accesschain_mapping_.at(node) == *precise_object_) {
  533. node->getWritableType().getQualifier().noContraction = true;
  534. }
  535. }
  536. return false;
  537. }
  538. // Visits a symbol node, if the symbol node ID (its access chain string) matches
  539. // with the given precise object, this node should be 'precise'.
  540. void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node)
  541. {
  542. // A symbol node should always be an object node, and should have been added
  543. // to the map from object nodes to their access chain strings.
  544. assert(accesschain_mapping_.count(node));
  545. if (accesschain_mapping_.at(node) == *precise_object_) {
  546. node->getWritableType().getQualifier().noContraction = true;
  547. }
  548. }
  549. //
  550. // A traverser that only traverses the right side of binary assignment nodes
  551. // and the operand node of unary assignment nodes.
  552. //
  553. // 1) Marks arithmetic operations as 'NoContraction'.
  554. //
  555. // 2) Find the object which should be marked as 'precise' in the right and
  556. // update the 'precise' object work list.
  557. //
  558. class TNoContractionPropagator : public glslang::TIntermTraverser {
  559. public:
  560. TNoContractionPropagator(ObjectAccesschainSet* precise_objects,
  561. const AccessChainMapping& accesschain_mapping)
  562. : TIntermTraverser(true, false, false),
  563. precise_objects_(*precise_objects), added_precise_object_ids_(),
  564. remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {}
  565. // Propagates 'precise' in the right nodes of a given assignment node with
  566. // access chain record from the assignee node to a 'precise' object it
  567. // contains.
  568. void
  569. propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node,
  570. const ObjectAccessChain& assignee_remained_accesschain)
  571. {
  572. remained_accesschain_ = assignee_remained_accesschain;
  573. if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) {
  574. assert(isAssignOperation(BN->getOp()));
  575. BN->getRight()->traverse(this);
  576. if (isArithmeticOperation(BN->getOp())) {
  577. BN->getWritableType().getQualifier().noContraction = true;
  578. }
  579. } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) {
  580. assert(isAssignOperation(UN->getOp()));
  581. UN->getOperand()->traverse(this);
  582. if (isArithmeticOperation(UN->getOp())) {
  583. UN->getWritableType().getQualifier().noContraction = true;
  584. }
  585. }
  586. }
  587. // Propagates 'precise' in a given precise return node.
  588. void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node)
  589. {
  590. remained_accesschain_ = "";
  591. assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression());
  592. return_node->getExpression()->traverse(this);
  593. }
  594. protected:
  595. TNoContractionPropagator& operator=(const TNoContractionPropagator&);
  596. // Visits an aggregate node. The node can be a initializer list, in which
  597. // case we need to find the 'precise' or 'precise' containing object node
  598. // with the access chain record. In other cases, just need to traverse all
  599. // the children nodes.
  600. bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override
  601. {
  602. if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) {
  603. // This is a struct initializer node, and the remained
  604. // access chain is not empty, we need to refer to the
  605. // assignee_remained_access_chain_ to find the nested
  606. // 'precise' object. And we don't need to visit other nodes in this
  607. // aggregate node.
  608. // Gets the struct dereference index that leads to 'precise' object.
  609. ObjectAccessChain precise_accesschain_index_str =
  610. getFrontElement(remained_accesschain_);
  611. unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10);
  612. // Gets the node pointed by the access chain index extracted before.
  613. glslang::TIntermTyped* potential_precise_node =
  614. node->getSequence()[precise_accesschain_index]->getAsTyped();
  615. assert(potential_precise_node);
  616. // Pop the front access chain index from the path, and visit the nested node.
  617. {
  618. ObjectAccessChain next_level_accesschain =
  619. subAccessChainFromSecondElement(remained_accesschain_);
  620. StateSettingGuard<ObjectAccessChain> setup_remained_accesschain_for_next_level(
  621. &remained_accesschain_, next_level_accesschain);
  622. potential_precise_node->traverse(this);
  623. }
  624. return false;
  625. }
  626. return true;
  627. }
  628. // Visits a binary node. A binary node can be an object node, e.g. a dereference node.
  629. // As only the top object nodes in the right side of an assignment needs to be visited
  630. // and added to 'precise' work list, this traverser won't visit the children nodes of
  631. // an object node. If the binary node does not represent an object node, it should
  632. // go on to traverse its children nodes and if it is an arithmetic operation node, this
  633. // operation should be marked as 'noContraction'.
  634. bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override
  635. {
  636. if (isDereferenceOperation(node->getOp())) {
  637. // This binary node is an object node. Need to update the precise
  638. // object set with the access chain of this node + remained
  639. // access chain .
  640. ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
  641. if (remained_accesschain_.empty()) {
  642. node->getWritableType().getQualifier().noContraction = true;
  643. } else {
  644. new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
  645. }
  646. // Cache the access chain as added precise object, so we won't add the
  647. // same object to the work list again.
  648. if (!added_precise_object_ids_.count(new_precise_accesschain)) {
  649. precise_objects_.insert(new_precise_accesschain);
  650. added_precise_object_ids_.insert(new_precise_accesschain);
  651. }
  652. // Only the upper-most object nodes should be visited, so do not
  653. // visit children of this object node.
  654. return false;
  655. }
  656. // If this is an arithmetic operation, marks this node as 'noContraction'.
  657. if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) {
  658. node->getWritableType().getQualifier().noContraction = true;
  659. }
  660. // As this node is not an object node, need to traverse the children nodes.
  661. return true;
  662. }
  663. // Visits a unary node. A unary node can not be an object node. If the operation
  664. // is an arithmetic operation, need to mark this node as 'noContraction'.
  665. bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override
  666. {
  667. // If this is an arithmetic operation, marks this with 'noContraction'
  668. if (isArithmeticOperation(node->getOp())) {
  669. node->getWritableType().getQualifier().noContraction = true;
  670. }
  671. return true;
  672. }
  673. // Visits a symbol node. A symbol node is always an object node. So we
  674. // should always be able to find its in our collected mapping from object
  675. // nodes to access chains. As an object node, a symbol node can be either
  676. // 'precise' or containing 'precise' objects according to unused
  677. // access chain information we have when we visit this node.
  678. void visitSymbol(glslang::TIntermSymbol* node) override
  679. {
  680. // Symbol nodes are object nodes and should always have an
  681. // access chain collected before matches with it.
  682. assert(accesschain_mapping_.count(node));
  683. ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
  684. // If the unused access chain is empty, this symbol node should be
  685. // marked as 'precise'. Otherwise, the unused access chain should be
  686. // appended to the symbol ID to build a new access chain which points to
  687. // the nested 'precise' object in this symbol object.
  688. if (remained_accesschain_.empty()) {
  689. node->getWritableType().getQualifier().noContraction = true;
  690. } else {
  691. new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
  692. }
  693. // Add the new 'precise' access chain to the work list and make sure we
  694. // don't visit it again.
  695. if (!added_precise_object_ids_.count(new_precise_accesschain)) {
  696. precise_objects_.insert(new_precise_accesschain);
  697. added_precise_object_ids_.insert(new_precise_accesschain);
  698. }
  699. }
  700. // A set of precise objects, represented as access chains.
  701. ObjectAccesschainSet& precise_objects_;
  702. // Visited symbol nodes, should not revisit these nodes.
  703. ObjectAccesschainSet added_precise_object_ids_;
  704. // The left node of an assignment operation might be an parent of 'precise' objects.
  705. // This means the left node might not be an 'precise' object node, but it may contains
  706. // 'precise' qualifier which should be propagated to the corresponding child node in
  707. // the right. So we need the path from the left node to its nested 'precise' node to
  708. // tell us how to find the corresponding 'precise' node in the right.
  709. ObjectAccessChain remained_accesschain_;
  710. // A map from node pointers to their access chains.
  711. const AccessChainMapping& accesschain_mapping_;
  712. };
  713. }
  714. namespace glslang {
  715. void PropagateNoContraction(const glslang::TIntermediate& intermediate)
  716. {
  717. // First, traverses the AST, records symbols with their defining operations
  718. // and collects the initial set of precise symbols (symbol nodes that marked
  719. // as 'noContraction') and precise return nodes.
  720. auto mappings_and_precise_objects =
  721. getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate);
  722. // The mapping of symbol node IDs to their defining nodes. This enables us
  723. // to get the defining node directly from a given symbol ID without
  724. // traversing the tree again.
  725. NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects);
  726. // The mapping of object nodes to their access chains recorded.
  727. AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects);
  728. // The initial set of 'precise' objects which are represented as the
  729. // access chain toward them.
  730. ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects);
  731. // The set of 'precise' return nodes.
  732. ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects);
  733. // Second, uses the initial set of precise objects as a work list, pops an
  734. // access chain, extract the symbol ID from it. Then:
  735. // 1) Check the assignee object, see if it is 'precise' object node or
  736. // contains 'precise' object. Obtain the incremental access chain from the
  737. // assignee node to its nested 'precise' node (if any).
  738. // 2) If the assignee object node is 'precise' or it contains 'precise'
  739. // objects, traverses the right side of the assignment operation
  740. // expression to mark arithmetic operations as 'noContration' and update
  741. // 'precise' access chain work list with new found object nodes.
  742. // Repeat above steps until the work list is empty.
  743. TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping);
  744. TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping);
  745. // We have two initial precise work lists to handle:
  746. // 1) precise return nodes
  747. // 2) precise object access chains
  748. // We should process the precise return nodes first and the involved
  749. // objects in the return expression should be added to the precise object
  750. // access chain set.
  751. while (!precise_return_nodes.empty()) {
  752. glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin();
  753. propagator.propagateNoContractionInReturnNode(precise_return_node);
  754. precise_return_nodes.erase(precise_return_node);
  755. }
  756. while (!precise_object_accesschains.empty()) {
  757. // Get the access chain of a precise object from the work list.
  758. ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin();
  759. // Get the symbol id from the access chain.
  760. ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain);
  761. // Get all the defining nodes of that symbol ID.
  762. std::pair<NodeMapping::iterator, NodeMapping::iterator> range =
  763. symbol_definition_mapping.equal_range(symbol_id);
  764. // Visits all the assignment nodes of that symbol ID and
  765. // 1) Check if the assignee node is 'precise' or contains 'precise'
  766. // objects.
  767. // 2) Propagate the 'precise' to the top layer object nodes
  768. // in the right side of the assignment operation, update the 'precise'
  769. // work list with new access chains representing the new 'precise'
  770. // objects, and mark arithmetic operations as 'noContraction'.
  771. for (NodeMapping::iterator defining_node_iter = range.first;
  772. defining_node_iter != range.second; defining_node_iter++) {
  773. TIntermOperator* defining_node = defining_node_iter->second;
  774. // Check the assignee node.
  775. auto checker_result = checker.getPrecisenessAndRemainedAccessChain(
  776. defining_node, precise_object_accesschain);
  777. bool& contain_precise = std::get<0>(checker_result);
  778. ObjectAccessChain& remained_accesschain = std::get<1>(checker_result);
  779. // If the assignee node is 'precise' or contains 'precise', propagate the
  780. // 'precise' to the right. Otherwise just skip this assignment node.
  781. if (contain_precise) {
  782. propagator.propagateNoContractionInOneExpression(defining_node,
  783. remained_accesschain);
  784. }
  785. }
  786. // Remove the last processed 'precise' object from the work list.
  787. precise_object_accesschains.erase(precise_object_accesschain);
  788. }
  789. }
  790. };
  791. #endif // GLSLANG_WEB