Precise and noContraction propagation

Reimplement the whole workflow to make that: precise'ness of struct
    members won't spread to other non-precise members of the same struct
    instance.

    Approach:
    1. Build the map from symbols to their defining nodes. And for each
    object node (StructIndex, DirectIndex, Symbol nodes, etc), generates an
    accesschain path. Different AST nodes that indicating a same object
    should have the same accesschain path.

    2. Along the building phase in step 1, collect the initial set of
    'precise' (AST qualifier: 'noContraction') objects' accesschain paths.

    3. Start with the initial set of 'precise' accesschain paths, use it as
    a worklist, do as the following steps until the worklist is empty:

        1) Pop an accesschain path from worklist.
        2) Get the symbol part from the accesschain path.
        3) Find the defining nodes of that symbol.
        4) For each defining node, check whether it is defining a 'precise'
        object, or its assignee has nested 'precise' object. Get the
        incremental path from assignee to its nested 'precise' object (if
        any).
        5) Traverse the right side of the defining node, obtain the
        accesschain paths of the corresponding involved 'precise' objects.
        Update the worklist with those new objects' accesschain paths.
        Label involved operations with 'noContraction'.

    In each step, whenever we find the parent object of an nested object is
    'precise' (has 'noContraction' qualifier), we let the nested object
    inherit the 'precise'ness from its parent object.
This commit is contained in:
qining 2016-05-04 17:34:38 -04:00
parent d3d3ce7160
commit 9220dbb078
15 changed files with 3307 additions and 1 deletions

View File

@ -385,6 +385,15 @@ spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifie
return (spv::Decoration)spv::BadValue;
}
// If glslang type is noContraction, return SPIR-V NoContraction decoration.
spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier)
{
if (qualifier.noContraction)
return spv::DecorationNoContraction;
else
return (spv::Decoration)spv::BadValue;
}
// Translate glslang built-in variable to SPIR-V built in decoration.
spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn)
{
@ -612,7 +621,8 @@ bool HasNonLayoutQualifiers(const glslang::TQualifier& qualifier)
// - struct members can inherit from a struct declaration
// - effect decorations on the struct members (note smooth does not, and expecting something like volatile to effect the whole object)
// - are not part of the offset/st430/etc or row/column-major layout
return qualifier.invariant || qualifier.nopersp || qualifier.flat || qualifier.centroid || qualifier.patch || qualifier.sample || qualifier.hasLocation();
return qualifier.invariant || qualifier.nopersp || qualifier.flat || qualifier.centroid || qualifier.patch || qualifier.sample || qualifier.hasLocation() ||
qualifier.noContraction;
}
//
@ -877,6 +887,9 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T
convertGlslangToSpvType(node->getType()), leftRValue, rValue,
node->getType().getBasicType());
// Decorate this instruction, if this node has 'noContraction' qualifier.
addDecoration(rValue, TranslateNoContractionDecoration(node->getType().getQualifier()));
// these all need their counterparts in createBinaryOperation()
assert(rValue != spv::NoResult);
}
@ -1000,6 +1013,8 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T
logger->missingFunctionality("unknown glslang binary operation");
return true; // pick up a child as the place-holder result
} else {
// Decorate this instruction, if this node has 'noContraction' qualifier.
addDecoration(result, TranslateNoContractionDecoration(node->getType().getQualifier()));
builder.setAccessChainRValue(result);
return false;
}
@ -1068,6 +1083,8 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI
result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand, node->getOperand()->getBasicType());
if (result) {
// Decorate this instruction, if this node has 'noContraction' qualifier.
addDecoration(result, TranslateNoContractionDecoration(node->getType().getQualifier()));
builder.clearAccessChain();
builder.setAccessChainRValue(result);
@ -1100,6 +1117,8 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI
convertGlslangToSpvType(node->getType()), operand, one,
node->getType().getBasicType());
assert(result != spv::NoResult);
// Decorate this instruction, if this node has 'noContraction' qualifier.
addDecoration(result, TranslateNoContractionDecoration(node->getType().getQualifier()));
// The result of operation is always stored, but conditionally the
// consumed result. The consumed result is always an r-value.

View File

@ -0,0 +1,728 @@
precise.tesc
Warning, version 450 is not yet complete; most version-specific features are present, but some are missing.
Shader version: 450
Requested GL_EXT_gpu_shader5
Requested GL_EXT_shader_io_blocks
Requested GL_EXT_tessellation_shader
vertices = -1
0:? Sequence
0:5 Function Definition: minimal( (global float)
0:5 Function Parameters:
0:6 Sequence
0:6 Sequence
0:6 move second child to first child (temp float)
0:6 'result' (noContraction temp float)
0:6 Constant:
0:6 5.000000
0:7 Sequence
0:7 move second child to first child (temp float)
0:7 'a' (noContraction temp float)
0:7 Constant:
0:7 10.000000
0:8 Sequence
0:8 move second child to first child (temp float)
0:8 'b' (noContraction temp float)
0:8 Constant:
0:8 20.000000
0:9 Sequence
0:9 move second child to first child (temp float)
0:9 'c' (noContraction temp float)
0:9 Constant:
0:9 30.000000
0:10 Sequence
0:10 move second child to first child (temp float)
0:10 'd' (noContraction temp float)
0:10 Constant:
0:10 40.000000
0:11 move second child to first child (temp float)
0:11 'result' (noContraction temp float)
0:11 add (noContraction temp float)
0:11 component-wise multiply (noContraction temp float)
0:11 'a' (noContraction temp float)
0:11 'b' (noContraction temp float)
0:11 component-wise multiply (noContraction temp float)
0:11 'c' (noContraction temp float)
0:11 'd' (noContraction temp float)
0:12 Branch: Return with expression
0:12 'result' (noContraction temp float)
0:15 Function Definition: continuous_assignment( (global void)
0:15 Function Parameters:
0:16 Sequence
0:16 Sequence
0:16 move second child to first child (temp float)
0:16 'result' (noContraction temp float)
0:16 Constant:
0:16 5.000000
0:17 Sequence
0:17 move second child to first child (temp int)
0:17 'a' (noContraction temp int)
0:17 Constant:
0:17 10 (const int)
0:18 Sequence
0:18 move second child to first child (temp int)
0:18 'b' (noContraction temp int)
0:18 Constant:
0:18 20 (const int)
0:19 move second child to first child (temp float)
0:19 'result' (noContraction temp float)
0:19 Convert int to float (temp float)
0:19 move second child to first child (temp int)
0:19 'a' (noContraction temp int)
0:19 add (noContraction temp int)
0:19 'b' (noContraction temp int)
0:19 Constant:
0:19 4 (const int)
0:22 Function Definition: convert( (global void)
0:22 Function Parameters:
0:? Sequence
0:24 Sequence
0:24 move second child to first child (temp int)
0:24 'a' (noContraction temp int)
0:24 Constant:
0:24 10 (const int)
0:25 Sequence
0:25 move second child to first child (temp int)
0:25 'b' (noContraction temp int)
0:25 Constant:
0:25 20 (const int)
0:26 move second child to first child (temp int)
0:26 'b' (noContraction temp int)
0:26 add (noContraction temp int)
0:26 'a' (noContraction temp int)
0:26 'b' (noContraction temp int)
0:27 move second child to first child (temp float)
0:27 'result' (noContraction temp float)
0:27 Convert int to float (temp float)
0:27 'b' (noContraction temp int)
0:30 Function Definition: loop_for( (global float)
0:30 Function Parameters:
0:31 Sequence
0:31 Sequence
0:31 move second child to first child (temp float)
0:31 'r1' (noContraction temp float)
0:31 Constant:
0:31 5.000000
0:32 Sequence
0:32 move second child to first child (temp float)
0:32 'r2' (noContraction temp float)
0:32 Constant:
0:32 10.000000
0:33 Sequence
0:33 move second child to first child (temp int)
0:33 'a' (temp int)
0:33 Constant:
0:33 10 (const int)
0:34 Sequence
0:34 move second child to first child (temp int)
0:34 'b' (noContraction temp int)
0:34 Constant:
0:34 20 (const int)
0:35 Sequence
0:35 move second child to first child (temp int)
0:35 'c' (noContraction temp int)
0:35 Constant:
0:35 30 (const int)
0:36 Sequence
0:36 Sequence
0:36 move second child to first child (temp int)
0:36 'i' (noContraction temp int)
0:36 Constant:
0:36 0 (const int)
0:36 Loop with condition tested first
0:36 Loop Condition
0:36 Compare Less Than (temp bool)
0:36 'i' (temp int)
0:36 'a' (temp int)
0:36 Loop Body
0:37 Sequence
0:37 add second child into first child (noContraction temp float)
0:37 'r1' (noContraction temp float)
0:37 add (noContraction temp float)
0:37 add (noContraction temp float)
0:37 Constant:
0:37 3.120000
0:37 Convert int to float (temp float)
0:37 'b' (noContraction temp int)
0:37 Convert int to float (temp float)
0:37 'i' (noContraction temp int)
0:38 add second child into first child (noContraction temp int)
0:38 'c' (noContraction temp int)
0:38 Constant:
0:38 1 (const int)
0:36 Loop Terminal Expression
0:36 Post-Increment (noContraction temp int)
0:36 'i' (noContraction temp int)
0:40 add second child into first child (temp int)
0:40 'a' (temp int)
0:40 Constant:
0:40 1 (const int)
0:41 move second child to first child (temp float)
0:41 'r2' (noContraction temp float)
0:41 Convert int to float (temp float)
0:41 'c' (noContraction temp int)
0:42 Branch: Return with expression
0:42 Construct float (temp float)
0:42 add (temp float)
0:42 'r1' (noContraction temp float)
0:42 'r2' (noContraction temp float)
0:45 Function Definition: loop_array( (global void)
0:45 Function Parameters:
0:46 Sequence
0:46 Sequence
0:46 move second child to first child (temp int)
0:46 'result' (noContraction temp int)
0:46 Constant:
0:46 5 (const int)
0:48 Sequence
0:48 move second child to first child (temp int)
0:48 'x' (noContraction temp int)
0:48 Constant:
0:48 22 (const int)
0:49 Sequence
0:49 move second child to first child (temp int)
0:49 'y' (noContraction temp int)
0:49 Constant:
0:49 33 (const int)
0:52 add second child into first child (noContraction temp int)
0:52 'result' (noContraction temp int)
0:52 add (noContraction temp int)
0:52 'x' (noContraction temp int)
0:52 'y' (noContraction temp int)
0:54 Sequence
0:54 Sequence
0:54 move second child to first child (temp int)
0:54 'i' (temp int)
0:54 Constant:
0:54 0 (const int)
0:54 Loop with condition tested first
0:54 Loop Condition
0:54 Compare Less Than (temp bool)
0:54 'i' (temp int)
0:54 Constant:
0:54 3 (const int)
0:54 Loop Body
0:56 Sequence
0:56 add second child into first child (noContraction temp int)
0:56 'result' (noContraction temp int)
0:56 add (noContraction temp int)
0:56 indirect index (noContraction temp int)
0:56 'a0' (temp 3-element array of int)
0:56 'i' (temp int)
0:56 Constant:
0:56 2 (const int)
0:58 move second child to first child (temp int)
0:58 indirect index (noContraction temp int)
0:58 'a0' (noContraction temp 3-element array of int)
0:58 'i' (temp int)
0:58 subtract (noContraction temp int)
0:58 Constant:
0:58 3 (const int)
0:58 Post-Increment (noContraction temp int)
0:58 'result' (noContraction temp int)
0:54 Loop Terminal Expression
0:54 Pre-Increment (temp int)
0:54 'i' (temp int)
0:62 Function Definition: loop_while( (global void)
0:62 Function Parameters:
0:63 Sequence
0:63 Sequence
0:63 move second child to first child (temp float)
0:63 'result' (noContraction temp float)
0:63 Constant:
0:63 5.000000
0:64 Sequence
0:64 move second child to first child (temp int)
0:64 'a' (noContraction temp int)
0:64 Constant:
0:64 10 (const int)
0:65 Sequence
0:65 move second child to first child (temp int)
0:65 'b' (noContraction temp int)
0:65 Constant:
0:65 20 (const int)
0:66 Loop with condition tested first
0:66 Loop Condition
0:66 Compare Less Than (temp bool)
0:66 'result' (noContraction temp float)
0:66 Constant:
0:66 10.000000
0:66 Loop Body
0:67 Sequence
0:67 add second child into first child (noContraction temp float)
0:67 'result' (noContraction temp float)
0:67 add (noContraction temp float)
0:67 Constant:
0:67 3.120000
0:67 Convert int to float (temp float)
0:67 'b' (noContraction temp int)
0:69 move second child to first child (temp float)
0:69 'result' (noContraction temp float)
0:69 Convert int to float (temp float)
0:69 add (noContraction temp int)
0:69 add (noContraction temp int)
0:69 'a' (noContraction temp int)
0:69 'b' (noContraction temp int)
0:69 Constant:
0:69 5 (const int)
0:70 move second child to first child (temp float)
0:70 'result' (noContraction temp float)
0:70 Constant:
0:70 11.100000
0:73 Function Definition: fma_not_decorated( (global float)
0:73 Function Parameters:
0:? Sequence
0:75 Sequence
0:75 move second child to first child (temp float)
0:75 'a' (noContraction temp float)
0:75 Constant:
0:75 1.000000
0:76 Sequence
0:76 move second child to first child (temp float)
0:76 'b' (noContraction temp float)
0:76 Constant:
0:76 2.000000
0:77 Sequence
0:77 move second child to first child (temp float)
0:77 'c' (noContraction temp float)
0:77 Constant:
0:77 3.000000
0:78 move second child to first child (temp float)
0:78 'b' (noContraction temp float)
0:78 add (noContraction temp float)
0:78 'b' (noContraction temp float)
0:78 'c' (noContraction temp float)
0:79 move second child to first child (temp float)
0:79 'result' (noContraction temp float)
0:79 fma (global float)
0:79 'a' (noContraction temp float)
0:79 'b' (noContraction temp float)
0:79 'c' (noContraction temp float)
0:80 Branch: Return with expression
0:80 'result' (noContraction temp float)
0:83 Function Definition: precise_return_exp_func( (noContraction temp float)
0:83 Function Parameters:
0:84 Sequence
0:84 Sequence
0:84 move second child to first child (temp float)
0:84 'a' (noContraction temp float)
0:84 Constant:
0:84 1.000000
0:85 Sequence
0:85 move second child to first child (temp float)
0:85 'b' (noContraction temp float)
0:85 Constant:
0:85 2.000000
0:86 Branch: Return with expression
0:86 add (noContraction temp float)
0:86 'a' (noContraction temp float)
0:86 'b' (noContraction temp float)
0:89 Function Definition: precise_return_val_func( (noContraction temp float)
0:89 Function Parameters:
0:90 Sequence
0:90 Sequence
0:90 move second child to first child (temp float)
0:90 'a' (noContraction temp float)
0:90 Constant:
0:90 1.000000
0:91 Sequence
0:91 move second child to first child (temp float)
0:91 'b' (noContraction temp float)
0:91 Constant:
0:91 2.000000
0:92 Sequence
0:92 move second child to first child (temp float)
0:92 'result' (noContraction temp float)
0:92 add (noContraction temp float)
0:92 'a' (noContraction temp float)
0:92 'b' (noContraction temp float)
0:93 Branch: Return with expression
0:93 'result' (noContraction temp float)
0:96 Function Definition: precise_func_parameter(f1;f1; (global float)
0:96 Function Parameters:
0:96 'b' (in float)
0:96 'c' (out float)
0:97 Sequence
0:97 Sequence
0:97 move second child to first child (temp float)
0:97 'a' (temp float)
0:97 Constant:
0:97 0.500000
0:98 move second child to first child (temp float)
0:98 'c' (out float)
0:98 add (temp float)
0:98 'a' (temp float)
0:98 'b' (in float)
0:99 Branch: Return with expression
0:99 subtract (temp float)
0:99 'a' (temp float)
0:99 'b' (in float)
0:102 Function Definition: main( (global void)
0:102 Function Parameters:
0:? Linker Objects
Linked tessellation control stage:
ERROR: Linking tessellation control stage: At least one shader must specify an output layout(vertices=...)
Shader version: 450
Requested GL_EXT_gpu_shader5
Requested GL_EXT_shader_io_blocks
Requested GL_EXT_tessellation_shader
vertices = -1
0:? Sequence
0:5 Function Definition: minimal( (global float)
0:5 Function Parameters:
0:6 Sequence
0:6 Sequence
0:6 move second child to first child (temp float)
0:6 'result' (noContraction temp float)
0:6 Constant:
0:6 5.000000
0:7 Sequence
0:7 move second child to first child (temp float)
0:7 'a' (noContraction temp float)
0:7 Constant:
0:7 10.000000
0:8 Sequence
0:8 move second child to first child (temp float)
0:8 'b' (noContraction temp float)
0:8 Constant:
0:8 20.000000
0:9 Sequence
0:9 move second child to first child (temp float)
0:9 'c' (noContraction temp float)
0:9 Constant:
0:9 30.000000
0:10 Sequence
0:10 move second child to first child (temp float)
0:10 'd' (noContraction temp float)
0:10 Constant:
0:10 40.000000
0:11 move second child to first child (temp float)
0:11 'result' (noContraction temp float)
0:11 add (noContraction temp float)
0:11 component-wise multiply (noContraction temp float)
0:11 'a' (noContraction temp float)
0:11 'b' (noContraction temp float)
0:11 component-wise multiply (noContraction temp float)
0:11 'c' (noContraction temp float)
0:11 'd' (noContraction temp float)
0:12 Branch: Return with expression
0:12 'result' (noContraction temp float)
0:15 Function Definition: continuous_assignment( (global void)
0:15 Function Parameters:
0:16 Sequence
0:16 Sequence
0:16 move second child to first child (temp float)
0:16 'result' (noContraction temp float)
0:16 Constant:
0:16 5.000000
0:17 Sequence
0:17 move second child to first child (temp int)
0:17 'a' (noContraction temp int)
0:17 Constant:
0:17 10 (const int)
0:18 Sequence
0:18 move second child to first child (temp int)
0:18 'b' (noContraction temp int)
0:18 Constant:
0:18 20 (const int)
0:19 move second child to first child (temp float)
0:19 'result' (noContraction temp float)
0:19 Convert int to float (temp float)
0:19 move second child to first child (temp int)
0:19 'a' (noContraction temp int)
0:19 add (noContraction temp int)
0:19 'b' (noContraction temp int)
0:19 Constant:
0:19 4 (const int)
0:22 Function Definition: convert( (global void)
0:22 Function Parameters:
0:? Sequence
0:24 Sequence
0:24 move second child to first child (temp int)
0:24 'a' (noContraction temp int)
0:24 Constant:
0:24 10 (const int)
0:25 Sequence
0:25 move second child to first child (temp int)
0:25 'b' (noContraction temp int)
0:25 Constant:
0:25 20 (const int)
0:26 move second child to first child (temp int)
0:26 'b' (noContraction temp int)
0:26 add (noContraction temp int)
0:26 'a' (noContraction temp int)
0:26 'b' (noContraction temp int)
0:27 move second child to first child (temp float)
0:27 'result' (noContraction temp float)
0:27 Convert int to float (temp float)
0:27 'b' (noContraction temp int)
0:30 Function Definition: loop_for( (global float)
0:30 Function Parameters:
0:31 Sequence
0:31 Sequence
0:31 move second child to first child (temp float)
0:31 'r1' (noContraction temp float)
0:31 Constant:
0:31 5.000000
0:32 Sequence
0:32 move second child to first child (temp float)
0:32 'r2' (noContraction temp float)
0:32 Constant:
0:32 10.000000
0:33 Sequence
0:33 move second child to first child (temp int)
0:33 'a' (temp int)
0:33 Constant:
0:33 10 (const int)
0:34 Sequence
0:34 move second child to first child (temp int)
0:34 'b' (noContraction temp int)
0:34 Constant:
0:34 20 (const int)
0:35 Sequence
0:35 move second child to first child (temp int)
0:35 'c' (noContraction temp int)
0:35 Constant:
0:35 30 (const int)
0:36 Sequence
0:36 Sequence
0:36 move second child to first child (temp int)
0:36 'i' (noContraction temp int)
0:36 Constant:
0:36 0 (const int)
0:36 Loop with condition tested first
0:36 Loop Condition
0:36 Compare Less Than (temp bool)
0:36 'i' (temp int)
0:36 'a' (temp int)
0:36 Loop Body
0:37 Sequence
0:37 add second child into first child (noContraction temp float)
0:37 'r1' (noContraction temp float)
0:37 add (noContraction temp float)
0:37 add (noContraction temp float)
0:37 Constant:
0:37 3.120000
0:37 Convert int to float (temp float)
0:37 'b' (noContraction temp int)
0:37 Convert int to float (temp float)
0:37 'i' (noContraction temp int)
0:38 add second child into first child (noContraction temp int)
0:38 'c' (noContraction temp int)
0:38 Constant:
0:38 1 (const int)
0:36 Loop Terminal Expression
0:36 Post-Increment (noContraction temp int)
0:36 'i' (noContraction temp int)
0:40 add second child into first child (temp int)
0:40 'a' (temp int)
0:40 Constant:
0:40 1 (const int)
0:41 move second child to first child (temp float)
0:41 'r2' (noContraction temp float)
0:41 Convert int to float (temp float)
0:41 'c' (noContraction temp int)
0:42 Branch: Return with expression
0:42 Construct float (temp float)
0:42 add (temp float)
0:42 'r1' (noContraction temp float)
0:42 'r2' (noContraction temp float)
0:45 Function Definition: loop_array( (global void)
0:45 Function Parameters:
0:46 Sequence
0:46 Sequence
0:46 move second child to first child (temp int)
0:46 'result' (noContraction temp int)
0:46 Constant:
0:46 5 (const int)
0:48 Sequence
0:48 move second child to first child (temp int)
0:48 'x' (noContraction temp int)
0:48 Constant:
0:48 22 (const int)
0:49 Sequence
0:49 move second child to first child (temp int)
0:49 'y' (noContraction temp int)
0:49 Constant:
0:49 33 (const int)
0:52 add second child into first child (noContraction temp int)
0:52 'result' (noContraction temp int)
0:52 add (noContraction temp int)
0:52 'x' (noContraction temp int)
0:52 'y' (noContraction temp int)
0:54 Sequence
0:54 Sequence
0:54 move second child to first child (temp int)
0:54 'i' (temp int)
0:54 Constant:
0:54 0 (const int)
0:54 Loop with condition tested first
0:54 Loop Condition
0:54 Compare Less Than (temp bool)
0:54 'i' (temp int)
0:54 Constant:
0:54 3 (const int)
0:54 Loop Body
0:56 Sequence
0:56 add second child into first child (noContraction temp int)
0:56 'result' (noContraction temp int)
0:56 add (noContraction temp int)
0:56 indirect index (noContraction temp int)
0:56 'a0' (temp 3-element array of int)
0:56 'i' (temp int)
0:56 Constant:
0:56 2 (const int)
0:58 move second child to first child (temp int)
0:58 indirect index (noContraction temp int)
0:58 'a0' (noContraction temp 3-element array of int)
0:58 'i' (temp int)
0:58 subtract (noContraction temp int)
0:58 Constant:
0:58 3 (const int)
0:58 Post-Increment (noContraction temp int)
0:58 'result' (noContraction temp int)
0:54 Loop Terminal Expression
0:54 Pre-Increment (temp int)
0:54 'i' (temp int)
0:62 Function Definition: loop_while( (global void)
0:62 Function Parameters:
0:63 Sequence
0:63 Sequence
0:63 move second child to first child (temp float)
0:63 'result' (noContraction temp float)
0:63 Constant:
0:63 5.000000
0:64 Sequence
0:64 move second child to first child (temp int)
0:64 'a' (noContraction temp int)
0:64 Constant:
0:64 10 (const int)
0:65 Sequence
0:65 move second child to first child (temp int)
0:65 'b' (noContraction temp int)
0:65 Constant:
0:65 20 (const int)
0:66 Loop with condition tested first
0:66 Loop Condition
0:66 Compare Less Than (temp bool)
0:66 'result' (noContraction temp float)
0:66 Constant:
0:66 10.000000
0:66 Loop Body
0:67 Sequence
0:67 add second child into first child (noContraction temp float)
0:67 'result' (noContraction temp float)
0:67 add (noContraction temp float)
0:67 Constant:
0:67 3.120000
0:67 Convert int to float (temp float)
0:67 'b' (noContraction temp int)
0:69 move second child to first child (temp float)
0:69 'result' (noContraction temp float)
0:69 Convert int to float (temp float)
0:69 add (noContraction temp int)
0:69 add (noContraction temp int)
0:69 'a' (noContraction temp int)
0:69 'b' (noContraction temp int)
0:69 Constant:
0:69 5 (const int)
0:70 move second child to first child (temp float)
0:70 'result' (noContraction temp float)
0:70 Constant:
0:70 11.100000
0:73 Function Definition: fma_not_decorated( (global float)
0:73 Function Parameters:
0:? Sequence
0:75 Sequence
0:75 move second child to first child (temp float)
0:75 'a' (noContraction temp float)
0:75 Constant:
0:75 1.000000
0:76 Sequence
0:76 move second child to first child (temp float)
0:76 'b' (noContraction temp float)
0:76 Constant:
0:76 2.000000
0:77 Sequence
0:77 move second child to first child (temp float)
0:77 'c' (noContraction temp float)
0:77 Constant:
0:77 3.000000
0:78 move second child to first child (temp float)
0:78 'b' (noContraction temp float)
0:78 add (noContraction temp float)
0:78 'b' (noContraction temp float)
0:78 'c' (noContraction temp float)
0:79 move second child to first child (temp float)
0:79 'result' (noContraction temp float)
0:79 fma (global float)
0:79 'a' (noContraction temp float)
0:79 'b' (noContraction temp float)
0:79 'c' (noContraction temp float)
0:80 Branch: Return with expression
0:80 'result' (noContraction temp float)
0:83 Function Definition: precise_return_exp_func( (noContraction temp float)
0:83 Function Parameters:
0:84 Sequence
0:84 Sequence
0:84 move second child to first child (temp float)
0:84 'a' (noContraction temp float)
0:84 Constant:
0:84 1.000000
0:85 Sequence
0:85 move second child to first child (temp float)
0:85 'b' (noContraction temp float)
0:85 Constant:
0:85 2.000000
0:86 Branch: Return with expression
0:86 add (noContraction temp float)
0:86 'a' (noContraction temp float)
0:86 'b' (noContraction temp float)
0:89 Function Definition: precise_return_val_func( (noContraction temp float)
0:89 Function Parameters:
0:90 Sequence
0:90 Sequence
0:90 move second child to first child (temp float)
0:90 'a' (noContraction temp float)
0:90 Constant:
0:90 1.000000
0:91 Sequence
0:91 move second child to first child (temp float)
0:91 'b' (noContraction temp float)
0:91 Constant:
0:91 2.000000
0:92 Sequence
0:92 move second child to first child (temp float)
0:92 'result' (noContraction temp float)
0:92 add (noContraction temp float)
0:92 'a' (noContraction temp float)
0:92 'b' (noContraction temp float)
0:93 Branch: Return with expression
0:93 'result' (noContraction temp float)
0:96 Function Definition: precise_func_parameter(f1;f1; (global float)
0:96 Function Parameters:
0:96 'b' (in float)
0:96 'c' (out float)
0:97 Sequence
0:97 Sequence
0:97 move second child to first child (temp float)
0:97 'a' (temp float)
0:97 Constant:
0:97 0.500000
0:98 move second child to first child (temp float)
0:98 'c' (out float)
0:98 add (temp float)
0:98 'a' (temp float)
0:98 'b' (in float)
0:99 Branch: Return with expression
0:99 subtract (temp float)
0:99 'a' (temp float)
0:99 'b' (in float)
0:102 Function Definition: main( (global void)
0:102 Function Parameters:
0:? Linker Objects

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
spv.precise.tesc
Warning, version 310 is not yet complete; most version-specific features are present, but some are missing.
Linked tessellation control stage:
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 72
Capability Tessellation
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint TessellationControl 4 "main" 12 15 20 30 40 45
ExecutionMode 4 OutputVertices 3
Source ESSL 310
SourceExtension "GL_EXT_gpu_shader5"
SourceExtension "GL_EXT_shader_io_blocks"
SourceExtension "GL_EXT_tessellation_shader"
Name 4 "main"
Name 12 "in_te_position"
Name 15 "gl_InvocationID"
Name 20 "in_tc_position"
Name 30 "gl_TessLevelInner"
Name 40 "gl_TessLevelOuter"
Name 45 "in_tc_tessParam"
Decorate 12(in_te_position) Location 0
Decorate 15(gl_InvocationID) BuiltIn InvocationId
Decorate 20(in_tc_position) Location 0
Decorate 30(gl_TessLevelInner) Patch
Decorate 30(gl_TessLevelInner) BuiltIn TessLevelInner
Decorate 40(gl_TessLevelOuter) Patch
Decorate 40(gl_TessLevelOuter) BuiltIn TessLevelOuter
Decorate 45(in_tc_tessParam) Location 1
Decorate 52 NoContraction
Decorate 53 NoContraction
Decorate 54 NoContraction
Decorate 60 NoContraction
Decorate 61 NoContraction
Decorate 62 NoContraction
Decorate 68 NoContraction
Decorate 69 NoContraction
Decorate 70 NoContraction
2: TypeVoid
3: TypeFunction 2
6: TypeFloat 32
7: TypeVector 6(float) 2
8: TypeInt 32 0
9: 8(int) Constant 3
10: TypeArray 7(fvec2) 9
11: TypePointer Output 10
12(in_te_position): 11(ptr) Variable Output
13: TypeInt 32 1
14: TypePointer Input 13(int)
15(gl_InvocationID): 14(ptr) Variable Input
17: 8(int) Constant 32
18: TypeArray 7(fvec2) 17
19: TypePointer Input 18
20(in_tc_position): 19(ptr) Variable Input
22: TypePointer Input 7(fvec2)
25: TypePointer Output 7(fvec2)
27: 8(int) Constant 2
28: TypeArray 6(float) 27
29: TypePointer Output 28
30(gl_TessLevelInner): 29(ptr) Variable Output
31: 13(int) Constant 0
32: 6(float) Constant 1084227584
33: TypePointer Output 6(float)
35: 13(int) Constant 1
37: 8(int) Constant 4
38: TypeArray 6(float) 37
39: TypePointer Output 38
40(gl_TessLevelOuter): 39(ptr) Variable Output
41: 6(float) Constant 1065353216
42: 6(float) Constant 1105985536
43: TypeArray 6(float) 17
44: TypePointer Input 43
45(in_tc_tessParam): 44(ptr) Variable Input
46: TypePointer Input 6(float)
49: 13(int) Constant 2
4(main): 2 Function None 3
5: Label
16: 13(int) Load 15(gl_InvocationID)
21: 13(int) Load 15(gl_InvocationID)
23: 22(ptr) AccessChain 20(in_tc_position) 21
24: 7(fvec2) Load 23
26: 25(ptr) AccessChain 12(in_te_position) 16
Store 26 24
34: 33(ptr) AccessChain 30(gl_TessLevelInner) 31
Store 34 32
36: 33(ptr) AccessChain 30(gl_TessLevelInner) 35
Store 36 32
47: 46(ptr) AccessChain 45(in_tc_tessParam) 35
48: 6(float) Load 47
50: 46(ptr) AccessChain 45(in_tc_tessParam) 49
51: 6(float) Load 50
52: 6(float) FAdd 48 51
53: 6(float) FMul 42 52
54: 6(float) FAdd 41 53
55: 33(ptr) AccessChain 40(gl_TessLevelOuter) 31
Store 55 54
56: 46(ptr) AccessChain 45(in_tc_tessParam) 49
57: 6(float) Load 56
58: 46(ptr) AccessChain 45(in_tc_tessParam) 31
59: 6(float) Load 58
60: 6(float) FAdd 57 59
61: 6(float) FMul 42 60
62: 6(float) FAdd 41 61
63: 33(ptr) AccessChain 40(gl_TessLevelOuter) 35
Store 63 62
64: 46(ptr) AccessChain 45(in_tc_tessParam) 31
65: 6(float) Load 64
66: 46(ptr) AccessChain 45(in_tc_tessParam) 35
67: 6(float) Load 66
68: 6(float) FAdd 65 67
69: 6(float) FMul 42 68
70: 6(float) FAdd 41 69
71: 33(ptr) AccessChain 40(gl_TessLevelOuter) 49
Store 71 70
Return
FunctionEnd

View File

@ -0,0 +1,192 @@
spv.precise.tese
Warning, version 310 is not yet complete; most version-specific features are present, but some are missing.
Linked tessellation evaluation stage:
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 119
Capability Tessellation
Capability TessellationPointSize
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint TessellationEvaluation 4 "main" 12 21 62 112
ExecutionMode 4 Triangles
ExecutionMode 4 SpacingEqual
ExecutionMode 4 VertexOrderCcw
Source ESSL 310
SourceExtension "GL_EXT_gpu_shader5"
SourceExtension "GL_EXT_shader_io_blocks"
SourceExtension "GL_EXT_tessellation_shader"
Name 4 "main"
Name 9 "pos"
Name 12 "gl_TessCoord"
Name 21 "in_te_position"
Name 45 "f"
Name 62 "in_f_color"
Name 73 "bits"
Name 77 "numBits"
Name 78 "i"
Name 110 "gl_PerVertex"
MemberName 110(gl_PerVertex) 0 "gl_Position"
MemberName 110(gl_PerVertex) 1 "gl_PointSize"
Name 112 ""
Decorate 12(gl_TessCoord) BuiltIn TessCoord
Decorate 21(in_te_position) Location 0
Decorate 27 NoContraction
Decorate 34 NoContraction
Decorate 35 NoContraction
Decorate 42 NoContraction
Decorate 43 NoContraction
Decorate 62(in_f_color) RelaxedPrecision
Decorate 62(in_f_color) Location 0
Decorate 67 RelaxedPrecision
Decorate 68 RelaxedPrecision
Decorate 69 RelaxedPrecision
Decorate 70 RelaxedPrecision
Decorate 97 NoContraction
Decorate 99 NoContraction
Decorate 101 NoContraction
Decorate 106 NoContraction
Decorate 109 NoContraction
MemberDecorate 110(gl_PerVertex) 0 BuiltIn Position
MemberDecorate 110(gl_PerVertex) 1 BuiltIn PointSize
Decorate 110(gl_PerVertex) Block
2: TypeVoid
3: TypeFunction 2
6: TypeFloat 32
7: TypeVector 6(float) 2
8: TypePointer Function 7(fvec2)
10: TypeVector 6(float) 3
11: TypePointer Input 10(fvec3)
12(gl_TessCoord): 11(ptr) Variable Input
13: TypeInt 32 0
14: 13(int) Constant 0
15: TypePointer Input 6(float)
18: 13(int) Constant 32
19: TypeArray 7(fvec2) 18
20: TypePointer Input 19
21(in_te_position): 20(ptr) Variable Input
22: TypeInt 32 1
23: 22(int) Constant 0
24: TypePointer Input 7(fvec2)
28: 13(int) Constant 1
31: 22(int) Constant 1
36: 13(int) Constant 2
39: 22(int) Constant 2
44: TypePointer Function 6(float)
46: 6(float) Constant 1077936128
57: 6(float) Constant 1056964608
60: TypeVector 6(float) 4
61: TypePointer Output 60(fvec4)
62(in_f_color): 61(ptr) Variable Output
66: 6(float) Constant 1065353216
71: TypeVector 13(int) 2
72: TypePointer Function 71(ivec2)
76: TypePointer Function 13(int)
85: TypeBool
105: 6(float) Constant 1025758986
110(gl_PerVertex): TypeStruct 60(fvec4) 6(float)
111: TypePointer Output 110(gl_PerVertex)
112: 111(ptr) Variable Output
114: 6(float) Constant 0
4(main): 2 Function None 3
5: Label
9(pos): 8(ptr) Variable Function
45(f): 44(ptr) Variable Function
73(bits): 72(ptr) Variable Function
77(numBits): 76(ptr) Variable Function
78(i): 76(ptr) Variable Function
16: 15(ptr) AccessChain 12(gl_TessCoord) 14
17: 6(float) Load 16
25: 24(ptr) AccessChain 21(in_te_position) 23
26: 7(fvec2) Load 25
27: 7(fvec2) VectorTimesScalar 26 17
29: 15(ptr) AccessChain 12(gl_TessCoord) 28
30: 6(float) Load 29
32: 24(ptr) AccessChain 21(in_te_position) 31
33: 7(fvec2) Load 32
34: 7(fvec2) VectorTimesScalar 33 30
35: 7(fvec2) FAdd 27 34
37: 15(ptr) AccessChain 12(gl_TessCoord) 36
38: 6(float) Load 37
40: 24(ptr) AccessChain 21(in_te_position) 39
41: 7(fvec2) Load 40
42: 7(fvec2) VectorTimesScalar 41 38
43: 7(fvec2) FAdd 35 42
Store 9(pos) 43
47: 15(ptr) AccessChain 12(gl_TessCoord) 14
48: 6(float) Load 47
49: 15(ptr) AccessChain 12(gl_TessCoord) 28
50: 6(float) Load 49
51: 15(ptr) AccessChain 12(gl_TessCoord) 36
52: 6(float) Load 51
53: 6(float) ExtInst 1(GLSL.std.450) 37(FMin) 50 52
54: 6(float) ExtInst 1(GLSL.std.450) 37(FMin) 48 53
55: 6(float) FMul 46 54
56: 6(float) ExtInst 1(GLSL.std.450) 31(Sqrt) 55
58: 6(float) FMul 56 57
59: 6(float) FAdd 58 57
Store 45(f) 59
63: 10(fvec3) Load 12(gl_TessCoord)
64: 6(float) Load 45(f)
65: 10(fvec3) VectorTimesScalar 63 64
67: 6(float) CompositeExtract 65 0
68: 6(float) CompositeExtract 65 1
69: 6(float) CompositeExtract 65 2
70: 60(fvec4) CompositeConstruct 67 68 69 66
Store 62(in_f_color) 70
74: 7(fvec2) Load 9(pos)
75: 71(ivec2) Bitcast 74
Store 73(bits) 75
Store 77(numBits) 14
Store 78(i) 14
Branch 79
79: Label
LoopMerge 81 82 None
Branch 83
83: Label
84: 13(int) Load 78(i)
86: 85(bool) ULessThan 84 18
BranchConditional 86 80 81
80: Label
87: 76(ptr) AccessChain 73(bits) 14
88: 13(int) Load 87
89: 13(int) Load 78(i)
90: 13(int) ShiftLeftLogical 88 89
91: 13(int) BitwiseAnd 90 28
92: 76(ptr) AccessChain 73(bits) 28
93: 13(int) Load 92
94: 13(int) Load 78(i)
95: 13(int) ShiftLeftLogical 93 94
96: 13(int) BitwiseAnd 95 28
97: 13(int) IAdd 91 96
98: 13(int) Load 77(numBits)
99: 13(int) IAdd 98 97
Store 77(numBits) 99
Branch 82
82: Label
100: 13(int) Load 78(i)
101: 13(int) IAdd 100 31
Store 78(i) 101
Branch 79
81: Label
102: 13(int) Load 77(numBits)
103: 13(int) BitwiseAnd 102 28
104: 6(float) ConvertUToF 103
106: 6(float) FMul 104 105
107: 7(fvec2) Load 9(pos)
108: 7(fvec2) CompositeConstruct 106 106
109: 7(fvec2) FAdd 107 108
Store 9(pos) 109
113: 7(fvec2) Load 9(pos)
115: 6(float) CompositeExtract 113 0
116: 6(float) CompositeExtract 113 1
117: 60(fvec4) CompositeConstruct 115 116 114 66
118: 61(ptr) AccessChain 112 23
Store 118 117
Return
FunctionEnd

102
Test/precise.tesc Normal file
View File

@ -0,0 +1,102 @@
#version 450
#extension GL_EXT_tessellation_shader : require
#extension GL_EXT_gpu_shader5 : require
float minimal() {
precise float result = 5.0;
float a = 10.0;
float b = 20.0;
float c = 30.0;
float d = 40.0;
result = a * b + c * d; // c * d, a * b and rvalue1 + rvalue2 should be 'noContraction'.
return result;
}
void continuous_assignment() {
precise float result = 5.0;
int a = 10;
int b = 20;
result = a = b + 4; // b + 4 should be 'noContraction'.
}
void convert() {
precise float result;
int a = 10;
int b = 20;
b = a + b; // a + b should be 'noContraction'.
result = float(b); // convert operation should not be 'noContraction'.
}
float loop_for() {
precise float r1 = 5.0;
precise float r2 = 10.0;
int a = 10;
int b = 20;
int c = 30;
for (int i = 0; i < a; i++) {
r1 += 3.12 + b + i; // 'noContration', this make i++ also 'noContraction'
c += 1; // 'noContration'
}
a += 1; // a + 1 should not be 'noContraction'.
r2 = c; // The calculation of c should be 'noContration'.
return float(r1 + r2); // conversion should not be 'noContration'.
}
void loop_array(void) {
precise int result = 5;
int x = 22;
int y = 33;
int a0[3];
result += x + y; // x + y should be 'noContraction' also result + rvalue.
for (int i = 0; i < 3; ++i) {
// a's dereference + 2 should be 'noContraction'.
result += a0[i] + 2;
// result + 1 and 3 - rvalue should be 'noContraction'.
a0[i] = 3 - result++;
}
}
void loop_while() {
precise float result = 5.0;
int a = 10;
int b = 20;
while (result < 10) {
result += 3.12 + b; // result + 3.12 should be 'noContraction'.
}
result = a + b + 5; // b + 5 should be 'noCtraction' and also a + rvalue.
result = 11.1;
}
float fma_not_decorated() {
precise float result;
float a = 1.0;
float b = 2.0;
float c = 3.0;
b = b + c; // b + c should be decorated with 'noContraction'
result = fma(a, b, c); // fma() should not be decorated with 'noContradtion'
return result;
}
precise float precise_return_exp_func() {
float a = 1.0;
float b = 2.0;
return a + b; // the ADD operation should be 'noContraction'
}
precise float precise_return_val_func() {
float a = 1.0;
float b = 2.0;
float result = a + b; // the ADD operation should be 'noContraction'
return result;
}
float precise_func_parameter(float b, precise out float c) {
float a = 0.5;
c = a + b; // noContration
return a - b; // Not noContraction
}
void main(){}

View File

@ -0,0 +1,89 @@
#version 450
struct T {
float f1;
float f2;
};
out B1 {precise T s; float x;} partial_precise_block;
precise out B2 {T s; float x;} all_precise_block;
float struct_member() {
float a = 1.0;
float b = 2.0;
float c = 3.0;
float d = 4.0;
precise float result;
T S, S2, S3;
S2.f1 = a + 0.2; // NoContraction
S2.f2 = b + 0.2; // NOT NoContraction
S3.f1 = a + b; // NOT NoContraction
S = S2; // "precise" propagated through parent object nodes
result = S.f1 + 0.1; // the ADD operation should be NoContraction
return result;
}
float complex_array_struct() {
precise float result;
struct T1 {
float t1_array[3];
float t1_scalar;
};
struct T2 {
T1 t1a[5];
T1 t1b[6];
T1 t1c[7];
};
struct T3 {float f; T2 t2; vec4 v; int p;};
T3 t3[10];
for(int i=0; i<10; i++) {
t3[i].f = i / 3.0; // Not NoContraction
t3[i].v = vec4(i * 1.5); // NoContraction
t3[i].p = i + 1;
for(int j=0; j<5; j++) {
for(int k = 0; k<3; k++) {
t3[i].t2.t1a[j].t1_array[k] = i * j + k; // Not NoContraction
}
t3[i].t2.t1a[j].t1_scalar = j * 2.0 / i; // Not NoContration
}
for(int j=0; j<6; j++) {
for(int k = 0; k<3; k++) {
t3[i].t2.t1b[j].t1_array[k] = i * j + k; // Not NoContraction
}
t3[i].t2.t1b[j].t1_scalar = j * 2.0 / i; // NoContraction
}
for(int j=0; j<6; j++) {
for(int k = 0; k<3; k++) {
t3[i].t2.t1c[j].t1_array[k] = i * j + k; // NoContraction
}
t3[i].t2.t1c[j].t1_scalar = j * 2.0 / i; // Not NoContraction
}
}
int i = 2;
result = t3[5].t2.t1c[6].t1_array[1]
+ t3[2].t2.t1b[1].t1_scalar
+ t3[i - 1].v.xy.x; // NoContraction
return result;
}
float out_block() {
float a = 0.1;
float b = 0.2;
partial_precise_block.s.f1 = a + b; // NoContraction
partial_precise_block.s.f2 = a - b; // NoContraction
partial_precise_block.x = a * b; // Not NoContraction
all_precise_block.s.f1 = a + b + 1.0; // NoContraction
all_precise_block.s.f2 = a - b - 1.0; // NoContraction
all_precise_block.x = a * b * 2.0; // Also NoContraction
return a + b; // Not NoContraction
}
void main(){}

24
Test/spv.precise.tesc Normal file
View File

@ -0,0 +1,24 @@
#version 310 es
#extension GL_EXT_tessellation_shader : require
#extension GL_EXT_gpu_shader5 : require
layout(vertices = 3) out;
layout(location = 0) in highp vec2 in_tc_position[];
layout(location = 1) in highp float in_tc_tessParam[];
layout(location = 0) out highp vec2 in_te_position[];
precise gl_TessLevelOuter;
void main (void)
{
in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];
gl_TessLevelInner[0] = 5.0;
gl_TessLevelInner[1] = 5.0;
gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);
gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);
gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);
}

36
Test/spv.precise.tese Normal file
View File

@ -0,0 +1,36 @@
#version 310 es
#extension GL_EXT_tessellation_shader : require
#extension GL_EXT_gpu_shader5 : require
layout(triangles, equal_spacing) in;
layout(location = 0) in highp vec2 in_te_position[];
layout(location = 0) out mediump vec4 in_f_color;
precise gl_Position;
void main(void) {
highp vec2 pos = gl_TessCoord.x * in_te_position[0] +
gl_TessCoord.y * in_te_position[1] +
gl_TessCoord.z * in_te_position[2];
highp float f =
sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) *
0.5 +
0.5;
in_f_color = vec4(gl_TessCoord * f, 1.0);
// Offset the position slightly, based on the parity of the bits in the float
// representation.
// This is done to detect possible small differences in edge vertex positions
// between patches.
uvec2 bits = floatBitsToUint(pos);
uint numBits = 0u;
for (uint i = 0u; i < 32u; i++)
numBits +=
((bits[0] << i) & 1u) + ((bits[1] << i) & 1u);
pos += float(numBits & 1u) * 0.04;
gl_Position = vec4(pos, 0.0, 1.0);
}

View File

@ -107,6 +107,8 @@ spv.specConstant.vert
spv.specConstant.comp
spv.specConstantComposite.vert
spv.specConstantOperations.vert
spv.precise.tese
spv.precise.tesc
# GLSL-level semantics
vulkan.frag
vulkan.vert

View File

@ -129,3 +129,5 @@ voidFunction.frag
whileLoop.frag
nonVulkan.frag
spv.atomic.comp
precise.tesc
precise_struct_block.vert

View File

@ -33,6 +33,7 @@ set(SOURCES
MachineIndependent/preprocessor/PpScanner.cpp
MachineIndependent/preprocessor/PpSymbols.cpp
MachineIndependent/preprocessor/PpTokens.cpp
MachineIndependent/propagateNoContraction.cpp
GenericCodeGen/CodeGen.cpp
GenericCodeGen/Link.cpp)
@ -62,6 +63,7 @@ set(HEADERS
MachineIndependent/SymbolTable.h
MachineIndependent/Versions.h
MachineIndependent/parseVersions.h
MachineIndependent/propagateNoContraction.h
MachineIndependent/preprocessor/PpContext.h
MachineIndependent/preprocessor/PpTokens.h)

View File

@ -42,6 +42,7 @@
#include "localintermediate.h"
#include "RemoveTree.h"
#include "SymbolTable.h"
#include "propagateNoContraction.h"
#include <float.h>
@ -1066,6 +1067,9 @@ bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/)
if (aggRoot && aggRoot->getOp() == EOpNull)
aggRoot->setOperator(EOpSequence);
// Propagate 'noContraction' label in backward from 'precise' variables.
glslang::PropagateNoContraction(*this);
return true;
}

View File

@ -0,0 +1,886 @@
//
// Copyright (C) 2015-2016 Google, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Visit the nodes in the glslang intermediate tree representation to
// propagate 'noContraction' qualifier.
//
#include "propagateNoContraction.h"
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include "localintermediate.h"
namespace {
// Use string to hold the accesschain information, as in most cases we the
// accesschain is short and may contain only one element, which is the symbol ID.
using ObjectAccessChain = std::string;
#ifndef StructAccessChainDelimiter
#define StructAccessChainDelimiter '/'
#endif
// Mapping from Symbol IDs of symbol nodes, to their defining operation
// nodes.
using NodeMapping = std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator *>;
// Mapping from object nodes to their accesschain info string.
using AccessChainMapping = std::unordered_map<glslang::TIntermTyped *, ObjectAccessChain>;
// Set of object IDs.
using ObjectAccesschainSet = std::unordered_set<ObjectAccessChain>;
// Set of return branch nodes.
using ReturnBranchNodeSet = std::unordered_set<glslang::TIntermBranch*>;
// A helper function to tell whether a node is 'noContraction'. Returns true if
// the node has 'noContraction' qualifier, otherwise false.
bool isPreciseObjectNode(glslang::TIntermTyped *node)
{
return node->getType().getQualifier().noContraction;
}
// Returns true if the opcode is a dereferencing one.
bool isDereferenceOperation(glslang::TOperator op)
{
switch (op) {
case glslang::EOpIndexDirect:
case glslang::EOpIndexDirectStruct:
case glslang::EOpIndexIndirect:
case glslang::EOpVectorSwizzle:
return true;
default:
return false;
}
}
// Returns true if the opcode leads to an assignment operation.
bool isAssignOperation(glslang::TOperator op)
{
switch (op) {
case glslang::EOpAssign:
case glslang::EOpAddAssign:
case glslang::EOpSubAssign:
case glslang::EOpMulAssign:
case glslang::EOpVectorTimesMatrixAssign:
case glslang::EOpVectorTimesScalarAssign:
case glslang::EOpMatrixTimesScalarAssign:
case glslang::EOpMatrixTimesMatrixAssign:
case glslang::EOpDivAssign:
case glslang::EOpModAssign:
case glslang::EOpAndAssign:
case glslang::EOpLeftShiftAssign:
case glslang::EOpRightShiftAssign:
case glslang::EOpInclusiveOrAssign:
case glslang::EOpExclusiveOrAssign:
case glslang::EOpPostIncrement:
case glslang::EOpPostDecrement:
case glslang::EOpPreIncrement:
case glslang::EOpPreDecrement:
return true;
default:
return false;
}
}
// A helper function to get the unsigned int from a given constant union node.
// Note the node should only holds a uint scalar.
unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped *node)
{
assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar());
unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst();
return struct_dereference_index;
}
// A helper function to generate symbol_label.
ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol *node)
{
ObjectAccessChain symbol_id = std::to_string(node->getId()) + "(" + node->getName().c_str() + ")";
return symbol_id;
}
// Return true if the operation is an arithmetic operation and valid for
// 'NoContraction' decoration.
bool isArithmeticOperation(glslang::TOperator op)
{
switch (op) {
case glslang::EOpAddAssign:
case glslang::EOpSubAssign:
case glslang::EOpMulAssign:
case glslang::EOpVectorTimesMatrixAssign:
case glslang::EOpVectorTimesScalarAssign:
case glslang::EOpMatrixTimesScalarAssign:
case glslang::EOpMatrixTimesMatrixAssign:
case glslang::EOpDivAssign:
case glslang::EOpModAssign:
case glslang::EOpNegative:
case glslang::EOpAdd:
case glslang::EOpSub:
case glslang::EOpMul:
case glslang::EOpDiv:
case glslang::EOpMod:
case glslang::EOpVectorTimesScalar:
case glslang::EOpVectorTimesMatrix:
case glslang::EOpMatrixTimesVector:
case glslang::EOpMatrixTimesScalar:
case glslang::EOpDot:
case glslang::EOpAddCarry:
case glslang::EOpSubBorrow:
case glslang::EOpUMulExtended:
case glslang::EOpIMulExtended:
case glslang::EOpPostIncrement:
case glslang::EOpPostDecrement:
case glslang::EOpPreIncrement:
case glslang::EOpPreDecrement:
return true;
default:
return false;
}
}
// A helper class to help managing populating_initial_no_contraction_ flag.
template <typename T> class StateSettingGuard {
public:
StateSettingGuard(T *state_ptr, T new_state_value)
: state_ptr_(state_ptr), previous_state_(*state_ptr)
{
*state_ptr = new_state_value;
}
StateSettingGuard(T *state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {}
void setState(T new_state_value)
{
*state_ptr_ = new_state_value;
}
~StateSettingGuard() { *state_ptr_ = previous_state_; }
private:
T *state_ptr_;
T previous_state_;
};
// A helper function to get the front element from a given ObjectAccessChain
ObjectAccessChain getFrontElement(const ObjectAccessChain &chain)
{
size_t pos_delimiter = chain.find(StructAccessChainDelimiter);
return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter);
}
// A helper function to get the accesschain starting from the second element.
ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain)
{
size_t pos_delimiter = chain.find(StructAccessChainDelimiter);
return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1);
}
//
// A traverser which traverses the whole AST and populates:
// 1) A mapping from symbol nodes' IDs to their defining operation nodes.
// 2) A set of accesschains of the initial precise object nodes.
//
class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser {
public:
TSymbolDefinitionCollectingTraverser(
NodeMapping *symbol_definition_mapping, AccessChainMapping *accesschain_mapping,
ObjectAccesschainSet *precise_objects,
ReturnBranchNodeSet *precise_return_nodes);
// bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate *) override;
bool visitUnary(glslang::TVisit, glslang::TIntermUnary *) override;
bool visitBinary(glslang::TVisit, glslang::TIntermBinary *) override;
void visitSymbol(glslang::TIntermSymbol *) override;
bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate *) override;
bool visitBranch(glslang::TVisit, glslang::TIntermBranch *) override;
protected:
// The mapping from symbol node IDs to their defining nodes. This should be
// populated along traversing the AST.
NodeMapping &symbol_definition_mapping_;
// The set of symbol node IDs for precise symbol nodes, the ones marked as
// 'noContraction'.
ObjectAccesschainSet &precise_objects_;
// The set of precise return nodes.
ReturnBranchNodeSet &precise_return_nodes_;
// A temporary cache of the symbol node whose defining node is to be found
// currently along traversing the AST.
ObjectAccessChain object_to_be_defined_;
// A map from object node to its accesschain. This traverser stores
// the built accesschains into this map for each object node it has
// visited.
AccessChainMapping &accesschain_mapping_;
// The pointer to the Function Definition node, so we can get the
// precise'ness of the return expression from it when we traverse the
// return branch node.
glslang::TIntermAggregate* current_function_definition_node_;
};
TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser(
NodeMapping *symbol_definition_mapping, AccessChainMapping *accesschain_mapping,
ObjectAccesschainSet *precise_objects,
std::unordered_set<glslang::TIntermBranch *> *precise_return_nodes)
: TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping),
precise_objects_(*precise_objects), object_to_be_defined_(),
accesschain_mapping_(*accesschain_mapping), current_function_definition_node_(nullptr),
precise_return_nodes_(*precise_return_nodes) {}
// Visits a symbol node, set the object_to_be_defined_ to the
// current node symbol ID, and record a mapping from this node to the current
// object_to_be_defined_, which is the just obtained symbol
// ID.
void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol *node)
{
object_to_be_defined_ = generateSymbolLabel(node);
accesschain_mapping_[node] = object_to_be_defined_;
}
// Visits an aggregate node, traverses all of its children.
bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit,
glslang::TIntermAggregate *node)
{
// This aggreagate node might be a function definition node, in which case we need to
// cache this node, so we can get the precise'ness information of the return value
// of this function later.
StateSettingGuard<glslang::TIntermAggregate *> current_function_definition_node_setting_guard(
&current_function_definition_node_);
if (node->getOp() == glslang::EOpFunction) {
// This is function definition node, we need to cache this node so that we can
// get the precise'ness of the return value later.
current_function_definition_node_setting_guard.setState(node);
}
// Traverse the items in the sequence.
glslang::TIntermSequence &seq = node->getSequence();
for (int i = 0; i < (int)seq.size(); ++i) {
object_to_be_defined_.clear();
seq[i]->traverse(this);
}
return false;
}
bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit,
glslang::TIntermBranch *node)
{
if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() &&
current_function_definition_node_ &&
current_function_definition_node_->getType().getQualifier().noContraction) {
// This node is a return node with expression, and its function has
// precise return value. We need to find the involved objects in its
// expression and add them to the set of initial precise objects.
precise_return_nodes_.insert(node);
node->getExpression()->traverse(this);
}
return false;
}
// Visits an unary node. This might be an implicit assignment like i++, i--. etc.
bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */,
glslang::TIntermUnary *node)
{
object_to_be_defined_.clear();
node->getOperand()->traverse(this);
if (isAssignOperation(node->getOp())) {
// We should always be able to get an accesschain of the operand node.
// But we have some tests in which it is intented to have invalid operand
// nodes, so just return for now.
if (object_to_be_defined_.empty()) return false;
// If the operand node object is 'precise', we collect its accesschain
// for the initial set of 'precise' objects.
if (isPreciseObjectNode(node->getOperand())) {
// The operand node is an 'precise' object node, add its
// accesschain to the set of 'precise' objects. This is to collect
// the initial set of 'precise' objects.
precise_objects_.insert(object_to_be_defined_);
}
// Gets the symbol ID from the object's accesschain.
ObjectAccessChain id_symbol = getFrontElement(object_to_be_defined_);
// Add a mapping from the symbol ID to this assignment operation node.
symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
}
// Unary node is not a dereference node, so we clear the accesschain which
// is under construction.
object_to_be_defined_.clear();
return false;
}
// Visits a binary node and updates the mapping from symbol IDs to the definition
// nodes. Also collects the accesschains for the initial precise objects.
bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */,
glslang::TIntermBinary *node)
{
// Traverses the left node to build the accesschain info for the object.
object_to_be_defined_.clear();
node->getLeft()->traverse(this);
if (isAssignOperation(node->getOp())) {
// We should always be able to get an accesschain for the left node.
// But we have some tests in which it is intented to have invalid left
// nodes, so just return false in such cases for now.
if (object_to_be_defined_.empty()) return false;
// If the left node object is 'precise', it is an initial precise object
// specified in the shader source. Adds it to the initial worklist to
// process later.
if (isPreciseObjectNode(node->getLeft())) {
// The left node is an 'precise' object node, add its accesschain to
// the set of 'precise' objects. This is to collect the initial set
// of 'precise' objects.
precise_objects_.insert(object_to_be_defined_);
}
// Gets the symbol ID from the object accesschain, which should be the
// first element recorded in the accesschain.
ObjectAccessChain id_symbol = getFrontElement(object_to_be_defined_);
// Adds a mapping from the symbol ID to this assignment operation node.
symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
// Traverses the right node, there may be other 'assignment'
// operatrions in the right.
object_to_be_defined_.clear();
node->getRight()->traverse(this);
return false;
} else if (isDereferenceOperation(node->getOp())) {
// If the left node is 'precise' object node, this node should also
// be 'precise' object node, and all the members of this node too. There
// is no need to append accesschain information into the object id.
if (isPreciseObjectNode(node->getLeft())) {
node->getWritableType().getQualifier().noContraction = true;
accesschain_mapping_[node] = object_to_be_defined_;
return false;
}
// If the opcode is not EOpIndexDirectStruct, the left node is not be a
// struct type object, hence there is no need to append dereference
// indices. For other composite type objects, the precise'ness of
// members should always matches with the 'precise'ness of the
// composite type object.
if (node->getOp() != glslang::EOpIndexDirectStruct) {
accesschain_mapping_[node] = object_to_be_defined_;
return false;
}
// The left node (parent node) is not 'precise' and it is a struct type
// object. We need to record the accesschain information of the current
// node into its object id.
unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight());
object_to_be_defined_.push_back(StructAccessChainDelimiter);
object_to_be_defined_.append(std::to_string(struct_dereference_index));
accesschain_mapping_[node] = object_to_be_defined_;
// For dereference node, there is no need to traverse the right child
// node as the right node should always be an integer type object.
return false;
} else {
// For other binary nodes, still traverse the right node.
object_to_be_defined_.clear();
node->getRight()->traverse(this);
return false;
}
}
// Traverses the AST and returns a tuple of three members:
// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols.
// 2) a mapping from object nodes in the AST to the accesschains of these objects.
// 3) a set of accesschains of precise objects.
std::tuple<NodeMapping, AccessChainMapping, ObjectAccesschainSet, ReturnBranchNodeSet>
getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate &intermediate)
{
auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(), ReturnBranchNodeSet());
TIntermNode *root = intermediate.getTreeRoot();
if (root == 0) return result_tuple;
NodeMapping &symbol_definition_mapping = std::get<0>(result_tuple);
AccessChainMapping &accesschain_mapping = std::get<1>(result_tuple);
ObjectAccesschainSet &precise_objects = std::get<2>(result_tuple);
ReturnBranchNodeSet &precise_return_nodes = std::get<3>(result_tuple);
// Traverses the AST and populate the results.
TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping,
&precise_objects, &precise_return_nodes);
root->traverse(&collector);
return result_tuple;
}
//
// A traverser that determine whether the left node (or operand node for unary
// node) of an assignment node is 'precise', containing 'precise' or not,
// according to the accesschain a given precise object which share the same
// symbol as the left node.
//
// Post-orderly traverses the left node subtree of an binary assignment node and:
//
// 1) Propagates the 'precise' from the left object nodes to this object node.
//
// 2) Builds object accesschain along the traversal, and also compares with
// the accesschain of the given 'precise' object along with the traversal to
// tell if the node to be defined is 'precise' or not.
//
class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser {
enum DecisionStatus {
// The object node to be assigned to may contain 'precise' objects and also not 'precise' objects.
Mixed = 0,
// The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'.
Precise = 1,
// The object node to be assigned to is not a 'precise' object.
NotPreicse = 2,
};
public:
TNoContractionAssigneeCheckingTraverser()
: TIntermTraverser(true, false, false), accesschain_to_precise_object_(), decision_(Mixed) {}
// Checks the precise'ness of a given assignment node with a precise object
// represented as accesschain. The precise object shares the same symbol
// with the assignee of the given assignment node. Return a tuple of two:
//
// 1) The precise'ness of the assignee node of this assignment node. True
// if the assignee contains 'precise' objects or is 'precise', false if
// the assignee is not 'precise' according to the accesschain of the given
// precise object.
//
// 2) The incremental accesschain from the assignee node to its nested
// 'precise' object, according to the accesschain of the given precise
// object. This incremental accesschain can be empty, which means the
// assignee is 'precise'. Otherwise it shows the path to the nested
// precise object.
std::tuple<bool, ObjectAccessChain>
getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node,
const ObjectAccessChain &precise_object)
{
assert(isAssignOperation(node->getOp()));
accesschain_to_precise_object_ = precise_object;
decision_ = Mixed;
node->traverse(this);
return make_tuple(decision_ != NotPreicse, accesschain_to_precise_object_);
}
protected:
bool visitBinary(glslang::TVisit, glslang::TIntermBinary *node) override;
bool visitUnary(glslang::TVisit, glslang::TIntermUnary *node) override;
void visitSymbol(glslang::TIntermSymbol *node) override;
// The accesschain toward the given precise object. It will be iniailized
// with the accesschain of a given precise object, then trimmed along the
// traversal of the assignee subtree. The remained accesschain at the end
// of traversal shows the path from the assignee node to its nested
// 'precise' object. If the assignee node is 'precise' object object, this
// should be empty.
ObjectAccessChain accesschain_to_precise_object_;
// A state to tell the precise'ness of the assignee node according to the
// accesschain of the given precise object:
//
// 'Mixed': contains both 'precise' and 'non-precise' object
// (accesschain_to_precise_object_ is not empty),
//
// 'Precise': is precise object (accesschain_to_precise_object is empty),
//
// 'NotPrecise': is not precise object (mismatch in the struct dereference
// indices).
DecisionStatus decision_;
};
// Visit a binary node. As this traverser's job is to check the precise'ness of
// the assignee node in an assignment operation, it only needs to traverse the
// object nodes along the left branches. For struct type object nodes, it needs
// to obtain the struct dereference index from the right node to build the
// accesschain for this node.
bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit,
glslang::TIntermBinary *node)
{
node->getLeft()->traverse(this);
// For dereference operation nodes, we may need to check if the accesschain
// of the given precise object matches with the struct dereference indices
// of the assignee subtree.
if (isDereferenceOperation(node->getOp())) {
if (isPreciseObjectNode(node->getLeft())) {
// The left node is 'precise', which means the object node in the
// left contains the object represented in this node. If the left node
// is 'precise', this object node should also be 'precise' and no need
// to check the accesschain and struct deference indices anymore.
node->getWritableType().getQualifier().noContraction = true;
decision_ = Precise;
return false;
}
if (node->getOp() == glslang::EOpIndexDirectStruct && decision_ == Mixed) {
std::string struct_index =
std::to_string(getStructIndexFromConstantUnion(node->getRight()));
ObjectAccessChain precise_struct_index = getFrontElement(accesschain_to_precise_object_);
if (precise_struct_index == struct_index) {
// The struct dereference index matches with the record in the
// accesschain to the precise object. Pop the front access
// chain index from the precise object access chain.
accesschain_to_precise_object_ =
subAccessChainFromSecondElement(accesschain_to_precise_object_);
// If the given access chain to precise object is empty now,
// it means we've found the corresponding precise object in
// the assignee subtree.
if (accesschain_to_precise_object_.empty()) {
node->getWritableType().getQualifier().noContraction = true;
decision_ = Precise;
}
} else {
// The access chain index does not match with the record in the precise object id.
// This object should not be labelled as 'precise' here.
decision_ = NotPreicse;
}
}
}
return false;
}
// Visits an unary node, traverses its operand. If the node is an assignment node,
// determines the precise'ness of the assignee directly based on the assignee node's
// precise'ness.
bool TNoContractionAssigneeCheckingTraverser::visitUnary(glslang::TVisit,
glslang::TIntermUnary *node)
{
node->getOperand()->traverse(this);
if (isAssignOperation(node->getOp())) {
if (isPreciseObjectNode(node->getOperand())) {
decision_ = Precise;
// As the assignee node is 'precise', all (if any) the
// member objects the that node should also be 'precise'. This means
// we won't need to propagate extra access chain info.
accesschain_to_precise_object_.clear();
} else {
decision_ = NotPreicse;
}
}
return false;
}
// Visits a symbol node. The symbol ID of this node should match with the symbol ID, which is
// the front element, in the accesschain of the given 'precise' object.
void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol *node)
{
ObjectAccessChain symbol_id = generateSymbolLabel(node);
// The root symbol of the given access chain should be the same with the one represented by the symbol node here.
assert(symbol_id == getFrontElement(accesschain_to_precise_object_));
// Pop the symbol node part from the front end of the accesschain string.
accesschain_to_precise_object_ =
subAccessChainFromSecondElement(accesschain_to_precise_object_);
if (accesschain_to_precise_object_.empty()) {
node->getWritableType().getQualifier().noContraction = true;
decision_ = Precise;
}
// If this symbol node is 'precise', all its members should be 'precise' so the assignee of the processing
// assignment operations is 'precise'.
if (isPreciseObjectNode(node)) {
decision_ = Precise;
}
}
//
// A traverser that only traverses the right side of binary assignment nodes
// and the operand node of unary assignment nodes.
//
// 1) Marks arithmetic operations 'NoContraction'.
//
// 2) Find the object which should be marked as 'precise' in the right and
// update the 'precise' object worklist.
//
class TNoContractionPropagator : public glslang::TIntermTraverser {
public:
TNoContractionPropagator(ObjectAccesschainSet *precise_objects,
const AccessChainMapping &accesschain_mapping)
: TIntermTraverser(true, false, false), remained_accesschain_(),
precise_objects_(*precise_objects),
accesschain_mapping_(accesschain_mapping), added_precise_object_ids_() {}
// Propagates 'precise' in the right nodes of a given assignment node with
// accesschain record from the assignee node to a 'precise' object it
// contains.
void
propagateNoContractionInOneExpression(glslang::TIntermTyped *defining_node,
const ObjectAccessChain &assignee_remained_accesschain)
{
remained_accesschain_ = assignee_remained_accesschain;
if (glslang::TIntermBinary *BN = defining_node->getAsBinaryNode()) {
assert(isAssignOperation(BN->getOp()));
BN->getRight()->traverse(this);
if (isArithmeticOperation(BN->getOp())) {
BN->getWritableType().getQualifier().noContraction = true;
}
} else if (glslang::TIntermUnary *UN = defining_node->getAsUnaryNode()) {
assert(isAssignOperation(UN->getOp()));
UN->getOperand()->traverse(this);
if (isArithmeticOperation(UN->getOp())) {
UN->getWritableType().getQualifier().noContraction = true;
}
}
}
// Propagates 'precise' in a given precise return node.
void
propagateNoContractionInReturnNode(glslang::TIntermBranch *return_node)
{
remained_accesschain_ = "";
assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression());
return_node->getExpression()->traverse(this);
}
protected:
// Visit an aggregate node. The node can be a initializer list, in which
// case we need to find the 'precise' or 'precise' containing object node
// with the accesschain record. In other cases, just need to traverse all
// the children nodes.
bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate *node) override
{
if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) {
// This is a struct initializer node, and the remained
// accesschain is not empty, we need to refer to the
// assignee_remained_access_chain_ to find the nested
// 'precise' object. And we don't need to visit other nodes in this
// aggreagate node.
// Gets the struct dereference index that leads to 'precise' object.
ObjectAccessChain precise_accesschain_index_str =
getFrontElement(remained_accesschain_);
unsigned precise_accesschain_index = std::stoul(precise_accesschain_index_str);
// Gets the node pointed by the accesschain index extracted before.
glslang::TIntermTyped *potential_precise_node =
node->getSequence()[precise_accesschain_index]->getAsTyped();
assert(potential_precise_node);
// Pop the front accesschain index from the path, and visit the nested node.
{
ObjectAccessChain next_level_accesschain =
subAccessChainFromSecondElement(remained_accesschain_);
StateSettingGuard<ObjectAccessChain> setup_remained_accesschain_for_next_level(
&remained_accesschain_, next_level_accesschain);
potential_precise_node->traverse(this);
}
} else {
// If this is not a struct constructor, just visit each nested node.
glslang::TIntermSequence &seq = node->getSequence();
for (int i = 0; i < (int)seq.size(); ++i) {
seq[i]->traverse(this);
}
}
return false;
}
// Visit a binary node. A binary node can be an object node, e.g. a dereference node.
// As only the top object nodes in the right side of an assignment needs to be visited
// and added to 'precise' worklist, this traverser won't visit the children nodes of
// an object node. If the binary node does not represent an object node, it should
// go on to traverse its children nodes and if it is an arithmetic operation node, this
// operation should be marked as 'noContraction'.
bool visitBinary(glslang::TVisit, glslang::TIntermBinary *node) override
{
if (isDereferenceOperation(node->getOp())) {
// This binary node is an object node. Need to update the precise
// object set with the accesschain of this node + remained
// accesschain .
ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
if (remained_accesschain_.empty()) {
node->getWritableType().getQualifier().noContraction = true;
} else {
new_precise_accesschain +=
StructAccessChainDelimiter + remained_accesschain_;
}
// Cache the accesschain as added precise object, so we won't add the
// same object to the worklist again.
if (!added_precise_object_ids_.count(new_precise_accesschain)) {
precise_objects_.insert(new_precise_accesschain);
added_precise_object_ids_.insert(new_precise_accesschain);
}
// Only the upper-most object nodes should be visited, so do not
// visit children of this object node.
return false;
}
// If this is an arithmetic operation, marks this node as 'noContraction'.
if (isArithmeticOperation(node->getOp())) {
node->getWritableType().getQualifier().noContraction = true;
}
// As this node is not an object node, need to traverse the children nodes.
node->getLeft()->traverse(this);
node->getRight()->traverse(this);
return false;
}
// Visits an unary node. An unary node can not be an object node. If the operation
// is an arithmetic operation, need to mark this node as 'noContraction'.
bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary *node) override
{
// If this is an arithmetic operation, marks this with 'noContraction'
if (isArithmeticOperation(node->getOp())) {
node->getWritableType().getQualifier().noContraction = true;
}
node->getOperand()->traverse(this);
return false;
}
// Visits a symbol node. A symbol node is always an object node. So we
// should always be able to find its in our colected mapping from object
// nodes to accesschains. As an object node, a symbol node can be either
// 'precise' or containing 'precise' objects according to unused
// accesschain information we have when we visit this node.
void visitSymbol(glslang::TIntermSymbol *node) override
{
// Symbol nodes are object nodes and should always have an
// accesschain collected before matches with it.
assert(accesschain_mapping_.count(node));
ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
// If the unused accesschain is empty, this symbol node should be
// marked as 'precise'. Otherwise, the unused accesschain should be
// appended to the symbol ID to build a new accesschain which points to
// the nested 'precise' object in this symbol object.
if (remained_accesschain_.empty()) {
node->getWritableType().getQualifier().noContraction = true;
} else {
new_precise_accesschain += StructAccessChainDelimiter + remained_accesschain_;
}
// Add the new 'precise' accesschain to the worklist and make sure we
// don't visit it again.
if (!added_precise_object_ids_.count(new_precise_accesschain)) {
precise_objects_.insert(new_precise_accesschain);
added_precise_object_ids_.insert(new_precise_accesschain);
}
}
// A set of precise objects, represented as accesschains.
ObjectAccesschainSet &precise_objects_;
// Visited symbol nodes, should not revisit these nodes.
ObjectAccesschainSet added_precise_object_ids_;
// The left node of an assignment operation might be an parent of 'precise' objects.
// This means the left node might not be an 'precise' object node, but it may contains
// 'precise' qualifier which should be propagated to the corresponding child node in
// the right. So we need the path from the left node to its nested 'precise' node to
// tell us how to find the corresponding 'precise' node in the right.
ObjectAccessChain remained_accesschain_;
// A map from node pointers to their accesschains.
const AccessChainMapping &accesschain_mapping_;
};
#undef StructAccessChainDelimiter
}
namespace glslang {
void PropagateNoContraction(const glslang::TIntermediate &intermediate)
{
// First, traverses the AST, records symbols with their defining operations
// and collects the initial set of precise symbols (symbol nodes that marked
// as 'noContraction').
auto mappings_and_precise_objects =
getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate);
// The mapping of symbol node IDs to their defining nodes. This enables us
// to get the defining node directly from a given symbol ID without
// traversing the tree again.
NodeMapping &symbol_definition_mapping = std::get<0>(mappings_and_precise_objects);
// The mapping of object nodes to their accesschains recorded.
AccessChainMapping &accesschain_mapping = std::get<1>(mappings_and_precise_objects);
// The initial set of 'precise' objects which are represented as the
// accesschain toward them.
ObjectAccesschainSet &precise_object_accesschains =
std::get<2>(mappings_and_precise_objects);
// The set of 'precise' return nodes.
ReturnBranchNodeSet &precise_return_nodes = std::get<3>(mappings_and_precise_objects);
// Second, uses the initial set of precise objects as a worklist, pops an
// accesschain, extract the symbol ID from it. Then:
// 1) Check the assignee object, see if it is 'precise' object node or
// contains 'precise' object. Obtain the incremental accesschain from the
// assignee node to its nested 'precise' node (if any).
// 2) If the assignee object node is 'precise' or it contains 'precise'
// objects, traverses the right side of the assignment operation
// expression to mark arithmetic operations as 'noContration' and update
// 'precise' accesschain worklist with new found object nodes.
// Repeat above steps until the worklist is empty.
TNoContractionAssigneeCheckingTraverser checker;
TNoContractionPropagator propagator(&precise_object_accesschains,
accesschain_mapping);
// We have to initial precise worklist to handle:
// 1) precise return nodes
// 2) precise object accesschains
// We should process the precise return nodes first and the involved
// objects in the return expression should be added to the precise object
// accesschain set.
while (!precise_return_nodes.empty()) {
glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin();
propagator.propagateNoContractionInReturnNode(precise_return_node);
precise_return_nodes.erase(precise_return_node);
}
while (!precise_object_accesschains.empty()) {
// Get the accesschain of a precise object from the worklist.
ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin();
// Get the symbol id from the accesschain.
ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain);
// Get all the defining nodes of that symbol ID.
std::pair<NodeMapping::iterator, NodeMapping::iterator> range =
symbol_definition_mapping.equal_range(symbol_id);
// Visit all the assignment nodes of that symbol ID and
// 1) Check if the assignee node is 'precise' or contains 'precise'
// objects.
// 2) Propagate the 'precise' to the top layer object ndoes
// in the right side of the assignment operation, update the 'precise'
// worklist with new accesschains representing the new 'precise'
// objects, and mark arithmetic operations as 'noContraction'.
for (NodeMapping::iterator defining_node_iter = range.first;
defining_node_iter != range.second; defining_node_iter++) {
TIntermOperator *defining_node = defining_node_iter->second;
// Check the assignee node.
auto checker_result = checker.getPrecisenessAndRemainedAccessChain(
defining_node, precise_object_accesschain);
bool &contain_precise = std::get<0>(checker_result);
ObjectAccessChain &remained_accesschain = std::get<1>(checker_result);
// If the assignee node is 'precise' or contains 'precise', propagate the
// 'precise' to the right. Otherwise just skip this assignment node.
if (contain_precise) {
propagator.propagateNoContractionInOneExpression(defining_node,
remained_accesschain);
}
}
// Remove the last processed 'precise' object from the worklist.
precise_object_accesschains.erase(precise_object_accesschain);
}
}
};

View File

@ -0,0 +1,53 @@
//
// Copyright (C) 2015-2016 Google, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Visit the nodes in the glslang intermediate tree representation to
// propagate 'noContraction' qualifier.
//
#include "../Include/intermediate.h"
namespace glslang {
// Propagates the 'precise' qualifier for objects (objects marked with
// 'noContraction' qualifier) from the shader source specified 'precise'
// variables to all the involved objects, and add 'noContraction' qualifier for
// the involved arithmetic operations.
// Note that the same qualifier: 'noContraction' is used in both object nodes
// and arithmetic operation nodes, but has different meaning. For object nodes,
// 'noContraction' means the object is 'precise'; and for arithmetic operation
// nodes, it means the operation should not be contracted.
void PropagateNoContraction(const glslang::TIntermediate& intermediate);
};