From 75694fdacde96490db28ae2f2f273b5432a5bce5 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sun, 16 Mar 2014 23:03:07 +0000 Subject: [PATCH] Implement implicit conversions of function-call arguments (both in and out) as explicit conversions in the AST, through handleArgumentConversions(). Also - uniformly handle EvqConstReadOnly as an input argument in a function, with isParamInput() and isParamOutput() queries in TQualifier. - provide a makeTemporary() in TQualifier, for erasing original qualification when making a temp - provide a makeInternalVariable() call to make a shader variable not seen in the shader source git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@25912 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- Test/120.vert | 24 +- Test/baseResults/120.vert.out | 108 +++++++- Test/baseResults/functionSemantics.frag.out | 242 +++++++++++------- glslang/Include/BaseTypes.h | 3 +- glslang/Include/Types.h | 32 ++- glslang/Include/intermediate.h | 2 +- glslang/MachineIndependent/Intermediate.cpp | 158 ++++++------ glslang/MachineIndependent/ParseHelper.cpp | 163 +++++++++--- glslang/MachineIndependent/ParseHelper.h | 6 +- glslang/MachineIndependent/SymbolTable.h | 17 ++ .../MachineIndependent/localintermediate.h | 10 +- 11 files changed, 530 insertions(+), 235 deletions(-) diff --git a/Test/120.vert b/Test/120.vert index 3981b2c7..8790980d 100644 --- a/Test/120.vert +++ b/Test/120.vert @@ -58,15 +58,15 @@ float overloadA(float); // ERROR, different return value for same sig float overloadA(out float f, int); float overloadA(int i); -vec2 overloadB(float, float); +void overloadB(float, const in float) { } vec2 overloadC(int, int); -vec2 overloadC(int, float); +vec2 overloadC(const in int, float); vec2 overloadC(float, int); vec2 overloadC(vec2, vec2); vec3 overloadD(int, float); -vec3 overloadD(float, int); +vec3 overloadD(float, in int); vec3 overloadE(float[2]); vec3 overloadE(mat2 m); @@ -119,3 +119,21 @@ void foo() } varying vec4 gl_TexCoord[35]; // ERROR, size too big + +// tests for output conversions +void outFun(in float, out ivec2, in int, out float); +int outFunRet(in float, out int, const in int, out ivec4); +ivec2 outFunRet(in float, out ivec4, in int, out ivec4); + +void foo2() +{ + vec2 v2; + vec4 v4; + float f; + int i; + + outFun(i, v2, i, f); + outFunRet(i, f, i, v4); + float ret = outFunRet(i, f, i, v4); + vec2 ret2 = outFunRet(i, v4, i, v4); +} diff --git a/Test/baseResults/120.vert.out b/Test/baseResults/120.vert.out index a2063a0e..df74e19a 100644 --- a/Test/baseResults/120.vert.out +++ b/Test/baseResults/120.vert.out @@ -94,20 +94,25 @@ ERROR: node is still EOpNull! 0:43 'gl_PointSize' (invariant gl_PointSize float) 0:43 Constant: 0:43 3.800000 +0:61 Function Definition: overloadB(f1;f1; (void) +0:61 Function Parameters: +0:61 '' (in float) +0:61 '' (const (read only) float) 0:78 Function Definition: foo( (void) 0:78 Function Parameters: 0:? Sequence -0:83 Function Call: overloadB(f1;f1; (2-component vector of float) +0:83 Function Call: overloadB(f1;f1; (void) 0:83 'f' (float) 0:83 'f' (float) -0:84 Function Call: overloadB(f1;f1; (2-component vector of float) +0:84 Function Call: overloadB(f1;f1; (void) 0:84 'f' (float) 0:84 Constant: -0:84 2 (const int) -0:85 Function Call: overloadB(f1;f1; (2-component vector of float) +0:84 2.000000 +0:85 Function Call: overloadB(f1;f1; (void) 0:85 Constant: -0:85 1 (const int) -0:85 'i' (int) +0:85 1.000000 +0:85 Convert int to float (float) +0:85 'i' (int) 0:87 Constant: 0:87 0.000000 0:88 Function Call: overloadC(i1;i1; (2-component vector of float) @@ -125,8 +130,8 @@ ERROR: node is still EOpNull! 0:90 0.000000 0:91 Function Call: overloadC(vf2;vf2; (2-component vector of float) 0:91 Constant: -0:91 1 (const int) -0:91 1 (const int) +0:91 1.000000 +0:91 1.000000 0:91 Constant: 0:91 2.000000 0:91 2.000000 @@ -137,7 +142,8 @@ ERROR: node is still EOpNull! 0:94 'f' (float) 0:94 'i' (int) 0:95 Function Call: overloadD(f1;i1; (3-component vector of float) -0:95 'i' (int) +0:95 Convert int to float (float) +0:95 'i' (int) 0:95 'i' (int) 0:98 Constant: 0:98 0.000000 @@ -146,8 +152,8 @@ ERROR: node is still EOpNull! 0:101 Function Call: texture2D(s21;vf2; (4-component vector of float) 0:101 's2D' (uniform sampler2D) 0:101 Constant: -0:101 0 (const int) -0:101 0 (const int) +0:101 0.000000 +0:101 0.000000 0:102 clamp (4-component vector of float) 0:102 'attv4' (in 4-component vector of float) 0:102 Constant: @@ -181,8 +187,8 @@ ERROR: node is still EOpNull! 0:111 0.000000 0:112 Function Call: overloadE(vf2; (3-component vector of float) 0:112 Constant: -0:112 1 (const int) -0:112 1 (const int) +0:112 1.000000 +0:112 1.000000 0:115 Function Call: overloadE(f1[2]; (3-component vector of float) 0:115 'b' (2-element array of float) 0:117 Constant: @@ -190,6 +196,82 @@ ERROR: node is still EOpNull! 0:118 Function Call: overloadF(i1; (3-component vector of float) 0:118 Constant: 0:118 1 (const int) +0:128 Function Definition: foo2( (void) +0:128 Function Parameters: +0:? Sequence +0:135 Comma (void) +0:135 Function Call: outFun(f1;vi2;i1;f1; (void) +0:135 Convert int to float (float) +0:135 'i' (int) +0:135 'tempArg' (out 2-component vector of int) +0:135 'i' (int) +0:135 'f' (float) +0:135 move second child to first child (2-component vector of float) +0:135 'v2' (2-component vector of float) +0:135 Convert int to float (2-component vector of float) +0:135 'tempArg' (out 2-component vector of int) +0:136 Comma (int) +0:136 move second child to first child (int) +0:136 'tempReturn' (int) +0:136 Function Call: outFunRet(f1;i1;i1;vi4; (int) +0:136 Convert int to float (float) +0:136 'i' (int) +0:136 'tempArg' (out int) +0:136 'i' (int) +0:136 'tempArg' (out 4-component vector of int) +0:136 move second child to first child (float) +0:136 'f' (float) +0:136 Convert int to float (float) +0:136 'tempArg' (out int) +0:136 move second child to first child (4-component vector of float) +0:136 'v4' (4-component vector of float) +0:136 Convert int to float (4-component vector of float) +0:136 'tempArg' (out 4-component vector of int) +0:136 'tempReturn' (int) +0:137 Sequence +0:137 move second child to first child (float) +0:137 'ret' (float) +0:137 Convert int to float (float) +0:137 Comma (int) +0:137 move second child to first child (int) +0:137 'tempReturn' (int) +0:137 Function Call: outFunRet(f1;i1;i1;vi4; (int) +0:137 Convert int to float (float) +0:137 'i' (int) +0:137 'tempArg' (out int) +0:137 'i' (int) +0:137 'tempArg' (out 4-component vector of int) +0:137 move second child to first child (float) +0:137 'f' (float) +0:137 Convert int to float (float) +0:137 'tempArg' (out int) +0:137 move second child to first child (4-component vector of float) +0:137 'v4' (4-component vector of float) +0:137 Convert int to float (4-component vector of float) +0:137 'tempArg' (out 4-component vector of int) +0:137 'tempReturn' (int) +0:138 Sequence +0:138 move second child to first child (2-component vector of float) +0:138 'ret2' (2-component vector of float) +0:138 Convert int to float (2-component vector of float) +0:138 Comma (2-component vector of int) +0:138 move second child to first child (2-component vector of int) +0:138 'tempReturn' (2-component vector of int) +0:138 Function Call: outFunRet(f1;vi4;i1;vi4; (2-component vector of int) +0:138 Convert int to float (float) +0:138 'i' (int) +0:138 'tempArg' (out 4-component vector of int) +0:138 'i' (int) +0:138 'tempArg' (out 4-component vector of int) +0:138 move second child to first child (4-component vector of float) +0:138 'v4' (4-component vector of float) +0:138 Convert int to float (4-component vector of float) +0:138 'tempArg' (out 4-component vector of int) +0:138 move second child to first child (4-component vector of float) +0:138 'v4' (4-component vector of float) +0:138 Convert int to float (4-component vector of float) +0:138 'tempArg' (out 4-component vector of int) +0:138 'tempReturn' (2-component vector of int) 0:? Linker Objects 0:? 'i' (in 4-component vector of float) 0:? 'o' (smooth out 4-component vector of float) diff --git a/Test/baseResults/functionSemantics.frag.out b/Test/baseResults/functionSemantics.frag.out index f164916b..5ce38cbf 100644 --- a/Test/baseResults/functionSemantics.frag.out +++ b/Test/baseResults/functionSemantics.frag.out @@ -1,122 +1,170 @@ ../../LunarGLASS/test/functionSemantics.frag +Warning, version 400 is not yet complete; some version-specific features are present, but many are missing. 0:? Sequence -0:3 Function Definition: foo(i1;i1;i1;i1;i1;i1; (mediump int) +0:3 Function Definition: foo(i1;i1;i1;i1;i1;i1; (int) 0:3 Function Parameters: -0:3 'a' (in mediump int) -0:3 'b' (const (read only) mediump int) -0:3 'c' (in mediump int) -0:3 'd' (const (read only) mediump int) -0:3 'e' (out mediump int) -0:3 'f' (inout mediump int) +0:3 'a' (in int) +0:3 'b' (const (read only) int) +0:3 'c' (in int) +0:3 'd' (const (read only) int) +0:3 'e' (out int) +0:3 'f' (inout int) 0:5 Sequence 0:5 Sequence -0:5 move second child to first child (mediump int) -0:5 'sum' (mediump int) -0:5 add (mediump int) -0:5 add (mediump int) -0:5 add (mediump int) -0:5 add (mediump int) -0:5 'a' (in mediump int) -0:5 'b' (const (read only) mediump int) -0:5 'c' (in mediump int) -0:5 'd' (const (read only) mediump int) -0:5 'f' (inout mediump int) -0:8 multiply second child into first child (mediump int) -0:8 'a' (in mediump int) +0:5 move second child to first child (int) +0:5 'sum' (int) +0:5 add (int) +0:5 add (int) +0:5 add (int) +0:5 add (int) +0:5 'a' (in int) +0:5 'b' (const (read only) int) +0:5 'c' (in int) +0:5 'd' (const (read only) int) +0:5 'f' (inout int) +0:8 multiply second child into first child (int) +0:8 'a' (in int) 0:8 Constant: 0:8 64 (const int) -0:10 multiply second child into first child (mediump int) -0:10 'c' (in mediump int) +0:10 multiply second child into first child (int) +0:10 'c' (in int) 0:10 Constant: 0:10 64 (const int) -0:12 move second child to first child (mediump int) -0:12 'e' (out mediump int) +0:12 move second child to first child (int) +0:12 'e' (out int) 0:12 Constant: 0:12 1024 (const int) -0:13 multiply second child into first child (mediump int) -0:13 'f' (inout mediump int) +0:13 multiply second child into first child (int) +0:13 'f' (inout int) 0:13 Constant: 0:13 64 (const int) -0:15 add second child into first child (mediump int) -0:15 'sum' (mediump int) -0:15 add (mediump int) -0:15 add (mediump int) -0:15 add (mediump int) -0:15 add (mediump int) -0:15 add (mediump int) -0:15 'a' (in mediump int) -0:15 component-wise multiply (mediump int) +0:15 add second child into first child (int) +0:15 'sum' (int) +0:15 add (int) +0:15 add (int) +0:15 add (int) +0:15 add (int) +0:15 add (int) +0:15 'a' (in int) +0:15 component-wise multiply (int) 0:15 Constant: 0:15 64 (const int) -0:15 'b' (const (read only) mediump int) -0:15 'c' (in mediump int) -0:15 component-wise multiply (mediump int) +0:15 'b' (const (read only) int) +0:15 'c' (in int) +0:15 component-wise multiply (int) 0:15 Constant: 0:15 64 (const int) -0:15 'd' (const (read only) mediump int) -0:15 'e' (out mediump int) -0:15 'f' (inout mediump int) +0:15 'd' (const (read only) int) +0:15 'e' (out int) +0:15 'f' (inout int) 0:18 Branch: Return with expression -0:18 'sum' (mediump int) -0:21 Function Definition: main( (void) +0:18 'sum' (int) +0:21 Function Definition: foo2(f1;vf3;i1; (int) 0:21 Function Parameters: +0:21 'a' (in float) +0:21 'b' (in 3-component vector of float) +0:21 'r' (out int) +0:23 Sequence +0:23 move second child to first child (int) +0:23 'r' (out int) +0:23 Convert float to int (int) +0:23 component-wise multiply (float) +0:23 Constant: +0:23 3.000000 +0:23 'a' (in float) +0:24 Branch: Return with expression +0:24 Convert float to int (int) +0:24 component-wise multiply (float) +0:24 Constant: +0:24 5.000000 +0:24 direct index (float) +0:24 'b' (in 3-component vector of float) +0:24 Constant: +0:24 1 (const int) +0:27 Function Definition: main( (void) +0:27 Function Parameters: 0:? Sequence -0:24 Sequence -0:24 move second child to first child (mediump int) -0:24 't' (mediump int) -0:24 Constant: -0:24 2 (const int) -0:28 move second child to first child (mediump int) -0:28 direct index (mediump int) -0:28 t: direct index for structure (mediump 4-component vector of int) -0:28 'f' (structure{mediump 4-component vector of int t}) -0:28 Constant: -0:28 0 (const int) -0:28 Constant: -0:28 1 (const int) -0:28 Constant: -0:28 32 (const int) 0:30 Sequence -0:30 move second child to first child (mediump int) -0:30 'color' (mediump int) -0:30 Function Call: foo(i1;i1;i1;i1;i1;i1; (mediump int) -0:30 Constant: -0:30 1 (const int) -0:30 Constant: -0:30 2 (const int) -0:30 add (mediump int) -0:30 't' (mediump int) -0:30 't' (mediump int) -0:30 Constant: -0:30 8 (const int) -0:30 'e' (mediump int) -0:30 direct index (mediump int) -0:30 t: direct index for structure (mediump 4-component vector of int) -0:30 'f' (structure{mediump 4-component vector of int t}) -0:30 Constant: -0:30 0 (const int) -0:30 Constant: -0:30 1 (const int) -0:32 add second child into first child (mediump int) -0:32 'color' (mediump int) -0:32 component-wise multiply (mediump int) -0:32 Constant: -0:32 128 (const int) -0:32 add (mediump int) -0:32 'e' (mediump int) -0:32 direct index (mediump int) -0:32 t: direct index for structure (mediump 4-component vector of int) -0:32 'f' (structure{mediump 4-component vector of int t}) -0:32 Constant: -0:32 0 (const int) -0:32 Constant: -0:32 1 (const int) -0:35 move second child to first child (mediump 4-component vector of float) -0:35 'gl_FragColor' (fragColor mediump 4-component vector of float) -0:35 Construct vec4 (mediump 4-component vector of float) -0:35 Convert int to float (mediump float) -0:35 'color' (mediump int) +0:30 move second child to first child (int) +0:30 't' (int) +0:30 Constant: +0:30 2 (const int) +0:34 move second child to first child (int) +0:34 direct index (int) +0:34 t: direct index for structure (4-component vector of int) +0:34 'f' (structure{4-component vector of int t}) +0:34 Constant: +0:34 0 (const int) +0:34 Constant: +0:34 1 (const int) +0:34 Constant: +0:34 32 (const int) +0:37 Sequence +0:37 move second child to first child (int) +0:37 'color' (int) +0:37 Function Call: foo(i1;i1;i1;i1;i1;i1; (int) +0:37 Constant: +0:37 1 (const int) +0:37 Constant: +0:37 2 (const int) +0:37 add (int) +0:37 't' (int) +0:37 't' (int) +0:37 Constant: +0:37 8 (const int) +0:37 'e' (int) +0:37 direct index (int) +0:37 t: direct index for structure (4-component vector of int) +0:37 'f' (structure{4-component vector of int t}) +0:37 Constant: +0:37 0 (const int) +0:37 Constant: +0:37 1 (const int) +0:39 add second child into first child (int) +0:39 'color' (int) +0:39 component-wise multiply (int) +0:39 Constant: +0:39 128 (const int) +0:39 add (int) +0:39 'e' (int) +0:39 direct index (int) +0:39 t: direct index for structure (4-component vector of int) +0:39 'f' (structure{4-component vector of int t}) +0:39 Constant: +0:39 0 (const int) +0:39 Constant: +0:39 1 (const int) +0:45 move second child to first child (float) +0:45 'ret' (float) +0:45 Convert int to float (float) +0:45 Comma (int) +0:45 move second child to first child (int) +0:45 'tempReturn' (int) +0:45 Function Call: foo2(f1;vf3;i1; (int) +0:45 Constant: +0:45 4.000000 +0:45 Constant: +0:45 1.000000 +0:45 2.000000 +0:45 3.000000 +0:45 'tempArg' (out int) +0:45 move second child to first child (float) +0:45 'arg' (float) +0:45 Convert int to float (float) +0:45 'tempArg' (out int) +0:45 'tempReturn' (int) +0:46 add second child into first child (int) +0:46 'color' (int) +0:46 Convert float to int (int) +0:46 add (float) +0:46 'ret' (float) +0:46 'arg' (float) +0:48 move second child to first child (4-component vector of float) +0:48 'gl_FragColor' (fragColor 4-component vector of float) +0:48 Construct vec4 (4-component vector of float) +0:48 Convert int to float (float) +0:48 'color' (int) 0:? Linker Objects diff --git a/glslang/Include/BaseTypes.h b/glslang/Include/BaseTypes.h index 93fa11a0..1690df89 100644 --- a/glslang/Include/BaseTypes.h +++ b/glslang/Include/BaseTypes.h @@ -74,8 +74,7 @@ enum TStorageQualifier { EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter EvqInOut, - - EvqConstReadOnly, // read-only types, not having a constant value or constant-value semantics + EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics // built-ins read by vertex shader EvqVertexId, diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index ff9267cf..b6caf504 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -238,9 +238,15 @@ class TQualifier { public: void clear() { - storage = EvqTemporary; precision = EpqNone; invariant = false; + makeTemporary(); + } + + // drop qualifiers that don't belong in a temporary variable + void makeTemporary() + { + storage = EvqTemporary; centroid = false; smooth = false; flat = false; @@ -255,6 +261,7 @@ public: writeonly = false; clearLayout(); } + TStorageQualifier storage : 6; TPrecisionQualifier precision : 3; bool invariant : 1; @@ -314,6 +321,29 @@ public: } } + bool isParamInput() const + { + switch (storage) { + case EvqIn: + case EvqInOut: + case EvqConstReadOnly: + return true; + default: + return false; + } + } + + bool isParamOutput() const + { + switch (storage) { + case EvqOut: + case EvqInOut: + return true; + default: + return false; + } + } + bool isUniform() const { switch (storage) { diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 51a9144e..66f8891a 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -247,7 +247,7 @@ enum TOperator { // EOpConstructGuardStart, - EOpConstructInt, + EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed EOpConstructUint, EOpConstructBool, EOpConstructFloat, diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 80c98f1b..4224efd0 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -67,6 +67,11 @@ TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType return node; } +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, TSourceLoc loc) +{ + return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), loc); +} + // // Connect two nodes with a new parent that does a binary operation on the nodes. // @@ -363,14 +368,16 @@ TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator o } // -// Convert one type to another. +// Convert the node's type to the given type, as allowed by the operation involved 'op'. +// For implicit conversions, 'op' is not the requested conversion, it is the explicit +// operation requiring the implicit conversion. // // Returns the node representing the conversion, which could be the same // node passed in if no conversion was needed. // // Return 0 if a conversion can't be done. // -TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) +TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) const { // // Does the base type allow operation? @@ -490,88 +497,86 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt return 0; } - if (node->getAsConstantUnion()) { - + if (node->getAsConstantUnion()) return promoteConstantUnion(promoteTo, node->getAsConstantUnion()); - } else { - // - // Add a new newNode for the conversion. - // - TIntermUnary* newNode = 0; - TOperator newOp = EOpNull; + // + // Add a new newNode for the conversion. + // + TIntermUnary* newNode = 0; - // This is 'mechanism' here, it does any conversion told. The policy comes - // from the shader or the above code. - switch (promoteTo) { - case EbtDouble: - //switch (node->getBasicType()) { - //case EbtInt: newOp = EOpConvIntToDouble; break; - //case EbtUint: newOp = EOpConvUintToDouble; break; - //case EbtBool: newOp = EOpConvBoolToDouble; break; - //case EbtFloat: newOp = EOpConvFloatToDouble; break; - //default: - return 0; - //} - break; - case EbtFloat: - switch (node->getBasicType()) { - case EbtInt: newOp = EOpConvIntToFloat; break; - case EbtUint: newOp = EOpConvUintToFloat; break; - case EbtBool: newOp = EOpConvBoolToFloat; break; - case EbtDouble: newOp = EOpConvDoubleToFloat; break; - default: - return 0; - } - break; - case EbtBool: - switch (node->getBasicType()) { - case EbtInt: newOp = EOpConvIntToBool; break; - case EbtUint: newOp = EOpConvUintToBool; break; - case EbtFloat: newOp = EOpConvFloatToBool; break; - case EbtDouble: newOp = EOpConvDoubleToBool; break; - default: - return 0; - } - break; - case EbtInt: - switch (node->getBasicType()) { - case EbtUint: newOp = EOpConvUintToInt; break; - case EbtBool: newOp = EOpConvBoolToInt; break; - case EbtFloat: newOp = EOpConvFloatToInt; break; - case EbtDouble: newOp = EOpConvDoubleToInt; break; - default: - return 0; - } - break; - case EbtUint: - switch (node->getBasicType()) { - case EbtInt: newOp = EOpConvIntToUint; break; - case EbtBool: newOp = EOpConvBoolToUint; break; - case EbtFloat: newOp = EOpConvFloatToUint; break; - case EbtDouble: newOp = EOpConvDoubleToUint; break; - default: - return 0; - } - break; - default: + TOperator newOp = EOpNull; + + // This is 'mechanism' here, it does any conversion told. The policy comes + // from the shader or the above code. + switch (promoteTo) { + case EbtDouble: + //switch (node->getBasicType()) { + //case EbtInt: newOp = EOpConvIntToDouble; break; + //case EbtUint: newOp = EOpConvUintToDouble; break; + //case EbtBool: newOp = EOpConvBoolToDouble; break; + //case EbtFloat: newOp = EOpConvFloatToDouble; break; + //default: + return 0; + //} + break; + case EbtFloat: + switch (node->getBasicType()) { + case EbtInt: newOp = EOpConvIntToFloat; break; + case EbtUint: newOp = EOpConvUintToFloat; break; + case EbtBool: newOp = EOpConvBoolToFloat; break; + case EbtDouble: newOp = EOpConvDoubleToFloat; break; + default: return 0; } - - TType type(promoteTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); - newNode = new TIntermUnary(newOp, type); - newNode->setLoc(node->getLoc()); - newNode->setOperand(node); - - return newNode; + break; + case EbtBool: + switch (node->getBasicType()) { + case EbtInt: newOp = EOpConvIntToBool; break; + case EbtUint: newOp = EOpConvUintToBool; break; + case EbtFloat: newOp = EOpConvFloatToBool; break; + case EbtDouble: newOp = EOpConvDoubleToBool; break; + default: + return 0; + } + break; + case EbtInt: + switch (node->getBasicType()) { + case EbtUint: newOp = EOpConvUintToInt; break; + case EbtBool: newOp = EOpConvBoolToInt; break; + case EbtFloat: newOp = EOpConvFloatToInt; break; + case EbtDouble: newOp = EOpConvDoubleToInt; break; + default: + return 0; + } + break; + case EbtUint: + switch (node->getBasicType()) { + case EbtInt: newOp = EOpConvIntToUint; break; + case EbtBool: newOp = EOpConvBoolToUint; break; + case EbtFloat: newOp = EOpConvFloatToUint; break; + case EbtDouble: newOp = EOpConvDoubleToUint; break; + default: + return 0; + } + break; + default: + return 0; } + + TType newType(promoteTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); + newNode = new TIntermUnary(newOp, newType); + newNode->setLoc(node->getLoc()); + newNode->setOperand(node); + + return newNode; } // // See if the 'from' type is allowed to be implicitly converted to the // 'to' type. This is not about vector/array/struct, only about basic type. // -bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to) +bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to) const { if (profile == EEsProfile || version == 110) return false; @@ -713,8 +718,7 @@ TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, T TIntermTyped *commaAggregate = growAggregate(left, right, loc); commaAggregate->getAsAggregate()->setOperator(EOpComma); commaAggregate->setType(right->getType()); - commaAggregate->getWritableType().getQualifier().storage = EvqTemporary; - commaAggregate->getWritableType().getQualifier().precision = right->getType().getQualifier().precision; + commaAggregate->getWritableType().getQualifier().makeTemporary(); return commaAggregate; } @@ -782,7 +786,7 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* true // Returns the constant union node created. // -TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, TSourceLoc loc, bool literal) +TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, TSourceLoc loc, bool literal) const { TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t); node->setLoc(loc); @@ -1028,7 +1032,7 @@ bool TIntermUnary::promote() } setType(operand->getType()); - getWritableType().getQualifier().storage = EvqTemporary; + getWritableType().getQualifier().makeTemporary(); return true; } @@ -1412,7 +1416,7 @@ void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) } } -TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) +TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const { const TConstUnionArray& rightUnionArray = node->getConstArray(); int size = node->getType().computeNumComponents(); diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index aff1e625..35a126c9 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -381,7 +381,7 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt if (anon) { // it was a member of an anonymous container, have to insert its dereference const TVariable* variable = anon->getAnonContainer().getAsVariable(); - TIntermTyped* container = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), loc); + TIntermTyped* container = intermediate.addSymbol(*variable, loc); TConstUnionArray unionArray(1); unionArray[0].setUConst(anon->getMemberNumber()); TIntermTyped* constNode = intermediate.addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc); @@ -892,9 +892,7 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(TSourceLoc loc, TFunct // Add the parameter to the HIL paramNodes = intermediate.growAggregate(paramNodes, - intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), loc), + intermediate.addSymbol(*variable, loc), loc); } } else @@ -916,13 +914,13 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(TSourceLoc loc, TFunct // - user function // - subroutine call (not implemented yet) // -TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCall, TIntermNode* intermNode) +TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* function, TIntermNode* intermNode) { TIntermTyped* result = 0; - TOperator op = fnCall->getBuiltInOp(); + TOperator op = function->getBuiltInOp(); if (op == EOpArrayLength) - result = handleLengthMethod(loc, fnCall, intermNode); + result = handleLengthMethod(loc, function, intermNode); else if (op != EOpNull) { // // Then this should be a constructor. @@ -930,7 +928,7 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal // Their parameters will be verified algorithmically. // TType type(EbtVoid); // use this to get the type back - if (! constructorError(loc, intermNode, *fnCall, op, type)) { + if (! constructorError(loc, intermNode, *function, op, type)) { // // It's a constructor, of type 'type'. // @@ -944,7 +942,7 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal // const TFunction* fnCandidate; bool builtIn; - fnCandidate = findFunction(loc, *fnCall, builtIn); + fnCandidate = findFunction(loc, *function, builtIn); if (fnCandidate) { // Error check for function requiring specific extensions present. if (builtIn && fnCandidate->getNumExtensions()) @@ -966,30 +964,33 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal } else { // This is a function call not mapped to built-in operation result = intermediate.setAggregateOperator(intermNode, EOpFunctionCall, fnCandidate->getType(), loc); - result->getAsAggregate()->setName(fnCandidate->getMangledName()); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(fnCandidate->getMangledName()); // this is how we know whether the given function is a built-in function or a user-defined function // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also // if builtIn == true, it's definitely a built-in function with EOpNull if (! builtIn) { - result->getAsAggregate()->setUserDefined(); + call->setUserDefined(); intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); } // Make sure storage qualifications work for these arguments. TStorageQualifier qual; - TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + TQualifierList& qualifierList = call->getQualifierList(); for (int i = 0; i < fnCandidate->getParamCount(); ++i) { qual = (*fnCandidate)[i].type->getQualifier().storage; if (qual == EvqOut || qual == EvqInOut) { - if (lValueErrorCheck(result->getLoc(), "assign", result->getAsAggregate()->getSequence()[i]->getAsTyped())) + if (lValueErrorCheck(call->getLoc(), "assign", call->getSequence()[i]->getAsTyped())) error(intermNode->getLoc(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error", ""); } qualifierList.push_back(qual); } + result = handleArgumentConversions(*fnCandidate, *call); + if (builtIn) - nonOpBuiltInCheck(loc, *fnCandidate, *result->getAsAggregate()); + nonOpBuiltInCheck(loc, *fnCandidate, *call); } } } @@ -1007,14 +1008,14 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal // Do all processing handling object.length(). // Return resulting tree node. -TIntermTyped* TParseContext::handleLengthMethod(TSourceLoc loc, TFunction* fnCall, TIntermNode* intermNode) +TIntermTyped* TParseContext::handleLengthMethod(TSourceLoc loc, TFunction* function, TIntermNode* intermNode) { int length = 0; - if (fnCall->getParamCount() > 0) - error(loc, "method does not accept any arguments", fnCall->getName().c_str(), ""); + if (function->getParamCount() > 0) + error(loc, "method does not accept any arguments", function->getName().c_str(), ""); if (intermNode->getAsTyped() == 0 || ! intermNode->getAsTyped()->getType().isArray()) - error(loc, "", fnCall->getName().c_str(), "can only be applied to an array"); + error(loc, "", function->getName().c_str(), "can only be applied to an array"); else if (intermNode->getAsTyped()->getType().getArraySize() == 0) { bool implicitlySized = false; if (intermNode->getAsSymbolNode() && isIoResizeArray(intermNode->getAsTyped()->getType())) { @@ -1028,9 +1029,9 @@ TIntermTyped* TParseContext::handleLengthMethod(TSourceLoc loc, TFunction* fnCal } if (length == 0) { if (intermNode->getAsSymbolNode() && isIoResizeArray(intermNode->getAsTyped()->getType())) - error(loc, "", fnCall->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); + error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); else - error(loc, "", fnCall->getName().c_str(), "array must be declared with a size before using this method"); + error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); } } else length = intermNode->getAsTyped()->getType().getArraySize(); @@ -1042,6 +1043,86 @@ TIntermTyped* TParseContext::handleLengthMethod(TSourceLoc loc, TFunction* fnCal return intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc); } + +// +// Add any needed implicit conversions for function-call arguments. This is +// straightforward for input parameters, but output parameters need to +// a different tree topology, complicated further by whether the function +// has a return value. Create the new subtree, as neeeded. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* TParseContext::handleArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const +{ + TIntermSequence& arguments = intermNode.getSequence(); + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().storage == EvqOut) { + outputConversions = true; + break; + } + } + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = 0; + TVariable* tempRet = 0; + if (outputConversions) { + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc()); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + } + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType()) { + if (function[i].type->getQualifier().isParamInput()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + arguments[i] = intermediate.addConversion(EOpAssign, *function[i].type, arguments[i]->getAsTyped()); + } else if (function[i].type->getQualifier().isParamOutput()) { + // Out-qualified arguments need to use the topology set up above. + // do the " ...(tempArg, ...), arg = tempArg" bit from above + TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + } + } + } + + // Done if no output conversions + if (! outputConversions) + return &intermNode; + + // Otherwise, finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc()); + } + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); + + return conversionTree; +} + // // Do additional checking of built-in function calls that were not mapped // to built-in operations (e.g., texturing functions). @@ -1136,12 +1217,12 @@ void TParseContext::nonOpBuiltInCheck(TSourceLoc loc, const TFunction& fnCandida } // -// Handle seeing a built-in-type constructor call in the grammar. +// Handle seeing a built-in constructor in a grammar production. // -TFunction* TParseContext::handleConstructorCall(TSourceLoc loc, TPublicType& publicType) +TFunction* TParseContext::handleConstructorCall(TSourceLoc loc, const TPublicType& publicType) { - publicType.qualifier.precision = EpqNone; TType type(publicType); + type.getQualifier().precision = EpqNone; if (type.isArray()) { profileRequires(loc, ENoProfile, 120, GL_3DL_array_objects, "arrayed constructor"); @@ -1151,10 +1232,9 @@ TFunction* TParseContext::handleConstructorCall(TSourceLoc loc, TPublicType& pub TOperator op = mapTypeToConstructorOp(type); if (op == EOpNull) { - error(loc, "cannot construct this type", TType::getBasicString(publicType.basicType), ""); + error(loc, "cannot construct this type", type.getBasicString(), ""); op = EOpConstructFloat; - publicType.basicType = EbtFloat; - TType errorType(publicType); + TType errorType(EbtFloat); type.shallowCopy(errorType); } @@ -1164,9 +1244,9 @@ TFunction* TParseContext::handleConstructorCall(TSourceLoc loc, TPublicType& pub } // -// Given a type, find what operation would construct it. +// Given a type, find what operation would fully construct it. // -TOperator TParseContext::mapTypeToConstructorOp(const TType& type) +TOperator TParseContext::mapTypeToConstructorOp(const TType& type) const { if (type.isStruct()) return EOpConstructStruct; @@ -1335,9 +1415,7 @@ void TParseContext::variableCheck(TIntermTyped*& nodePtr) symbolTable.insert(*fakeVariable); // substitute a symbol node for this new variable - nodePtr = intermediate.addSymbol(fakeVariable->getUniqueId(), - fakeVariable->getName(), - fakeVariable->getType(), symbol->getLoc()); + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); } else { switch (symbol->getQualifier().storage) { case EvqPointCoord: @@ -3340,12 +3418,11 @@ const TFunction* TParseContext::findFunction120(TSourceLoc loc, const TFunction& possibleMatch = false; else { // do direction-specific checks for conversion of basic type - TStorageQualifier qualifier = function[i].type->getQualifier().storage; - if (qualifier == EvqIn || qualifier == EvqInOut) { + if (function[i].type->getQualifier().isParamInput()) { if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType())) possibleMatch = false; } - if (qualifier == EvqOut || qualifier == EvqInOut) { + if (function[i].type->getQualifier().isParamOutput()) { if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType())) possibleMatch = false; } @@ -3471,6 +3548,22 @@ void TParseContext::inheritGlobalDefaults(TQualifier& dst) const } } +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = new TString(name); + TSourceLoc loc = {0, 0}; + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + // // Declare a non-array variable, the main point being there is no redeclaration // for resizing allowed. @@ -3569,7 +3662,7 @@ TIntermNode* TParseContext::executeInitializer(TSourceLoc loc, TString& identifi variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); } else { // normal assigning of a value to a variable... - TIntermSymbol* intermSymbol = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), loc); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); TIntermNode* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc); if (! initNode) assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 71338a53..f4a89aeb 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -98,8 +98,9 @@ public: TIntermAggregate* handleFunctionDefinition(TSourceLoc, TFunction&); TIntermTyped* handleFunctionCall(TSourceLoc, TFunction*, TIntermNode*); TIntermTyped* handleLengthMethod(TSourceLoc, TFunction*, TIntermNode*); + TIntermTyped* handleArgumentConversions(const TFunction&, TIntermAggregate&) const; void nonOpBuiltInCheck(TSourceLoc, const TFunction&, TIntermAggregate&); - TFunction* handleConstructorCall(TSourceLoc, TPublicType&); + TFunction* handleConstructorCall(TSourceLoc, const TPublicType&); bool parseVectorFields(TSourceLoc, const TString&, int vecSize, TVectorFields&); void assignError(TSourceLoc, const char* op, TString left, TString right); @@ -205,11 +206,12 @@ public: protected: void nonInitConstCheck(TSourceLoc, TString& identifier, TType& type); void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; TVariable* declareNonArray(TSourceLoc, TString& identifier, TType&, bool& newDeclaration); void declareArray(TSourceLoc, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration); TIntermNode* executeInitializer(TSourceLoc, TString& identifier, TIntermTyped* initializer, TVariable* variable); TIntermTyped* convertInitializerList(TSourceLoc, const TType&, TIntermTyped* initializer); - TOperator mapTypeToConstructorOp(const TType&); + TOperator mapTypeToConstructorOp(const TType&) const; void finalErrorCheck(); public: diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index fd19c699..7d9afaf4 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -502,6 +502,12 @@ public: table.pop_back(); } + // + // Insert a visible symbol into the symbol table so it can + // be found later by name. + // + // Returns false if the was a name collision. + // bool insert(TSymbol& symbol) { symbol.setUniqueId(++uniqueId); @@ -523,6 +529,17 @@ public: return table[currentLevel()]->insert(symbol, separateNameSpaces); } + // + // To allocate an internal temporary, which will need to be uniquely + // identified by the consumer of the AST, but never need to + // found by doing a symbol table search by name, hence allowed an + // arbitrary name in the symbol with no worry of collision. + // + void makeInternalVariable(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + } + // // Copy a variable or anonymous member's structure from a shared level up // to the current level, so it can be modified without impacting other users diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 9bb17099..7c91686b 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -102,6 +102,7 @@ struct TXfbBuffer { class TSymbolTable; class TSymbol; +class TVariable; // // Set of helper functions to help parse and build the tree. @@ -133,13 +134,14 @@ public: bool isRecursive() const { return recursive; } TIntermSymbol* addSymbol(int Id, const TString&, const TType&, TSourceLoc); - TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); + TIntermSymbol* addSymbol(const TVariable&, TSourceLoc); + TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*) const; TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, TSourceLoc); TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc); TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc); TIntermTyped* addUnaryMath(TOperator, TIntermNode* child, TSourceLoc); TIntermTyped* addBuiltInFunctionCall(TSourceLoc line, TOperator, bool unary, TIntermNode*, const TType& returnType); - bool canImplicitlyPromote(TBasicType from, TBasicType to); + bool canImplicitlyPromote(TBasicType from, TBasicType to) const; TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right); TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, TSourceLoc); TIntermAggregate* makeAggregate(TIntermNode* node); @@ -150,8 +152,8 @@ public: TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, TSourceLoc); TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, TSourceLoc); TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, TSourceLoc); - TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, TSourceLoc, bool literal = false); - TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) ; + TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, TSourceLoc, bool literal = false) const; + TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, TSourceLoc); TIntermBranch* addBranch(TOperator, TSourceLoc);