OSDN Git Service

Removed double argument() calls
[android-x86/external-swiftshader.git] / src / OpenGL / compiler / OutputASM.cpp
1 // SwiftShader Software Renderer\r
2 //\r
3 // Copyright(c) 2005-2013 TransGaming Inc.\r
4 //\r
5 // All rights reserved. No part of this software may be copied, distributed, transmitted,\r
6 // transcribed, stored in a retrieval system, translated into any human or computer\r
7 // language by any means, or disclosed to third parties without the explicit written\r
8 // agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express\r
9 // or implied, including but not limited to any patent rights, are granted to you.\r
10 //\r
11 \r
12 #include "OutputASM.h"\r
13 #include "Common/Math.hpp"\r
14 \r
15 #include "common/debug.h"\r
16 #include "InfoSink.h"\r
17 \r
18 #include "libGLESv2/Shader.h"\r
19 \r
20 #include <GLES2/gl2.h>\r
21 #include <GLES2/gl2ext.h>\r
22 #include <GLES3/gl3.h>\r
23 \r
24 namespace glsl\r
25 {\r
26         // Integer to TString conversion\r
27         TString str(int i)\r
28         {\r
29                 char buffer[20];\r
30                 sprintf(buffer, "%d", i);\r
31                 return buffer;\r
32         }\r
33 \r
34         class Temporary : public TIntermSymbol\r
35         {\r
36         public:\r
37                 Temporary(OutputASM *assembler) : TIntermSymbol(TSymbolTableLevel::nextUniqueId(), "tmp", TType(EbtFloat, EbpHigh, EvqTemporary, 4, 1, false)), assembler(assembler)\r
38                 {\r
39                 }\r
40 \r
41                 ~Temporary()\r
42                 {\r
43                         assembler->freeTemporary(this);\r
44                 }\r
45 \r
46         private:\r
47                 OutputASM *const assembler;\r
48         };\r
49 \r
50         class Constant : public TIntermConstantUnion\r
51         {\r
52         public:\r
53                 Constant(float x, float y, float z, float w) : TIntermConstantUnion(constants, TType(EbtFloat, EbpHigh, EvqConstExpr, 4, 1, false))\r
54                 {\r
55                         constants[0].setFConst(x);\r
56                         constants[1].setFConst(y);\r
57                         constants[2].setFConst(z);\r
58                         constants[3].setFConst(w);\r
59                 }\r
60 \r
61                 Constant(bool b) : TIntermConstantUnion(constants, TType(EbtBool, EbpHigh, EvqConstExpr, 1, 1, false))\r
62                 {\r
63                         constants[0].setBConst(b);\r
64                 }\r
65 \r
66                 Constant(int i) : TIntermConstantUnion(constants, TType(EbtInt, EbpHigh, EvqConstExpr, 1, 1, false))\r
67                 {\r
68                         constants[0].setIConst(i);\r
69                 }\r
70 \r
71                 ~Constant()\r
72                 {\r
73                 }\r
74 \r
75         private:\r
76                 ConstantUnion constants[4];\r
77         };\r
78 \r
79         Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, int arraySize, int registerIndex, int blockId, const BlockMemberInfo& blockMemberInfo) :\r
80                 type(type), precision(precision), name(name), arraySize(arraySize), registerIndex(registerIndex), blockId(blockId), blockInfo(blockMemberInfo)\r
81         {\r
82         }\r
83 \r
84         UniformBlock::UniformBlock(const std::string& name, unsigned int dataSize, unsigned int arraySize,\r
85                                    TLayoutBlockStorage layout, bool isRowMajorLayout, int registerIndex, int blockId) :\r
86                 name(name), dataSize(dataSize), arraySize(arraySize), layout(layout),\r
87                 isRowMajorLayout(isRowMajorLayout), registerIndex(registerIndex), blockId(blockId)\r
88         {\r
89         }\r
90 \r
91         BlockLayoutEncoder::BlockLayoutEncoder(bool rowMajor)\r
92                 : mCurrentOffset(0), isRowMajor(rowMajor)\r
93         {\r
94         }\r
95 \r
96         BlockMemberInfo BlockLayoutEncoder::encodeType(const TType &type)\r
97         {\r
98                 int arrayStride;\r
99                 int matrixStride;\r
100 \r
101                 getBlockLayoutInfo(type, type.getArraySize(), isRowMajor, &arrayStride, &matrixStride);\r
102 \r
103                 const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * BytesPerComponent),\r
104                                                  static_cast<int>(arrayStride * BytesPerComponent),\r
105                                                  static_cast<int>(matrixStride * BytesPerComponent),\r
106                                                  (matrixStride > 0) && isRowMajor);\r
107 \r
108                 advanceOffset(type, type.getArraySize(), isRowMajor, arrayStride, matrixStride);\r
109 \r
110                 return memberInfo;\r
111         }\r
112 \r
113         // static\r
114         size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info)\r
115         {\r
116                 return (info.offset / BytesPerComponent) / ComponentsPerRegister;\r
117         }\r
118 \r
119         // static\r
120         size_t BlockLayoutEncoder::getBlockRegisterElement(const BlockMemberInfo &info)\r
121         {\r
122                 return (info.offset / BytesPerComponent) % ComponentsPerRegister;\r
123         }\r
124 \r
125         void BlockLayoutEncoder::nextRegister()\r
126         {\r
127                 mCurrentOffset = sw::align(mCurrentOffset, ComponentsPerRegister);\r
128         }\r
129 \r
130         Std140BlockEncoder::Std140BlockEncoder(bool rowMajor) : BlockLayoutEncoder(rowMajor)\r
131         {\r
132         }\r
133 \r
134         void Std140BlockEncoder::enterAggregateType()\r
135         {\r
136                 nextRegister();\r
137         }\r
138 \r
139         void Std140BlockEncoder::exitAggregateType()\r
140         {\r
141                 nextRegister();\r
142         }\r
143 \r
144         void Std140BlockEncoder::getBlockLayoutInfo(const TType &type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut)\r
145         {\r
146                 size_t baseAlignment = 0;\r
147                 int matrixStride = 0;\r
148                 int arrayStride = 0;\r
149 \r
150                 if(type.isMatrix())\r
151                 {\r
152                         baseAlignment = ComponentsPerRegister;\r
153                         matrixStride = ComponentsPerRegister;\r
154 \r
155                         if(arraySize > 0)\r
156                         {\r
157                                 const int numRegisters = isRowMajorMatrix ? type.getSecondarySize() : type.getNominalSize();\r
158                                 arrayStride = ComponentsPerRegister * numRegisters;\r
159                         }\r
160                 }\r
161                 else if(arraySize > 0)\r
162                 {\r
163                         baseAlignment = ComponentsPerRegister;\r
164                         arrayStride = ComponentsPerRegister;\r
165                 }\r
166                 else\r
167                 {\r
168                         const int numComponents = type.getElementSize();\r
169                         baseAlignment = (numComponents == 3 ? 4u : static_cast<size_t>(numComponents));\r
170                 }\r
171 \r
172                 mCurrentOffset = sw::align(mCurrentOffset, baseAlignment);\r
173 \r
174                 *matrixStrideOut = matrixStride;\r
175                 *arrayStrideOut = arrayStride;\r
176         }\r
177 \r
178         void Std140BlockEncoder::advanceOffset(const TType &type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride)\r
179         {\r
180                 if(arraySize > 0)\r
181                 {\r
182                         mCurrentOffset += arrayStride * arraySize;\r
183                 }\r
184                 else if(type.isMatrix())\r
185                 {\r
186                         ASSERT(matrixStride == ComponentsPerRegister);\r
187                         const int numRegisters = isRowMajorMatrix ? type.getSecondarySize() : type.getNominalSize();\r
188                         mCurrentOffset += ComponentsPerRegister * numRegisters;\r
189                 }\r
190                 else\r
191                 {\r
192                         mCurrentOffset += type.getElementSize();\r
193                 }\r
194         }\r
195 \r
196         Attribute::Attribute()\r
197         {\r
198                 type = GL_NONE;\r
199                 arraySize = 0;\r
200                 registerIndex = 0;\r
201         }\r
202 \r
203         Attribute::Attribute(GLenum type, const std::string &name, int arraySize, int location, int registerIndex)\r
204         {\r
205                 this->type = type;\r
206                 this->name = name;\r
207                 this->arraySize = arraySize;\r
208                 this->location = location;\r
209                 this->registerIndex = registerIndex;\r
210         }\r
211 \r
212         sw::PixelShader *Shader::getPixelShader() const\r
213         {\r
214                 return 0;\r
215         }\r
216 \r
217         sw::VertexShader *Shader::getVertexShader() const\r
218         {\r
219                 return 0;\r
220         }\r
221 \r
222         OutputASM::TextureFunction::TextureFunction(const TString& nodeName) : method(IMPLICIT), proj(false), offset(false)\r
223         {\r
224                 TString name = TFunction::unmangleName(nodeName);\r
225 \r
226                 if(name == "texture2D" || name == "textureCube" || name == "texture" || name == "texture3D")\r
227                 {\r
228                         method = IMPLICIT;\r
229                 }\r
230                 else if(name == "texture2DProj" || name == "textureProj")\r
231                 {\r
232                         method = IMPLICIT;\r
233                         proj = true;\r
234                 }\r
235                 else if(name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod")\r
236                 {\r
237                         method = LOD;\r
238                 }\r
239                 else if(name == "texture2DProjLod" || name == "textureProjLod")\r
240                 {\r
241                         method = LOD;\r
242                         proj = true;\r
243                 }\r
244                 else if(name == "textureSize")\r
245                 {\r
246                         method = SIZE;\r
247                 }\r
248                 else if(name == "textureOffset")\r
249                 {\r
250                         method = IMPLICIT;\r
251                         offset = true;\r
252                 }\r
253                 else if(name == "textureProjOffset")\r
254                 {\r
255                         method = IMPLICIT;\r
256                         offset = true;\r
257                         proj = true;\r
258                 }\r
259                 else if(name == "textureLodOffset")\r
260                 {\r
261                         method = LOD;\r
262                         offset = true;\r
263                 }\r
264                 else if(name == "textureProjLodOffset")\r
265                 {\r
266                         method = LOD;\r
267                         proj = true;\r
268                         offset = true;\r
269                 }\r
270                 else if(name == "texelFetch")\r
271                 {\r
272                         method = FETCH;\r
273                 }\r
274                 else if(name == "texelFetchOffset")\r
275                 {\r
276                         method = FETCH;\r
277                         offset = true;\r
278                 }\r
279                 else if(name == "textureGrad")\r
280                 {\r
281                         method = GRAD;\r
282                 }\r
283                 else if(name == "textureGradOffset")\r
284                 {\r
285                         method = GRAD;\r
286                         offset = true;\r
287                 }\r
288                 else if(name == "textureProjGrad")\r
289                 {\r
290                         method = GRAD;\r
291                         proj = true;\r
292                 }\r
293                 else if(name == "textureProjGradOffset")\r
294                 {\r
295                         method = GRAD;\r
296                         proj = true;\r
297                         offset = true;\r
298                 }\r
299                 else UNREACHABLE(0);\r
300         }\r
301 \r
302         OutputASM::OutputASM(TParseContext &context, Shader *shaderObject) : TIntermTraverser(true, true, true), shaderObject(shaderObject), mContext(context)\r
303         {\r
304                 shader = 0;\r
305                 pixelShader = 0;\r
306                 vertexShader = 0;\r
307 \r
308                 if(shaderObject)\r
309                 {\r
310                         shader = shaderObject->getShader();\r
311                         pixelShader = shaderObject->getPixelShader();\r
312                         vertexShader = shaderObject->getVertexShader();\r
313                 }\r
314 \r
315                 functionArray.push_back(Function(0, "main(", 0, 0));\r
316                 currentFunction = 0;\r
317                 outputQualifier = EvqOutput; // Set outputQualifier to any value other than EvqFragColor or EvqFragData\r
318         }\r
319 \r
320         OutputASM::~OutputASM()\r
321         {\r
322         }\r
323 \r
324         void OutputASM::output()\r
325         {\r
326                 if(shader)\r
327                 {\r
328                         emitShader(GLOBAL);\r
329 \r
330                         if(functionArray.size() > 1)   // Only call main() when there are other functions\r
331                         {\r
332                                 Instruction *callMain = emit(sw::Shader::OPCODE_CALL);\r
333                                 callMain->dst.type = sw::Shader::PARAMETER_LABEL;\r
334                                 callMain->dst.index = 0;   // main()\r
335 \r
336                                 emit(sw::Shader::OPCODE_RET);\r
337                         }\r
338 \r
339                         emitShader(FUNCTION);\r
340                 }\r
341         }\r
342 \r
343         void OutputASM::emitShader(Scope scope)\r
344         {\r
345                 emitScope = scope;\r
346                 currentScope = GLOBAL;\r
347                 mContext.getTreeRoot()->traverse(this);\r
348         }\r
349 \r
350         void OutputASM::freeTemporary(Temporary *temporary)\r
351         {\r
352                 free(temporaries, temporary);\r
353         }\r
354 \r
355         sw::Shader::Opcode OutputASM::getOpcode(sw::Shader::Opcode op, TIntermTyped *in) const\r
356         {\r
357                 TBasicType baseType = in->getType().getBasicType();\r
358 \r
359                 switch(op)\r
360                 {\r
361                 case sw::Shader::OPCODE_NEG:\r
362                         switch(baseType)\r
363                         {\r
364                         case EbtInt:\r
365                         case EbtUInt:\r
366                                 return sw::Shader::OPCODE_INEG;\r
367                         case EbtFloat:\r
368                         default:\r
369                                 return op;\r
370                         }\r
371                 case sw::Shader::OPCODE_ADD:\r
372                         switch(baseType)\r
373                         {\r
374                         case EbtInt:\r
375                         case EbtUInt:\r
376                                 return sw::Shader::OPCODE_IADD;\r
377                         case EbtFloat:\r
378                         default:\r
379                                 return op;\r
380                         }\r
381                 case sw::Shader::OPCODE_SUB:\r
382                         switch(baseType)\r
383                         {\r
384                         case EbtInt:\r
385                         case EbtUInt:\r
386                                 return sw::Shader::OPCODE_ISUB;\r
387                         case EbtFloat:\r
388                         default:\r
389                                 return op;\r
390                         }\r
391                 case sw::Shader::OPCODE_MUL:\r
392                         switch(baseType)\r
393                         {\r
394                         case EbtInt:\r
395                         case EbtUInt:\r
396                                 return sw::Shader::OPCODE_IMUL;\r
397                         case EbtFloat:\r
398                         default:\r
399                                 return op;\r
400                         }\r
401                 case sw::Shader::OPCODE_DIV:\r
402                         switch(baseType)\r
403                         {\r
404                         case EbtInt:\r
405                                 return sw::Shader::OPCODE_IDIV;\r
406                         case EbtUInt:\r
407                                 return sw::Shader::OPCODE_UDIV;\r
408                         case EbtFloat:\r
409                         default:\r
410                                 return op;\r
411                         }\r
412                 case sw::Shader::OPCODE_IMOD:\r
413                         return baseType == EbtUInt ? sw::Shader::OPCODE_UMOD : op;\r
414                 case sw::Shader::OPCODE_ISHR:\r
415                         return baseType == EbtUInt ? sw::Shader::OPCODE_USHR : op;\r
416                 case sw::Shader::OPCODE_MIN:\r
417                         switch(baseType)\r
418                         {\r
419                         case EbtInt:\r
420                                 return sw::Shader::OPCODE_IMIN;\r
421                         case EbtUInt:\r
422                                 return sw::Shader::OPCODE_UMIN;\r
423                         case EbtFloat:\r
424                         default:\r
425                                 return op;\r
426                         }\r
427                 case sw::Shader::OPCODE_MAX:\r
428                         switch(baseType)\r
429                         {\r
430                         case EbtInt:\r
431                                 return sw::Shader::OPCODE_IMAX;\r
432                         case EbtUInt:\r
433                                 return sw::Shader::OPCODE_UMAX;\r
434                         case EbtFloat:\r
435                         default:\r
436                                 return op;\r
437                         }\r
438                 default:\r
439                         return op;\r
440                 }\r
441         }\r
442 \r
443         void OutputASM::visitSymbol(TIntermSymbol *symbol)\r
444         {\r
445                 // Vertex varyings don't have to be actively used to successfully link\r
446                 // against pixel shaders that use them. So make sure they're declared.\r
447                 if(symbol->getQualifier() == EvqVaryingOut || symbol->getQualifier() == EvqInvariantVaryingOut || symbol->getQualifier() == EvqVertexOut)\r
448                 {\r
449                         if(symbol->getBasicType() != EbtInvariant)   // Typeless declarations are not new varyings\r
450                         {\r
451                                 declareVarying(symbol, -1);\r
452                         }\r
453                 }\r
454 \r
455                 TInterfaceBlock* block = symbol->getType().getInterfaceBlock();\r
456                 // OpenGL ES 3.0.4 spec, section 2.12.6 Uniform Variables:\r
457                 // "All members of a named uniform block declared with a shared or std140 layout qualifier\r
458                 // are considered active, even if they are not referenced in any shader in the program.\r
459                 // The uniform block itself is also considered active, even if no member of the block is referenced."\r
460                 if(block && ((block->blockStorage() == EbsShared) || (block->blockStorage() == EbsStd140)))\r
461                 {\r
462                         uniformRegister(symbol);\r
463                 }\r
464         }\r
465 \r
466         bool OutputASM::visitBinary(Visit visit, TIntermBinary *node)\r
467         {\r
468                 if(currentScope != emitScope)\r
469                 {\r
470                         return false;\r
471                 }\r
472 \r
473                 TIntermTyped *result = node;\r
474                 TIntermTyped *left = node->getLeft();\r
475                 TIntermTyped *right = node->getRight();\r
476                 const TType &leftType = left->getType();\r
477                 const TType &rightType = right->getType();\r
478                 const TType &resultType = node->getType();\r
479 \r
480                 switch(node->getOp())\r
481                 {\r
482                 case EOpAssign:\r
483                         if(visit == PostVisit)\r
484                         {\r
485                                 assignLvalue(left, right);\r
486                                 copy(result, right);\r
487                         }\r
488                         break;\r
489                 case EOpInitialize:\r
490                         if(visit == PostVisit)\r
491                         {\r
492                                 copy(left, right);\r
493                         }\r
494                         break;\r
495                 case EOpMatrixTimesScalarAssign:\r
496                         if(visit == PostVisit)\r
497                         {\r
498                                 for(int i = 0; i < leftType.getNominalSize(); i++)\r
499                                 {\r
500                                         emit(sw::Shader::OPCODE_MUL, result, i, left, i, right);\r
501                                 }\r
502 \r
503                                 assignLvalue(left, result);\r
504                         }\r
505                         break;\r
506                 case EOpVectorTimesMatrixAssign:\r
507                         if(visit == PostVisit)\r
508                         {\r
509                                 int size = leftType.getNominalSize();\r
510 \r
511                                 for(int i = 0; i < size; i++)\r
512                                 {\r
513                                         Instruction *dot = emit(sw::Shader::OPCODE_DP(size), result, 0, left, 0, right, i);\r
514                                         dot->dst.mask = 1 << i;\r
515                                 }\r
516 \r
517                                 assignLvalue(left, result);\r
518                         }\r
519                         break;\r
520                 case EOpMatrixTimesMatrixAssign:\r
521                         if(visit == PostVisit)\r
522                         {\r
523                                 int dim = leftType.getNominalSize();\r
524 \r
525                                 for(int i = 0; i < dim; i++)\r
526                                 {\r
527                                         Instruction *mul = emit(sw::Shader::OPCODE_MUL, result, i, left, 0, right, i);\r
528                                         mul->src[1].swizzle = 0x00;\r
529 \r
530                                         for(int j = 1; j < dim; j++)\r
531                                         {\r
532                                                 Instruction *mad = emit(sw::Shader::OPCODE_MAD, result, i, left, j, right, i, result, i);\r
533                                                 mad->src[1].swizzle = j * 0x55;\r
534                                         }\r
535                                 }\r
536 \r
537                                 assignLvalue(left, result);\r
538                         }\r
539                         break;\r
540                 case EOpIndexDirect:\r
541                         if(visit == PostVisit)\r
542                         {\r
543                                 int index = right->getAsConstantUnion()->getIConst(0);\r
544 \r
545                                 if(result->isMatrix() || result->isStruct() || result->isInterfaceBlock())\r
546                                 {\r
547                                         ASSERT(left->isArray());\r
548                                         copy(result, left, index * left->elementRegisterCount());\r
549                                 }\r
550                                 else if(result->isRegister())\r
551                                 {\r
552                                         int srcIndex = 0;\r
553                                         if(left->isRegister())\r
554                                         {\r
555                                                 srcIndex = 0;\r
556                                         }\r
557                                         else if(left->isArray())\r
558                                         {\r
559                                                 srcIndex = index * left->elementRegisterCount();\r
560                                         }\r
561                                         else if(left->isMatrix())\r
562                                         {\r
563                                                 ASSERT(index < left->getNominalSize());   // FIXME: Report semantic error\r
564                                                 srcIndex = index;\r
565                                         }\r
566                                         else UNREACHABLE(0);\r
567 \r
568                                         Instruction *mov = emit(sw::Shader::OPCODE_MOV, result, 0, left, srcIndex);\r
569 \r
570                                         if(left->isRegister())\r
571                                         {\r
572                                                 mov->src[0].swizzle = index;\r
573                                         }\r
574                                 }\r
575                                 else UNREACHABLE(0);\r
576                         }\r
577                         break;\r
578                 case EOpIndexIndirect:\r
579                         if(visit == PostVisit)\r
580                         {\r
581                                 if(left->isArray() || left->isMatrix())\r
582                                 {\r
583                                         for(int index = 0; index < result->totalRegisterCount(); index++)\r
584                                         {\r
585                                                 Instruction *mov = emit(sw::Shader::OPCODE_MOV, result, index, left, index);\r
586                                                 mov->dst.mask = writeMask(result, index);\r
587 \r
588                                                 if(left->totalRegisterCount() > 1)\r
589                                                 {\r
590                                                         sw::Shader::SourceParameter relativeRegister;\r
591                                                         argument(relativeRegister, right);\r
592 \r
593                                                         mov->src[0].rel.type = relativeRegister.type;\r
594                                                         mov->src[0].rel.index = relativeRegister.index;\r
595                                                         mov->src[0].rel.scale = result->totalRegisterCount();\r
596                                                         mov->src[0].rel.deterministic = !(vertexShader && left->getQualifier() == EvqUniform);\r
597                                                 }\r
598                                         }\r
599                                 }\r
600                                 else if(left->isRegister())\r
601                                 {\r
602                                         emit(sw::Shader::OPCODE_EXTRACT, result, left, right);\r
603                                 }\r
604                                 else UNREACHABLE(0);\r
605                         }\r
606                         break;\r
607                 case EOpIndexDirectStruct:\r
608                 case EOpIndexDirectInterfaceBlock:\r
609                         if(visit == PostVisit)\r
610                         {\r
611                                 ASSERT(leftType.isStruct() || (leftType.isInterfaceBlock()));\r
612 \r
613                                 const TFieldList& fields = (node->getOp() == EOpIndexDirectStruct) ?\r
614                                                            leftType.getStruct()->fields() :\r
615                                                            leftType.getInterfaceBlock()->fields();\r
616                                 int index = right->getAsConstantUnion()->getIConst(0);\r
617                                 int fieldOffset = 0;\r
618 \r
619                                 for(int i = 0; i < index; i++)\r
620                                 {\r
621                                         fieldOffset += fields[i]->type()->totalRegisterCount();\r
622                                 }\r
623 \r
624                                 copy(result, left, fieldOffset);\r
625                         }\r
626                         break;\r
627                 case EOpVectorSwizzle:\r
628                         if(visit == PostVisit)\r
629                         {\r
630                                 int swizzle = 0;\r
631                                 TIntermAggregate *components = right->getAsAggregate();\r
632 \r
633                                 if(components)\r
634                                 {\r
635                                         TIntermSequence &sequence = components->getSequence();\r
636                                         int component = 0;\r
637 \r
638                                         for(TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++)\r
639                                         {\r
640                                                 TIntermConstantUnion *element = (*sit)->getAsConstantUnion();\r
641 \r
642                                                 if(element)\r
643                                                 {\r
644                                                         int i = element->getUnionArrayPointer()[0].getIConst();\r
645                                                         swizzle |= i << (component * 2);\r
646                                                         component++;\r
647                                                 }\r
648                                                 else UNREACHABLE(0);\r
649                                         }\r
650                                 }\r
651                                 else UNREACHABLE(0);\r
652 \r
653                                 Instruction *mov = emit(sw::Shader::OPCODE_MOV, result, left);\r
654                                 mov->src[0].swizzle = swizzle;\r
655                         }\r
656                         break;\r
657                 case EOpAddAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_ADD, result), result, left, left, right); break;\r
658                 case EOpAdd:       if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_ADD, result), result, left, right);       break;\r
659                 case EOpSubAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_SUB, result), result, left, left, right); break;\r
660                 case EOpSub:       if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_SUB, result), result, left, right);       break;\r
661                 case EOpMulAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_MUL, result), result, left, left, right); break;\r
662                 case EOpMul:       if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_MUL, result), result, left, right);       break;\r
663                 case EOpDivAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_DIV, result), result, left, left, right); break;\r
664                 case EOpDiv:       if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_DIV, result), result, left, right);       break;\r
665                 case EOpIModAssign:          if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_IMOD, result), result, left, left, right); break;\r
666                 case EOpIMod:                if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_IMOD, result), result, left, right);       break;\r
667                 case EOpBitShiftLeftAssign:  if(visit == PostVisit) emitAssign(sw::Shader::OPCODE_SHL, result, left, left, right); break;\r
668                 case EOpBitShiftLeft:        if(visit == PostVisit) emitBinary(sw::Shader::OPCODE_SHL, result, left, right);       break;\r
669                 case EOpBitShiftRightAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_ISHR, result), result, left, left, right); break;\r
670                 case EOpBitShiftRight:       if(visit == PostVisit) emitBinary(getOpcode(sw::Shader::OPCODE_ISHR, result), result, left, right);       break;\r
671                 case EOpBitwiseAndAssign:    if(visit == PostVisit) emitAssign(sw::Shader::OPCODE_AND, result, left, left, right); break;\r
672                 case EOpBitwiseAnd:          if(visit == PostVisit) emitBinary(sw::Shader::OPCODE_AND, result, left, right);       break;\r
673                 case EOpBitwiseXorAssign:    if(visit == PostVisit) emitAssign(sw::Shader::OPCODE_XOR, result, left, left, right); break;\r
674                 case EOpBitwiseXor:          if(visit == PostVisit) emitBinary(sw::Shader::OPCODE_XOR, result, left, right);       break;\r
675                 case EOpBitwiseOrAssign:     if(visit == PostVisit) emitAssign(sw::Shader::OPCODE_OR, result, left, left, right);  break;\r
676                 case EOpBitwiseOr:           if(visit == PostVisit) emitBinary(sw::Shader::OPCODE_OR, result, left, right);        break;\r
677                 case EOpEqual:\r
678                         if(visit == PostVisit)\r
679                         {\r
680                                 emitBinary(sw::Shader::OPCODE_EQ, result, left, right);\r
681 \r
682                                 for(int index = 1; index < left->totalRegisterCount(); index++)\r
683                                 {\r
684                                         Temporary equal(this);\r
685                                         emit(sw::Shader::OPCODE_EQ, &equal, 0, left, index, right, index);\r
686                                         emit(sw::Shader::OPCODE_AND, result, result, &equal);\r
687                                 }\r
688                         }\r
689                         break;\r
690                 case EOpNotEqual:\r
691                         if(visit == PostVisit)\r
692                         {\r
693                                 emitBinary(sw::Shader::OPCODE_NE, result, left, right);\r
694 \r
695                                 for(int index = 1; index < left->totalRegisterCount(); index++)\r
696                                 {\r
697                                         Temporary notEqual(this);\r
698                                         emit(sw::Shader::OPCODE_NE, &notEqual, 0, left, index, right, index);\r
699                                         emit(sw::Shader::OPCODE_OR, result, result, &notEqual);\r
700                                 }\r
701                         }\r
702                         break;\r
703                 case EOpLessThan:                if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_LT, result, left, right); break;\r
704                 case EOpGreaterThan:             if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_GT, result, left, right); break;\r
705                 case EOpLessThanEqual:           if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_LE, result, left, right); break;\r
706                 case EOpGreaterThanEqual:        if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_GE, result, left, right); break;\r
707                 case EOpVectorTimesScalarAssign: if(visit == PostVisit) emitAssign(getOpcode(sw::Shader::OPCODE_MUL, left), result, left, left, right); break;\r
708                 case EOpVectorTimesScalar:       if(visit == PostVisit) emit(getOpcode(sw::Shader::OPCODE_MUL, left), result, left, right); break;\r
709                 case EOpMatrixTimesScalar:\r
710                         if(visit == PostVisit)\r
711                         {\r
712                                 for(int i = 0; i < leftType.getNominalSize(); i++)\r
713                                 {\r
714                                         emit(sw::Shader::OPCODE_MUL, result, i, left, i, right);\r
715                                 }\r
716                         }\r
717                         break;\r
718                 case EOpVectorTimesMatrix:\r
719                         if(visit == PostVisit)\r
720                         {\r
721                                 sw::Shader::Opcode dpOpcode = sw::Shader::OPCODE_DP(leftType.getNominalSize());\r
722 \r
723                                 int size = rightType.getNominalSize();\r
724                                 for(int i = 0; i < size; i++)\r
725                                 {\r
726                                         Instruction *dot = emit(dpOpcode, result, 0, left, 0, right, i);\r
727                                         dot->dst.mask = 1 << i;\r
728                                 }\r
729                         }\r
730                         break;\r
731                 case EOpMatrixTimesVector:\r
732                         if(visit == PostVisit)\r
733                         {\r
734                                 Instruction *mul = emit(sw::Shader::OPCODE_MUL, result, left, right);\r
735                                 mul->src[1].swizzle = 0x00;\r
736 \r
737                                 int size = rightType.getNominalSize();\r
738                                 for(int i = 1; i < size; i++)\r
739                                 {\r
740                                         Instruction *mad = emit(sw::Shader::OPCODE_MAD, result, 0, left, i, right, 0, result);\r
741                                         mad->src[1].swizzle = i * 0x55;\r
742                                 }\r
743                         }\r
744                         break;\r
745                 case EOpMatrixTimesMatrix:\r
746                         if(visit == PostVisit)\r
747                         {\r
748                                 int dim = leftType.getNominalSize();\r
749 \r
750                                 int size = rightType.getNominalSize();\r
751                                 for(int i = 0; i < size; i++)\r
752                                 {\r
753                                         Instruction *mul = emit(sw::Shader::OPCODE_MUL, result, i, left, 0, right, i);\r
754                                         mul->src[1].swizzle = 0x00;\r
755 \r
756                                         for(int j = 1; j < dim; j++)\r
757                                         {\r
758                                                 Instruction *mad = emit(sw::Shader::OPCODE_MAD, result, i, left, j, right, i, result, i);\r
759                                                 mad->src[1].swizzle = j * 0x55;\r
760                                         }\r
761                                 }\r
762                         }\r
763                         break;\r
764                 case EOpLogicalOr:\r
765                         if(trivial(right, 6))\r
766                         {\r
767                                 if(visit == PostVisit)\r
768                                 {\r
769                                         emit(sw::Shader::OPCODE_OR, result, left, right);\r
770                                 }\r
771                         }\r
772                         else   // Short-circuit evaluation\r
773                         {\r
774                                 if(visit == InVisit)\r
775                                 {\r
776                                         emit(sw::Shader::OPCODE_MOV, result, left);\r
777                                         Instruction *ifnot = emit(sw::Shader::OPCODE_IF, 0, result);\r
778                                         ifnot->src[0].modifier = sw::Shader::MODIFIER_NOT;\r
779                                 }\r
780                                 else if(visit == PostVisit)\r
781                                 {\r
782                                         emit(sw::Shader::OPCODE_MOV, result, right);\r
783                                         emit(sw::Shader::OPCODE_ENDIF);\r
784                                 }\r
785                         }\r
786                         break;\r
787                 case EOpLogicalXor:        if(visit == PostVisit) emit(sw::Shader::OPCODE_XOR, result, left, right); break;\r
788                 case EOpLogicalAnd:\r
789                         if(trivial(right, 6))\r
790                         {\r
791                                 if(visit == PostVisit)\r
792                                 {\r
793                                         emit(sw::Shader::OPCODE_AND, result, left, right);\r
794                                 }\r
795                         }\r
796                         else   // Short-circuit evaluation\r
797                         {\r
798                                 if(visit == InVisit)\r
799                                 {\r
800                                         emit(sw::Shader::OPCODE_MOV, result, left);\r
801                                         emit(sw::Shader::OPCODE_IF, 0, result);\r
802                                 }\r
803                                 else if(visit == PostVisit)\r
804                                 {\r
805                                         emit(sw::Shader::OPCODE_MOV, result, right);\r
806                                         emit(sw::Shader::OPCODE_ENDIF);\r
807                                 }\r
808                         }\r
809                         break;\r
810                 default: UNREACHABLE(node->getOp());\r
811                 }\r
812 \r
813                 return true;\r
814         }\r
815 \r
816         void OutputASM::emitDeterminant(TIntermTyped *result, TIntermTyped *arg, int size, int col, int row, int outCol, int outRow)\r
817         {\r
818                 switch(size)\r
819                 {\r
820                 case 1: // Used for cofactor computation only\r
821                         {\r
822                                 // For a 2x2 matrix, the cofactor is simply a transposed move or negate\r
823                                 bool isMov = (row == col);\r
824                                 sw::Shader::Opcode op = isMov ? sw::Shader::OPCODE_MOV : sw::Shader::OPCODE_NEG;\r
825                                 Instruction *mov = emit(op, result, outCol, arg, isMov ? 1 - row : row);\r
826                                 mov->src[0].swizzle = 0x55 * (isMov ? 1 - col : col);\r
827                                 mov->dst.mask = 1 << outRow;\r
828                         }\r
829                         break;\r
830                 case 2:\r
831                         {\r
832                                 static const unsigned int swizzle[3] = { 0x99, 0x88, 0x44 }; // xy?? : yzyz, xzxz, xyxy\r
833 \r
834                                 bool isCofactor = (col >= 0) && (row >= 0);\r
835                                 int col0 = (isCofactor && (col <= 0)) ? 1 : 0;\r
836                                 int col1 = (isCofactor && (col <= 1)) ? 2 : 1;\r
837                                 bool negate = isCofactor && ((col & 0x01) ^ (row & 0x01));\r
838 \r
839                                 Instruction *det = emit(sw::Shader::OPCODE_DET2, result, outCol, arg, negate ? col1 : col0, arg, negate ? col0 : col1);\r
840                                 det->src[0].swizzle = det->src[1].swizzle = swizzle[isCofactor ? row : 2];\r
841                                 det->dst.mask = 1 << outRow;\r
842                         }\r
843                         break;\r
844                 case 3:\r
845                         {\r
846                                 static const unsigned int swizzle[4] = { 0xF9, 0xF8, 0xF4, 0xE4 }; // xyz? : yzww, xzww, xyww, xyzw\r
847 \r
848                                 bool isCofactor = (col >= 0) && (row >= 0);\r
849                                 int col0 = (isCofactor && (col <= 0)) ? 1 : 0;\r
850                                 int col1 = (isCofactor && (col <= 1)) ? 2 : 1;\r
851                                 int col2 = (isCofactor && (col <= 2)) ? 3 : 2;\r
852                                 bool negate = isCofactor && ((col & 0x01) ^ (row & 0x01));\r
853 \r
854                                 Instruction *det = emit(sw::Shader::OPCODE_DET3, result, outCol, arg, col0, arg, negate ? col2 : col1, arg, negate ? col1 : col2);\r
855                                 det->src[0].swizzle = det->src[1].swizzle = det->src[2].swizzle = swizzle[isCofactor ? row : 3];\r
856                                 det->dst.mask = 1 << outRow;\r
857                         }\r
858                         break;\r
859                 case 4:\r
860                         {\r
861                                 Instruction *det = emit(sw::Shader::OPCODE_DET4, result, outCol, arg, 0, arg, 1, arg, 2, arg, 3);\r
862                                 det->dst.mask = 1 << outRow;\r
863                         }\r
864                         break;\r
865                 default:\r
866                         UNREACHABLE(size);\r
867                         break;\r
868                 }\r
869         }\r
870 \r
871         bool OutputASM::visitUnary(Visit visit, TIntermUnary *node)\r
872         {\r
873                 if(currentScope != emitScope)\r
874                 {\r
875                         return false;\r
876                 }\r
877 \r
878                 TIntermTyped *result = node;\r
879                 TIntermTyped *arg = node->getOperand();\r
880                 TBasicType basicType = arg->getType().getBasicType();\r
881 \r
882                 union\r
883                 {\r
884                         float f;\r
885                         int i;\r
886                 } one_value;\r
887 \r
888                 if(basicType == EbtInt || basicType == EbtUInt)\r
889                 {\r
890                         one_value.i = 1;\r
891                 }\r
892                 else\r
893                 {\r
894                         one_value.f = 1.0f;\r
895                 }\r
896 \r
897                 Constant one(one_value.f, one_value.f, one_value.f, one_value.f);\r
898                 Constant rad(1.74532925e-2f, 1.74532925e-2f, 1.74532925e-2f, 1.74532925e-2f);\r
899                 Constant deg(5.72957795e+1f, 5.72957795e+1f, 5.72957795e+1f, 5.72957795e+1f);\r
900 \r
901                 switch(node->getOp())\r
902                 {\r
903                 case EOpNegative:\r
904                         if(visit == PostVisit)\r
905                         {\r
906                                 sw::Shader::Opcode negOpcode = getOpcode(sw::Shader::OPCODE_NEG, arg);\r
907                                 for(int index = 0; index < arg->totalRegisterCount(); index++)\r
908                                 {\r
909                                         emit(negOpcode, result, index, arg, index);\r
910                                 }\r
911                         }\r
912                         break;\r
913                 case EOpVectorLogicalNot: if(visit == PostVisit) emit(sw::Shader::OPCODE_NOT, result, arg); break;\r
914                 case EOpLogicalNot:       if(visit == PostVisit) emit(sw::Shader::OPCODE_NOT, result, arg); break;\r
915                 case EOpPostIncrement:\r
916                         if(visit == PostVisit)\r
917                         {\r
918                                 copy(result, arg);\r
919 \r
920                                 sw::Shader::Opcode addOpcode = getOpcode(sw::Shader::OPCODE_ADD, arg);\r
921                                 for(int index = 0; index < arg->totalRegisterCount(); index++)\r
922                                 {\r
923                                         emit(addOpcode, arg, index, arg, index, &one);\r
924                                 }\r
925 \r
926                                 assignLvalue(arg, arg);\r
927                         }\r
928                         break;\r
929                 case EOpPostDecrement:\r
930                         if(visit == PostVisit)\r
931                         {\r
932                                 copy(result, arg);\r
933 \r
934                                 sw::Shader::Opcode subOpcode = getOpcode(sw::Shader::OPCODE_SUB, arg);\r
935                                 for(int index = 0; index < arg->totalRegisterCount(); index++)\r
936                                 {\r
937                                         emit(subOpcode, arg, index, arg, index, &one);\r
938                                 }\r
939 \r
940                                 assignLvalue(arg, arg);\r
941                         }\r
942                         break;\r
943                 case EOpPreIncrement:\r
944                         if(visit == PostVisit)\r
945                         {\r
946                                 sw::Shader::Opcode addOpcode = getOpcode(sw::Shader::OPCODE_ADD, arg);\r
947                                 for(int index = 0; index < arg->totalRegisterCount(); index++)\r
948                                 {\r
949                                         emit(addOpcode, result, index, arg, index, &one);\r
950                                 }\r
951 \r
952                                 assignLvalue(arg, result);\r
953                         }\r
954                         break;\r
955                 case EOpPreDecrement:\r
956                         if(visit == PostVisit)\r
957                         {\r
958                                 sw::Shader::Opcode subOpcode = getOpcode(sw::Shader::OPCODE_SUB, arg);\r
959                                 for(int index = 0; index < arg->totalRegisterCount(); index++)\r
960                                 {\r
961                                         emit(subOpcode, result, index, arg, index, &one);\r
962                                 }\r
963 \r
964                                 assignLvalue(arg, result);\r
965                         }\r
966                         break;\r
967                 case EOpRadians:          if(visit == PostVisit) emit(sw::Shader::OPCODE_MUL, result, arg, &rad); break;\r
968                 case EOpDegrees:          if(visit == PostVisit) emit(sw::Shader::OPCODE_MUL, result, arg, &deg); break;\r
969                 case EOpSin:              if(visit == PostVisit) emit(sw::Shader::OPCODE_SIN, result, arg); break;\r
970                 case EOpCos:              if(visit == PostVisit) emit(sw::Shader::OPCODE_COS, result, arg); break;\r
971                 case EOpTan:              if(visit == PostVisit) emit(sw::Shader::OPCODE_TAN, result, arg); break;\r
972                 case EOpAsin:             if(visit == PostVisit) emit(sw::Shader::OPCODE_ASIN, result, arg); break;\r
973                 case EOpAcos:             if(visit == PostVisit) emit(sw::Shader::OPCODE_ACOS, result, arg); break;\r
974                 case EOpAtan:             if(visit == PostVisit) emit(sw::Shader::OPCODE_ATAN, result, arg); break;\r
975                 case EOpSinh:             if(visit == PostVisit) emit(sw::Shader::OPCODE_SINH, result, arg); break;\r
976                 case EOpCosh:             if(visit == PostVisit) emit(sw::Shader::OPCODE_COSH, result, arg); break;\r
977                 case EOpTanh:             if(visit == PostVisit) emit(sw::Shader::OPCODE_TANH, result, arg); break;\r
978                 case EOpAsinh:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ASINH, result, arg); break;\r
979                 case EOpAcosh:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ACOSH, result, arg); break;\r
980                 case EOpAtanh:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ATANH, result, arg); break;\r
981                 case EOpExp:              if(visit == PostVisit) emit(sw::Shader::OPCODE_EXP, result, arg); break;\r
982                 case EOpLog:              if(visit == PostVisit) emit(sw::Shader::OPCODE_LOG, result, arg); break;\r
983                 case EOpExp2:             if(visit == PostVisit) emit(sw::Shader::OPCODE_EXP2, result, arg); break;\r
984                 case EOpLog2:             if(visit == PostVisit) emit(sw::Shader::OPCODE_LOG2, result, arg); break;\r
985                 case EOpSqrt:             if(visit == PostVisit) emit(sw::Shader::OPCODE_SQRT, result, arg); break;\r
986                 case EOpInverseSqrt:      if(visit == PostVisit) emit(sw::Shader::OPCODE_RSQ, result, arg); break;\r
987                 case EOpAbs:              if(visit == PostVisit) emit(sw::Shader::OPCODE_ABS, result, arg); break;\r
988                 case EOpSign:             if(visit == PostVisit) emit(sw::Shader::OPCODE_SGN, result, arg); break;\r
989                 case EOpFloor:            if(visit == PostVisit) emit(sw::Shader::OPCODE_FLOOR, result, arg); break;\r
990                 case EOpTrunc:            if(visit == PostVisit) emit(sw::Shader::OPCODE_TRUNC, result, arg); break;\r
991                 case EOpRound:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ROUND, result, arg); break;\r
992                 case EOpRoundEven:        if(visit == PostVisit) emit(sw::Shader::OPCODE_ROUNDEVEN, result, arg); break;\r
993                 case EOpCeil:             if(visit == PostVisit) emit(sw::Shader::OPCODE_CEIL, result, arg, result); break;\r
994                 case EOpFract:            if(visit == PostVisit) emit(sw::Shader::OPCODE_FRC, result, arg); break;\r
995                 case EOpIsNan:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ISNAN, result, arg); break;\r
996                 case EOpIsInf:            if(visit == PostVisit) emit(sw::Shader::OPCODE_ISINF, result, arg); break;\r
997                 case EOpLength:           if(visit == PostVisit) emit(sw::Shader::OPCODE_LEN(dim(arg)), result, arg); break;\r
998                 case EOpNormalize:        if(visit == PostVisit) emit(sw::Shader::OPCODE_NRM(dim(arg)), result, arg); break;\r
999                 case EOpDFdx:             if(visit == PostVisit) emit(sw::Shader::OPCODE_DFDX, result, arg); break;\r
1000                 case EOpDFdy:             if(visit == PostVisit) emit(sw::Shader::OPCODE_DFDY, result, arg); break;\r
1001                 case EOpFwidth:           if(visit == PostVisit) emit(sw::Shader::OPCODE_FWIDTH, result, arg); break;\r
1002                 case EOpAny:              if(visit == PostVisit) emit(sw::Shader::OPCODE_ANY, result, arg); break;\r
1003                 case EOpAll:              if(visit == PostVisit) emit(sw::Shader::OPCODE_ALL, result, arg); break;\r
1004                 case EOpFloatBitsToInt:   if(visit == PostVisit) emit(sw::Shader::OPCODE_FLOATBITSTOINT, result, arg); break;\r
1005                 case EOpFloatBitsToUint:  if(visit == PostVisit) emit(sw::Shader::OPCODE_FLOATBITSTOUINT, result, arg); break;\r
1006                 case EOpIntBitsToFloat:   if(visit == PostVisit) emit(sw::Shader::OPCODE_INTBITSTOFLOAT, result, arg); break;\r
1007                 case EOpUintBitsToFloat:  if(visit == PostVisit) emit(sw::Shader::OPCODE_UINTBITSTOFLOAT, result, arg); break;\r
1008                 case EOpPackSnorm2x16:    if(visit == PostVisit) emit(sw::Shader::OPCODE_PACKSNORM2x16, result, arg); break;\r
1009                 case EOpPackUnorm2x16:    if(visit == PostVisit) emit(sw::Shader::OPCODE_PACKUNORM2x16, result, arg); break;\r
1010                 case EOpPackHalf2x16:     if(visit == PostVisit) emit(sw::Shader::OPCODE_PACKHALF2x16, result, arg); break;\r
1011                 case EOpUnpackSnorm2x16:  if(visit == PostVisit) emit(sw::Shader::OPCODE_UNPACKSNORM2x16, result, arg); break;\r
1012                 case EOpUnpackUnorm2x16:  if(visit == PostVisit) emit(sw::Shader::OPCODE_UNPACKUNORM2x16, result, arg); break;\r
1013                 case EOpUnpackHalf2x16:   if(visit == PostVisit) emit(sw::Shader::OPCODE_UNPACKHALF2x16, result, arg); break;\r
1014                 case EOpTranspose:\r
1015                         if(visit == PostVisit)\r
1016                         {\r
1017                                 int numCols = arg->getNominalSize();\r
1018                                 int numRows = arg->getSecondarySize();\r
1019                                 for(int i = 0; i < numCols; ++i)\r
1020                                 {\r
1021                                         for(int j = 0; j < numRows; ++j)\r
1022                                         {\r
1023                                                 Instruction *mov = emit(sw::Shader::OPCODE_MOV, result, j, arg, i);\r
1024                                                 mov->src[0].swizzle = 0x55 * j;\r
1025                                                 mov->dst.mask = 1 << i;\r
1026                                         }\r
1027                                 }\r
1028                         }\r
1029                         break;\r
1030                 case EOpDeterminant:\r
1031                         if(visit == PostVisit)\r
1032                         {\r
1033                                 int size = arg->getNominalSize();\r
1034                                 ASSERT(size == arg->getSecondarySize());\r
1035 \r
1036                                 emitDeterminant(result, arg, size);\r
1037                         }\r
1038                         break;\r
1039                 case EOpInverse:\r
1040                         if(visit == PostVisit)\r
1041                         {\r
1042                                 int size = arg->getNominalSize();\r
1043                                 ASSERT(size == arg->getSecondarySize());\r
1044 \r
1045                                 // Compute transposed matrix of cofactors\r
1046                                 for(int i = 0; i < size; ++i)\r
1047                                 {\r
1048                                         for(int j = 0; j < size; ++j)\r
1049                                         {\r
1050                                                 // For a 2x2 matrix, the cofactor is simply a transposed move or negate\r
1051                                                 // For a 3x3 or 4x4 matrix, the cofactor is a transposed determinant\r
1052                                                 emitDeterminant(result, arg, size - 1, j, i, i, j);\r
1053                                         }\r
1054                                 }\r
1055 \r
1056                                 // Compute 1 / determinant\r
1057                                 Temporary invDet(this);\r
1058                                 emitDeterminant(&invDet, arg, size);\r
1059                                 Constant one(1.0f, 1.0f, 1.0f, 1.0f);\r
1060                                 Instruction *div = emit(sw::Shader::OPCODE_DIV, &invDet, &one, &invDet);\r
1061                                 div->src[1].swizzle = 0x00; // xxxx\r
1062 \r
1063                                 // Divide transposed matrix of cofactors by determinant\r
1064                                 for(int i = 0; i < size; ++i)\r
1065                                 {\r
1066                                         emit(sw::Shader::OPCODE_MUL, result, i, result, i, &invDet);\r
1067                                 }\r
1068                         }\r
1069                         break;\r
1070                 default: UNREACHABLE(node->getOp());\r
1071                 }\r
1072 \r
1073                 return true;\r
1074         }\r
1075 \r
1076         bool OutputASM::visitAggregate(Visit visit, TIntermAggregate *node)\r
1077         {\r
1078                 if(currentScope != emitScope && node->getOp() != EOpFunction && node->getOp() != EOpSequence)\r
1079                 {\r
1080                         return false;\r
1081                 }\r
1082 \r
1083                 Constant zero(0.0f, 0.0f, 0.0f, 0.0f);\r
1084 \r
1085                 TIntermTyped *result = node;\r
1086                 const TType &resultType = node->getType();\r
1087                 TIntermSequence &arg = node->getSequence();\r
1088                 int argumentCount = arg.size();\r
1089 \r
1090                 switch(node->getOp())\r
1091                 {\r
1092                 case EOpSequence:           break;\r
1093                 case EOpDeclaration:        break;\r
1094                 case EOpPrototype:          break;\r
1095                 case EOpComma:\r
1096                         if(visit == PostVisit)\r
1097                         {\r
1098                                 copy(result, arg[1]);\r
1099                         }\r
1100                         break;\r
1101                 case EOpFunction:\r
1102                         if(visit == PreVisit)\r
1103                         {\r
1104                                 const TString &name = node->getName();\r
1105 \r
1106                                 if(emitScope == FUNCTION)\r
1107                                 {\r
1108                                         if(functionArray.size() > 1)   // No need for a label when there's only main()\r
1109                                         {\r
1110                                                 Instruction *label = emit(sw::Shader::OPCODE_LABEL);\r
1111                                                 label->dst.type = sw::Shader::PARAMETER_LABEL;\r
1112 \r
1113                                                 const Function *function = findFunction(name);\r
1114                                                 ASSERT(function);   // Should have been added during global pass\r
1115                                                 label->dst.index = function->label;\r
1116                                                 currentFunction = function->label;\r
1117                                         }\r
1118                                 }\r
1119                                 else if(emitScope == GLOBAL)\r
1120                                 {\r
1121                                         if(name != "main(")\r
1122                                         {\r
1123                                                 TIntermSequence &arguments = node->getSequence()[0]->getAsAggregate()->getSequence();\r
1124                                                 functionArray.push_back(Function(functionArray.size(), name, &arguments, node));\r
1125                                         }\r
1126                                 }\r
1127                                 else UNREACHABLE(emitScope);\r
1128 \r
1129                                 currentScope = FUNCTION;\r
1130                         }\r
1131                         else if(visit == PostVisit)\r
1132                         {\r
1133                                 if(emitScope == FUNCTION)\r
1134                                 {\r
1135                                         if(functionArray.size() > 1)   // No need to return when there's only main()\r
1136                                         {\r
1137                                                 emit(sw::Shader::OPCODE_RET);\r
1138                                         }\r
1139                                 }\r
1140 \r
1141                                 currentScope = GLOBAL;\r
1142                         }\r
1143                         break;\r
1144                 case EOpFunctionCall:\r
1145                         if(visit == PostVisit)\r
1146                         {\r
1147                                 if(node->isUserDefined())\r
1148                                 {\r
1149                                         const TString &name = node->getName();\r
1150                                         const Function *function = findFunction(name);\r
1151 \r
1152                                         if(!function)\r
1153                                         {\r
1154                                                 mContext.error(node->getLine(), "function definition not found", name.c_str());\r
1155                                                 return false;\r
1156                                         }\r
1157 \r
1158                                         TIntermSequence &arguments = *function->arg;\r
1159 \r
1160                                         for(int i = 0; i < argumentCount; i++)\r
1161                                         {\r
1162                                                 TIntermTyped *in = arguments[i]->getAsTyped();\r
1163 \r
1164                                                 if(in->getQualifier() == EvqIn ||\r
1165                                                    in->getQualifier() == EvqInOut ||\r
1166                                                    in->getQualifier() == EvqConstReadOnly)\r
1167                                                 {\r
1168                                                         copy(in, arg[i]);\r
1169                                                 }\r
1170                                         }\r
1171 \r
1172                                         Instruction *call = emit(sw::Shader::OPCODE_CALL);\r
1173                                         call->dst.type = sw::Shader::PARAMETER_LABEL;\r
1174                                         call->dst.index = function->label;\r
1175 \r
1176                                         if(function->ret && function->ret->getType().getBasicType() != EbtVoid)\r
1177                                         {\r
1178                                                 copy(result, function->ret);\r
1179                                         }\r
1180 \r
1181                                         for(int i = 0; i < argumentCount; i++)\r
1182                                         {\r
1183                                                 TIntermTyped *argument = arguments[i]->getAsTyped();\r
1184                                                 TIntermTyped *out = arg[i]->getAsTyped();\r
1185 \r
1186                                                 if(argument->getQualifier() == EvqOut ||\r
1187                                                    argument->getQualifier() == EvqInOut)\r
1188                                                 {\r
1189                                                         copy(out, argument);\r
1190                                                 }\r
1191                                         }\r
1192                                 }\r
1193                                 else\r
1194                                 {\r
1195                                         const TextureFunction textureFunction(node->getName());\r
1196                                         switch(textureFunction.method)\r
1197                                         {\r
1198                                         case TextureFunction::IMPLICIT:\r
1199                                                 {\r
1200                                                         TIntermTyped *t = arg[1]->getAsTyped();\r
1201 \r
1202                                                         TIntermNode* offset = textureFunction.offset ? arg[2] : 0;\r
1203 \r
1204                                                         if(argumentCount == 2 || (textureFunction.offset && argumentCount == 3))\r
1205                                                         {\r
1206                                                                 Instruction *tex = emit(textureFunction.offset ? sw::Shader::OPCODE_TEXOFFSET : sw::Shader::OPCODE_TEX,\r
1207                                                                                         result, arg[1], arg[0], offset);\r
1208                                                                 if(textureFunction.proj)\r
1209                                                                 {\r
1210                                                                         tex->project = true;\r
1211 \r
1212                                                                         switch(t->getNominalSize())\r
1213                                                                         {\r
1214                                                                         case 2: tex->src[0].swizzle = 0x54; break; // xyyy\r
1215                                                                         case 3: tex->src[0].swizzle = 0xA4; break; // xyzz\r
1216                                                                         case 4: break; // xyzw\r
1217                                                                         default:\r
1218                                                                                 UNREACHABLE(t->getNominalSize());\r
1219                                                                                 break;\r
1220                                                                         }\r
1221                                                                 }\r
1222                                                         }\r
1223                                                         else if(argumentCount == 3 || (textureFunction.offset && argumentCount == 4))   // bias\r
1224                                                         {\r
1225                                                                 Temporary proj(this);\r
1226                                                                 if(textureFunction.proj)\r
1227                                                                 {\r
1228                                                                         Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);\r
1229                                                                         div->dst.mask = 0x3;\r
1230 \r
1231                                                                         switch(t->getNominalSize())\r
1232                                                                         {\r
1233                                                                         case 2:\r
1234                                                                         case 3:\r
1235                                                                         case 4:\r
1236                                                                                 div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);\r
1237                                                                                 break;\r
1238                                                                         default:\r
1239                                                                                 UNREACHABLE(t->getNominalSize());\r
1240                                                                                 break;\r
1241                                                                         }\r
1242                                                                 }\r
1243                                                                 else\r
1244                                                                 {\r
1245                                                                         emit(sw::Shader::OPCODE_MOV, &proj, arg[1]);\r
1246                                                                 }\r
1247 \r
1248                                                                 Instruction *bias = emit(sw::Shader::OPCODE_MOV, &proj, arg[textureFunction.offset ? 3 : 2]);\r
1249                                                                 bias->dst.mask = 0x8;\r
1250 \r
1251                                                                 Instruction *tex = emit(textureFunction.offset ? sw::Shader::OPCODE_TEXOFFSET : sw::Shader::OPCODE_TEX,\r
1252                                                                                         result, &proj, arg[0], offset); // FIXME: Implement an efficient TEXLDB instruction\r
1253                                                                 tex->bias = true;\r
1254                                                         }\r
1255                                                         else UNREACHABLE(argumentCount);\r
1256                                                 }\r
1257                                                 break;\r
1258                                         case TextureFunction::LOD:\r
1259                                                 {\r
1260                                                         TIntermTyped *t = arg[1]->getAsTyped();\r
1261                                                         Temporary proj(this);\r
1262 \r
1263                                                         if(textureFunction.proj)\r
1264                                                         {\r
1265                                                                 Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);\r
1266                                                                 div->dst.mask = 0x3;\r
1267 \r
1268                                                                 switch(t->getNominalSize())\r
1269                                                                 {\r
1270                                                                 case 2:\r
1271                                                                 case 3:\r
1272                                                                 case 4:\r
1273                                                                         div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);\r
1274                                                                         break;\r
1275                                                                 default:\r
1276                                                                         UNREACHABLE(t->getNominalSize());\r
1277                                                                         break;\r
1278                                                                 }\r
1279                                                         }\r
1280                                                         else\r
1281                                                         {\r
1282                                                                 emit(sw::Shader::OPCODE_MOV, &proj, arg[1]);\r
1283                                                         }\r
1284 \r
1285                                                         Instruction *lod = emit(sw::Shader::OPCODE_MOV, &proj, arg[2]);\r
1286                                                         lod->dst.mask = 0x8;\r
1287 \r
1288                                                         emit(textureFunction.offset ? sw::Shader::OPCODE_TEXLDLOFFSET : sw::Shader::OPCODE_TEXLDL,\r
1289                                                              result, &proj, arg[0], textureFunction.offset ? arg[3] : 0);\r
1290                                                 }\r
1291                                                 break;\r
1292                                         case TextureFunction::FETCH:\r
1293                                                 {\r
1294                                                         TIntermTyped *t = arg[1]->getAsTyped();\r
1295 \r
1296                                                         if(argumentCount == 3 || (textureFunction.offset && argumentCount == 4))\r
1297                                                         {\r
1298                                                                 TIntermNode* offset = textureFunction.offset ? arg[3] : 0;\r
1299 \r
1300                                                                 emit(textureFunction.offset ? sw::Shader::OPCODE_TEXELFETCHOFFSET : sw::Shader::OPCODE_TEXELFETCH,\r
1301                                                                      result, arg[1], arg[0], arg[2], offset);\r
1302                                                         }\r
1303                                                         else UNREACHABLE(argumentCount);\r
1304                                                 }\r
1305                                                 break;\r
1306                                         case TextureFunction::GRAD:\r
1307                                                 {\r
1308                                                         TIntermTyped *t = arg[1]->getAsTyped();\r
1309 \r
1310                                                         if(argumentCount == 4 || (textureFunction.offset && argumentCount == 5))\r
1311                                                         {\r
1312                                                                 Temporary uvwb(this);\r
1313 \r
1314                                                                 if(textureFunction.proj)\r
1315                                                                 {\r
1316                                                                         Instruction *div = emit(sw::Shader::OPCODE_DIV, &uvwb, arg[1], arg[1]);\r
1317                                                                         div->dst.mask = 0x3;\r
1318 \r
1319                                                                         switch(t->getNominalSize())\r
1320                                                                         {\r
1321                                                                         case 2:\r
1322                                                                         case 3:\r
1323                                                                         case 4:\r
1324                                                                                 div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);\r
1325                                                                                 break;\r
1326                                                                         default:\r
1327                                                                                 UNREACHABLE(t->getNominalSize());\r
1328                                                                                 break;\r
1329                                                                         }\r
1330                                                                 }\r
1331                                                                 else\r
1332                                                                 {\r
1333                                                                         emit(sw::Shader::OPCODE_MOV, &uvwb, arg[1]);\r
1334                                                                 }\r
1335 \r
1336                                                                 TIntermNode* offset = textureFunction.offset ? arg[4] : 0;\r
1337 \r
1338                                                                 emit(textureFunction.offset ? sw::Shader::OPCODE_TEXGRADOFFSET : sw::Shader::OPCODE_TEXGRAD,\r
1339                                                                      result, &uvwb, arg[0], arg[2], arg[3], offset);\r
1340                                                         }\r
1341                                                         else UNREACHABLE(argumentCount);\r
1342                                                 }\r
1343                                                 break;\r
1344                                         case TextureFunction::SIZE:\r
1345                                                 emit(sw::Shader::OPCODE_TEXSIZE, result, arg[1], arg[0]);\r
1346                                                 break;\r
1347                                         default:\r
1348                                                 UNREACHABLE(textureFunction.method);\r
1349                                         }\r
1350                                 }\r
1351                         }\r
1352                         break;\r
1353                 case EOpParameters:\r
1354                         break;\r
1355                 case EOpConstructFloat:\r
1356                 case EOpConstructVec2:\r
1357                 case EOpConstructVec3:\r
1358                 case EOpConstructVec4:\r
1359                 case EOpConstructBool:\r
1360                 case EOpConstructBVec2:\r
1361                 case EOpConstructBVec3:\r
1362                 case EOpConstructBVec4:\r
1363                 case EOpConstructInt:\r
1364                 case EOpConstructIVec2:\r
1365                 case EOpConstructIVec3:\r
1366                 case EOpConstructIVec4:\r
1367                 case EOpConstructUInt:\r
1368                 case EOpConstructUVec2:\r
1369                 case EOpConstructUVec3:\r
1370                 case EOpConstructUVec4:\r
1371                         if(visit == PostVisit)\r
1372                         {\r
1373                                 int component = 0;\r
1374 \r
1375                                 for(int i = 0; i < argumentCount; i++)\r
1376                                 {\r
1377                                         TIntermTyped *argi = arg[i]->getAsTyped();\r
1378                                         int size = argi->getNominalSize();\r
1379 \r
1380                                         if(!argi->isMatrix())\r
1381                                         {\r
1382                                                 Instruction *mov = emitCast(result, argi);\r
1383                                                 mov->dst.mask = (0xF << component) & 0xF;\r
1384                                                 mov->src[0].swizzle = readSwizzle(argi, size) << (component * 2);\r
1385 \r
1386                                                 component += size;\r
1387                                         }\r
1388                                         else   // Matrix\r
1389                                         {\r
1390                                                 int column = 0;\r
1391 \r
1392                                                 while(component < resultType.getNominalSize())\r
1393                                                 {\r
1394                                                         Instruction *mov = emitCast(result, 0, argi, column);\r
1395                                                         mov->dst.mask = (0xF << component) & 0xF;\r
1396                                                         mov->src[0].swizzle = readSwizzle(argi, size) << (component * 2);\r
1397 \r
1398                                                         column++;\r
1399                                                         component += size;\r
1400                                                 }\r
1401                                         }\r
1402                                 }\r
1403                         }\r
1404                         break;\r
1405                 case EOpConstructMat2:\r
1406                 case EOpConstructMat2x3:\r
1407                 case EOpConstructMat2x4:\r
1408                 case EOpConstructMat3x2:\r
1409                 case EOpConstructMat3:\r
1410                 case EOpConstructMat3x4:\r
1411                 case EOpConstructMat4x2:\r
1412                 case EOpConstructMat4x3:\r
1413                 case EOpConstructMat4:\r
1414                         if(visit == PostVisit)\r
1415                         {\r
1416                                 TIntermTyped *arg0 = arg[0]->getAsTyped();\r
1417                                 const int outCols = result->getNominalSize();\r
1418                                 const int outRows = result->getSecondarySize();\r
1419 \r
1420                                 if(arg0->isScalar() && arg.size() == 1)   // Construct scale matrix\r
1421                                 {\r
1422                                         for(int i = 0; i < outCols; i++)\r
1423                                         {\r
1424                                                 Instruction *init = emit(sw::Shader::OPCODE_MOV, result, i, &zero);\r
1425                                                 Instruction *mov = emitCast(result, i, arg0, 0);\r
1426                                                 mov->dst.mask = 1 << i;\r
1427                                                 ASSERT(mov->src[0].swizzle == 0x00);\r
1428                                         }\r
1429                                 }\r
1430                                 else if(arg0->isMatrix())\r
1431                                 {\r
1432                                         const int inCols = arg0->getNominalSize();\r
1433                                         const int inRows = arg0->getSecondarySize();\r
1434 \r
1435                                         for(int i = 0; i < outCols; i++)\r
1436                                         {\r
1437                                                 if(i >= inCols || outRows > inRows)\r
1438                                                 {\r
1439                                                         // Initialize to identity matrix\r
1440                                                         Constant col((i == 0 ? 1.0f : 0.0f), (i == 1 ? 1.0f : 0.0f), (i == 2 ? 1.0f : 0.0f), (i == 3 ? 1.0f : 0.0f));\r
1441                                                         Instruction *mov = emitCast(result, i, &col, 0);\r
1442                                                 }\r
1443 \r
1444                                                 if(i < inCols)\r
1445                                                 {\r
1446                                                         Instruction *mov = emitCast(result, i, arg0, i);\r
1447                                                         mov->dst.mask = 0xF >> (4 - inRows);\r
1448                                                 }\r
1449                                         }\r
1450                                 }\r
1451                                 else\r
1452                                 {\r
1453                                         int column = 0;\r
1454                                         int row = 0;\r
1455 \r
1456                                         for(int i = 0; i < argumentCount; i++)\r
1457                                         {\r
1458                                                 TIntermTyped *argi = arg[i]->getAsTyped();\r
1459                                                 int size = argi->getNominalSize();\r
1460                                                 int element = 0;\r
1461 \r
1462                                                 while(element < size)\r
1463                                                 {\r
1464                                                         Instruction *mov = emitCast(result, column, argi, 0);\r
1465                                                         mov->dst.mask = (0xF << row) & 0xF;\r
1466                                                         mov->src[0].swizzle = (readSwizzle(argi, size) << (row * 2)) + 0x55 * element;\r
1467 \r
1468                                                         int end = row + size - element;\r
1469                                                         column = end >= outRows ? column + 1 : column;\r
1470                                                         element = element + outRows - row;\r
1471                                                         row = end >= outRows ? 0 : end;\r
1472                                                 }\r
1473                                         }\r
1474                                 }\r
1475                         }\r
1476                         break;\r
1477                 case EOpConstructStruct:\r
1478                         if(visit == PostVisit)\r
1479                         {\r
1480                                 int offset = 0;\r
1481                                 for(int i = 0; i < argumentCount; i++)\r
1482                                 {\r
1483                                         TIntermTyped *argi = arg[i]->getAsTyped();\r
1484                                         int size = argi->totalRegisterCount();\r
1485 \r
1486                                         for(int index = 0; index < size; index++)\r
1487                                         {\r
1488                                                 Instruction *mov = emit(sw::Shader::OPCODE_MOV, result, index + offset, argi, index);\r
1489                                                 mov->dst.mask = writeMask(result, offset + index);\r
1490                                         }\r
1491 \r
1492                                         offset += size;\r
1493                                 }\r
1494                         }\r
1495                         break;\r
1496                 case EOpLessThan:         if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_LT, result, arg[0], arg[1]); break;\r
1497                 case EOpGreaterThan:      if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_GT, result, arg[0], arg[1]); break;\r
1498                 case EOpLessThanEqual:    if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_LE, result, arg[0], arg[1]); break;\r
1499                 case EOpGreaterThanEqual: if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_GE, result, arg[0], arg[1]); break;\r
1500                 case EOpVectorEqual:      if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_EQ, result, arg[0], arg[1]); break;\r
1501                 case EOpVectorNotEqual:   if(visit == PostVisit) emitCmp(sw::Shader::CONTROL_NE, result, arg[0], arg[1]); break;\r
1502                 case EOpMod:              if(visit == PostVisit) emit(sw::Shader::OPCODE_MOD, result, arg[0], arg[1]); break;\r
1503                 case EOpPow:              if(visit == PostVisit) emit(sw::Shader::OPCODE_POW, result, arg[0], arg[1]); break;\r
1504                 case EOpAtan:             if(visit == PostVisit) emit(sw::Shader::OPCODE_ATAN2, result, arg[0], arg[1]); break;\r
1505                 case EOpMin:              if(visit == PostVisit) emit(getOpcode(sw::Shader::OPCODE_MIN, result), result, arg[0], arg[1]); break;\r
1506                 case EOpMax:              if(visit == PostVisit) emit(getOpcode(sw::Shader::OPCODE_MAX, result), result, arg[0], arg[1]); break;\r
1507                 case EOpClamp:\r
1508                         if(visit == PostVisit)\r
1509                         {\r
1510                                 emit(getOpcode(sw::Shader::OPCODE_MAX, result), result, arg[0], arg[1]);\r
1511                                 emit(getOpcode(sw::Shader::OPCODE_MIN, result), result, result, arg[2]);\r
1512                         }\r
1513                         break;\r
1514                 case EOpMix:         if(visit == PostVisit) emit(sw::Shader::OPCODE_LRP, result, arg[2], arg[1], arg[0]); break;\r
1515                 case EOpStep:        if(visit == PostVisit) emit(sw::Shader::OPCODE_STEP, result, arg[0], arg[1]); break;\r
1516                 case EOpSmoothStep:  if(visit == PostVisit) emit(sw::Shader::OPCODE_SMOOTH, result, arg[0], arg[1], arg[2]); break;\r
1517                 case EOpDistance:    if(visit == PostVisit) emit(sw::Shader::OPCODE_DIST(dim(arg[0])), result, arg[0], arg[1]); break;\r
1518                 case EOpDot:         if(visit == PostVisit) emit(sw::Shader::OPCODE_DP(dim(arg[0])), result, arg[0], arg[1]); break;\r
1519                 case EOpCross:       if(visit == PostVisit) emit(sw::Shader::OPCODE_CRS, result, arg[0], arg[1]); break;\r
1520                 case EOpFaceForward: if(visit == PostVisit) emit(sw::Shader::OPCODE_FORWARD(dim(arg[0])), result, arg[0], arg[1], arg[2]); break;\r
1521                 case EOpReflect:     if(visit == PostVisit) emit(sw::Shader::OPCODE_REFLECT(dim(arg[0])), result, arg[0], arg[1]); break;\r
1522                 case EOpRefract:     if(visit == PostVisit) emit(sw::Shader::OPCODE_REFRACT(dim(arg[0])), result, arg[0], arg[1], arg[2]); break;\r
1523                 case EOpMul:\r
1524                         if(visit == PostVisit)\r
1525                         {\r
1526                                 TIntermTyped *arg0 = arg[0]->getAsTyped();\r
1527                                 TIntermTyped *arg1 = arg[1]->getAsTyped();\r
1528                                 ASSERT((arg0->getNominalSize() == arg1->getNominalSize()) && (arg0->getSecondarySize() == arg1->getSecondarySize()));\r
1529 \r
1530                                 int size = arg0->getNominalSize();\r
1531                                 for(int i = 0; i < size; i++)\r
1532                                 {\r
1533                                         emit(sw::Shader::OPCODE_MUL, result, i, arg[0], i, arg[1], i);\r
1534                                 }\r
1535                         }\r
1536                         break;\r
1537                 case EOpOuterProduct:\r
1538                         if(visit == PostVisit)\r
1539                         {\r
1540                                 for(int i = 0; i < dim(arg[1]); i++)\r
1541                                 {\r
1542                                         Instruction *mul = emit(sw::Shader::OPCODE_MUL, result, i, arg[0], 0, arg[1]);\r
1543                                         mul->src[1].swizzle = 0x55 * i;\r
1544                                 }\r
1545                         }\r
1546                         break;\r
1547                 default: UNREACHABLE(node->getOp());\r
1548                 }\r
1549 \r
1550                 return true;\r
1551         }\r
1552 \r
1553         bool OutputASM::visitSelection(Visit visit, TIntermSelection *node)\r
1554         {\r
1555                 if(currentScope != emitScope)\r
1556                 {\r
1557                         return false;\r
1558                 }\r
1559 \r
1560                 TIntermTyped *condition = node->getCondition();\r
1561                 TIntermNode *trueBlock = node->getTrueBlock();\r
1562                 TIntermNode *falseBlock = node->getFalseBlock();\r
1563                 TIntermConstantUnion *constantCondition = condition->getAsConstantUnion();\r
1564 \r
1565                 condition->traverse(this);\r
1566 \r
1567                 if(node->usesTernaryOperator())\r
1568                 {\r
1569                         if(constantCondition)\r
1570                         {\r
1571                                 bool trueCondition = constantCondition->getUnionArrayPointer()->getBConst();\r
1572 \r
1573                                 if(trueCondition)\r
1574                                 {\r
1575                                         trueBlock->traverse(this);\r
1576                                         copy(node, trueBlock);\r
1577                                 }\r
1578                                 else\r
1579                                 {\r
1580                                         falseBlock->traverse(this);\r
1581                                         copy(node, falseBlock);\r
1582                                 }\r
1583                         }\r
1584                         else if(trivial(node, 6))   // Fast to compute both potential results and no side effects\r
1585                         {\r
1586                                 trueBlock->traverse(this);\r
1587                                 falseBlock->traverse(this);\r
1588                                 emit(sw::Shader::OPCODE_SELECT, node, condition, trueBlock, falseBlock);\r
1589                         }\r
1590                         else\r
1591                         {\r
1592                                 emit(sw::Shader::OPCODE_IF, 0, condition);\r
1593 \r
1594                                 if(trueBlock)\r
1595                                 {\r
1596                                         trueBlock->traverse(this);\r
1597                                         copy(node, trueBlock);\r
1598                                 }\r
1599 \r
1600                                 if(falseBlock)\r
1601                                 {\r
1602                                         emit(sw::Shader::OPCODE_ELSE);\r
1603                                         falseBlock->traverse(this);\r
1604                                         copy(node, falseBlock);\r
1605                                 }\r
1606 \r
1607                                 emit(sw::Shader::OPCODE_ENDIF);\r
1608                         }\r
1609                 }\r
1610                 else  // if/else statement\r
1611                 {\r
1612                         if(constantCondition)\r
1613                         {\r
1614                                 bool trueCondition = constantCondition->getUnionArrayPointer()->getBConst();\r
1615 \r
1616                                 if(trueCondition)\r
1617                                 {\r
1618                                         if(trueBlock)\r
1619                                         {\r
1620                                                 trueBlock->traverse(this);\r
1621                                         }\r
1622                                 }\r
1623                                 else\r
1624                                 {\r
1625                                         if(falseBlock)\r
1626                                         {\r
1627                                                 falseBlock->traverse(this);\r
1628                                         }\r
1629                                 }\r
1630                         }\r
1631                         else\r
1632                         {\r
1633                                 emit(sw::Shader::OPCODE_IF, 0, condition);\r
1634 \r
1635                                 if(trueBlock)\r
1636                                 {\r
1637                                         trueBlock->traverse(this);\r
1638                                 }\r
1639 \r
1640                                 if(falseBlock)\r
1641                                 {\r
1642                                         emit(sw::Shader::OPCODE_ELSE);\r
1643                                         falseBlock->traverse(this);\r
1644                                 }\r
1645 \r
1646                                 emit(sw::Shader::OPCODE_ENDIF);\r
1647                         }\r
1648                 }\r
1649 \r
1650                 return false;\r
1651         }\r
1652 \r
1653         bool OutputASM::visitLoop(Visit visit, TIntermLoop *node)\r
1654         {\r
1655                 if(currentScope != emitScope)\r
1656                 {\r
1657                         return false;\r
1658                 }\r
1659 \r
1660                 unsigned int iterations = loopCount(node);\r
1661 \r
1662                 if(iterations == 0)\r
1663                 {\r
1664                         return false;\r
1665                 }\r
1666 \r
1667                 bool unroll = (iterations <= 4);\r
1668 \r
1669                 if(unroll)\r
1670                 {\r
1671                         DetectLoopDiscontinuity detectLoopDiscontinuity;\r
1672                         unroll = !detectLoopDiscontinuity.traverse(node);\r
1673                 }\r
1674 \r
1675                 TIntermNode *init = node->getInit();\r
1676                 TIntermTyped *condition = node->getCondition();\r
1677                 TIntermTyped *expression = node->getExpression();\r
1678                 TIntermNode *body = node->getBody();\r
1679 \r
1680                 if(node->getType() == ELoopDoWhile)\r
1681                 {\r
1682                         Temporary iterate(this);\r
1683                         Constant True(true);\r
1684                         emit(sw::Shader::OPCODE_MOV, &iterate, &True);\r
1685 \r
1686                         emit(sw::Shader::OPCODE_WHILE, 0, &iterate);   // FIXME: Implement real do-while\r
1687 \r
1688                         if(body)\r
1689                         {\r
1690                                 body->traverse(this);\r
1691                         }\r
1692 \r
1693                         emit(sw::Shader::OPCODE_TEST);\r
1694 \r
1695                         condition->traverse(this);\r
1696                         emit(sw::Shader::OPCODE_MOV, &iterate, condition);\r
1697 \r
1698                         emit(sw::Shader::OPCODE_ENDWHILE);\r
1699                 }\r
1700                 else\r
1701                 {\r
1702                         if(init)\r
1703                         {\r
1704                                 init->traverse(this);\r
1705                         }\r
1706 \r
1707                         if(unroll)\r
1708                         {\r
1709                                 for(unsigned int i = 0; i < iterations; i++)\r
1710                                 {\r
1711                                 //      condition->traverse(this);   // Condition could contain statements, but not in an unrollable loop\r
1712 \r
1713                                         if(body)\r
1714                                         {\r
1715                                                 body->traverse(this);\r
1716                                         }\r
1717 \r
1718                                         if(expression)\r
1719                                         {\r
1720                                                 expression->traverse(this);\r
1721                                         }\r
1722                                 }\r
1723                         }\r
1724                         else\r
1725                         {\r
1726                                 if(condition)\r
1727                                 {\r
1728                                         condition->traverse(this);\r
1729                                 }\r
1730 \r
1731                                 emit(sw::Shader::OPCODE_WHILE, 0, condition);\r
1732 \r
1733                                 if(body)\r
1734                                 {\r
1735                                         body->traverse(this);\r
1736                                 }\r
1737 \r
1738                                 emit(sw::Shader::OPCODE_TEST);\r
1739 \r
1740                                 if(expression)\r
1741                                 {\r
1742                                         expression->traverse(this);\r
1743                                 }\r
1744 \r
1745                                 if(condition)\r
1746                                 {\r
1747                                         condition->traverse(this);\r
1748                                 }\r
1749 \r
1750                                 emit(sw::Shader::OPCODE_ENDWHILE);\r
1751                         }\r
1752                 }\r
1753 \r
1754                 return false;\r
1755         }\r
1756 \r
1757         bool OutputASM::visitBranch(Visit visit, TIntermBranch *node)\r
1758         {\r
1759                 if(currentScope != emitScope)\r
1760                 {\r
1761                         return false;\r
1762                 }\r
1763 \r
1764                 switch(node->getFlowOp())\r
1765                 {\r
1766                 case EOpKill:      if(visit == PostVisit) emit(sw::Shader::OPCODE_DISCARD);  break;\r
1767                 case EOpBreak:     if(visit == PostVisit) emit(sw::Shader::OPCODE_BREAK);    break;\r
1768                 case EOpContinue:  if(visit == PostVisit) emit(sw::Shader::OPCODE_CONTINUE); break;\r
1769                 case EOpReturn:\r
1770                         if(visit == PostVisit)\r
1771                         {\r
1772                                 TIntermTyped *value = node->getExpression();\r
1773 \r
1774                                 if(value)\r
1775                                 {\r
1776                                         copy(functionArray[currentFunction].ret, value);\r
1777                                 }\r
1778 \r
1779                                 emit(sw::Shader::OPCODE_LEAVE);\r
1780                         }\r
1781                         break;\r
1782                 default: UNREACHABLE(node->getFlowOp());\r
1783                 }\r
1784 \r
1785                 return true;\r
1786         }\r
1787 \r
1788         bool OutputASM::isSamplerRegister(TIntermTyped *operand)\r
1789         {\r
1790                 return operand && isSamplerRegister(operand->getType());\r
1791         }\r
1792 \r
1793         bool OutputASM::isSamplerRegister(const TType &type)\r
1794         {\r
1795                 // A sampler register's qualifiers can be:\r
1796                 // - EvqUniform: The sampler uniform is used as is in the code (default case).\r
1797                 // - EvqTemporary: The sampler is indexed. It's still a sampler register.\r
1798                 // - EvqIn (and other similar types): The sampler has been passed as a function argument. At this point,\r
1799                 //                                    the sampler has been copied and is no longer a sampler register.\r
1800                 return IsSampler(type.getBasicType()) && (type.getQualifier() == EvqUniform || type.getQualifier() == EvqTemporary);\r
1801         }\r
1802 \r
1803         Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2, TIntermNode *src3, TIntermNode *src4)\r
1804         {\r
1805                 return emit(op, dst, 0, src0, 0, src1, 0, src2, 0, src3, 0, src4, 0);\r
1806         }\r
1807 \r
1808         Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, int dstIndex, TIntermNode *src0, int index0, TIntermNode *src1, int index1,\r
1809                                      TIntermNode *src2, int index2, TIntermNode *src3, int index3, TIntermNode *src4, int index4)\r
1810         {\r
1811                 if(isSamplerRegister(dst))\r
1812                 {\r
1813                         op = sw::Shader::OPCODE_NULL;   // Can't assign to a sampler, but this is hit when indexing sampler arrays\r
1814                 }\r
1815 \r
1816                 Instruction *instruction = new Instruction(op);\r
1817 \r
1818                 if(dst)\r
1819                 {\r
1820                         instruction->dst.type = registerType(dst);\r
1821                         instruction->dst.index = registerIndex(dst) + dstIndex;\r
1822                         instruction->dst.mask = writeMask(dst);\r
1823                         instruction->dst.integer = (dst->getBasicType() == EbtInt);\r
1824                 }\r
1825 \r
1826                 argument(instruction->src[0], src0, index0);\r
1827                 argument(instruction->src[1], src1, index1);\r
1828                 argument(instruction->src[2], src2, index2);\r
1829                 argument(instruction->src[3], src3, index3);\r
1830                 argument(instruction->src[4], src4, index4);\r
1831 \r
1832                 shader->append(instruction);\r
1833 \r
1834                 return instruction;\r
1835         }\r
1836 \r
1837         Instruction *OutputASM::emitCast(TIntermTyped *dst, TIntermTyped *src)\r
1838         {\r
1839                 return emitCast(dst, 0, src, 0);\r
1840         }\r
1841 \r
1842         Instruction *OutputASM::emitCast(TIntermTyped *dst, int dstIndex, TIntermTyped *src, int srcIndex)\r
1843         {\r
1844                 switch(src->getBasicType())\r
1845                 {\r
1846                 case EbtBool:\r
1847                         switch(dst->getBasicType())\r
1848                         {\r
1849                         case EbtInt:   return emit(sw::Shader::OPCODE_B2I, dst, dstIndex, src, srcIndex);\r
1850                         case EbtUInt:  return emit(sw::Shader::OPCODE_B2I, dst, dstIndex, src, srcIndex);\r
1851                         case EbtFloat: return emit(sw::Shader::OPCODE_B2F, dst, dstIndex, src, srcIndex);\r
1852                         default:       break;\r
1853                         }\r
1854                         break;\r
1855                 case EbtInt:\r
1856                         switch(dst->getBasicType())\r
1857                         {\r
1858                         case EbtBool:  return emit(sw::Shader::OPCODE_I2B, dst, dstIndex, src, srcIndex);\r
1859                         case EbtFloat: return emit(sw::Shader::OPCODE_I2F, dst, dstIndex, src, srcIndex);\r
1860                         default:       break;\r
1861                         }\r
1862                         break;\r
1863                 case EbtUInt:\r
1864                         switch(dst->getBasicType())\r
1865                         {\r
1866                         case EbtBool:  return emit(sw::Shader::OPCODE_I2B, dst, dstIndex, src, srcIndex);\r
1867                         case EbtFloat: return emit(sw::Shader::OPCODE_U2F, dst, dstIndex, src, srcIndex);\r
1868                         default:       break;\r
1869                         }\r
1870                         break;\r
1871                 case EbtFloat:\r
1872                         switch(dst->getBasicType())\r
1873                         {\r
1874                         case EbtBool: return emit(sw::Shader::OPCODE_F2B, dst, dstIndex, src, srcIndex);\r
1875                         case EbtInt:  return emit(sw::Shader::OPCODE_F2I, dst, dstIndex, src, srcIndex);\r
1876                         case EbtUInt: return emit(sw::Shader::OPCODE_F2U, dst, dstIndex, src, srcIndex);\r
1877                         default:      break;\r
1878                         }\r
1879                         break;\r
1880                 default:\r
1881                         break;\r
1882                 }\r
1883 \r
1884                 ASSERT(src->getBasicType() == dst->getBasicType());\r
1885 \r
1886                 return emit(sw::Shader::OPCODE_MOV, dst, dstIndex, src, srcIndex);\r
1887         }\r
1888 \r
1889         void OutputASM::emitBinary(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2)\r
1890         {\r
1891                 for(int index = 0; index < dst->elementRegisterCount(); index++)\r
1892                 {\r
1893                         emit(op, dst, index, src0, index, src1, index, src2, index);\r
1894                 }\r
1895         }\r
1896 \r
1897         void OutputASM::emitAssign(sw::Shader::Opcode op, TIntermTyped *result, TIntermTyped *lhs, TIntermTyped *src0, TIntermTyped *src1)\r
1898         {\r
1899                 emitBinary(op, result, src0, src1);\r
1900                 assignLvalue(lhs, result);\r
1901         }\r
1902 \r
1903         void OutputASM::emitCmp(sw::Shader::Control cmpOp, TIntermTyped *dst, TIntermNode *left, TIntermNode *right, int index)\r
1904         {\r
1905                 sw::Shader::Opcode opcode;\r
1906                 switch(left->getAsTyped()->getBasicType())\r
1907                 {\r
1908                 case EbtBool:\r
1909                 case EbtInt:\r
1910                         opcode = sw::Shader::OPCODE_ICMP;\r
1911                         break;\r
1912                 case EbtUInt:\r
1913                         opcode = sw::Shader::OPCODE_UCMP;\r
1914                         break;\r
1915                 default:\r
1916                         opcode = sw::Shader::OPCODE_CMP;\r
1917                         break;\r
1918                 }\r
1919 \r
1920                 Instruction *cmp = emit(opcode, dst, 0, left, index, right, index);\r
1921                 cmp->control = cmpOp;\r
1922         }\r
1923 \r
1924         int componentCount(const TType &type, int registers)\r
1925         {\r
1926                 if(registers == 0)\r
1927                 {\r
1928                         return 0;\r
1929                 }\r
1930 \r
1931                 if(type.isArray() && registers >= type.elementRegisterCount())\r
1932                 {\r
1933                         int index = registers / type.elementRegisterCount();\r
1934                         registers -= index * type.elementRegisterCount();\r
1935                         return index * type.getElementSize() + componentCount(type, registers);\r
1936                 }\r
1937 \r
1938                 if(type.isStruct() || type.isInterfaceBlock())\r
1939                 {\r
1940                         const TFieldList& fields = type.getStruct() ? type.getStruct()->fields() : type.getInterfaceBlock()->fields();\r
1941                         int elements = 0;\r
1942 \r
1943                         for(TFieldList::const_iterator field = fields.begin(); field != fields.end(); field++)\r
1944                         {\r
1945                                 const TType &fieldType = *((*field)->type());\r
1946 \r
1947                                 if(fieldType.totalRegisterCount() <= registers)\r
1948                                 {\r
1949                                         registers -= fieldType.totalRegisterCount();\r
1950                                         elements += fieldType.getObjectSize();\r
1951                                 }\r
1952                                 else   // Register within this field\r
1953                                 {\r
1954                                         return elements + componentCount(fieldType, registers);\r
1955                                 }\r
1956                         }\r
1957                 }\r
1958                 else if(type.isMatrix())\r
1959                 {\r
1960                         return registers * type.registerSize();\r
1961                 }\r
1962 \r
1963                 UNREACHABLE(0);\r
1964                 return 0;\r
1965         }\r
1966 \r
1967         int registerSize(const TType &type, int registers)\r
1968         {\r
1969                 if(registers == 0)\r
1970                 {\r
1971                         if(type.isStruct())\r
1972                         {\r
1973                                 return registerSize(*((*(type.getStruct()->fields().begin()))->type()), 0);\r
1974                         }\r
1975                         else if(type.isInterfaceBlock())\r
1976                         {\r
1977                                 return registerSize(*((*(type.getInterfaceBlock()->fields().begin()))->type()), 0);\r
1978                         }\r
1979 \r
1980                         return type.registerSize();\r
1981                 }\r
1982 \r
1983                 if(type.isArray() && registers >= type.elementRegisterCount())\r
1984                 {\r
1985                         int index = registers / type.elementRegisterCount();\r
1986                         registers -= index * type.elementRegisterCount();\r
1987                         return registerSize(type, registers);\r
1988                 }\r
1989 \r
1990                 if(type.isStruct() || type.isInterfaceBlock())\r
1991                 {\r
1992                         const TFieldList& fields = type.getStruct() ? type.getStruct()->fields() : type.getInterfaceBlock()->fields();\r
1993                         int elements = 0;\r
1994 \r
1995                         for(TFieldList::const_iterator field = fields.begin(); field != fields.end(); field++)\r
1996                         {\r
1997                                 const TType &fieldType = *((*field)->type());\r
1998 \r
1999                                 if(fieldType.totalRegisterCount() <= registers)\r
2000                                 {\r
2001                                         registers -= fieldType.totalRegisterCount();\r
2002                                         elements += fieldType.getObjectSize();\r
2003                                 }\r
2004                                 else   // Register within this field\r
2005                                 {\r
2006                                         return registerSize(fieldType, registers);\r
2007                                 }\r
2008                         }\r
2009                 }\r
2010                 else if(type.isMatrix())\r
2011                 {\r
2012                         return registerSize(type, 0);\r
2013                 }\r
2014 \r
2015                 UNREACHABLE(0);\r
2016                 return 0;\r
2017         }\r
2018 \r
2019         void OutputASM::argument(sw::Shader::SourceParameter &parameter, TIntermNode *argument, int index)\r
2020         {\r
2021                 if(argument)\r
2022                 {\r
2023                         TIntermTyped *arg = argument->getAsTyped();\r
2024                         const TType &type = arg->getType();\r
2025                         index = (index >= arg->totalRegisterCount()) ? arg->totalRegisterCount() - 1 : index;\r
2026 \r
2027                         int size = registerSize(type, index);\r
2028 \r
2029                         parameter.type = registerType(arg);\r
2030 \r
2031                         if(arg->getQualifier() == EvqConstExpr)\r
2032                         {\r
2033                                 int component = componentCount(type, index);\r
2034                                 ConstantUnion *constants = arg->getAsConstantUnion()->getUnionArrayPointer();\r
2035 \r
2036                                 for(int i = 0; i < 4; i++)\r
2037                                 {\r
2038                                         if(size == 1)   // Replicate\r
2039                                         {\r
2040                                                 parameter.value[i] = constants[component + 0].getAsFloat();\r
2041                                         }\r
2042                                         else if(i < size)\r
2043                                         {\r
2044                                                 parameter.value[i] = constants[component + i].getAsFloat();\r
2045                                         }\r
2046                                         else\r
2047                                         {\r
2048                                                 parameter.value[i] = 0.0f;\r
2049                                         }\r
2050                                 }\r
2051                         }\r
2052                         else\r
2053                         {\r
2054                                 parameter.index = registerIndex(arg) + index;\r
2055 \r
2056                                 if(isSamplerRegister(arg))\r
2057                                 {\r
2058                                         TIntermBinary *binary = argument->getAsBinaryNode();\r
2059 \r
2060                                         if(binary)\r
2061                                         {\r
2062                                                 TIntermTyped *left = binary->getLeft();\r
2063                                                 TIntermTyped *right = binary->getRight();\r
2064 \r
2065                                                 switch(binary->getOp())\r
2066                                                 {\r
2067                                                 case EOpIndexDirect:\r
2068                                                         parameter.index += right->getAsConstantUnion()->getIConst(0);\r
2069                                                         break;\r
2070                                                 case EOpIndexIndirect:\r
2071                                                         if(left->getArraySize() > 1)\r
2072                                                         {\r
2073                                                                 parameter.rel.type = registerType(binary->getRight());\r
2074                                                                 parameter.rel.index = registerIndex(binary->getRight());\r
2075                                                                 parameter.rel.scale = 1;\r
2076                                                                 parameter.rel.deterministic = true;\r
2077                                                         }\r
2078                                                         break;\r
2079                                                 case EOpIndexDirectStruct:\r
2080                                                 case EOpIndexDirectInterfaceBlock:\r
2081                                                         parameter.index += right->getAsConstantUnion()->getIConst(0);\r
2082                                                         break;\r
2083                                                 default:\r
2084                                                         UNREACHABLE(binary->getOp());\r
2085                                                 }\r
2086                                         }\r
2087                                 }\r
2088                         }\r
2089 \r
2090                         if(!IsSampler(arg->getBasicType()))\r
2091                         {\r
2092                                 parameter.swizzle = readSwizzle(arg, size);\r
2093                         }\r
2094                 }\r
2095         }\r
2096 \r
2097         void OutputASM::copy(TIntermTyped *dst, TIntermNode *src, int offset)\r
2098         {\r
2099                 for(int index = 0; index < dst->totalRegisterCount(); index++)\r
2100                 {\r
2101                         Instruction *mov = emit(sw::Shader::OPCODE_MOV, dst, index, src, offset + index);\r
2102                         mov->dst.mask = writeMask(dst, index);\r
2103                 }\r
2104         }\r
2105 \r
2106         int swizzleElement(int swizzle, int index)\r
2107         {\r
2108                 return (swizzle >> (index * 2)) & 0x03;\r
2109         }\r
2110 \r
2111         int swizzleSwizzle(int leftSwizzle, int rightSwizzle)\r
2112         {\r
2113                 return (swizzleElement(leftSwizzle, swizzleElement(rightSwizzle, 0)) << 0) |\r
2114                        (swizzleElement(leftSwizzle, swizzleElement(rightSwizzle, 1)) << 2) |\r
2115                        (swizzleElement(leftSwizzle, swizzleElement(rightSwizzle, 2)) << 4) |\r
2116                        (swizzleElement(leftSwizzle, swizzleElement(rightSwizzle, 3)) << 6);\r
2117         }\r
2118 \r
2119         void OutputASM::assignLvalue(TIntermTyped *dst, TIntermTyped *src)\r
2120         {\r
2121                 if(src &&\r
2122                         ((src->isVector() && (!dst->isVector() || (dst->getNominalSize() != dst->getNominalSize()))) ||\r
2123                          (src->isMatrix() && (!dst->isMatrix() || (src->getNominalSize() != dst->getNominalSize()) || (src->getSecondarySize() != dst->getSecondarySize())))))\r
2124                 {\r
2125                         return mContext.error(src->getLine(), "Result type should match the l-value type in compound assignment", src->isVector() ? "vector" : "matrix");\r
2126                 }\r
2127 \r
2128                 TIntermBinary *binary = dst->getAsBinaryNode();\r
2129 \r
2130                 if(binary && binary->getOp() == EOpIndexIndirect && dst->isScalar())\r
2131                 {\r
2132                         Instruction *insert = new Instruction(sw::Shader::OPCODE_INSERT);\r
2133 \r
2134                         Temporary address(this);\r
2135                         lvalue(insert->dst, address, dst);\r
2136 \r
2137                         insert->src[0].type = insert->dst.type;\r
2138                         insert->src[0].index = insert->dst.index;\r
2139                         insert->src[0].rel = insert->dst.rel;\r
2140                         argument(insert->src[1], src);\r
2141                         argument(insert->src[2], binary->getRight());\r
2142 \r
2143                         shader->append(insert);\r
2144                 }\r
2145                 else\r
2146                 {\r
2147                         for(int offset = 0; offset < dst->totalRegisterCount(); offset++)\r
2148                         {\r
2149                                 Instruction *mov = new Instruction(sw::Shader::OPCODE_MOV);\r
2150 \r
2151                                 Temporary address(this);\r
2152                                 int swizzle = lvalue(mov->dst, address, dst);\r
2153                                 mov->dst.index += offset;\r
2154 \r
2155                                 if(offset > 0)\r
2156                                 {\r
2157                                         mov->dst.mask = writeMask(dst, offset);\r
2158                                 }\r
2159 \r
2160                                 argument(mov->src[0], src, offset);\r
2161                                 mov->src[0].swizzle = swizzleSwizzle(mov->src[0].swizzle, swizzle);\r
2162 \r
2163                                 shader->append(mov);\r
2164                         }\r
2165                 }\r
2166         }\r
2167 \r
2168         int OutputASM::lvalue(sw::Shader::DestinationParameter &dst, Temporary &address, TIntermTyped *node)\r
2169         {\r
2170                 TIntermTyped *result = node;\r
2171                 TIntermBinary *binary = node->getAsBinaryNode();\r
2172                 TIntermSymbol *symbol = node->getAsSymbolNode();\r
2173 \r
2174                 if(binary)\r
2175                 {\r
2176                         TIntermTyped *left = binary->getLeft();\r
2177                         TIntermTyped *right = binary->getRight();\r
2178 \r
2179                         int leftSwizzle = lvalue(dst, address, left);   // Resolve the l-value of the left side\r
2180 \r
2181                         switch(binary->getOp())\r
2182                         {\r
2183                         case EOpIndexDirect:\r
2184                                 {\r
2185                                         int rightIndex = right->getAsConstantUnion()->getIConst(0);\r
2186 \r
2187                                         if(left->isRegister())\r
2188                                         {\r
2189                                                 int leftMask = dst.mask;\r
2190 \r
2191                                                 dst.mask = 1;\r
2192                                                 while((leftMask & dst.mask) == 0)\r
2193                                                 {\r
2194                                                         dst.mask = dst.mask << 1;\r
2195                                                 }\r
2196 \r
2197                                                 int element = swizzleElement(leftSwizzle, rightIndex);\r
2198                                                 dst.mask = 1 << element;\r
2199 \r
2200                                                 return element;\r
2201                                         }\r
2202                                         else if(left->isArray() || left->isMatrix())\r
2203                                         {\r
2204                                                 dst.index += rightIndex * result->totalRegisterCount();\r
2205                                                 return 0xE4;\r
2206                                         }\r
2207                                         else UNREACHABLE(0);\r
2208                                 }\r
2209                                 break;\r
2210                         case EOpIndexIndirect:\r
2211                                 {\r
2212                                         if(left->isRegister())\r
2213                                         {\r
2214                                                 // Requires INSERT instruction (handled by calling function)\r
2215                                         }\r
2216                                         else if(left->isArray() || left->isMatrix())\r
2217                                         {\r
2218                                                 int scale = result->totalRegisterCount();\r
2219 \r
2220                                                 if(dst.rel.type == sw::Shader::PARAMETER_VOID)   // Use the index register as the relative address directly\r
2221                                                 {\r
2222                                                         if(left->totalRegisterCount() > 1)\r
2223                                                         {\r
2224                                                                 sw::Shader::SourceParameter relativeRegister;\r
2225                                                                 argument(relativeRegister, right);\r
2226 \r
2227                                                                 dst.rel.index = relativeRegister.index;\r
2228                                                                 dst.rel.type = relativeRegister.type;\r
2229                                                                 dst.rel.scale = scale;\r
2230                                                                 dst.rel.deterministic = !(vertexShader && left->getQualifier() == EvqUniform);\r
2231                                                         }\r
2232                                                 }\r
2233                                                 else if(dst.rel.index != registerIndex(&address))   // Move the previous index register to the address register\r
2234                                                 {\r
2235                                                         if(scale == 1)\r
2236                                                         {\r
2237                                                                 Constant oldScale((int)dst.rel.scale);\r
2238                                                                 Instruction *mad = emit(sw::Shader::OPCODE_IMAD, &address, &address, &oldScale, right);\r
2239                                                                 mad->src[0].index = dst.rel.index;\r
2240                                                                 mad->src[0].type = dst.rel.type;\r
2241                                                         }\r
2242                                                         else\r
2243                                                         {\r
2244                                                                 Constant oldScale((int)dst.rel.scale);\r
2245                                                                 Instruction *mul = emit(sw::Shader::OPCODE_IMUL, &address, &address, &oldScale);\r
2246                                                                 mul->src[0].index = dst.rel.index;\r
2247                                                                 mul->src[0].type = dst.rel.type;\r
2248 \r
2249                                                                 Constant newScale(scale);\r
2250                                                                 emit(sw::Shader::OPCODE_IMAD, &address, right, &newScale, &address);\r
2251                                                         }\r
2252 \r
2253                                                         dst.rel.type = sw::Shader::PARAMETER_TEMP;\r
2254                                                         dst.rel.index = registerIndex(&address);\r
2255                                                         dst.rel.scale = 1;\r
2256                                                 }\r
2257                                                 else   // Just add the new index to the address register\r
2258                                                 {\r
2259                                                         if(scale == 1)\r
2260                                                         {\r
2261                                                                 emit(sw::Shader::OPCODE_IADD, &address, &address, right);\r
2262                                                         }\r
2263                                                         else\r
2264                                                         {\r
2265                                                                 Constant newScale(scale);\r
2266                                                                 emit(sw::Shader::OPCODE_IMAD, &address, right, &newScale, &address);\r
2267                                                         }\r
2268                                                 }\r
2269                                         }\r
2270                                         else UNREACHABLE(0);\r
2271                                 }\r
2272                                 break;\r
2273                         case EOpIndexDirectStruct:\r
2274                         case EOpIndexDirectInterfaceBlock:\r
2275                                 {\r
2276                                         const TFieldList& fields = (binary->getOp() == EOpIndexDirectStruct) ?\r
2277                                                                left->getType().getStruct()->fields() :\r
2278                                                                left->getType().getInterfaceBlock()->fields();\r
2279                                         int index = right->getAsConstantUnion()->getIConst(0);\r
2280                                         int fieldOffset = 0;\r
2281 \r
2282                                         for(int i = 0; i < index; i++)\r
2283                                         {\r
2284                                                 fieldOffset += fields[i]->type()->totalRegisterCount();\r
2285                                         }\r
2286 \r
2287                                         dst.type = registerType(left);\r
2288                                         dst.index += fieldOffset;\r
2289                                         dst.mask = writeMask(right);\r
2290 \r
2291                                         return 0xE4;\r
2292                                 }\r
2293                                 break;\r
2294                         case EOpVectorSwizzle:\r
2295                                 {\r
2296                                         ASSERT(left->isRegister());\r
2297 \r
2298                                         int leftMask = dst.mask;\r
2299 \r
2300                                         int swizzle = 0;\r
2301                                         int rightMask = 0;\r
2302 \r
2303                                         TIntermSequence &sequence = right->getAsAggregate()->getSequence();\r
2304 \r
2305                                         for(unsigned int i = 0; i < sequence.size(); i++)\r
2306                                         {\r
2307                                                 int index = sequence[i]->getAsConstantUnion()->getIConst(0);\r
2308 \r
2309                                                 int element = swizzleElement(leftSwizzle, index);\r
2310                                                 rightMask = rightMask | (1 << element);\r
2311                                                 swizzle = swizzle | swizzleElement(leftSwizzle, i) << (element * 2);\r
2312                                         }\r
2313 \r
2314                                         dst.mask = leftMask & rightMask;\r
2315 \r
2316                                         return swizzle;\r
2317                                 }\r
2318                                 break;\r
2319                         default:\r
2320                                 UNREACHABLE(binary->getOp());   // Not an l-value operator\r
2321                                 break;\r
2322                         }\r
2323                 }\r
2324                 else if(symbol)\r
2325                 {\r
2326                         dst.type = registerType(symbol);\r
2327                         dst.index = registerIndex(symbol);\r
2328                         dst.mask = writeMask(symbol);\r
2329                         return 0xE4;\r
2330                 }\r
2331 \r
2332                 return 0xE4;\r
2333         }\r
2334 \r
2335         sw::Shader::ParameterType OutputASM::registerType(TIntermTyped *operand)\r
2336         {\r
2337                 if(isSamplerRegister(operand))\r
2338                 {\r
2339                         return sw::Shader::PARAMETER_SAMPLER;\r
2340                 }\r
2341 \r
2342                 const TQualifier qualifier = operand->getQualifier();\r
2343                 if((EvqFragColor == qualifier) || (EvqFragData == qualifier))\r
2344                 {\r
2345                         if(((EvqFragData == qualifier) && (EvqFragColor == outputQualifier)) ||\r
2346                            ((EvqFragColor == qualifier) && (EvqFragData == outputQualifier)))\r
2347                         {\r
2348                                 mContext.error(operand->getLine(), "static assignment to both gl_FragData and gl_FragColor", "");\r
2349                         }\r
2350                         outputQualifier = qualifier;\r
2351                 }\r
2352 \r
2353                 switch(qualifier)\r
2354                 {\r
2355                 case EvqTemporary:           return sw::Shader::PARAMETER_TEMP;\r
2356                 case EvqGlobal:              return sw::Shader::PARAMETER_TEMP;\r
2357                 case EvqConstExpr:           return sw::Shader::PARAMETER_FLOAT4LITERAL;   // All converted to float\r
2358                 case EvqAttribute:           return sw::Shader::PARAMETER_INPUT;\r
2359                 case EvqVaryingIn:           return sw::Shader::PARAMETER_INPUT;\r
2360                 case EvqVaryingOut:          return sw::Shader::PARAMETER_OUTPUT;\r
2361                 case EvqVertexIn:            return sw::Shader::PARAMETER_INPUT;\r
2362                 case EvqFragmentOut:         return sw::Shader::PARAMETER_COLOROUT;\r
2363                 case EvqVertexOut:           return sw::Shader::PARAMETER_OUTPUT;\r
2364                 case EvqFragmentIn:          return sw::Shader::PARAMETER_INPUT;\r
2365                 case EvqInvariantVaryingIn:  return sw::Shader::PARAMETER_INPUT;    // FIXME: Guarantee invariance at the backend\r
2366                 case EvqInvariantVaryingOut: return sw::Shader::PARAMETER_OUTPUT;   // FIXME: Guarantee invariance at the backend\r
2367                 case EvqSmooth:              return sw::Shader::PARAMETER_OUTPUT;\r
2368                 case EvqFlat:                return sw::Shader::PARAMETER_OUTPUT;\r
2369                 case EvqCentroidOut:         return sw::Shader::PARAMETER_OUTPUT;\r
2370                 case EvqSmoothIn:            return sw::Shader::PARAMETER_INPUT;\r
2371                 case EvqFlatIn:              return sw::Shader::PARAMETER_INPUT;\r
2372                 case EvqCentroidIn:          return sw::Shader::PARAMETER_INPUT;\r
2373                 case EvqUniform:             return sw::Shader::PARAMETER_CONST;\r
2374                 case EvqIn:                  return sw::Shader::PARAMETER_TEMP;\r
2375                 case EvqOut:                 return sw::Shader::PARAMETER_TEMP;\r
2376                 case EvqInOut:               return sw::Shader::PARAMETER_TEMP;\r
2377                 case EvqConstReadOnly:       return sw::Shader::PARAMETER_TEMP;\r
2378                 case EvqPosition:            return sw::Shader::PARAMETER_OUTPUT;\r
2379                 case EvqPointSize:           return sw::Shader::PARAMETER_OUTPUT;\r
2380                 case EvqInstanceID:          return sw::Shader::PARAMETER_MISCTYPE;\r
2381                 case EvqFragCoord:           return sw::Shader::PARAMETER_MISCTYPE;\r
2382                 case EvqFrontFacing:         return sw::Shader::PARAMETER_MISCTYPE;\r
2383                 case EvqPointCoord:          return sw::Shader::PARAMETER_INPUT;\r
2384                 case EvqFragColor:           return sw::Shader::PARAMETER_COLOROUT;\r
2385                 case EvqFragData:            return sw::Shader::PARAMETER_COLOROUT;\r
2386                 case EvqFragDepth:           return sw::Shader::PARAMETER_DEPTHOUT;\r
2387                 default: UNREACHABLE(qualifier);\r
2388                 }\r
2389 \r
2390                 return sw::Shader::PARAMETER_VOID;\r
2391         }\r
2392 \r
2393         unsigned int OutputASM::registerIndex(TIntermTyped *operand)\r
2394         {\r
2395                 if(isSamplerRegister(operand))\r
2396                 {\r
2397                         return samplerRegister(operand);\r
2398                 }\r
2399 \r
2400                 switch(operand->getQualifier())\r
2401                 {\r
2402                 case EvqTemporary:           return temporaryRegister(operand);\r
2403                 case EvqGlobal:              return temporaryRegister(operand);\r
2404                 case EvqConstExpr:           UNREACHABLE(EvqConstExpr);\r
2405                 case EvqAttribute:           return attributeRegister(operand);\r
2406                 case EvqVaryingIn:           return varyingRegister(operand);\r
2407                 case EvqVaryingOut:          return varyingRegister(operand);\r
2408                 case EvqVertexIn:            return attributeRegister(operand);\r
2409                 case EvqFragmentOut:         return fragmentOutputRegister(operand);\r
2410                 case EvqVertexOut:           return varyingRegister(operand);\r
2411                 case EvqFragmentIn:          return varyingRegister(operand);\r
2412                 case EvqInvariantVaryingIn:  return varyingRegister(operand);\r
2413                 case EvqInvariantVaryingOut: return varyingRegister(operand);\r
2414                 case EvqSmooth:              return varyingRegister(operand);\r
2415                 case EvqFlat:                return varyingRegister(operand);\r
2416                 case EvqCentroidOut:         return varyingRegister(operand);\r
2417                 case EvqSmoothIn:            return varyingRegister(operand);\r
2418                 case EvqFlatIn:              return varyingRegister(operand);\r
2419                 case EvqCentroidIn:          return varyingRegister(operand);\r
2420                 case EvqUniform:             return uniformRegister(operand);\r
2421                 case EvqIn:                  return temporaryRegister(operand);\r
2422                 case EvqOut:                 return temporaryRegister(operand);\r
2423                 case EvqInOut:               return temporaryRegister(operand);\r
2424                 case EvqConstReadOnly:       return temporaryRegister(operand);\r
2425                 case EvqPosition:            return varyingRegister(operand);\r
2426                 case EvqPointSize:           return varyingRegister(operand);\r
2427                 case EvqInstanceID:          vertexShader->instanceIdDeclared = true; return 0;\r
2428                 case EvqFragCoord:           pixelShader->vPosDeclared = true;  return 0;\r
2429                 case EvqFrontFacing:         pixelShader->vFaceDeclared = true; return 1;\r
2430                 case EvqPointCoord:          return varyingRegister(operand);\r
2431                 case EvqFragColor:           return 0;\r
2432                 case EvqFragData:            return 0;\r
2433                 case EvqFragDepth:           return 0;\r
2434                 default: UNREACHABLE(operand->getQualifier());\r
2435                 }\r
2436 \r
2437                 return 0;\r
2438         }\r
2439 \r
2440         int OutputASM::writeMask(TIntermTyped *destination, int index)\r
2441         {\r
2442                 if(destination->getQualifier() == EvqPointSize)\r
2443                 {\r
2444                         return 0x2;   // Point size stored in the y component\r
2445                 }\r
2446 \r
2447                 return 0xF >> (4 - registerSize(destination->getType(), index));\r
2448         }\r
2449 \r
2450         int OutputASM::readSwizzle(TIntermTyped *argument, int size)\r
2451         {\r
2452                 if(argument->getQualifier() == EvqPointSize)\r
2453                 {\r
2454                         return 0x55;   // Point size stored in the y component\r
2455                 }\r
2456 \r
2457                 static const unsigned char swizzleSize[5] = {0x00, 0x00, 0x54, 0xA4, 0xE4};   // (void), xxxx, xyyy, xyzz, xyzw\r
2458 \r
2459                 return swizzleSize[size];\r
2460         }\r
2461 \r
2462         // Conservatively checks whether an expression is fast to compute and has no side effects\r
2463         bool OutputASM::trivial(TIntermTyped *expression, int budget)\r
2464         {\r
2465                 if(!expression->isRegister())\r
2466                 {\r
2467                         return false;\r
2468                 }\r
2469 \r
2470                 return cost(expression, budget) >= 0;\r
2471         }\r
2472 \r
2473         // Returns the remaining computing budget (if < 0 the expression is too expensive or has side effects)\r
2474         int OutputASM::cost(TIntermNode *expression, int budget)\r
2475         {\r
2476                 if(budget < 0)\r
2477                 {\r
2478                         return budget;\r
2479                 }\r
2480 \r
2481                 if(expression->getAsSymbolNode())\r
2482                 {\r
2483                         return budget;\r
2484                 }\r
2485                 else if(expression->getAsConstantUnion())\r
2486                 {\r
2487                         return budget;\r
2488                 }\r
2489                 else if(expression->getAsBinaryNode())\r
2490                 {\r
2491                         TIntermBinary *binary = expression->getAsBinaryNode();\r
2492 \r
2493                         switch(binary->getOp())\r
2494                         {\r
2495                         case EOpVectorSwizzle:\r
2496                         case EOpIndexDirect:\r
2497                         case EOpIndexDirectStruct:\r
2498                         case EOpIndexDirectInterfaceBlock:\r
2499                                 return cost(binary->getLeft(), budget - 0);\r
2500                         case EOpAdd:\r
2501                         case EOpSub:\r
2502                         case EOpMul:\r
2503                                 return cost(binary->getLeft(), cost(binary->getRight(), budget - 1));\r
2504                         default:\r
2505                                 return -1;\r
2506                         }\r
2507                 }\r
2508                 else if(expression->getAsUnaryNode())\r
2509                 {\r
2510                         TIntermUnary *unary = expression->getAsUnaryNode();\r
2511 \r
2512                         switch(unary->getOp())\r
2513                         {\r
2514                         case EOpAbs:\r
2515                         case EOpNegative:\r
2516                                 return cost(unary->getOperand(), budget - 1);\r
2517                         default:\r
2518                                 return -1;\r
2519                         }\r
2520                 }\r
2521                 else if(expression->getAsSelectionNode())\r
2522                 {\r
2523                         TIntermSelection *selection = expression->getAsSelectionNode();\r
2524 \r
2525                         if(selection->usesTernaryOperator())\r
2526                         {\r
2527                                 TIntermTyped *condition = selection->getCondition();\r
2528                                 TIntermNode *trueBlock = selection->getTrueBlock();\r
2529                                 TIntermNode *falseBlock = selection->getFalseBlock();\r
2530                                 TIntermConstantUnion *constantCondition = condition->getAsConstantUnion();\r
2531 \r
2532                                 if(constantCondition)\r
2533                                 {\r
2534                                         bool trueCondition = constantCondition->getUnionArrayPointer()->getBConst();\r
2535 \r
2536                                         if(trueCondition)\r
2537                                         {\r
2538                                                 return cost(trueBlock, budget - 0);\r
2539                                         }\r
2540                                         else\r
2541                                         {\r
2542                                                 return cost(falseBlock, budget - 0);\r
2543                                         }\r
2544                                 }\r
2545                                 else\r
2546                                 {\r
2547                                         return cost(trueBlock, cost(falseBlock, budget - 2));\r
2548                                 }\r
2549                         }\r
2550                 }\r
2551 \r
2552                 return -1;\r
2553         }\r
2554 \r
2555         const Function *OutputASM::findFunction(const TString &name)\r
2556         {\r
2557                 for(unsigned int f = 0; f < functionArray.size(); f++)\r
2558                 {\r
2559                         if(functionArray[f].name == name)\r
2560                         {\r
2561                                 return &functionArray[f];\r
2562                         }\r
2563                 }\r
2564 \r
2565                 return 0;\r
2566         }\r
2567 \r
2568         int OutputASM::temporaryRegister(TIntermTyped *temporary)\r
2569         {\r
2570                 return allocate(temporaries, temporary);\r
2571         }\r
2572 \r
2573         int OutputASM::varyingRegister(TIntermTyped *varying)\r
2574         {\r
2575                 int var = lookup(varyings, varying);\r
2576 \r
2577                 if(var == -1)\r
2578                 {\r
2579                         var = allocate(varyings, varying);\r
2580                         int componentCount = varying->registerSize();\r
2581                         int registerCount = varying->totalRegisterCount();\r
2582 \r
2583                         if(pixelShader)\r
2584                         {\r
2585                                 if((var + registerCount) > sw::PixelShader::MAX_INPUT_VARYINGS)\r
2586                                 {\r
2587                                         mContext.error(varying->getLine(), "Varyings packing failed: Too many varyings", "fragment shader");\r
2588                                         return 0;\r
2589                                 }\r
2590 \r
2591                                 if(varying->getQualifier() == EvqPointCoord)\r
2592                                 {\r
2593                                         ASSERT(varying->isRegister());\r
2594                                         if(componentCount >= 1) pixelShader->semantic[var][0] = sw::Shader::Semantic(sw::Shader::USAGE_TEXCOORD, var);\r
2595                                         if(componentCount >= 2) pixelShader->semantic[var][1] = sw::Shader::Semantic(sw::Shader::USAGE_TEXCOORD, var);\r
2596                                         if(componentCount >= 3) pixelShader->semantic[var][2] = sw::Shader::Semantic(sw::Shader::USAGE_TEXCOORD, var);\r
2597                                         if(componentCount >= 4) pixelShader->semantic[var][3] = sw::Shader::Semantic(sw::Shader::USAGE_TEXCOORD, var);\r
2598                                 }\r
2599                                 else\r
2600                                 {\r
2601                                         for(int i = 0; i < varying->totalRegisterCount(); i++)\r
2602                                         {\r
2603                                                 if(componentCount >= 1) pixelShader->semantic[var + i][0] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, var + i);\r
2604                                                 if(componentCount >= 2) pixelShader->semantic[var + i][1] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, var + i);\r
2605                                                 if(componentCount >= 3) pixelShader->semantic[var + i][2] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, var + i);\r
2606                                                 if(componentCount >= 4) pixelShader->semantic[var + i][3] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, var + i);\r
2607                                         }\r
2608                                 }\r
2609                         }\r
2610                         else if(vertexShader)\r
2611                         {\r
2612                                 if((var + registerCount) > sw::VertexShader::MAX_OUTPUT_VARYINGS)\r
2613                                 {\r
2614                                         mContext.error(varying->getLine(), "Varyings packing failed: Too many varyings", "vertex shader");\r
2615                                         return 0;\r
2616                                 }\r
2617 \r
2618                                 if(varying->getQualifier() == EvqPosition)\r
2619                                 {\r
2620                                         ASSERT(varying->isRegister());\r
2621                                         vertexShader->output[var][0] = sw::Shader::Semantic(sw::Shader::USAGE_POSITION, 0);\r
2622                                         vertexShader->output[var][1] = sw::Shader::Semantic(sw::Shader::USAGE_POSITION, 0);\r
2623                                         vertexShader->output[var][2] = sw::Shader::Semantic(sw::Shader::USAGE_POSITION, 0);\r
2624                                         vertexShader->output[var][3] = sw::Shader::Semantic(sw::Shader::USAGE_POSITION, 0);\r
2625                                         vertexShader->positionRegister = var;\r
2626                                 }\r
2627                                 else if(varying->getQualifier() == EvqPointSize)\r
2628                                 {\r
2629                                         ASSERT(varying->isRegister());\r
2630                                         vertexShader->output[var][0] = sw::Shader::Semantic(sw::Shader::USAGE_PSIZE, 0);\r
2631                                         vertexShader->output[var][1] = sw::Shader::Semantic(sw::Shader::USAGE_PSIZE, 0);\r
2632                                         vertexShader->output[var][2] = sw::Shader::Semantic(sw::Shader::USAGE_PSIZE, 0);\r
2633                                         vertexShader->output[var][3] = sw::Shader::Semantic(sw::Shader::USAGE_PSIZE, 0);\r
2634                                         vertexShader->pointSizeRegister = var;\r
2635                                 }\r
2636                                 else\r
2637                                 {\r
2638                                         // Semantic indexes for user varyings will be assigned during program link to match the pixel shader\r
2639                                 }\r
2640                         }\r
2641                         else UNREACHABLE(0);\r
2642 \r
2643                         declareVarying(varying, var);\r
2644                 }\r
2645 \r
2646                 return var;\r
2647         }\r
2648 \r
2649         void OutputASM::declareVarying(TIntermTyped *varying, int reg)\r
2650         {\r
2651                 if(varying->getQualifier() != EvqPointCoord)   // gl_PointCoord does not need linking\r
2652                 {\r
2653                         const TType &type = varying->getType();\r
2654                         const char *name = varying->getAsSymbolNode()->getSymbol().c_str();\r
2655                         VaryingList &activeVaryings = shaderObject->varyings;\r
2656 \r
2657                         // Check if this varying has been declared before without having a register assigned\r
2658                         for(VaryingList::iterator v = activeVaryings.begin(); v != activeVaryings.end(); v++)\r
2659                         {\r
2660                                 if(v->name == name)\r
2661                                 {\r
2662                                         if(reg >= 0)\r
2663                                         {\r
2664                                                 ASSERT(v->reg < 0 || v->reg == reg);\r
2665                                                 v->reg = reg;\r
2666                                         }\r
2667 \r
2668                                         return;\r
2669                                 }\r
2670                         }\r
2671 \r
2672                         activeVaryings.push_back(glsl::Varying(glVariableType(type), name, varying->getArraySize(), reg, 0));\r
2673                 }\r
2674         }\r
2675 \r
2676         int OutputASM::uniformRegister(TIntermTyped *uniform)\r
2677         {\r
2678                 const TType &type = uniform->getType();\r
2679                 ASSERT(!IsSampler(type.getBasicType()));\r
2680                 TInterfaceBlock *block = type.getAsInterfaceBlock();\r
2681                 TIntermSymbol *symbol = uniform->getAsSymbolNode();\r
2682                 ASSERT(symbol || block);\r
2683 \r
2684                 if(symbol || block)\r
2685                 {\r
2686                         int index = lookup(uniforms, uniform);\r
2687 \r
2688                         if(index == -1)\r
2689                         {\r
2690                                 index = allocate(uniforms, uniform);\r
2691                                 const TString &name = symbol ? symbol->getSymbol() : block->name();\r
2692 \r
2693                                 declareUniform(type, name, index);\r
2694                         }\r
2695 \r
2696                         return index;\r
2697                 }\r
2698 \r
2699                 return 0;\r
2700         }\r
2701 \r
2702         int OutputASM::attributeRegister(TIntermTyped *attribute)\r
2703         {\r
2704                 ASSERT(!attribute->isArray());\r
2705 \r
2706                 int index = lookup(attributes, attribute);\r
2707 \r
2708                 if(index == -1)\r
2709                 {\r
2710                         TIntermSymbol *symbol = attribute->getAsSymbolNode();\r
2711                         ASSERT(symbol);\r
2712 \r
2713                         if(symbol)\r
2714                         {\r
2715                                 index = allocate(attributes, attribute);\r
2716                                 const TType &type = attribute->getType();\r
2717                                 int registerCount = attribute->totalRegisterCount();\r
2718 \r
2719                                 if(vertexShader && (index + registerCount) <= sw::VertexShader::MAX_INPUT_ATTRIBUTES)\r
2720                                 {\r
2721                                         for(int i = 0; i < registerCount; i++)\r
2722                                         {\r
2723                                                 vertexShader->input[index + i] = sw::Shader::Semantic(sw::Shader::USAGE_TEXCOORD, index + i);\r
2724                                         }\r
2725                                 }\r
2726 \r
2727                                 ActiveAttributes &activeAttributes = shaderObject->activeAttributes;\r
2728 \r
2729                                 const char *name = symbol->getSymbol().c_str();\r
2730                                 activeAttributes.push_back(Attribute(glVariableType(type), name, type.getArraySize(), type.getLayoutQualifier().location, index));\r
2731                         }\r
2732                 }\r
2733 \r
2734                 return index;\r
2735         }\r
2736 \r
2737         int OutputASM::fragmentOutputRegister(TIntermTyped *fragmentOutput)\r
2738         {\r
2739                 return allocate(fragmentOutputs, fragmentOutput);\r
2740         }\r
2741 \r
2742         int OutputASM::samplerRegister(TIntermTyped *sampler)\r
2743         {\r
2744                 ASSERT(IsSampler(sampler->getType().getBasicType()));\r
2745                 TIntermSymbol *symbol = sampler->getAsSymbolNode();\r
2746                 TIntermBinary *binary = sampler->getAsBinaryNode();\r
2747 \r
2748                 if(symbol)\r
2749                 {\r
2750                         return samplerRegister(symbol);\r
2751                 }\r
2752                 else if(binary)\r
2753                 {\r
2754                         ASSERT(binary->getOp() == EOpIndexDirect || binary->getOp() == EOpIndexIndirect ||\r
2755                                    binary->getOp() == EOpIndexDirectStruct || binary->getOp() == EOpIndexDirectInterfaceBlock);\r
2756 \r
2757                         return samplerRegister(binary->getLeft());   // Index added later\r
2758                 }\r
2759                 else UNREACHABLE(0);\r
2760 \r
2761                 return 0;\r
2762         }\r
2763 \r
2764         int OutputASM::samplerRegister(TIntermSymbol *sampler)\r
2765         {\r
2766                 const TType &type = sampler->getType();\r
2767                 ASSERT(IsSampler(type.getBasicType()) || type.getStruct());   // Structures can contain samplers\r
2768 \r
2769                 int index = lookup(samplers, sampler);\r
2770 \r
2771                 if(index == -1)\r
2772                 {\r
2773                         index = allocate(samplers, sampler);\r
2774 \r
2775                         if(sampler->getQualifier() == EvqUniform)\r
2776                         {\r
2777                                 const char *name = sampler->getSymbol().c_str();\r
2778                                 declareUniform(type, name, index);\r
2779                         }\r
2780                 }\r
2781 \r
2782                 return index;\r
2783         }\r
2784 \r
2785         int OutputASM::lookup(VariableArray &list, TIntermTyped *variable)\r
2786         {\r
2787                 for(unsigned int i = 0; i < list.size(); i++)\r
2788                 {\r
2789                         if(list[i] == variable)\r
2790                         {\r
2791                                 return i;   // Pointer match\r
2792                         }\r
2793                 }\r
2794 \r
2795                 TIntermSymbol *varSymbol = variable->getAsSymbolNode();\r
2796                 TInterfaceBlock *varBlock = variable->getType().getAsInterfaceBlock();\r
2797 \r
2798                 if(varBlock)\r
2799                 {\r
2800                         for(unsigned int i = 0; i < list.size(); i++)\r
2801                         {\r
2802                                 if(list[i])\r
2803                                 {\r
2804                                         TInterfaceBlock *listBlock = list[i]->getType().getAsInterfaceBlock();\r
2805 \r
2806                                         if(listBlock)\r
2807                                         {\r
2808                                                 if(listBlock->name() == varBlock->name())\r
2809                                                 {\r
2810                                                         ASSERT(listBlock->arraySize() == varBlock->arraySize());\r
2811                                                         ASSERT(listBlock->fields() == varBlock->fields());\r
2812                                                         ASSERT(listBlock->blockStorage() == varBlock->blockStorage());\r
2813                                                         ASSERT(listBlock->matrixPacking() == varBlock->matrixPacking());\r
2814 \r
2815                                                         return i;\r
2816                                                 }\r
2817                                         }\r
2818                                 }\r
2819                         }\r
2820                 }\r
2821                 else if(varSymbol)\r
2822                 {\r
2823                         for(unsigned int i = 0; i < list.size(); i++)\r
2824                         {\r
2825                                 if(list[i])\r
2826                                 {\r
2827                                         TIntermSymbol *listSymbol = list[i]->getAsSymbolNode();\r
2828 \r
2829                                         if(listSymbol)\r
2830                                         {\r
2831                                                 if(listSymbol->getId() == varSymbol->getId())\r
2832                                                 {\r
2833                                                         ASSERT(listSymbol->getSymbol() == varSymbol->getSymbol());\r
2834                                                         ASSERT(listSymbol->getType() == varSymbol->getType());\r
2835                                                         ASSERT(listSymbol->getQualifier() == varSymbol->getQualifier());\r
2836 \r
2837                                                         return i;\r
2838                                                 }\r
2839                                         }\r
2840                                 }\r
2841                         }\r
2842                 }\r
2843 \r
2844                 return -1;\r
2845         }\r
2846 \r
2847         int OutputASM::allocate(VariableArray &list, TIntermTyped *variable)\r
2848         {\r
2849                 int index = lookup(list, variable);\r
2850 \r
2851                 if(index == -1)\r
2852                 {\r
2853                         unsigned int registerCount = variable->totalRegisterCount();\r
2854 \r
2855                         for(unsigned int i = 0; i < list.size(); i++)\r
2856                         {\r
2857                                 if(list[i] == 0)\r
2858                                 {\r
2859                                         unsigned int j = 1;\r
2860                                         for( ; j < registerCount && (i + j) < list.size(); j++)\r
2861                                         {\r
2862                                                 if(list[i + j] != 0)\r
2863                                                 {\r
2864                                                         break;\r
2865                                                 }\r
2866                                         }\r
2867 \r
2868                                         if(j == registerCount)   // Found free slots\r
2869                                         {\r
2870                                                 for(unsigned int j = 0; j < registerCount; j++)\r
2871                                                 {\r
2872                                                         list[i + j] = variable;\r
2873                                                 }\r
2874 \r
2875                                                 return i;\r
2876                                         }\r
2877                                 }\r
2878                         }\r
2879 \r
2880                         index = list.size();\r
2881 \r
2882                         for(unsigned int i = 0; i < registerCount; i++)\r
2883                         {\r
2884                                 list.push_back(variable);\r
2885                         }\r
2886                 }\r
2887 \r
2888                 return index;\r
2889         }\r
2890 \r
2891         void OutputASM::free(VariableArray &list, TIntermTyped *variable)\r
2892         {\r
2893                 int index = lookup(list, variable);\r
2894 \r
2895                 if(index >= 0)\r
2896                 {\r
2897                         list[index] = 0;\r
2898                 }\r
2899         }\r
2900 \r
2901         void OutputASM::declareUniform(const TType &type, const TString &name, int registerIndex, int blockId, BlockLayoutEncoder* encoder)\r
2902         {\r
2903                 const TStructure *structure = type.getStruct();\r
2904                 const TInterfaceBlock *block = (type.isInterfaceBlock() || (blockId == -1)) ? type.getInterfaceBlock() : nullptr;\r
2905                 ActiveUniforms &activeUniforms = shaderObject->activeUniforms;\r
2906 \r
2907                 if(!structure && !block)\r
2908                 {\r
2909                         if(blockId >= 0)\r
2910                         {\r
2911                                 shaderObject->activeUniformBlocks[blockId].fields.push_back(activeUniforms.size());\r
2912                         }\r
2913                         BlockMemberInfo blockInfo = encoder ? encoder->encodeType(type) : BlockMemberInfo::getDefaultBlockInfo();\r
2914                         int regIndex = encoder ? registerIndex + BlockLayoutEncoder::getBlockRegister(blockInfo) : registerIndex;\r
2915                         activeUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(),\r
2916                                                          regIndex, blockId, blockInfo));\r
2917 \r
2918                         if(isSamplerRegister(type))\r
2919                         {\r
2920                                 for(int i = 0; i < type.totalRegisterCount(); i++)\r
2921                                 {\r
2922                                         shader->declareSampler(regIndex + i);\r
2923                                 }\r
2924                         }\r
2925                 }\r
2926                 else if(block)\r
2927                 {\r
2928                         ActiveUniformBlocks &activeUniformBlocks = shaderObject->activeUniformBlocks;\r
2929                         blockId = activeUniformBlocks.size();\r
2930                         bool isRowMajor = block->matrixPacking() == EmpRowMajor;\r
2931                         const TString &blockName = block->name();\r
2932                         activeUniformBlocks.push_back(UniformBlock(blockName.c_str(), 0, block->arraySize(),\r
2933                                                                    block->blockStorage(), isRowMajor, registerIndex, blockId));\r
2934 \r
2935                         const TFieldList& fields = block->fields();\r
2936                         Std140BlockEncoder currentBlockEncoder(isRowMajor);\r
2937                         for(size_t i = 0; i < fields.size(); i++)\r
2938                         {\r
2939                                 const TType &fieldType = *(fields[i]->type());\r
2940                                 const TString &fieldName = fields[i]->name();\r
2941                                 const TString uniformName = block->hasInstanceName() ? blockName + "." + fieldName : fieldName;\r
2942 \r
2943                                 declareUniform(fieldType, uniformName, registerIndex, blockId, &currentBlockEncoder);\r
2944                         }\r
2945                         activeUniformBlocks[blockId].dataSize = currentBlockEncoder.getBlockSize();\r
2946                 }\r
2947                 else\r
2948                 {\r
2949                         int fieldRegisterIndex = registerIndex;\r
2950 \r
2951                         const TFieldList& fields = structure->fields();\r
2952                         if(type.isArray() && (structure || type.isInterfaceBlock()))\r
2953                         {\r
2954                                 for(int i = 0; i < type.getArraySize(); i++)\r
2955                                 {\r
2956                                         if(encoder)\r
2957                                         {\r
2958                                                 encoder->enterAggregateType();\r
2959                                         }\r
2960                                         for(size_t j = 0; j < fields.size(); j++)\r
2961                                         {\r
2962                                                 const TType &fieldType = *(fields[j]->type());\r
2963                                                 const TString &fieldName = fields[j]->name();\r
2964                                                 const TString uniformName = name + "[" + str(i) + "]." + fieldName;\r
2965 \r
2966                                                 declareUniform(fieldType, uniformName, fieldRegisterIndex, blockId, encoder);\r
2967                                                 if(!encoder)\r
2968                                                 {\r
2969                                                         int registerCount = fieldType.totalRegisterCount();\r
2970                                                         fieldRegisterIndex += registerCount;\r
2971                                                 }\r
2972                                         }\r
2973                                         if(encoder)\r
2974                                         {\r
2975                                                 encoder->exitAggregateType();\r
2976                                         }\r
2977                                 }\r
2978                         }\r
2979                         else\r
2980                         {\r
2981                                 if(encoder)\r
2982                                 {\r
2983                                         encoder->enterAggregateType();\r
2984                                 }\r
2985                                 for(size_t i = 0; i < fields.size(); i++)\r
2986                                 {\r
2987                                         const TType &fieldType = *(fields[i]->type());\r
2988                                         const TString &fieldName = fields[i]->name();\r
2989                                         const TString uniformName = name + "." + fieldName;\r
2990 \r
2991                                         declareUniform(fieldType, uniformName, fieldRegisterIndex, blockId, encoder);\r
2992                                         if(!encoder)\r
2993                                         {\r
2994                                                 int registerCount = fieldType.totalRegisterCount();\r
2995                                                 fieldRegisterIndex += registerCount;\r
2996                                         }\r
2997                                 }\r
2998                                 if(encoder)\r
2999                                 {\r
3000                                         encoder->exitAggregateType();\r
3001                                 }\r
3002                         }\r
3003                 }\r
3004         }\r
3005 \r
3006         GLenum OutputASM::glVariableType(const TType &type)\r
3007         {\r
3008                 switch(type.getBasicType())\r
3009                 {\r
3010                 case EbtFloat:\r
3011                         if(type.isScalar())\r
3012                         {\r
3013                                 return GL_FLOAT;\r
3014                         }\r
3015                         else if(type.isVector())\r
3016                         {\r
3017                                 switch(type.getNominalSize())\r
3018                                 {\r
3019                                 case 2: return GL_FLOAT_VEC2;\r
3020                                 case 3: return GL_FLOAT_VEC3;\r
3021                                 case 4: return GL_FLOAT_VEC4;\r
3022                                 default: UNREACHABLE(type.getNominalSize());\r
3023                                 }\r
3024                         }\r
3025                         else if(type.isMatrix())\r
3026                         {\r
3027                                 switch(type.getNominalSize())\r
3028                                 {\r
3029                                 case 2:\r
3030                                         switch(type.getSecondarySize())\r
3031                                         {\r
3032                                         case 2: return GL_FLOAT_MAT2;\r
3033                                         case 3: return GL_FLOAT_MAT2x3;\r
3034                                         case 4: return GL_FLOAT_MAT2x4;\r
3035                                         default: UNREACHABLE(type.getSecondarySize());\r
3036                                         }\r
3037                                 case 3:\r
3038                                         switch(type.getSecondarySize())\r
3039                                         {\r
3040                                         case 2: return GL_FLOAT_MAT3x2;\r
3041                                         case 3: return GL_FLOAT_MAT3;\r
3042                                         case 4: return GL_FLOAT_MAT3x4;\r
3043                                         default: UNREACHABLE(type.getSecondarySize());\r
3044                                         }\r
3045                                 case 4:\r
3046                                         switch(type.getSecondarySize())\r
3047                                         {\r
3048                                         case 2: return GL_FLOAT_MAT4x2;\r
3049                                         case 3: return GL_FLOAT_MAT4x3;\r
3050                                         case 4: return GL_FLOAT_MAT4;\r
3051                                         default: UNREACHABLE(type.getSecondarySize());\r
3052                                         }\r
3053                                 default: UNREACHABLE(type.getNominalSize());\r
3054                                 }\r
3055                         }\r
3056                         else UNREACHABLE(0);\r
3057                         break;\r
3058                 case EbtInt:\r
3059                         if(type.isScalar())\r
3060                         {\r
3061                                 return GL_INT;\r
3062                         }\r
3063                         else if(type.isVector())\r
3064                         {\r
3065                                 switch(type.getNominalSize())\r
3066                                 {\r
3067                                 case 2: return GL_INT_VEC2;\r
3068                                 case 3: return GL_INT_VEC3;\r
3069                                 case 4: return GL_INT_VEC4;\r
3070                                 default: UNREACHABLE(type.getNominalSize());\r
3071                                 }\r
3072                         }\r
3073                         else UNREACHABLE(0);\r
3074                         break;\r
3075                 case EbtUInt:\r
3076                         if(type.isScalar())\r
3077                         {\r
3078                                 return GL_UNSIGNED_INT;\r
3079                         }\r
3080                         else if(type.isVector())\r
3081                         {\r
3082                                 switch(type.getNominalSize())\r
3083                                 {\r
3084                                 case 2: return GL_UNSIGNED_INT_VEC2;\r
3085                                 case 3: return GL_UNSIGNED_INT_VEC3;\r
3086                                 case 4: return GL_UNSIGNED_INT_VEC4;\r
3087                                 default: UNREACHABLE(type.getNominalSize());\r
3088                                 }\r
3089                         }\r
3090                         else UNREACHABLE(0);\r
3091                         break;\r
3092                 case EbtBool:\r
3093                         if(type.isScalar())\r
3094                         {\r
3095                                 return GL_BOOL;\r
3096                         }\r
3097                         else if(type.isVector())\r
3098                         {\r
3099                                 switch(type.getNominalSize())\r
3100                                 {\r
3101                                 case 2: return GL_BOOL_VEC2;\r
3102                                 case 3: return GL_BOOL_VEC3;\r
3103                                 case 4: return GL_BOOL_VEC4;\r
3104                                 default: UNREACHABLE(type.getNominalSize());\r
3105                                 }\r
3106                         }\r
3107                         else UNREACHABLE(0);\r
3108                         break;\r
3109                 case EbtSampler2D:\r
3110                         return GL_SAMPLER_2D;\r
3111                 case EbtISampler2D:\r
3112                         return GL_INT_SAMPLER_2D;\r
3113                 case EbtUSampler2D:\r
3114                         return GL_UNSIGNED_INT_SAMPLER_2D;\r
3115                 case EbtSamplerCube:\r
3116                         return GL_SAMPLER_CUBE;\r
3117                 case EbtISamplerCube:\r
3118                         return GL_INT_SAMPLER_CUBE;\r
3119                 case EbtUSamplerCube:\r
3120                         return GL_UNSIGNED_INT_SAMPLER_CUBE;\r
3121                 case EbtSamplerExternalOES:\r
3122                         return GL_SAMPLER_EXTERNAL_OES;\r
3123                 case EbtSampler3D:\r
3124                         return GL_SAMPLER_3D_OES;\r
3125                 case EbtISampler3D:\r
3126                         return GL_INT_SAMPLER_3D;\r
3127                 case EbtUSampler3D:\r
3128                         return GL_UNSIGNED_INT_SAMPLER_3D;\r
3129                 case EbtSampler2DArray:\r
3130                         return GL_SAMPLER_2D_ARRAY;\r
3131                 case EbtISampler2DArray:\r
3132                         return GL_INT_SAMPLER_2D_ARRAY;\r
3133                 case EbtUSampler2DArray:\r
3134                         return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY;\r
3135                 case EbtSampler2DShadow:\r
3136                         return GL_SAMPLER_2D_SHADOW;\r
3137                 case EbtSamplerCubeShadow:\r
3138                         return GL_SAMPLER_CUBE_SHADOW;\r
3139                 case EbtSampler2DArrayShadow:\r
3140                         return GL_SAMPLER_2D_ARRAY_SHADOW;\r
3141                 default:\r
3142                         UNREACHABLE(type.getBasicType());\r
3143                         break;\r
3144                 }\r
3145 \r
3146                 return GL_NONE;\r
3147         }\r
3148 \r
3149         GLenum OutputASM::glVariablePrecision(const TType &type)\r
3150         {\r
3151                 if(type.getBasicType() == EbtFloat)\r
3152                 {\r
3153                         switch(type.getPrecision())\r
3154                         {\r
3155                         case EbpHigh:   return GL_HIGH_FLOAT;\r
3156                         case EbpMedium: return GL_MEDIUM_FLOAT;\r
3157                         case EbpLow:    return GL_LOW_FLOAT;\r
3158                         case EbpUndefined:\r
3159                                 // Should be defined as the default precision by the parser\r
3160                         default: UNREACHABLE(type.getPrecision());\r
3161                         }\r
3162                 }\r
3163                 else if(type.getBasicType() == EbtInt)\r
3164                 {\r
3165                         switch(type.getPrecision())\r
3166                         {\r
3167                         case EbpHigh:   return GL_HIGH_INT;\r
3168                         case EbpMedium: return GL_MEDIUM_INT;\r
3169                         case EbpLow:    return GL_LOW_INT;\r
3170                         case EbpUndefined:\r
3171                                 // Should be defined as the default precision by the parser\r
3172                         default: UNREACHABLE(type.getPrecision());\r
3173                         }\r
3174                 }\r
3175 \r
3176                 // Other types (boolean, sampler) don't have a precision\r
3177                 return GL_NONE;\r
3178         }\r
3179 \r
3180         int OutputASM::dim(TIntermNode *v)\r
3181         {\r
3182                 TIntermTyped *vector = v->getAsTyped();\r
3183                 ASSERT(vector && vector->isRegister());\r
3184                 return vector->getNominalSize();\r
3185         }\r
3186 \r
3187         int OutputASM::dim2(TIntermNode *m)\r
3188         {\r
3189                 TIntermTyped *matrix = m->getAsTyped();\r
3190                 ASSERT(matrix && matrix->isMatrix() && !matrix->isArray());\r
3191                 return matrix->getSecondarySize();\r
3192         }\r
3193 \r
3194         // Returns ~0u if no loop count could be determined\r
3195         unsigned int OutputASM::loopCount(TIntermLoop *node)\r
3196         {\r
3197                 // Parse loops of the form:\r
3198                 // for(int index = initial; index [comparator] limit; index += increment)\r
3199                 TIntermSymbol *index = 0;\r
3200                 TOperator comparator = EOpNull;\r
3201                 int initial = 0;\r
3202                 int limit = 0;\r
3203                 int increment = 0;\r
3204 \r
3205                 // Parse index name and intial value\r
3206                 if(node->getInit())\r
3207                 {\r
3208                         TIntermAggregate *init = node->getInit()->getAsAggregate();\r
3209 \r
3210                         if(init)\r
3211                         {\r
3212                                 TIntermSequence &sequence = init->getSequence();\r
3213                                 TIntermTyped *variable = sequence[0]->getAsTyped();\r
3214 \r
3215                                 if(variable && variable->getQualifier() == EvqTemporary)\r
3216                                 {\r
3217                                         TIntermBinary *assign = variable->getAsBinaryNode();\r
3218 \r
3219                                         if(assign->getOp() == EOpInitialize)\r
3220                                         {\r
3221                                                 TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();\r
3222                                                 TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();\r
3223 \r
3224                                                 if(symbol && constant)\r
3225                                                 {\r
3226                                                         if(constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)\r
3227                                                         {\r
3228                                                                 index = symbol;\r
3229                                                                 initial = constant->getUnionArrayPointer()[0].getIConst();\r
3230                                                         }\r
3231                                                 }\r
3232                                         }\r
3233                                 }\r
3234                         }\r
3235                 }\r
3236 \r
3237                 // Parse comparator and limit value\r
3238                 if(index && node->getCondition())\r
3239                 {\r
3240                         TIntermBinary *test = node->getCondition()->getAsBinaryNode();\r
3241 \r
3242                         if(test && test->getLeft()->getAsSymbolNode()->getId() == index->getId())\r
3243                         {\r
3244                                 TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion();\r
3245 \r
3246                                 if(constant)\r
3247                                 {\r
3248                                         if(constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)\r
3249                                         {\r
3250                                                 comparator = test->getOp();\r
3251                                                 limit = constant->getUnionArrayPointer()[0].getIConst();\r
3252                                         }\r
3253                                 }\r
3254                         }\r
3255                 }\r
3256 \r
3257                 // Parse increment\r
3258                 if(index && comparator != EOpNull && node->getExpression())\r
3259                 {\r
3260                         TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode();\r
3261                         TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();\r
3262 \r
3263                         if(binaryTerminal)\r
3264                         {\r
3265                                 TOperator op = binaryTerminal->getOp();\r
3266                                 TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();\r
3267 \r
3268                                 if(constant)\r
3269                                 {\r
3270                                         if(constant->getBasicType() == EbtInt && constant->getNominalSize() == 1)\r
3271                                         {\r
3272                                                 int value = constant->getUnionArrayPointer()[0].getIConst();\r
3273 \r
3274                                                 switch(op)\r
3275                                                 {\r
3276                                                 case EOpAddAssign: increment = value;  break;\r
3277                                                 case EOpSubAssign: increment = -value; break;\r
3278                                                 default: UNIMPLEMENTED();\r
3279                                                 }\r
3280                                         }\r
3281                                 }\r
3282                         }\r
3283                         else if(unaryTerminal)\r
3284                         {\r
3285                                 TOperator op = unaryTerminal->getOp();\r
3286 \r
3287                                 switch(op)\r
3288                                 {\r
3289                                 case EOpPostIncrement: increment = 1;  break;\r
3290                                 case EOpPostDecrement: increment = -1; break;\r
3291                                 case EOpPreIncrement:  increment = 1;  break;\r
3292                                 case EOpPreDecrement:  increment = -1; break;\r
3293                                 default: UNIMPLEMENTED();\r
3294                                 }\r
3295                         }\r
3296                 }\r
3297 \r
3298                 if(index && comparator != EOpNull && increment != 0)\r
3299                 {\r
3300                         if(comparator == EOpLessThanEqual)\r
3301                         {\r
3302                                 comparator = EOpLessThan;\r
3303                                 limit += 1;\r
3304                         }\r
3305 \r
3306                         if(comparator == EOpLessThan)\r
3307                         {\r
3308                                 int iterations = (limit - initial) / increment;\r
3309 \r
3310                                 if(iterations <= 0)\r
3311                                 {\r
3312                                         iterations = 0;\r
3313                                 }\r
3314 \r
3315                                 return iterations;\r
3316                         }\r
3317                         else UNIMPLEMENTED();   // Falls through\r
3318                 }\r
3319 \r
3320                 return ~0u;\r
3321         }\r
3322 \r
3323         bool DetectLoopDiscontinuity::traverse(TIntermNode *node)\r
3324         {\r
3325                 loopDepth = 0;\r
3326                 loopDiscontinuity = false;\r
3327 \r
3328                 node->traverse(this);\r
3329 \r
3330                 return loopDiscontinuity;\r
3331         }\r
3332 \r
3333         bool DetectLoopDiscontinuity::visitLoop(Visit visit, TIntermLoop *loop)\r
3334         {\r
3335                 if(visit == PreVisit)\r
3336                 {\r
3337                         loopDepth++;\r
3338                 }\r
3339                 else if(visit == PostVisit)\r
3340                 {\r
3341                         loopDepth++;\r
3342                 }\r
3343 \r
3344                 return true;\r
3345         }\r
3346 \r
3347         bool DetectLoopDiscontinuity::visitBranch(Visit visit, TIntermBranch *node)\r
3348         {\r
3349                 if(loopDiscontinuity)\r
3350                 {\r
3351                         return false;\r
3352                 }\r
3353 \r
3354                 if(!loopDepth)\r
3355                 {\r
3356                         return true;\r
3357                 }\r
3358 \r
3359                 switch(node->getFlowOp())\r
3360                 {\r
3361                 case EOpKill:\r
3362                         break;\r
3363                 case EOpBreak:\r
3364                 case EOpContinue:\r
3365                 case EOpReturn:\r
3366                         loopDiscontinuity = true;\r
3367                         break;\r
3368                 default: UNREACHABLE(node->getFlowOp());\r
3369                 }\r
3370 \r
3371                 return !loopDiscontinuity;\r
3372         }\r
3373 \r
3374         bool DetectLoopDiscontinuity::visitAggregate(Visit visit, TIntermAggregate *node)\r
3375         {\r
3376                 return !loopDiscontinuity;\r
3377         }\r
3378 }\r