{"action":"created","comment":{"url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/comments/62404150","id":62404150,"diff_hunk":"@@ -0,0 +1,890 @@\n+//\n+// Copyright (C) 2015-2016 Google, Inc.\n+//\n+// All rights reserved.\n+//\n+// Redistribution and use in source and binary forms, with or without\n+// modification, are permitted provided that the following conditions\n+// are met:\n+//\n+//    Redistributions of source code must retain the above copyright\n+//    notice, this list of conditions and the following disclaimer.\n+//\n+//    Redistributions in binary form must reproduce the above\n+//    copyright notice, this list of conditions and the following\n+//    disclaimer in the documentation and/or other materials provided\n+//    with the distribution.\n+//\n+//    Neither the name of Google Inc. nor the names of its\n+//    contributors may be used to endorse or promote products derived\n+//    from this software without specific prior written permission.\n+//\n+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n+// POSSIBILITY OF SUCH DAMAGE.\n+\n+//\n+// Visit the nodes in the glslang intermediate tree representation to\n+// propagate 'noContraction' qualifier.\n+//\n+\n+#include \"propagateNoContraction.h\"\n+\n+#include <string>\n+#include <tuple>\n+#include <unordered_map>\n+#include <unordered_set>\n+\n+#include \"localintermediate.h\"\n+namespace {\n+\n+// Use string to hold the accesschain information, as in most cases the\n+// accesschain is short and may contain only one element, which is the symbol\n+// ID.\n+// Example: struct {float a; float b;} s;\n+//  Object s.a will be represented with: <symbol ID of s>/0\n+//  Object s.b will be represented with: <symbol ID of s>/1\n+//  Object s will be representend with: <symbol ID of s>\n+// For members of vector, matrix and arrays, they will be represented with the\n+// same symbol ID of their container symbol objects. This is because their\n+// precise'ness is always the same as their container symbol objects.\n+using ObjectAccessChain = std::string;\n+\n+// The delimiter used in the ObjectAccessChain string to separate symbol ID and\n+// different level of struct indices.\n+const char ObjectAccesschainDelimiter = '/';\n+\n+// Mapping from Symbol IDs of symbol nodes, to their defining operation\n+// nodes.\n+using NodeMapping = std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator*>;\n+// Mapping from object nodes to their accesschain info string.\n+using AccessChainMapping = std::unordered_map<glslang::TIntermTyped*, ObjectAccessChain>;\n+\n+// Set of object IDs.\n+using ObjectAccesschainSet = std::unordered_set<ObjectAccessChain>;\n+// Set of return branch nodes.\n+using ReturnBranchNodeSet = std::unordered_set<glslang::TIntermBranch*>;\n+\n+// A helper function to tell whether a node is 'noContraction'. Returns true if\n+// the node has 'noContraction' qualifier, otherwise false.\n+bool isPreciseObjectNode(glslang::TIntermTyped* node)\n+{\n+    return node->getType().getQualifier().noContraction;\n+}\n+\n+// Returns true if the opcode is a dereferencing one.\n+bool isDereferenceOperation(glslang::TOperator op)\n+{\n+    switch (op) {\n+    case glslang::EOpIndexDirect:\n+    case glslang::EOpIndexDirectStruct:\n+    case glslang::EOpIndexIndirect:\n+    case glslang::EOpVectorSwizzle:\n+        return true;\n+    default:\n+        return false;\n+    }\n+}\n+\n+// Returns true if the opcode leads to an assignment operation.\n+bool isAssignOperation(glslang::TOperator op)\n+{\n+    switch (op) {\n+    case glslang::EOpAssign:\n+    case glslang::EOpAddAssign:\n+    case glslang::EOpSubAssign:\n+    case glslang::EOpMulAssign:\n+    case glslang::EOpVectorTimesMatrixAssign:\n+    case glslang::EOpVectorTimesScalarAssign:\n+    case glslang::EOpMatrixTimesScalarAssign:\n+    case glslang::EOpMatrixTimesMatrixAssign:\n+    case glslang::EOpDivAssign:\n+    case glslang::EOpModAssign:\n+    case glslang::EOpAndAssign:\n+    case glslang::EOpLeftShiftAssign:\n+    case glslang::EOpRightShiftAssign:\n+    case glslang::EOpInclusiveOrAssign:\n+    case glslang::EOpExclusiveOrAssign:\n+\n+    case glslang::EOpPostIncrement:\n+    case glslang::EOpPostDecrement:\n+    case glslang::EOpPreIncrement:\n+    case glslang::EOpPreDecrement:\n+        return true;\n+    default:\n+        return false;\n+    }\n+}\n+\n+// A helper function to get the unsigned int from a given constant union node.\n+// Note the node should only holds a uint scalar.\n+unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node)\n+{\n+    assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar());\n+    unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst();\n+    return struct_dereference_index;\n+}\n+\n+// A helper function to generate symbol_label.\n+ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node)\n+{\n+    ObjectAccessChain symbol_id =\n+        std::to_string(node->getId()) + \"(\" + node->getName().c_str() + \")\";\n+    return symbol_id;\n+}\n+\n+// Return true if the operation is an arithmetic operation and valid for\n+// 'NoContraction' decoration.\n+bool isArithmeticOperation(glslang::TOperator op)\n+{\n+    switch (op) {\n+    case glslang::EOpAddAssign:\n+    case glslang::EOpSubAssign:\n+    case glslang::EOpMulAssign:\n+    case glslang::EOpVectorTimesMatrixAssign:\n+    case glslang::EOpVectorTimesScalarAssign:\n+    case glslang::EOpMatrixTimesScalarAssign:\n+    case glslang::EOpMatrixTimesMatrixAssign:\n+    case glslang::EOpDivAssign:\n+    case glslang::EOpModAssign:\n+\n+    case glslang::EOpNegative:\n+\n+    case glslang::EOpAdd:\n+    case glslang::EOpSub:\n+    case glslang::EOpMul:\n+    case glslang::EOpDiv:\n+    case glslang::EOpMod:\n+\n+    case glslang::EOpVectorTimesScalar:\n+    case glslang::EOpVectorTimesMatrix:\n+    case glslang::EOpMatrixTimesVector:\n+    case glslang::EOpMatrixTimesScalar:\n+    case glslang::EOpMatrixTimesMatrix:\n+\n+    case glslang::EOpDot:\n+\n+    case glslang::EOpAddCarry:\n+    case glslang::EOpSubBorrow:\n+    case glslang::EOpUMulExtended:\n+    case glslang::EOpIMulExtended:\n+\n+    case glslang::EOpPostIncrement:\n+    case glslang::EOpPostDecrement:\n+    case glslang::EOpPreIncrement:\n+    case glslang::EOpPreDecrement:\n+        return true;\n+    default:\n+        return false;\n+    }\n+}\n+\n+// A helper class to help managing populating_initial_no_contraction_ flag.\n+template <typename T> class StateSettingGuard {\n+public:\n+    StateSettingGuard(T* state_ptr, T new_state_value)\n+        : state_ptr_(state_ptr), previous_state_(*state_ptr)\n+    {\n+        *state_ptr = new_state_value;\n+    }\n+    StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {}\n+    void setState(T new_state_value) { *state_ptr_ = new_state_value; }\n+    ~StateSettingGuard() { *state_ptr_ = previous_state_; }\n+\n+private:\n+    T* state_ptr_;\n+    T previous_state_;\n+};\n+\n+// A helper function to get the front element from a given ObjectAccessChain\n+ObjectAccessChain getFrontElement(const ObjectAccessChain& chain)\n+{\n+    size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);\n+    return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter);\n+}\n+\n+// A helper function to get the accesschain starting from the second element.\n+ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain)\n+{\n+    size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);\n+    return pos_delimiter == std::string::npos ? \"\" : chain.substr(pos_delimiter + 1);\n+}\n+\n+// A helper function to get the accesschain after removing a given prefix.\n+ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain,\n+                                               const ObjectAccessChain& prefix)\n+{\n+    size_t pos = chain.find(prefix);\n+    if (pos != 0) return chain;\n+    return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter));\n+}\n+\n+//\n+// A traverser which traverses the whole AST and populates:\n+//  1) A mapping from symbol nodes' IDs to their defining operation nodes.\n+//  2) A set of accesschains of the initial precise object nodes.\n+//\n+class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser {\n+public:\n+    TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping,\n+                                         AccessChainMapping* accesschain_mapping,\n+                                         ObjectAccesschainSet* precise_objects,\n+                                         ReturnBranchNodeSet* precise_return_nodes);\n+\n+    bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override;\n+    bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override;\n+    void visitSymbol(glslang::TIntermSymbol*) override;\n+    bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override;\n+    bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override;\n+\n+protected:\n+    // The mapping from symbol node IDs to their defining nodes. This should be\n+    // populated along traversing the AST.\n+    NodeMapping& symbol_definition_mapping_;\n+    // The set of symbol node IDs for precise symbol nodes, the ones marked as\n+    // 'noContraction'.\n+    ObjectAccesschainSet& precise_objects_;\n+    // The set of precise return nodes.\n+    ReturnBranchNodeSet& precise_return_nodes_;\n+    // A temporary cache of the symbol node whose defining node is to be found\n+    // currently along traversing the AST.\n+    ObjectAccessChain object_to_be_defined_;\n+    // A map from object node to its accesschain. This traverser stores\n+    // the built accesschains into this map for each object node it has\n+    // visited.\n+    AccessChainMapping& accesschain_mapping_;\n+    // The pointer to the Function Definition node, so we can get the\n+    // precise'ness of the return expression from it when we traverse the\n+    // return branch node.\n+    glslang::TIntermAggregate* current_function_definition_node_;\n+};\n+\n+TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser(\n+    NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping,\n+    ObjectAccesschainSet* precise_objects,\n+    std::unordered_set<glslang::TIntermBranch*>* precise_return_nodes)\n+    : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping),\n+      precise_objects_(*precise_objects), object_to_be_defined_(),\n+      accesschain_mapping_(*accesschain_mapping), current_function_definition_node_(nullptr),\n+      precise_return_nodes_(*precise_return_nodes) {}\n+\n+// Visits a symbol node, set the object_to_be_defined_ to the\n+// current node symbol ID, and record a mapping from this node to the current\n+// object_to_be_defined_, which is the just obtained symbol\n+// ID.\n+void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node)\n+{\n+    object_to_be_defined_ = generateSymbolLabel(node);\n+    accesschain_mapping_[node] = object_to_be_defined_;\n+}\n+\n+// Visits an aggregate node, traverses all of its children.\n+bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit,\n+                                                          glslang::TIntermAggregate* node)\n+{\n+    // This aggreagate node might be a function definition node, in which case we need to\n+    // cache this node, so we can get the precise'ness information of the return value\n+    // of this function later.\n+    StateSettingGuard<glslang::TIntermAggregate*> current_function_definition_node_setting_guard(\n+        &current_function_definition_node_);\n+    if (node->getOp() == glslang::EOpFunction) {\n+        // This is function definition node, we need to cache this node so that we can\n+        // get the precise'ness of the return value later.\n+        current_function_definition_node_setting_guard.setState(node);\n+    }\n+    // Traverse the items in the sequence.\n+    glslang::TIntermSequence& seq = node->getSequence();\n+    for (int i = 0; i < (int)seq.size(); ++i) {\n+        object_to_be_defined_.clear();\n+        seq[i]->traverse(this);\n+    }\n+    return false;\n+}\n+\n+bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit,\n+                                                       glslang::TIntermBranch* node)\n+{\n+    if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() &&\n+        current_function_definition_node_ &&\n+        current_function_definition_node_->getType().getQualifier().noContraction) {\n+        // This node is a return node with expression, and its function has\n+        // precise return value. We need to find the involved objects in its\n+        // expression and add them to the set of initial precise objects.\n+        precise_return_nodes_.insert(node);\n+        node->getExpression()->traverse(this);\n+    }\n+    return false;\n+}\n+\n+// Visits an unary node. This might be an implicit assignment like i++, i--. etc.\n+bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */,\n+                                                      glslang::TIntermUnary* node)\n+{\n+    object_to_be_defined_.clear();\n+    node->getOperand()->traverse(this);\n+    if (isAssignOperation(node->getOp())) {\n+        // We should always be able to get an accesschain of the operand node.\n+        // But we have some tests in which it is intented to have invalid operand\n+        // nodes, so just return for now.\n+        if (object_to_be_defined_.empty()) return false;\n+\n+        // If the operand node object is 'precise', we collect its accesschain\n+        // for the initial set of 'precise' objects.\n+        if (isPreciseObjectNode(node->getOperand())) {\n+            // The operand node is an 'precise' object node, add its\n+            // accesschain to the set of 'precise' objects. This is to collect\n+            // the initial set of 'precise' objects.\n+            precise_objects_.insert(object_to_be_defined_);\n+        }\n+        // Gets the symbol ID from the object's accesschain.\n+        ObjectAccessChain id_symbol = getFrontElement(object_to_be_defined_);\n+        // Add a mapping from the symbol ID to this assignment operation node.\n+        symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));\n+    }\n+    // Unary node is not a dereference node, so we clear the accesschain which\n+    // is under construction.\n+    object_to_be_defined_.clear();\n+    return false;\n+}\n+\n+// Visits a binary node and updates the mapping from symbol IDs to the definition\n+// nodes. Also collects the accesschains for the initial precise objects.\n+bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */,\n+                                                       glslang::TIntermBinary* node)\n+{\n+    // Traverses the left node to build the accesschain info for the object.\n+    object_to_be_defined_.clear();\n+    node->getLeft()->traverse(this);\n+\n+    if (isAssignOperation(node->getOp())) {\n+        // We should always be able to get an accesschain for the left node.\n+        // But we have some tests in which it is intented to have invalid left\n+        // nodes, so just return false in such cases for now.\n+        if (object_to_be_defined_.empty()) return false;\n+\n+        // If the left node object is 'precise', it is an initial precise object\n+        // specified in the shader source. Adds it to the initial worklist to\n+        // process later.\n+        if (isPreciseObjectNode(node->getLeft())) {\n+            // The left node is an 'precise' object node, add its accesschain to\n+            // the set of 'precise' objects. This is to collect the initial set\n+            // of 'precise' objects.\n+            precise_objects_.insert(object_to_be_defined_);\n+        }\n+        // Gets the symbol ID from the object accesschain, which should be the\n+        // first element recorded in the accesschain.\n+        ObjectAccessChain id_symbol = getFrontElement(object_to_be_defined_);\n+        // Adds a mapping from the symbol ID to this assignment operation node.\n+        symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));\n+\n+        // Traverses the right node, there may be other 'assignment'\n+        // operatrions in the right.\n+        object_to_be_defined_.clear();\n+        node->getRight()->traverse(this);\n+\n+        return false;\n+    } else if (isDereferenceOperation(node->getOp())) {\n+        // If the left node is 'precise' object node, this node should also\n+        // be 'precise' object node, and all the members of this node too. There\n+        // is no need to append accesschain information into the object id.\n+        if (isPreciseObjectNode(node->getLeft())) {\n+            node->getWritableType().getQualifier().noContraction = true;","path":"glslang/MachineIndependent/propagateNoContraction.cpp","position":400,"original_position":400,"commit_id":"ca4fe001d121bfc9d2cd049dd22b6a8fdbadfda7","original_commit_id":"ca4fe001d121bfc9d2cd049dd22b6a8fdbadfda7","user":{"login":"johnkslang","id":5607714,"avatar_url":"https://avatars.githubusercontent.com/u/5607714?v=3","gravatar_id":"","url":"https://api.github.com/users/johnkslang","html_url":"https://github.com/johnkslang","followers_url":"https://api.github.com/users/johnkslang/followers","following_url":"https://api.github.com/users/johnkslang/following{/other_user}","gists_url":"https://api.github.com/users/johnkslang/gists{/gist_id}","starred_url":"https://api.github.com/users/johnkslang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/johnkslang/subscriptions","organizations_url":"https://api.github.com/users/johnkslang/orgs","repos_url":"https://api.github.com/users/johnkslang/repos","events_url":"https://api.github.com/users/johnkslang/events{/privacy}","received_events_url":"https://api.github.com/users/johnkslang/received_events","type":"User","site_admin":false},"body":"I think this is what was already done during parsing.  Generally, I'm confused to see code here looking at the left of a dereference to see if something is precise.  It should just have to look at the left side of an assignment.\r\n\r\nAlso, if for a hypothetical example, there were some members precise didn't go to, due to semantics in the spec., or we change the inheritance rules, the parser should be deciding exactly when to do it, and not getting overridden here by unconditionally doing it.\r\n","created_at":"2016-05-06T23:29:18Z","updated_at":"2016-05-06T23:29:18Z","html_url":"https://github.com/KhronosGroup/glslang/pull/222#discussion_r62404150","pull_request_url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222","_links":{"self":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/comments/62404150"},"html":{"href":"https://github.com/KhronosGroup/glslang/pull/222#discussion_r62404150"},"pull_request":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222"}}},"pull_request":{"url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222","id":65555387,"html_url":"https://github.com/KhronosGroup/glslang/pull/222","diff_url":"https://github.com/KhronosGroup/glslang/pull/222.diff","patch_url":"https://github.com/KhronosGroup/glslang/pull/222.patch","issue_url":"https://api.github.com/repos/KhronosGroup/glslang/issues/222","number":222,"state":"open","locked":false,"title":"Support precise qualifier","user":{"login":"Qining","id":2983685,"avatar_url":"https://avatars.githubusercontent.com/u/2983685?v=3","gravatar_id":"","url":"https://api.github.com/users/Qining","html_url":"https://github.com/Qining","followers_url":"https://api.github.com/users/Qining/followers","following_url":"https://api.github.com/users/Qining/following{/other_user}","gists_url":"https://api.github.com/users/Qining/gists{/gist_id}","starred_url":"https://api.github.com/users/Qining/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Qining/subscriptions","organizations_url":"https://api.github.com/users/Qining/orgs","repos_url":"https://api.github.com/users/Qining/repos","events_url":"https://api.github.com/users/Qining/events{/privacy}","received_events_url":"https://api.github.com/users/Qining/received_events","type":"User","site_admin":false},"body":"Based on @yavn 's prior work:\r\nad62991\r\n\r\nGoal:\r\nArithmetic instructions that contribute to calculating 'precise'\r\nvariables, should be decorated with 'NoContraction' decorator.\r\n\r\nApproach:\r\nAdd a AST traversing pass at the end of shader parsing, called in\r\nDoFullParse(), to propagate 'noContraction' qualifier to AST nodes. Then\r\nin GlslangToSpvTraverser, add decoration for the nodes with\r\n'noContraction' qualifier.\r\n\r\nTo propagate 'noContraction' qualifier along lvalues, we need to use\r\nsymbol node IDs to record the involved lvalues.\r\n\r\nThe pass to propagate 'noContraction' qualifier is consist of three parts:\r\n\r\n1) Populate the 'precise' qualifiers to bottom symbol nodes as preprocessing\r\n\r\nSome 'precise' qualifiers are not attached to symbol nodes, but their\r\nparent nodes, which might be binary nodes, unary nodes or aggregate\r\nnodes (not very sure here). Populate the 'precise' of these nodes to\r\ntheir bottom symbol nodes so that we can use symbol IDs to propagate\r\n'NoContraction' through lvalues.\r\n\r\nExample shader:\r\n```\r\n\r\nlayout(triangles, equal_spacing) in;\r\nlayout(location = 0) in highp vec2 in_te_position[];\r\nlayout(location = 0) out mediump vec4 in_f_color;\r\nprecise gl_Position;\r\n\r\nvoid main(void) {\r\n  highp vec2 pos = vec2(3.0, 4.0);\r\n  pos += 0.04;\r\n  gl_Position = vec4(pos, 0.0, 1.0);\r\n}\r\n```\r\nIn the AST, a binary node representing gl_Position is marked as 'precise', but\r\nnot its underlying symbol node:\r\n```\r\nmove second child to first child (temp highp 4-component vector of float)\r\n  gl_Position: direct index for structure (precise gl_Position highp 4-component vector of float Position)\r\n    'anon@0' (out block{precise gl_Position highp 4-component vector of float Position gl_Position, gl_PointSize highp float PointSize gl_PointSize})\r\n    Constant:\r\n      0 (const uint)\r\n  Construct vec4 (temp highp 4-component vector of float)\r\n    'pos' (temp highp 2-component vector of float)\r\n    Constant:\r\n      0.000000\r\n    Constant:\r\n      1.000000\r\n```\r\nThis first procedure will populate 'precise' from the binary node:\r\n`gl_Position`, to its underlying symbol node: `anon@0`.\r\n\r\n2) Collect symbol and their defining nodes (assignment nodes), and\r\ncollect a set of symbol IDs to be processed as work set with\r\nsource-specified 'precise' variables.\r\n\r\n3) Start with the initial 'precise' variables collected. Mark arithmetic\r\ninstructions that contribute to these 'precise' variables in the\r\ndefining nodes (the assignment node subtrees) as 'noContraction', and\r\nadd the symbols involved to the work set. Remove the processed symbol\r\nIDs from the work set. Repeat until the work set is empty.\r\n\r\nIn the back-end (GlslangToSpvTraverser), when visiting binary/unary\r\nnodes, check if the current node is a 'noContraction' node. If so, add\r\n'NoContraction' decoration upon the rvalue ID of the binary/unary\r\noperation result.\r\n\r\nExpectation:\r\n1) In the AST, all the arithmetic operations that lead to defining\r\n'precise' symbols are marked with 'noContraction' qualifier.\r\n2) In the emitted SPIV code, 'NoContraction' decorations are added to\r\narithmetic operations. Note the 'NoContraction' decorations are not\r\nreferencing 'precise' variables (the pointers), but the arithmetic\r\noperations.\r\n\r\nKnown issue:\r\nThe operations marked as 'noContraction' are superset of the 'optimal'\r\nset. The problem is that we didn't build dominator tree.\r\nExample:\r\n```\r\nprecise int result;\r\nresult = a + b + 2;\r\na = d + 3;\r\n```\r\nBecause symbol a is involved in the calculation of result, once before,\r\n`a` will be added to the work set, then the addition `d + 3` will be\r\nmarked with `noContraction`. But it is actually not necessary.","created_at":"2016-04-07T00:01:19Z","updated_at":"2016-05-06T23:29:18Z","closed_at":null,"merged_at":null,"merge_commit_sha":"74b024011fe330515dde17d217717aefee3be7fb","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222/commits","review_comments_url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222/comments","review_comment_url":"https://api.github.com/repos/KhronosGroup/glslang/pulls/comments{/number}","comments_url":"https://api.github.com/repos/KhronosGroup/glslang/issues/222/comments","statuses_url":"https://api.github.com/repos/KhronosGroup/glslang/statuses/ca4fe001d121bfc9d2cd049dd22b6a8fdbadfda7","head":{"label":"Qining:support-precise","ref":"support-precise","sha":"ca4fe001d121bfc9d2cd049dd22b6a8fdbadfda7","user":{"login":"Qining","id":2983685,"avatar_url":"https://avatars.githubusercontent.com/u/2983685?v=3","gravatar_id":"","url":"https://api.github.com/users/Qining","html_url":"https://github.com/Qining","followers_url":"https://api.github.com/users/Qining/followers","following_url":"https://api.github.com/users/Qining/following{/other_user}","gists_url":"https://api.github.com/users/Qining/gists{/gist_id}","starred_url":"https://api.github.com/users/Qining/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Qining/subscriptions","organizations_url":"https://api.github.com/users/Qining/orgs","repos_url":"https://api.github.com/users/Qining/repos","events_url":"https://api.github.com/users/Qining/events{/privacy}","received_events_url":"https://api.github.com/users/Qining/received_events","type":"User","site_admin":false},"repo":{"id":53376214,"name":"glslang","full_name":"Qining/glslang","owner":{"login":"Qining","id":2983685,"avatar_url":"https://avatars.githubusercontent.com/u/2983685?v=3","gravatar_id":"","url":"https://api.github.com/users/Qining","html_url":"https://github.com/Qining","followers_url":"https://api.github.com/users/Qining/followers","following_url":"https://api.github.com/users/Qining/following{/other_user}","gists_url":"https://api.github.com/users/Qining/gists{/gist_id}","starred_url":"https://api.github.com/users/Qining/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Qining/subscriptions","organizations_url":"https://api.github.com/users/Qining/orgs","repos_url":"https://api.github.com/users/Qining/repos","events_url":"https://api.github.com/users/Qining/events{/privacy}","received_events_url":"https://api.github.com/users/Qining/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Qining/glslang","description":"Khronos reference front-end for GLSL and ESSL","fork":true,"url":"https://api.github.com/repos/Qining/glslang","forks_url":"https://api.github.com/repos/Qining/glslang/forks","keys_url":"https://api.github.com/repos/Qining/glslang/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Qining/glslang/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Qining/glslang/teams","hooks_url":"https://api.github.com/repos/Qining/glslang/hooks","issue_events_url":"https://api.github.com/repos/Qining/glslang/issues/events{/number}","events_url":"https://api.github.com/repos/Qining/glslang/events","assignees_url":"https://api.github.com/repos/Qining/glslang/assignees{/user}","branches_url":"https://api.github.com/repos/Qining/glslang/branches{/branch}","tags_url":"https://api.github.com/repos/Qining/glslang/tags","blobs_url":"https://api.github.com/repos/Qining/glslang/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Qining/glslang/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Qining/glslang/git/refs{/sha}","trees_url":"https://api.github.com/repos/Qining/glslang/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Qining/glslang/statuses/{sha}","languages_url":"https://api.github.com/repos/Qining/glslang/languages","stargazers_url":"https://api.github.com/repos/Qining/glslang/stargazers","contributors_url":"https://api.github.com/repos/Qining/glslang/contributors","subscribers_url":"https://api.github.com/repos/Qining/glslang/subscribers","subscription_url":"https://api.github.com/repos/Qining/glslang/subscription","commits_url":"https://api.github.com/repos/Qining/glslang/commits{/sha}","git_commits_url":"https://api.github.com/repos/Qining/glslang/git/commits{/sha}","comments_url":"https://api.github.com/repos/Qining/glslang/comments{/number}","issue_comment_url":"https://api.github.com/repos/Qining/glslang/issues/comments{/number}","contents_url":"https://api.github.com/repos/Qining/glslang/contents/{+path}","compare_url":"https://api.github.com/repos/Qining/glslang/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Qining/glslang/merges","archive_url":"https://api.github.com/repos/Qining/glslang/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Qining/glslang/downloads","issues_url":"https://api.github.com/repos/Qining/glslang/issues{/number}","pulls_url":"https://api.github.com/repos/Qining/glslang/pulls{/number}","milestones_url":"https://api.github.com/repos/Qining/glslang/milestones{/number}","notifications_url":"https://api.github.com/repos/Qining/glslang/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Qining/glslang/labels{/name}","releases_url":"https://api.github.com/repos/Qining/glslang/releases{/id}","deployments_url":"https://api.github.com/repos/Qining/glslang/deployments","created_at":"2016-03-08T02:35:58Z","updated_at":"2016-03-08T02:36:02Z","pushed_at":"2016-05-06T22:57:26Z","git_url":"git://github.com/Qining/glslang.git","ssh_url":"git@github.com:Qining/glslang.git","clone_url":"https://github.com/Qining/glslang.git","svn_url":"https://github.com/Qining/glslang","homepage":null,"size":25723,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"KhronosGroup:master","ref":"master","sha":"d3d3ce7160001846ede74978954e67f4f9fd84e4","user":{"login":"KhronosGroup","id":1608701,"avatar_url":"https://avatars.githubusercontent.com/u/1608701?v=3","gravatar_id":"","url":"https://api.github.com/users/KhronosGroup","html_url":"https://github.com/KhronosGroup","followers_url":"https://api.github.com/users/KhronosGroup/followers","following_url":"https://api.github.com/users/KhronosGroup/following{/other_user}","gists_url":"https://api.github.com/users/KhronosGroup/gists{/gist_id}","starred_url":"https://api.github.com/users/KhronosGroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KhronosGroup/subscriptions","organizations_url":"https://api.github.com/users/KhronosGroup/orgs","repos_url":"https://api.github.com/users/KhronosGroup/repos","events_url":"https://api.github.com/users/KhronosGroup/events{/privacy}","received_events_url":"https://api.github.com/users/KhronosGroup/received_events","type":"Organization","site_admin":false},"repo":{"id":38064932,"name":"glslang","full_name":"KhronosGroup/glslang","owner":{"login":"KhronosGroup","id":1608701,"avatar_url":"https://avatars.githubusercontent.com/u/1608701?v=3","gravatar_id":"","url":"https://api.github.com/users/KhronosGroup","html_url":"https://github.com/KhronosGroup","followers_url":"https://api.github.com/users/KhronosGroup/followers","following_url":"https://api.github.com/users/KhronosGroup/following{/other_user}","gists_url":"https://api.github.com/users/KhronosGroup/gists{/gist_id}","starred_url":"https://api.github.com/users/KhronosGroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KhronosGroup/subscriptions","organizations_url":"https://api.github.com/users/KhronosGroup/orgs","repos_url":"https://api.github.com/users/KhronosGroup/repos","events_url":"https://api.github.com/users/KhronosGroup/events{/privacy}","received_events_url":"https://api.github.com/users/KhronosGroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/KhronosGroup/glslang","description":"Khronos reference front-end for GLSL and ESSL","fork":false,"url":"https://api.github.com/repos/KhronosGroup/glslang","forks_url":"https://api.github.com/repos/KhronosGroup/glslang/forks","keys_url":"https://api.github.com/repos/KhronosGroup/glslang/keys{/key_id}","collaborators_url":"https://api.github.com/repos/KhronosGroup/glslang/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/KhronosGroup/glslang/teams","hooks_url":"https://api.github.com/repos/KhronosGroup/glslang/hooks","issue_events_url":"https://api.github.com/repos/KhronosGroup/glslang/issues/events{/number}","events_url":"https://api.github.com/repos/KhronosGroup/glslang/events","assignees_url":"https://api.github.com/repos/KhronosGroup/glslang/assignees{/user}","branches_url":"https://api.github.com/repos/KhronosGroup/glslang/branches{/branch}","tags_url":"https://api.github.com/repos/KhronosGroup/glslang/tags","blobs_url":"https://api.github.com/repos/KhronosGroup/glslang/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/KhronosGroup/glslang/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/KhronosGroup/glslang/git/refs{/sha}","trees_url":"https://api.github.com/repos/KhronosGroup/glslang/git/trees{/sha}","statuses_url":"https://api.github.com/repos/KhronosGroup/glslang/statuses/{sha}","languages_url":"https://api.github.com/repos/KhronosGroup/glslang/languages","stargazers_url":"https://api.github.com/repos/KhronosGroup/glslang/stargazers","contributors_url":"https://api.github.com/repos/KhronosGroup/glslang/contributors","subscribers_url":"https://api.github.com/repos/KhronosGroup/glslang/subscribers","subscription_url":"https://api.github.com/repos/KhronosGroup/glslang/subscription","commits_url":"https://api.github.com/repos/KhronosGroup/glslang/commits{/sha}","git_commits_url":"https://api.github.com/repos/KhronosGroup/glslang/git/commits{/sha}","comments_url":"https://api.github.com/repos/KhronosGroup/glslang/comments{/number}","issue_comment_url":"https://api.github.com/repos/KhronosGroup/glslang/issues/comments{/number}","contents_url":"https://api.github.com/repos/KhronosGroup/glslang/contents/{+path}","compare_url":"https://api.github.com/repos/KhronosGroup/glslang/compare/{base}...{head}","merges_url":"https://api.github.com/repos/KhronosGroup/glslang/merges","archive_url":"https://api.github.com/repos/KhronosGroup/glslang/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/KhronosGroup/glslang/downloads","issues_url":"https://api.github.com/repos/KhronosGroup/glslang/issues{/number}","pulls_url":"https://api.github.com/repos/KhronosGroup/glslang/pulls{/number}","milestones_url":"https://api.github.com/repos/KhronosGroup/glslang/milestones{/number}","notifications_url":"https://api.github.com/repos/KhronosGroup/glslang/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/KhronosGroup/glslang/labels{/name}","releases_url":"https://api.github.com/repos/KhronosGroup/glslang/releases{/id}","deployments_url":"https://api.github.com/repos/KhronosGroup/glslang/deployments","created_at":"2015-06-25T17:54:39Z","updated_at":"2016-05-04T19:49:36Z","pushed_at":"2016-05-06T22:57:27Z","git_url":"git://github.com/KhronosGroup/glslang.git","ssh_url":"git@github.com:KhronosGroup/glslang.git","clone_url":"https://github.com/KhronosGroup/glslang.git","svn_url":"https://github.com/KhronosGroup/glslang","homepage":null,"size":26517,"stargazers_count":276,"watchers_count":276,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":84,"mirror_url":null,"open_issues_count":53,"forks":84,"open_issues":53,"watchers":276,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222"},"html":{"href":"https://github.com/KhronosGroup/glslang/pull/222"},"issue":{"href":"https://api.github.com/repos/KhronosGroup/glslang/issues/222"},"comments":{"href":"https://api.github.com/repos/KhronosGroup/glslang/issues/222/comments"},"review_comments":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222/comments"},"review_comment":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/KhronosGroup/glslang/pulls/222/commits"},"statuses":{"href":"https://api.github.com/repos/KhronosGroup/glslang/statuses/ca4fe001d121bfc9d2cd049dd22b6a8fdbadfda7"}}}}