mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
	
	
		
			3128 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			3128 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | //
 | ||
|  | // Copyright (C) 2014-2015 LunarG, Inc.
 | ||
|  | // Copyright (C) 2015-2018 Google, Inc.
 | ||
|  | // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
 | ||
|  | //
 | ||
|  | // All rights reserved.
 | ||
|  | //
 | ||
|  | // Redistribution and use in source and binary forms, with or without
 | ||
|  | // modification, are permitted provided that the following conditions
 | ||
|  | // are met:
 | ||
|  | //
 | ||
|  | //    Redistributions of source code must retain the above copyright
 | ||
|  | //    notice, this list of conditions and the following disclaimer.
 | ||
|  | //
 | ||
|  | //    Redistributions in binary form must reproduce the above
 | ||
|  | //    copyright notice, this list of conditions and the following
 | ||
|  | //    disclaimer in the documentation and/or other materials provided
 | ||
|  | //    with the distribution.
 | ||
|  | //
 | ||
|  | //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
 | ||
|  | //    contributors may be used to endorse or promote products derived
 | ||
|  | //    from this software without specific prior written permission.
 | ||
|  | //
 | ||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | ||
|  | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 | ||
|  | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | ||
|  | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | ||
|  | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | ||
|  | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | ||
|  | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | ||
|  | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 | ||
|  | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | ||
|  | // POSSIBILITY OF SUCH DAMAGE.
 | ||
|  | 
 | ||
|  | //
 | ||
|  | // Helper for making SPIR-V IR.  Generally, this is documented in the header
 | ||
|  | // SpvBuilder.h.
 | ||
|  | //
 | ||
|  | 
 | ||
|  | #include <cassert>
 | ||
|  | #include <cstdlib>
 | ||
|  | 
 | ||
|  | #include <unordered_set>
 | ||
|  | #include <algorithm>
 | ||
|  | 
 | ||
|  | #include "SpvBuilder.h"
 | ||
|  | 
 | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  | #include "hex_float.h"
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifndef _WIN32
 | ||
|  |     #include <cstdio>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | namespace spv { | ||
|  | 
 | ||
|  | Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : | ||
|  |     spvVersion(spvVersion), | ||
|  |     source(SourceLanguageUnknown), | ||
|  |     sourceVersion(0), | ||
|  |     sourceFileStringId(NoResult), | ||
|  |     currentLine(0), | ||
|  |     currentFile(nullptr), | ||
|  |     emitOpLines(false), | ||
|  |     addressModel(AddressingModelLogical), | ||
|  |     memoryModel(MemoryModelGLSL450), | ||
|  |     builderNumber(magicNumber), | ||
|  |     buildPoint(0), | ||
|  |     uniqueId(0), | ||
|  |     entryPointFunction(0), | ||
|  |     generatingOpCodeForSpecConst(false), | ||
|  |     logger(buildLogger) | ||
|  | { | ||
|  |     clearAccessChain(); | ||
|  | } | ||
|  | 
 | ||
|  | Builder::~Builder() | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::import(const char* name) | ||
|  | { | ||
|  |     Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); | ||
|  |     import->addStringOperand(name); | ||
|  |     module.mapInstruction(import); | ||
|  | 
 | ||
|  |     imports.push_back(std::unique_ptr<Instruction>(import)); | ||
|  |     return import->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Emit instruction for non-filename-based #line directives (ie. no filename
 | ||
|  | // seen yet): emit an OpLine if we've been asked to emit OpLines and the line
 | ||
|  | // number has changed since the last time, and is a valid line number.
 | ||
|  | void Builder::setLine(int lineNum) | ||
|  | { | ||
|  |     if (lineNum != 0 && lineNum != currentLine) { | ||
|  |         currentLine = lineNum; | ||
|  |         if (emitOpLines) | ||
|  |             addLine(sourceFileStringId, currentLine, 0); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // If no filename, do non-filename-based #line emit. Else do filename-based emit.
 | ||
|  | // Emit OpLine if we've been asked to emit OpLines and the line number or filename
 | ||
|  | // has changed since the last time, and line number is valid.
 | ||
|  | void Builder::setLine(int lineNum, const char* filename) | ||
|  | { | ||
|  |     if (filename == nullptr) { | ||
|  |         setLine(lineNum); | ||
|  |         return; | ||
|  |     } | ||
|  |     if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr || | ||
|  |             strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) { | ||
|  |         currentLine = lineNum; | ||
|  |         currentFile = filename; | ||
|  |         if (emitOpLines) { | ||
|  |             spv::Id strId = getStringId(filename); | ||
|  |             addLine(strId, currentLine, 0); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addLine(Id fileName, int lineNum, int column) | ||
|  | { | ||
|  |     Instruction* line = new Instruction(OpLine); | ||
|  |     line->addIdOperand(fileName); | ||
|  |     line->addImmediateOperand(lineNum); | ||
|  |     line->addImmediateOperand(column); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(line)); | ||
|  | } | ||
|  | 
 | ||
|  | // For creating new groupedTypes (will return old type if the requested one was already made).
 | ||
|  | Id Builder::makeVoidType() | ||
|  | { | ||
|  |     Instruction* type; | ||
|  |     if (groupedTypes[OpTypeVoid].size() == 0) { | ||
|  |         type = new Instruction(getUniqueId(), NoType, OpTypeVoid); | ||
|  |         groupedTypes[OpTypeVoid].push_back(type); | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |         module.mapInstruction(type); | ||
|  |     } else | ||
|  |         type = groupedTypes[OpTypeVoid].back(); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeBoolType() | ||
|  | { | ||
|  |     Instruction* type; | ||
|  |     if (groupedTypes[OpTypeBool].size() == 0) { | ||
|  |         type = new Instruction(getUniqueId(), NoType, OpTypeBool); | ||
|  |         groupedTypes[OpTypeBool].push_back(type); | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |         module.mapInstruction(type); | ||
|  |     } else | ||
|  |         type = groupedTypes[OpTypeBool].back(); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeSamplerType() | ||
|  | { | ||
|  |     Instruction* type; | ||
|  |     if (groupedTypes[OpTypeSampler].size() == 0) { | ||
|  |         type = new Instruction(getUniqueId(), NoType, OpTypeSampler); | ||
|  |         groupedTypes[OpTypeSampler].push_back(type); | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |         module.mapInstruction(type); | ||
|  |     } else | ||
|  |         type = groupedTypes[OpTypeSampler].back(); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makePointer(StorageClass storageClass, Id pointee) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypePointer][t]; | ||
|  |         if (type->getImmediateOperand(0) == (unsigned)storageClass && | ||
|  |             type->getIdOperand(1) == pointee) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypePointer); | ||
|  |     type->addImmediateOperand(storageClass); | ||
|  |     type->addIdOperand(pointee); | ||
|  |     groupedTypes[OpTypePointer].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeForwardPointer(StorageClass storageClass) | ||
|  | { | ||
|  |     // Caching/uniquifying doesn't work here, because we don't know the
 | ||
|  |     // pointee type and there can be multiple forward pointers of the same
 | ||
|  |     // storage type. Somebody higher up in the stack must keep track.
 | ||
|  |     Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); | ||
|  |     type->addImmediateOperand(storageClass); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypePointer][t]; | ||
|  |         if (type->getImmediateOperand(0) == (unsigned)storageClass && | ||
|  |             type->getIdOperand(1) == pointee) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     type = new Instruction(forwardPointerType, NoType, OpTypePointer); | ||
|  |     type->addImmediateOperand(storageClass); | ||
|  |     type->addIdOperand(pointee); | ||
|  |     groupedTypes[OpTypePointer].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeIntegerType(int width, bool hasSign) | ||
|  | { | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     assert(width == 32); | ||
|  |     width = 32; | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeInt][t]; | ||
|  |         if (type->getImmediateOperand(0) == (unsigned)width && | ||
|  |             type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeInt); | ||
|  |     type->addImmediateOperand(width); | ||
|  |     type->addImmediateOperand(hasSign ? 1 : 0); | ||
|  |     groupedTypes[OpTypeInt].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     // deal with capabilities
 | ||
|  |     switch (width) { | ||
|  |     case 8: | ||
|  |     case 16: | ||
|  |         // these are currently handled by storage-type declarations and post processing
 | ||
|  |         break; | ||
|  |     case 64: | ||
|  |         addCapability(CapabilityInt64); | ||
|  |         break; | ||
|  |     default: | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeFloatType(int width) | ||
|  | { | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     assert(width == 32); | ||
|  |     width = 32; | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeFloat][t]; | ||
|  |         if (type->getImmediateOperand(0) == (unsigned)width) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeFloat); | ||
|  |     type->addImmediateOperand(width); | ||
|  |     groupedTypes[OpTypeFloat].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     // deal with capabilities
 | ||
|  |     switch (width) { | ||
|  |     case 16: | ||
|  |         // currently handled by storage-type declarations and post processing
 | ||
|  |         break; | ||
|  |     case 64: | ||
|  |         addCapability(CapabilityFloat64); | ||
|  |         break; | ||
|  |     default: | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Make a struct without checking for duplication.
 | ||
|  | // See makeStructResultType() for non-decorated structs
 | ||
|  | // needed as the result of some instructions, which does
 | ||
|  | // check for duplicates.
 | ||
|  | Id Builder::makeStructType(const std::vector<Id>& members, const char* name) | ||
|  | { | ||
|  |     // Don't look for previous one, because in the general case,
 | ||
|  |     // structs can be duplicated except for decorations.
 | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); | ||
|  |     for (int op = 0; op < (int)members.size(); ++op) | ||
|  |         type->addIdOperand(members[op]); | ||
|  |     groupedTypes[OpTypeStruct].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  |     addName(type->getResultId(), name); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Make a struct for the simple results of several instructions,
 | ||
|  | // checking for duplication.
 | ||
|  | Id Builder::makeStructResultType(Id type0, Id type1) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeStruct][t]; | ||
|  |         if (type->getNumOperands() != 2) | ||
|  |             continue; | ||
|  |         if (type->getIdOperand(0) != type0 || | ||
|  |             type->getIdOperand(1) != type1) | ||
|  |             continue; | ||
|  |         return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     std::vector<spv::Id> members; | ||
|  |     members.push_back(type0); | ||
|  |     members.push_back(type1); | ||
|  | 
 | ||
|  |     return makeStructType(members, "ResType"); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeVectorType(Id component, int size) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeVector][t]; | ||
|  |         if (type->getIdOperand(0) == component && | ||
|  |             type->getImmediateOperand(1) == (unsigned)size) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeVector); | ||
|  |     type->addIdOperand(component); | ||
|  |     type->addImmediateOperand(size); | ||
|  |     groupedTypes[OpTypeVector].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeMatrixType(Id component, int cols, int rows) | ||
|  | { | ||
|  |     assert(cols <= maxMatrixSize && rows <= maxMatrixSize); | ||
|  | 
 | ||
|  |     Id column = makeVectorType(component, rows); | ||
|  | 
 | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeMatrix][t]; | ||
|  |         if (type->getIdOperand(0) == column && | ||
|  |             type->getImmediateOperand(1) == (unsigned)cols) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); | ||
|  |     type->addIdOperand(column); | ||
|  |     type->addImmediateOperand(cols); | ||
|  |     groupedTypes[OpTypeMatrix].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeCooperativeMatrixNV][t]; | ||
|  |         if (type->getIdOperand(0) == component && | ||
|  |             type->getIdOperand(1) == scope && | ||
|  |             type->getIdOperand(2) == rows && | ||
|  |             type->getIdOperand(3) == cols) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); | ||
|  |     type->addIdOperand(component); | ||
|  |     type->addIdOperand(scope); | ||
|  |     type->addIdOperand(rows); | ||
|  |     type->addIdOperand(cols); | ||
|  |     groupedTypes[OpTypeCooperativeMatrixNV].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // TODO: performance: track arrays per stride
 | ||
|  | // If a stride is supplied (non-zero) make an array.
 | ||
|  | // If no stride (0), reuse previous array types.
 | ||
|  | // 'size' is an Id of a constant or specialization constant of the array size
 | ||
|  | Id Builder::makeArrayType(Id element, Id sizeId, int stride) | ||
|  | { | ||
|  |     Instruction* type; | ||
|  |     if (stride == 0) { | ||
|  |         // try to find existing type
 | ||
|  |         for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { | ||
|  |             type = groupedTypes[OpTypeArray][t]; | ||
|  |             if (type->getIdOperand(0) == element && | ||
|  |                 type->getIdOperand(1) == sizeId) | ||
|  |                 return type->getResultId(); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeArray); | ||
|  |     type->addIdOperand(element); | ||
|  |     type->addIdOperand(sizeId); | ||
|  |     groupedTypes[OpTypeArray].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeRuntimeArray(Id element) | ||
|  | { | ||
|  |     Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); | ||
|  |     type->addIdOperand(element); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeFunction][t]; | ||
|  |         if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) | ||
|  |             continue; | ||
|  |         bool mismatch = false; | ||
|  |         for (int p = 0; p < (int)paramTypes.size(); ++p) { | ||
|  |             if (paramTypes[p] != type->getIdOperand(p + 1)) { | ||
|  |                 mismatch = true; | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  |         if (! mismatch) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeFunction); | ||
|  |     type->addIdOperand(returnType); | ||
|  |     for (int p = 0; p < (int)paramTypes.size(); ++p) | ||
|  |         type->addIdOperand(paramTypes[p]); | ||
|  |     groupedTypes[OpTypeFunction].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, | ||
|  |     ImageFormat format) | ||
|  | { | ||
|  |     assert(sampled == 1 || sampled == 2); | ||
|  | 
 | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeImage][t]; | ||
|  |         if (type->getIdOperand(0) == sampledType && | ||
|  |             type->getImmediateOperand(1) == (unsigned int)dim && | ||
|  |             type->getImmediateOperand(2) == (  depth ? 1u : 0u) && | ||
|  |             type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && | ||
|  |             type->getImmediateOperand(4) == (     ms ? 1u : 0u) && | ||
|  |             type->getImmediateOperand(5) == sampled && | ||
|  |             type->getImmediateOperand(6) == (unsigned int)format) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeImage); | ||
|  |     type->addIdOperand(sampledType); | ||
|  |     type->addImmediateOperand(   dim); | ||
|  |     type->addImmediateOperand(  depth ? 1 : 0); | ||
|  |     type->addImmediateOperand(arrayed ? 1 : 0); | ||
|  |     type->addImmediateOperand(     ms ? 1 : 0); | ||
|  |     type->addImmediateOperand(sampled); | ||
|  |     type->addImmediateOperand((unsigned int)format); | ||
|  | 
 | ||
|  |     groupedTypes[OpTypeImage].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  |     // deal with capabilities
 | ||
|  |     switch (dim) { | ||
|  |     case DimBuffer: | ||
|  |         if (sampled == 1) | ||
|  |             addCapability(CapabilitySampledBuffer); | ||
|  |         else | ||
|  |             addCapability(CapabilityImageBuffer); | ||
|  |         break; | ||
|  |     case Dim1D: | ||
|  |         if (sampled == 1) | ||
|  |             addCapability(CapabilitySampled1D); | ||
|  |         else | ||
|  |             addCapability(CapabilityImage1D); | ||
|  |         break; | ||
|  |     case DimCube: | ||
|  |         if (arrayed) { | ||
|  |             if (sampled == 1) | ||
|  |                 addCapability(CapabilitySampledCubeArray); | ||
|  |             else | ||
|  |                 addCapability(CapabilityImageCubeArray); | ||
|  |         } | ||
|  |         break; | ||
|  |     case DimRect: | ||
|  |         if (sampled == 1) | ||
|  |             addCapability(CapabilitySampledRect); | ||
|  |         else | ||
|  |             addCapability(CapabilityImageRect); | ||
|  |         break; | ||
|  |     case DimSubpassData: | ||
|  |         addCapability(CapabilityInputAttachment); | ||
|  |         break; | ||
|  |     default: | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (ms) { | ||
|  |         if (sampled == 2) { | ||
|  |             // Images used with subpass data are not storage
 | ||
|  |             // images, so don't require the capability for them.
 | ||
|  |             if (dim != Dim::DimSubpassData) | ||
|  |                 addCapability(CapabilityStorageImageMultisample); | ||
|  |             if (arrayed) | ||
|  |                 addCapability(CapabilityImageMSArray); | ||
|  |         } | ||
|  |     } | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeSampledImageType(Id imageType) | ||
|  | { | ||
|  |     // try to find it
 | ||
|  |     Instruction* type; | ||
|  |     for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { | ||
|  |         type = groupedTypes[OpTypeSampledImage][t]; | ||
|  |         if (type->getIdOperand(0) == imageType) | ||
|  |             return type->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // not found, make it
 | ||
|  |     type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); | ||
|  |     type->addIdOperand(imageType); | ||
|  | 
 | ||
|  |     groupedTypes[OpTypeSampledImage].push_back(type); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |     module.mapInstruction(type); | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  | Id Builder::makeAccelerationStructureType() | ||
|  | { | ||
|  |     Instruction *type; | ||
|  |     if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { | ||
|  |         type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); | ||
|  |         groupedTypes[OpTypeAccelerationStructureKHR].push_back(type); | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |         module.mapInstruction(type); | ||
|  |     } else { | ||
|  |         type = groupedTypes[OpTypeAccelerationStructureKHR].back(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeRayQueryType() | ||
|  | { | ||
|  |     Instruction *type; | ||
|  |     if (groupedTypes[OpTypeRayQueryProvisionalKHR].size() == 0) { | ||
|  |         type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryProvisionalKHR); | ||
|  |         groupedTypes[OpTypeRayQueryProvisionalKHR].push_back(type); | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); | ||
|  |         module.mapInstruction(type); | ||
|  |     } else { | ||
|  |         type = groupedTypes[OpTypeRayQueryProvisionalKHR].back(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return type->getResultId(); | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | Id Builder::getDerefTypeId(Id resultId) const | ||
|  | { | ||
|  |     Id typeId = getTypeId(resultId); | ||
|  |     assert(isPointerType(typeId)); | ||
|  | 
 | ||
|  |     return module.getInstruction(typeId)->getIdOperand(1); | ||
|  | } | ||
|  | 
 | ||
|  | Op Builder::getMostBasicTypeClass(Id typeId) const | ||
|  | { | ||
|  |     Instruction* instr = module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     Op typeClass = instr->getOpCode(); | ||
|  |     switch (typeClass) | ||
|  |     { | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeMatrix: | ||
|  |     case OpTypeArray: | ||
|  |     case OpTypeRuntimeArray: | ||
|  |         return getMostBasicTypeClass(instr->getIdOperand(0)); | ||
|  |     case OpTypePointer: | ||
|  |         return getMostBasicTypeClass(instr->getIdOperand(1)); | ||
|  |     default: | ||
|  |         return typeClass; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | int Builder::getNumTypeConstituents(Id typeId) const | ||
|  | { | ||
|  |     Instruction* instr = module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     switch (instr->getOpCode()) | ||
|  |     { | ||
|  |     case OpTypeBool: | ||
|  |     case OpTypeInt: | ||
|  |     case OpTypeFloat: | ||
|  |     case OpTypePointer: | ||
|  |         return 1; | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeMatrix: | ||
|  |         return instr->getImmediateOperand(1); | ||
|  |     case OpTypeArray: | ||
|  |     { | ||
|  |         Id lengthId = instr->getIdOperand(1); | ||
|  |         return module.getInstruction(lengthId)->getImmediateOperand(0); | ||
|  |     } | ||
|  |     case OpTypeStruct: | ||
|  |         return instr->getNumOperands(); | ||
|  |     case OpTypeCooperativeMatrixNV: | ||
|  |         // has only one constituent when used with OpCompositeConstruct.
 | ||
|  |         return 1; | ||
|  |     default: | ||
|  |         assert(0); | ||
|  |         return 1; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Return the lowest-level type of scalar that an homogeneous composite is made out of.
 | ||
|  | // Typically, this is just to find out if something is made out of ints or floats.
 | ||
|  | // However, it includes returning a structure, if say, it is an array of structure.
 | ||
|  | Id Builder::getScalarTypeId(Id typeId) const | ||
|  | { | ||
|  |     Instruction* instr = module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     Op typeClass = instr->getOpCode(); | ||
|  |     switch (typeClass) | ||
|  |     { | ||
|  |     case OpTypeVoid: | ||
|  |     case OpTypeBool: | ||
|  |     case OpTypeInt: | ||
|  |     case OpTypeFloat: | ||
|  |     case OpTypeStruct: | ||
|  |         return instr->getResultId(); | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeMatrix: | ||
|  |     case OpTypeArray: | ||
|  |     case OpTypeRuntimeArray: | ||
|  |     case OpTypePointer: | ||
|  |         return getScalarTypeId(getContainedTypeId(typeId)); | ||
|  |     default: | ||
|  |         assert(0); | ||
|  |         return NoResult; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Return the type of 'member' of a composite.
 | ||
|  | Id Builder::getContainedTypeId(Id typeId, int member) const | ||
|  | { | ||
|  |     Instruction* instr = module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     Op typeClass = instr->getOpCode(); | ||
|  |     switch (typeClass) | ||
|  |     { | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeMatrix: | ||
|  |     case OpTypeArray: | ||
|  |     case OpTypeRuntimeArray: | ||
|  |     case OpTypeCooperativeMatrixNV: | ||
|  |         return instr->getIdOperand(0); | ||
|  |     case OpTypePointer: | ||
|  |         return instr->getIdOperand(1); | ||
|  |     case OpTypeStruct: | ||
|  |         return instr->getIdOperand(member); | ||
|  |     default: | ||
|  |         assert(0); | ||
|  |         return NoResult; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Return the immediately contained type of a given composite type.
 | ||
|  | Id Builder::getContainedTypeId(Id typeId) const | ||
|  | { | ||
|  |     return getContainedTypeId(typeId, 0); | ||
|  | } | ||
|  | 
 | ||
|  | // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
 | ||
|  | // of width 'width'. The 'width' is only consumed for int and float types.
 | ||
|  | // Returns false otherwise.
 | ||
|  | bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const | ||
|  | { | ||
|  |     const Instruction& instr = *module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     Op typeClass = instr.getOpCode(); | ||
|  |     switch (typeClass) | ||
|  |     { | ||
|  |     case OpTypeInt: | ||
|  |     case OpTypeFloat: | ||
|  |         return typeClass == typeOp && instr.getImmediateOperand(0) == width; | ||
|  |     case OpTypeStruct: | ||
|  |         for (int m = 0; m < instr.getNumOperands(); ++m) { | ||
|  |             if (containsType(instr.getIdOperand(m), typeOp, width)) | ||
|  |                 return true; | ||
|  |         } | ||
|  |         return false; | ||
|  |     case OpTypePointer: | ||
|  |         return false; | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeMatrix: | ||
|  |     case OpTypeArray: | ||
|  |     case OpTypeRuntimeArray: | ||
|  |         return containsType(getContainedTypeId(typeId), typeOp, width); | ||
|  |     default: | ||
|  |         return typeClass == typeOp; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // return true if the type is a pointer to PhysicalStorageBufferEXT or an
 | ||
|  | // array of such pointers. These require restrict/aliased decorations.
 | ||
|  | bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const | ||
|  | { | ||
|  |     const Instruction& instr = *module.getInstruction(typeId); | ||
|  | 
 | ||
|  |     Op typeClass = instr.getOpCode(); | ||
|  |     switch (typeClass) | ||
|  |     { | ||
|  |     case OpTypePointer: | ||
|  |         return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; | ||
|  |     case OpTypeArray: | ||
|  |         return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); | ||
|  |     default: | ||
|  |         return false; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // See if a scalar constant of this type has already been created, so it
 | ||
|  | // can be reused rather than duplicated.  (Required by the specification).
 | ||
|  | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) | ||
|  | { | ||
|  |     Instruction* constant; | ||
|  |     for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { | ||
|  |         constant = groupedConstants[typeClass][i]; | ||
|  |         if (constant->getOpCode() == opcode && | ||
|  |             constant->getTypeId() == typeId && | ||
|  |             constant->getImmediateOperand(0) == value) | ||
|  |             return constant->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
 | ||
|  | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) | ||
|  | { | ||
|  |     Instruction* constant; | ||
|  |     for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { | ||
|  |         constant = groupedConstants[typeClass][i]; | ||
|  |         if (constant->getOpCode() == opcode && | ||
|  |             constant->getTypeId() == typeId && | ||
|  |             constant->getImmediateOperand(0) == v1 && | ||
|  |             constant->getImmediateOperand(1) == v2) | ||
|  |             return constant->getResultId(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | // Return true if consuming 'opcode' means consuming a constant.
 | ||
|  | // "constant" here means after final transform to executable code,
 | ||
|  | // the value consumed will be a constant, so includes specialization.
 | ||
|  | bool Builder::isConstantOpCode(Op opcode) const | ||
|  | { | ||
|  |     switch (opcode) { | ||
|  |     case OpUndef: | ||
|  |     case OpConstantTrue: | ||
|  |     case OpConstantFalse: | ||
|  |     case OpConstant: | ||
|  |     case OpConstantComposite: | ||
|  |     case OpConstantSampler: | ||
|  |     case OpConstantNull: | ||
|  |     case OpSpecConstantTrue: | ||
|  |     case OpSpecConstantFalse: | ||
|  |     case OpSpecConstant: | ||
|  |     case OpSpecConstantComposite: | ||
|  |     case OpSpecConstantOp: | ||
|  |         return true; | ||
|  |     default: | ||
|  |         return false; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Return true if consuming 'opcode' means consuming a specialization constant.
 | ||
|  | bool Builder::isSpecConstantOpCode(Op opcode) const | ||
|  | { | ||
|  |     switch (opcode) { | ||
|  |     case OpSpecConstantTrue: | ||
|  |     case OpSpecConstantFalse: | ||
|  |     case OpSpecConstant: | ||
|  |     case OpSpecConstantComposite: | ||
|  |     case OpSpecConstantOp: | ||
|  |         return true; | ||
|  |     default: | ||
|  |         return false; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeBoolConstant(bool b, bool specConstant) | ||
|  | { | ||
|  |     Id typeId = makeBoolType(); | ||
|  |     Instruction* constant; | ||
|  |     Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (! specConstant) { | ||
|  |         Id existing = 0; | ||
|  |         for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { | ||
|  |             constant = groupedConstants[OpTypeBool][i]; | ||
|  |             if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) | ||
|  |                 existing = constant->getResultId(); | ||
|  |         } | ||
|  | 
 | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Make it
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeBool].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) | ||
|  | { | ||
|  |     Op opcode = specConstant ? OpSpecConstant : OpConstant; | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (! specConstant) { | ||
|  |         Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     c->addImmediateOperand(value); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeInt].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) | ||
|  | { | ||
|  |     Op opcode = specConstant ? OpSpecConstant : OpConstant; | ||
|  | 
 | ||
|  |     unsigned op1 = value & 0xFFFFFFFF; | ||
|  |     unsigned op2 = value >> 32; | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (! specConstant) { | ||
|  |         Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     c->addImmediateOperand(op1); | ||
|  |     c->addImmediateOperand(op2); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeInt].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeFloatConstant(float f, bool specConstant) | ||
|  | { | ||
|  |     Op opcode = specConstant ? OpSpecConstant : OpConstant; | ||
|  |     Id typeId = makeFloatType(32); | ||
|  |     union { float fl; unsigned int ui; } u; | ||
|  |     u.fl = f; | ||
|  |     unsigned value = u.ui; | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (! specConstant) { | ||
|  |         Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     c->addImmediateOperand(value); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeFloat].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeDoubleConstant(double d, bool specConstant) | ||
|  | { | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     assert(0); | ||
|  |     return NoResult; | ||
|  | #else
 | ||
|  |     Op opcode = specConstant ? OpSpecConstant : OpConstant; | ||
|  |     Id typeId = makeFloatType(64); | ||
|  |     union { double db; unsigned long long ull; } u; | ||
|  |     u.db = d; | ||
|  |     unsigned long long value = u.ull; | ||
|  |     unsigned op1 = value & 0xFFFFFFFF; | ||
|  |     unsigned op2 = value >> 32; | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (! specConstant) { | ||
|  |         Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     c->addImmediateOperand(op1); | ||
|  |     c->addImmediateOperand(op2); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeFloat].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeFloat16Constant(float f16, bool specConstant) | ||
|  | { | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     assert(0); | ||
|  |     return NoResult; | ||
|  | #else
 | ||
|  |     Op opcode = specConstant ? OpSpecConstant : OpConstant; | ||
|  |     Id typeId = makeFloatType(16); | ||
|  | 
 | ||
|  |     spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16); | ||
|  |     spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0); | ||
|  |     fVal.castTo(f16Val, spvutils::kRoundToZero); | ||
|  | 
 | ||
|  |     unsigned value = f16Val.value().getAsFloat().get_value(); | ||
|  | 
 | ||
|  |     // See if we already made it. Applies only to regular constants, because specialization constants
 | ||
|  |     // must remain distinct for the purpose of applying a SpecId decoration.
 | ||
|  |     if (!specConstant) { | ||
|  |         Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); | ||
|  |         if (existing) | ||
|  |             return existing; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     c->addImmediateOperand(value); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     groupedConstants[OpTypeFloat].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::makeFpConstant(Id type, double d, bool specConstant) | ||
|  | { | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     const int width = 32; | ||
|  |     assert(width == getScalarTypeWidth(type)); | ||
|  | #else
 | ||
|  |     const int width = getScalarTypeWidth(type); | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     assert(isFloatType(type)); | ||
|  | 
 | ||
|  |     switch (width) { | ||
|  |     case 16: | ||
|  |             return makeFloat16Constant((float)d, specConstant); | ||
|  |     case 32: | ||
|  |             return makeFloatConstant((float)d, specConstant); | ||
|  |     case 64: | ||
|  |             return makeDoubleConstant(d, specConstant); | ||
|  |     default: | ||
|  |             break; | ||
|  |     } | ||
|  | 
 | ||
|  |     assert(false); | ||
|  |     return NoResult; | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps) | ||
|  | { | ||
|  |     Instruction* constant = 0; | ||
|  |     bool found = false; | ||
|  |     for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { | ||
|  |         constant = groupedConstants[typeClass][i]; | ||
|  | 
 | ||
|  |         if (constant->getTypeId() != typeId) | ||
|  |             continue; | ||
|  | 
 | ||
|  |         // same contents?
 | ||
|  |         bool mismatch = false; | ||
|  |         for (int op = 0; op < constant->getNumOperands(); ++op) { | ||
|  |             if (constant->getIdOperand(op) != comps[op]) { | ||
|  |                 mismatch = true; | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  |         if (! mismatch) { | ||
|  |             found = true; | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return found ? constant->getResultId() : NoResult; | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps) | ||
|  | { | ||
|  |     Instruction* constant = 0; | ||
|  |     bool found = false; | ||
|  |     for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { | ||
|  |         constant = groupedStructConstants[typeId][i]; | ||
|  | 
 | ||
|  |         // same contents?
 | ||
|  |         bool mismatch = false; | ||
|  |         for (int op = 0; op < constant->getNumOperands(); ++op) { | ||
|  |             if (constant->getIdOperand(op) != comps[op]) { | ||
|  |                 mismatch = true; | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  |         if (! mismatch) { | ||
|  |             found = true; | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return found ? constant->getResultId() : NoResult; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant) | ||
|  | { | ||
|  |     Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; | ||
|  |     assert(typeId); | ||
|  |     Op typeClass = getTypeClass(typeId); | ||
|  | 
 | ||
|  |     switch (typeClass) { | ||
|  |     case OpTypeVector: | ||
|  |     case OpTypeArray: | ||
|  |     case OpTypeMatrix: | ||
|  |     case OpTypeCooperativeMatrixNV: | ||
|  |         if (! specConstant) { | ||
|  |             Id existing = findCompositeConstant(typeClass, typeId, members); | ||
|  |             if (existing) | ||
|  |                 return existing; | ||
|  |         } | ||
|  |         break; | ||
|  |     case OpTypeStruct: | ||
|  |         if (! specConstant) { | ||
|  |             Id existing = findStructConstant(typeId, members); | ||
|  |             if (existing) | ||
|  |                 return existing; | ||
|  |         } | ||
|  |         break; | ||
|  |     default: | ||
|  |         assert(0); | ||
|  |         return makeFloatConstant(0.0); | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* c = new Instruction(getUniqueId(), typeId, opcode); | ||
|  |     for (int op = 0; op < (int)members.size(); ++op) | ||
|  |         c->addIdOperand(members[op]); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); | ||
|  |     if (typeClass == OpTypeStruct) | ||
|  |         groupedStructConstants[typeId].push_back(c); | ||
|  |     else | ||
|  |         groupedConstants[typeClass].push_back(c); | ||
|  |     module.mapInstruction(c); | ||
|  | 
 | ||
|  |     return c->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) | ||
|  | { | ||
|  |     Instruction* entryPoint = new Instruction(OpEntryPoint); | ||
|  |     entryPoint->addImmediateOperand(model); | ||
|  |     entryPoint->addIdOperand(function->getId()); | ||
|  |     entryPoint->addStringOperand(name); | ||
|  | 
 | ||
|  |     entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint)); | ||
|  | 
 | ||
|  |     return entryPoint; | ||
|  | } | ||
|  | 
 | ||
|  | // Currently relying on the fact that all 'value' of interest are small non-negative values.
 | ||
|  | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) | ||
|  | { | ||
|  |     Instruction* instr = new Instruction(OpExecutionMode); | ||
|  |     instr->addIdOperand(entryPoint->getId()); | ||
|  |     instr->addImmediateOperand(mode); | ||
|  |     if (value1 >= 0) | ||
|  |         instr->addImmediateOperand(value1); | ||
|  |     if (value2 >= 0) | ||
|  |         instr->addImmediateOperand(value2); | ||
|  |     if (value3 >= 0) | ||
|  |         instr->addImmediateOperand(value3); | ||
|  | 
 | ||
|  |     executionModes.push_back(std::unique_ptr<Instruction>(instr)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addName(Id id, const char* string) | ||
|  | { | ||
|  |     Instruction* name = new Instruction(OpName); | ||
|  |     name->addIdOperand(id); | ||
|  |     name->addStringOperand(string); | ||
|  | 
 | ||
|  |     names.push_back(std::unique_ptr<Instruction>(name)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addMemberName(Id id, int memberNumber, const char* string) | ||
|  | { | ||
|  |     Instruction* name = new Instruction(OpMemberName); | ||
|  |     name->addIdOperand(id); | ||
|  |     name->addImmediateOperand(memberNumber); | ||
|  |     name->addStringOperand(string); | ||
|  | 
 | ||
|  |     names.push_back(std::unique_ptr<Instruction>(name)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addDecoration(Id id, Decoration decoration, int num) | ||
|  | { | ||
|  |     if (decoration == spv::DecorationMax) | ||
|  |         return; | ||
|  | 
 | ||
|  |     Instruction* dec = new Instruction(OpDecorate); | ||
|  |     dec->addIdOperand(id); | ||
|  |     dec->addImmediateOperand(decoration); | ||
|  |     if (num >= 0) | ||
|  |         dec->addImmediateOperand(num); | ||
|  | 
 | ||
|  |     decorations.push_back(std::unique_ptr<Instruction>(dec)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addDecoration(Id id, Decoration decoration, const char* s) | ||
|  | { | ||
|  |     if (decoration == spv::DecorationMax) | ||
|  |         return; | ||
|  | 
 | ||
|  |     Instruction* dec = new Instruction(OpDecorateStringGOOGLE); | ||
|  |     dec->addIdOperand(id); | ||
|  |     dec->addImmediateOperand(decoration); | ||
|  |     dec->addStringOperand(s); | ||
|  | 
 | ||
|  |     decorations.push_back(std::unique_ptr<Instruction>(dec)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) | ||
|  | { | ||
|  |     if (decoration == spv::DecorationMax) | ||
|  |         return; | ||
|  | 
 | ||
|  |     Instruction* dec = new Instruction(OpDecorateId); | ||
|  |     dec->addIdOperand(id); | ||
|  |     dec->addImmediateOperand(decoration); | ||
|  |     dec->addIdOperand(idDecoration); | ||
|  | 
 | ||
|  |     decorations.push_back(std::unique_ptr<Instruction>(dec)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) | ||
|  | { | ||
|  |     if (decoration == spv::DecorationMax) | ||
|  |         return; | ||
|  | 
 | ||
|  |     Instruction* dec = new Instruction(OpMemberDecorate); | ||
|  |     dec->addIdOperand(id); | ||
|  |     dec->addImmediateOperand(member); | ||
|  |     dec->addImmediateOperand(decoration); | ||
|  |     if (num >= 0) | ||
|  |         dec->addImmediateOperand(num); | ||
|  | 
 | ||
|  |     decorations.push_back(std::unique_ptr<Instruction>(dec)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) | ||
|  | { | ||
|  |     if (decoration == spv::DecorationMax) | ||
|  |         return; | ||
|  | 
 | ||
|  |     Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); | ||
|  |     dec->addIdOperand(id); | ||
|  |     dec->addImmediateOperand(member); | ||
|  |     dec->addImmediateOperand(decoration); | ||
|  |     dec->addStringOperand(s); | ||
|  | 
 | ||
|  |     decorations.push_back(std::unique_ptr<Instruction>(dec)); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Function* Builder::makeEntryPoint(const char* entryPoint) | ||
|  | { | ||
|  |     assert(! entryPointFunction); | ||
|  | 
 | ||
|  |     Block* entry; | ||
|  |     std::vector<Id> params; | ||
|  |     std::vector<std::vector<Decoration>> decorations; | ||
|  | 
 | ||
|  |     entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry); | ||
|  | 
 | ||
|  |     return entryPointFunction; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, | ||
|  |                                      const std::vector<Id>& paramTypes, | ||
|  |                                      const std::vector<std::vector<Decoration>>& decorations, Block **entry) | ||
|  | { | ||
|  |     // Make the function and initial instructions in it
 | ||
|  |     Id typeId = makeFunctionType(returnType, paramTypes); | ||
|  |     Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); | ||
|  |     Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module); | ||
|  | 
 | ||
|  |     // Set up the precisions
 | ||
|  |     setPrecision(function->getId(), precision); | ||
|  |     for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { | ||
|  |         for (int d = 0; d < (int)decorations[p].size(); ++d) | ||
|  |             addDecoration(firstParamId + p, decorations[p][d]); | ||
|  |     } | ||
|  | 
 | ||
|  |     // CFG
 | ||
|  |     if (entry) { | ||
|  |         *entry = new Block(getUniqueId(), *function); | ||
|  |         function->addBlock(*entry); | ||
|  |         setBuildPoint(*entry); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (name) | ||
|  |         addName(function->getId(), name); | ||
|  | 
 | ||
|  |     functions.push_back(std::unique_ptr<Function>(function)); | ||
|  | 
 | ||
|  |     return function; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::makeReturn(bool implicit, Id retVal) | ||
|  | { | ||
|  |     if (retVal) { | ||
|  |         Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); | ||
|  |         inst->addIdOperand(retVal); | ||
|  |         buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); | ||
|  |     } else | ||
|  |         buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn))); | ||
|  | 
 | ||
|  |     if (! implicit) | ||
|  |         createAndSetNoPredecessorBlock("post-return"); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::leaveFunction() | ||
|  | { | ||
|  |     Block* block = buildPoint; | ||
|  |     Function& function = buildPoint->getParent(); | ||
|  |     assert(block); | ||
|  | 
 | ||
|  |     // If our function did not contain a return, add a return void now.
 | ||
|  |     if (! block->isTerminated()) { | ||
|  |         if (function.getReturnType() == makeVoidType()) | ||
|  |             makeReturn(true); | ||
|  |         else { | ||
|  |             makeReturn(true, createUndefined(function.getReturnType())); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::makeDiscard() | ||
|  | { | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill))); | ||
|  |     createAndSetNoPredecessorBlock("post-discard"); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createVariable(StorageClass storageClass, Id type, const char* name, Id initializer) | ||
|  | { | ||
|  |     Id pointerType = makePointer(storageClass, type); | ||
|  |     Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); | ||
|  |     inst->addImmediateOperand(storageClass); | ||
|  |     if (initializer != NoResult) | ||
|  |         inst->addIdOperand(initializer); | ||
|  | 
 | ||
|  |     switch (storageClass) { | ||
|  |     case StorageClassFunction: | ||
|  |         // Validation rules require the declaration in the entry block
 | ||
|  |         buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst)); | ||
|  |         break; | ||
|  | 
 | ||
|  |     default: | ||
|  |         constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); | ||
|  |         module.mapInstruction(inst); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (name) | ||
|  |         addName(inst->getResultId(), name); | ||
|  | 
 | ||
|  |     return inst->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createUndefined(Id type) | ||
|  | { | ||
|  |   Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); | ||
|  |   buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); | ||
|  |   return inst->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // av/vis/nonprivate are unnecessary and illegal for some storage classes.
 | ||
|  | spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) | ||
|  |     const | ||
|  | { | ||
|  |     switch (sc) { | ||
|  |     case spv::StorageClassUniform: | ||
|  |     case spv::StorageClassWorkgroup: | ||
|  |     case spv::StorageClassStorageBuffer: | ||
|  |     case spv::StorageClassPhysicalStorageBufferEXT: | ||
|  |         break; | ||
|  |     default: | ||
|  |         memoryAccess = spv::MemoryAccessMask(memoryAccess &  | ||
|  |                         ~(spv::MemoryAccessMakePointerAvailableKHRMask | | ||
|  |                           spv::MemoryAccessMakePointerVisibleKHRMask | | ||
|  |                           spv::MemoryAccessNonPrivatePointerKHRMask)); | ||
|  |         break; | ||
|  |     } | ||
|  |     return memoryAccess; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, | ||
|  |     unsigned int alignment) | ||
|  | { | ||
|  |     Instruction* store = new Instruction(OpStore); | ||
|  |     store->addIdOperand(lValue); | ||
|  |     store->addIdOperand(rValue); | ||
|  | 
 | ||
|  |     memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); | ||
|  | 
 | ||
|  |     if (memoryAccess != MemoryAccessMaskNone) { | ||
|  |         store->addImmediateOperand(memoryAccess); | ||
|  |         if (memoryAccess & spv::MemoryAccessAlignedMask) { | ||
|  |             store->addImmediateOperand(alignment); | ||
|  |         } | ||
|  |         if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { | ||
|  |             store->addIdOperand(makeUintConstant(scope)); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(store)); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) | ||
|  | { | ||
|  |     Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); | ||
|  |     load->addIdOperand(lValue); | ||
|  | 
 | ||
|  |     memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); | ||
|  | 
 | ||
|  |     if (memoryAccess != MemoryAccessMaskNone) { | ||
|  |         load->addImmediateOperand(memoryAccess); | ||
|  |         if (memoryAccess & spv::MemoryAccessAlignedMask) { | ||
|  |             load->addImmediateOperand(alignment); | ||
|  |         } | ||
|  |         if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { | ||
|  |             load->addIdOperand(makeUintConstant(scope)); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(load)); | ||
|  | 
 | ||
|  |     return load->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets) | ||
|  | { | ||
|  |     // Figure out the final resulting type.
 | ||
|  |     spv::Id typeId = getTypeId(base); | ||
|  |     assert(isPointerType(typeId) && offsets.size() > 0); | ||
|  |     typeId = getContainedTypeId(typeId); | ||
|  |     for (int i = 0; i < (int)offsets.size(); ++i) { | ||
|  |         if (isStructType(typeId)) { | ||
|  |             assert(isConstantScalar(offsets[i])); | ||
|  |             typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i])); | ||
|  |         } else | ||
|  |             typeId = getContainedTypeId(typeId, offsets[i]); | ||
|  |     } | ||
|  |     typeId = makePointer(storageClass, typeId); | ||
|  | 
 | ||
|  |     // Make the instruction
 | ||
|  |     Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); | ||
|  |     chain->addIdOperand(base); | ||
|  |     for (int i = 0; i < (int)offsets.size(); ++i) | ||
|  |         chain->addIdOperand(offsets[i]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(chain)); | ||
|  | 
 | ||
|  |     return chain->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createArrayLength(Id base, unsigned int member) | ||
|  | { | ||
|  |     spv::Id intType = makeUintType(32); | ||
|  |     Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); | ||
|  |     length->addIdOperand(base); | ||
|  |     length->addImmediateOperand(member); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(length)); | ||
|  | 
 | ||
|  |     return length->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createCooperativeMatrixLength(Id type) | ||
|  | { | ||
|  |     spv::Id intType = makeUintType(32); | ||
|  | 
 | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>()); | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); | ||
|  |     length->addIdOperand(type); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(length)); | ||
|  | 
 | ||
|  |     return length->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) | ||
|  | { | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), | ||
|  |             std::vector<Id>(1, index)); | ||
|  |     } | ||
|  |     Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); | ||
|  |     extract->addIdOperand(composite); | ||
|  |     extract->addImmediateOperand(index); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); | ||
|  | 
 | ||
|  |     return extract->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes) | ||
|  | { | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes); | ||
|  |     } | ||
|  |     Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); | ||
|  |     extract->addIdOperand(composite); | ||
|  |     for (int i = 0; i < (int)indexes.size(); ++i) | ||
|  |         extract->addImmediateOperand(indexes[i]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); | ||
|  | 
 | ||
|  |     return extract->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) | ||
|  | { | ||
|  |     Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); | ||
|  |     insert->addIdOperand(object); | ||
|  |     insert->addIdOperand(composite); | ||
|  |     insert->addImmediateOperand(index); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); | ||
|  | 
 | ||
|  |     return insert->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes) | ||
|  | { | ||
|  |     Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); | ||
|  |     insert->addIdOperand(object); | ||
|  |     insert->addIdOperand(composite); | ||
|  |     for (int i = 0; i < (int)indexes.size(); ++i) | ||
|  |         insert->addImmediateOperand(indexes[i]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); | ||
|  | 
 | ||
|  |     return insert->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) | ||
|  | { | ||
|  |     Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); | ||
|  |     extract->addIdOperand(vector); | ||
|  |     extract->addIdOperand(componentIndex); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); | ||
|  | 
 | ||
|  |     return extract->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) | ||
|  | { | ||
|  |     Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); | ||
|  |     insert->addIdOperand(vector); | ||
|  |     insert->addIdOperand(component); | ||
|  |     insert->addIdOperand(componentIndex); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); | ||
|  | 
 | ||
|  |     return insert->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // An opcode that has no operands, no result id, and no type
 | ||
|  | void Builder::createNoResultOp(Op opCode) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(opCode); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | // An opcode that has one id operand, no result id, and no type
 | ||
|  | void Builder::createNoResultOp(Op opCode, Id operand) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(opCode); | ||
|  |     op->addIdOperand(operand); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | // An opcode that has one or more operands, no result id, and no type
 | ||
|  | void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(opCode); | ||
|  |     for (auto it = operands.cbegin(); it != operands.cend(); ++it) { | ||
|  |         op->addIdOperand(*it); | ||
|  |     } | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | // An opcode that has multiple operands, no result id, and no type
 | ||
|  | void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(opCode); | ||
|  |     for (auto it = operands.cbegin(); it != operands.cend(); ++it) { | ||
|  |         if (it->isId) | ||
|  |             op->addIdOperand(it->word); | ||
|  |         else | ||
|  |             op->addImmediateOperand(it->word); | ||
|  |     } | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(OpControlBarrier); | ||
|  |     op->addIdOperand(makeUintConstant(execution)); | ||
|  |     op->addIdOperand(makeUintConstant(memory)); | ||
|  |     op->addIdOperand(makeUintConstant(semantics)); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(OpMemoryBarrier); | ||
|  |     op->addIdOperand(makeUintConstant(executionScope)); | ||
|  |     op->addIdOperand(makeUintConstant(memorySemantics)); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | } | ||
|  | 
 | ||
|  | // An opcode that has one operands, a result id, and a type
 | ||
|  | Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) | ||
|  | { | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>()); | ||
|  |     } | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, opCode); | ||
|  |     op->addIdOperand(operand); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) | ||
|  | { | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         std::vector<Id> operands(2); | ||
|  |         operands[0] = left; operands[1] = right; | ||
|  |         return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>()); | ||
|  |     } | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, opCode); | ||
|  |     op->addIdOperand(left); | ||
|  |     op->addIdOperand(right); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) | ||
|  | { | ||
|  |     // Generate code for spec constants if in spec constant operation
 | ||
|  |     // generation mode.
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         std::vector<Id> operands(3); | ||
|  |         operands[0] = op1; | ||
|  |         operands[1] = op2; | ||
|  |         operands[2] = op3; | ||
|  |         return createSpecConstantOp( | ||
|  |             opCode, typeId, operands, std::vector<Id>()); | ||
|  |     } | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, opCode); | ||
|  |     op->addIdOperand(op1); | ||
|  |     op->addIdOperand(op2); | ||
|  |     op->addIdOperand(op3); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, opCode); | ||
|  |     for (auto it = operands.cbegin(); it != operands.cend(); ++it) | ||
|  |         op->addIdOperand(*it); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, opCode); | ||
|  |     for (auto it = operands.cbegin(); it != operands.cend(); ++it) { | ||
|  |         if (it->isId) | ||
|  |             op->addIdOperand(it->word); | ||
|  |         else | ||
|  |             op->addImmediateOperand(it->word); | ||
|  |     } | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, | ||
|  |     const std::vector<unsigned>& literals) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); | ||
|  |     op->addImmediateOperand((unsigned) opCode); | ||
|  |     for (auto it = operands.cbegin(); it != operands.cend(); ++it) | ||
|  |         op->addIdOperand(*it); | ||
|  |     for (auto it = literals.cbegin(); it != literals.cend(); ++it) | ||
|  |         op->addImmediateOperand(*it); | ||
|  |     module.mapInstruction(op); | ||
|  |     constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args) | ||
|  | { | ||
|  |     Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); | ||
|  |     op->addIdOperand(function->getId()); | ||
|  |     for (int a = 0; a < (int)args.size(); ++a) | ||
|  |         op->addIdOperand(args[a]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels) | ||
|  | { | ||
|  |     if (channels.size() == 1) | ||
|  |         return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); | ||
|  | 
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         std::vector<Id> operands(2); | ||
|  |         operands[0] = operands[1] = source; | ||
|  |         return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); | ||
|  |     } | ||
|  |     Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); | ||
|  |     assert(isVector(source)); | ||
|  |     swizzle->addIdOperand(source); | ||
|  |     swizzle->addIdOperand(source); | ||
|  |     for (int i = 0; i < (int)channels.size(); ++i) | ||
|  |         swizzle->addImmediateOperand(channels[i]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); | ||
|  | 
 | ||
|  |     return setPrecision(swizzle->getResultId(), precision); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels) | ||
|  | { | ||
|  |     if (channels.size() == 1 && getNumComponents(source) == 1) | ||
|  |         return createCompositeInsert(source, target, typeId, channels.front()); | ||
|  | 
 | ||
|  |     Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); | ||
|  | 
 | ||
|  |     assert(isVector(target)); | ||
|  |     swizzle->addIdOperand(target); | ||
|  | 
 | ||
|  |     assert(getNumComponents(source) == (int)channels.size()); | ||
|  |     assert(isVector(source)); | ||
|  |     swizzle->addIdOperand(source); | ||
|  | 
 | ||
|  |     // Set up an identity shuffle from the base value to the result value
 | ||
|  |     unsigned int components[4]; | ||
|  |     int numTargetComponents = getNumComponents(target); | ||
|  |     for (int i = 0; i < numTargetComponents; ++i) | ||
|  |         components[i] = i; | ||
|  | 
 | ||
|  |     // Punch in the l-value swizzle
 | ||
|  |     for (int i = 0; i < (int)channels.size(); ++i) | ||
|  |         components[channels[i]] = numTargetComponents + i; | ||
|  | 
 | ||
|  |     // finish the instruction with these components selectors
 | ||
|  |     for (int i = 0; i < numTargetComponents; ++i) | ||
|  |         swizzle->addImmediateOperand(components[i]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); | ||
|  | 
 | ||
|  |     return swizzle->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::promoteScalar(Decoration precision, Id& left, Id& right) | ||
|  | { | ||
|  |     int direction = getNumComponents(right) - getNumComponents(left); | ||
|  | 
 | ||
|  |     if (direction > 0) | ||
|  |         left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); | ||
|  |     else if (direction < 0) | ||
|  |         right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); | ||
|  | 
 | ||
|  |     return; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) | ||
|  | { | ||
|  |     assert(getNumComponents(scalar) == 1); | ||
|  |     assert(getTypeId(scalar) == getScalarTypeId(vectorType)); | ||
|  | 
 | ||
|  |     int numComponents = getNumTypeComponents(vectorType); | ||
|  |     if (numComponents == 1) | ||
|  |         return scalar; | ||
|  | 
 | ||
|  |     Instruction* smear = nullptr; | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         auto members = std::vector<spv::Id>(numComponents, scalar); | ||
|  |         // Sometime even in spec-constant-op mode, the temporary vector created by
 | ||
|  |         // promoting a scalar might not be a spec constant. This should depend on
 | ||
|  |         // the scalar.
 | ||
|  |         // e.g.:
 | ||
|  |         //  const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
 | ||
|  |         // In such cases, the temporary vector created from a_front_end_const_scalar
 | ||
|  |         // is not a spec constant vector, even though the binary operation node is marked
 | ||
|  |         // as 'specConstant' and we are in spec-constant-op mode.
 | ||
|  |         auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); | ||
|  |         smear = module.getInstruction(result_id); | ||
|  |     } else { | ||
|  |         smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); | ||
|  |         for (int c = 0; c < numComponents; ++c) | ||
|  |             smear->addIdOperand(scalar); | ||
|  |         buildPoint->addInstruction(std::unique_ptr<Instruction>(smear)); | ||
|  |     } | ||
|  | 
 | ||
|  |     return setPrecision(smear->getResultId(), precision); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args) | ||
|  | { | ||
|  |     Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); | ||
|  |     inst->addIdOperand(builtins); | ||
|  |     inst->addImmediateOperand(entryPoint); | ||
|  |     for (int arg = 0; arg < (int)args.size(); ++arg) | ||
|  |         inst->addIdOperand(args[arg]); | ||
|  | 
 | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); | ||
|  | 
 | ||
|  |     return inst->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Accept all parameters needed to create a texture instruction.
 | ||
|  | // Create the correct instruction based on the inputs, and make the call.
 | ||
|  | Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, | ||
|  |     bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) | ||
|  | { | ||
|  |     static const int maxTextureArgs = 10; | ||
|  |     Id texArgs[maxTextureArgs] = {}; | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // Set up the fixed arguments
 | ||
|  |     //
 | ||
|  |     int numArgs = 0; | ||
|  |     bool explicitLod = false; | ||
|  |     texArgs[numArgs++] = parameters.sampler; | ||
|  |     texArgs[numArgs++] = parameters.coords; | ||
|  |     if (parameters.Dref != NoResult) | ||
|  |         texArgs[numArgs++] = parameters.Dref; | ||
|  |     if (parameters.component != NoResult) | ||
|  |         texArgs[numArgs++] = parameters.component; | ||
|  | 
 | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  |     if (parameters.granularity != NoResult) | ||
|  |         texArgs[numArgs++] = parameters.granularity; | ||
|  |     if (parameters.coarse != NoResult) | ||
|  |         texArgs[numArgs++] = parameters.coarse; | ||
|  | #endif 
 | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // Set up the optional arguments
 | ||
|  |     //
 | ||
|  |     int optArgNum = numArgs;    // track which operand, if it exists, is the mask of optional arguments
 | ||
|  |     ++numArgs;                  // speculatively make room for the mask operand
 | ||
|  |     ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
 | ||
|  |     if (parameters.bias) { | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); | ||
|  |         texArgs[numArgs++] = parameters.bias; | ||
|  |     } | ||
|  |     if (parameters.lod) { | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); | ||
|  |         texArgs[numArgs++] = parameters.lod; | ||
|  |         explicitLod = true; | ||
|  |     } else if (parameters.gradX) { | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); | ||
|  |         texArgs[numArgs++] = parameters.gradX; | ||
|  |         texArgs[numArgs++] = parameters.gradY; | ||
|  |         explicitLod = true; | ||
|  |     } else if (noImplicitLod && ! fetch && ! gather) { | ||
|  |         // have to explicitly use lod of 0 if not allowed to have them be implicit, and
 | ||
|  |         // we would otherwise be about to issue an implicit instruction
 | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); | ||
|  |         texArgs[numArgs++] = makeFloatConstant(0.0); | ||
|  |         explicitLod = true; | ||
|  |     } | ||
|  |     if (parameters.offset) { | ||
|  |         if (isConstant(parameters.offset)) | ||
|  |             mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); | ||
|  |         else { | ||
|  |             addCapability(CapabilityImageGatherExtended); | ||
|  |             mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); | ||
|  |         } | ||
|  |         texArgs[numArgs++] = parameters.offset; | ||
|  |     } | ||
|  |     if (parameters.offsets) { | ||
|  |         addCapability(CapabilityImageGatherExtended); | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); | ||
|  |         texArgs[numArgs++] = parameters.offsets; | ||
|  |     } | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  |     if (parameters.sample) { | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); | ||
|  |         texArgs[numArgs++] = parameters.sample; | ||
|  |     } | ||
|  |     if (parameters.lodClamp) { | ||
|  |         // capability if this bit is used
 | ||
|  |         addCapability(CapabilityMinLod); | ||
|  | 
 | ||
|  |         mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); | ||
|  |         texArgs[numArgs++] = parameters.lodClamp; | ||
|  |     } | ||
|  |     if (parameters.nonprivate) { | ||
|  |         mask = mask | ImageOperandsNonPrivateTexelKHRMask; | ||
|  |     } | ||
|  |     if (parameters.volatil) { | ||
|  |         mask = mask | ImageOperandsVolatileTexelKHRMask; | ||
|  |     } | ||
|  | #endif
 | ||
|  |     mask = mask | signExtensionMask; | ||
|  |     if (mask == ImageOperandsMaskNone) | ||
|  |         --numArgs;  // undo speculative reservation for the mask argument
 | ||
|  |     else | ||
|  |         texArgs[optArgNum] = mask; | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // Set up the instruction
 | ||
|  |     //
 | ||
|  |     Op opCode = OpNop;  // All paths below need to set this
 | ||
|  |     if (fetch) { | ||
|  |         if (sparse) | ||
|  |             opCode = OpImageSparseFetch; | ||
|  |         else | ||
|  |             opCode = OpImageFetch; | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  |     } else if (parameters.granularity && parameters.coarse) { | ||
|  |         opCode = OpImageSampleFootprintNV; | ||
|  |     } else if (gather) { | ||
|  |         if (parameters.Dref) | ||
|  |             if (sparse) | ||
|  |                 opCode = OpImageSparseDrefGather; | ||
|  |             else | ||
|  |                 opCode = OpImageDrefGather; | ||
|  |         else | ||
|  |             if (sparse) | ||
|  |                 opCode = OpImageSparseGather; | ||
|  |             else | ||
|  |                 opCode = OpImageGather; | ||
|  | #endif
 | ||
|  |     } else if (explicitLod) { | ||
|  |         if (parameters.Dref) { | ||
|  |             if (proj) | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleProjDrefExplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleProjDrefExplicitLod; | ||
|  |             else | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleDrefExplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleDrefExplicitLod; | ||
|  |         } else { | ||
|  |             if (proj) | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleProjExplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleProjExplicitLod; | ||
|  |             else | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleExplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleExplicitLod; | ||
|  |         } | ||
|  |     } else { | ||
|  |         if (parameters.Dref) { | ||
|  |             if (proj) | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleProjDrefImplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleProjDrefImplicitLod; | ||
|  |             else | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleDrefImplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleDrefImplicitLod; | ||
|  |         } else { | ||
|  |             if (proj) | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleProjImplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleProjImplicitLod; | ||
|  |             else | ||
|  |                 if (sparse) | ||
|  |                     opCode = OpImageSparseSampleImplicitLod; | ||
|  |                 else | ||
|  |                     opCode = OpImageSampleImplicitLod; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // See if the result type is expecting a smeared result.
 | ||
|  |     // This happens when a legacy shadow*() call is made, which
 | ||
|  |     // gets a vec4 back instead of a float.
 | ||
|  |     Id smearedType = resultType; | ||
|  |     if (! isScalarType(resultType)) { | ||
|  |         switch (opCode) { | ||
|  |         case OpImageSampleDrefImplicitLod: | ||
|  |         case OpImageSampleDrefExplicitLod: | ||
|  |         case OpImageSampleProjDrefImplicitLod: | ||
|  |         case OpImageSampleProjDrefExplicitLod: | ||
|  |             resultType = getScalarTypeId(resultType); | ||
|  |             break; | ||
|  |         default: | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     Id typeId0 = 0; | ||
|  |     Id typeId1 = 0; | ||
|  | 
 | ||
|  |     if (sparse) { | ||
|  |         typeId0 = resultType; | ||
|  |         typeId1 = getDerefTypeId(parameters.texelOut); | ||
|  |         resultType = makeStructResultType(typeId0, typeId1); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Build the SPIR-V instruction
 | ||
|  |     Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); | ||
|  |     for (int op = 0; op < optArgNum; ++op) | ||
|  |         textureInst->addIdOperand(texArgs[op]); | ||
|  |     if (optArgNum < numArgs) | ||
|  |         textureInst->addImmediateOperand(texArgs[optArgNum]); | ||
|  |     for (int op = optArgNum + 1; op < numArgs; ++op) | ||
|  |         textureInst->addIdOperand(texArgs[op]); | ||
|  |     setPrecision(textureInst->getResultId(), precision); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst)); | ||
|  | 
 | ||
|  |     Id resultId = textureInst->getResultId(); | ||
|  | 
 | ||
|  |     if (sparse) { | ||
|  |         // set capability
 | ||
|  |         addCapability(CapabilitySparseResidency); | ||
|  | 
 | ||
|  |         // Decode the return type that was a special structure
 | ||
|  |         createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); | ||
|  |         resultId = createCompositeExtract(resultId, typeId0, 0); | ||
|  |         setPrecision(resultId, precision); | ||
|  |     } else { | ||
|  |         // When a smear is needed, do it, as per what was computed
 | ||
|  |         // above when resultType was changed to a scalar type.
 | ||
|  |         if (resultType != smearedType) | ||
|  |             resultId = smearScalar(precision, resultId, smearedType); | ||
|  |     } | ||
|  | 
 | ||
|  |     return resultId; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) | ||
|  | { | ||
|  |     // Figure out the result type
 | ||
|  |     Id resultType = 0; | ||
|  |     switch (opCode) { | ||
|  |     case OpImageQuerySize: | ||
|  |     case OpImageQuerySizeLod: | ||
|  |     { | ||
|  |         int numComponents = 0; | ||
|  |         switch (getTypeDimensionality(getImageType(parameters.sampler))) { | ||
|  |         case Dim1D: | ||
|  |         case DimBuffer: | ||
|  |             numComponents = 1; | ||
|  |             break; | ||
|  |         case Dim2D: | ||
|  |         case DimCube: | ||
|  |         case DimRect: | ||
|  |         case DimSubpassData: | ||
|  |             numComponents = 2; | ||
|  |             break; | ||
|  |         case Dim3D: | ||
|  |             numComponents = 3; | ||
|  |             break; | ||
|  | 
 | ||
|  |         default: | ||
|  |             assert(0); | ||
|  |             break; | ||
|  |         } | ||
|  |         if (isArrayedImageType(getImageType(parameters.sampler))) | ||
|  |             ++numComponents; | ||
|  | 
 | ||
|  |         Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); | ||
|  |         if (numComponents == 1) | ||
|  |             resultType = intType; | ||
|  |         else | ||
|  |             resultType = makeVectorType(intType, numComponents); | ||
|  | 
 | ||
|  |         break; | ||
|  |     } | ||
|  |     case OpImageQueryLod: | ||
|  |         resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); | ||
|  |         break; | ||
|  |     case OpImageQueryLevels: | ||
|  |     case OpImageQuerySamples: | ||
|  |         resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); | ||
|  |         break; | ||
|  |     default: | ||
|  |         assert(0); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* query = new Instruction(getUniqueId(), resultType, opCode); | ||
|  |     query->addIdOperand(parameters.sampler); | ||
|  |     if (parameters.coords) | ||
|  |         query->addIdOperand(parameters.coords); | ||
|  |     if (parameters.lod) | ||
|  |         query->addIdOperand(parameters.lod); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(query)); | ||
|  |     addCapability(CapabilityImageQuery); | ||
|  | 
 | ||
|  |     return query->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // External comments in header.
 | ||
|  | // Operates recursively to visit the composite's hierarchy.
 | ||
|  | Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) | ||
|  | { | ||
|  |     Id boolType = makeBoolType(); | ||
|  |     Id valueType = getTypeId(value1); | ||
|  | 
 | ||
|  |     Id resultId = NoResult; | ||
|  | 
 | ||
|  |     int numConstituents = getNumTypeConstituents(valueType); | ||
|  | 
 | ||
|  |     // Scalars and Vectors
 | ||
|  | 
 | ||
|  |     if (isScalarType(valueType) || isVectorType(valueType)) { | ||
|  |         assert(valueType == getTypeId(value2)); | ||
|  |         // These just need a single comparison, just have
 | ||
|  |         // to figure out what it is.
 | ||
|  |         Op op; | ||
|  |         switch (getMostBasicTypeClass(valueType)) { | ||
|  |         case OpTypeFloat: | ||
|  |             op = equal ? OpFOrdEqual : OpFOrdNotEqual; | ||
|  |             break; | ||
|  |         case OpTypeInt: | ||
|  |         default: | ||
|  |             op = equal ? OpIEqual : OpINotEqual; | ||
|  |             break; | ||
|  |         case OpTypeBool: | ||
|  |             op = equal ? OpLogicalEqual : OpLogicalNotEqual; | ||
|  |             precision = NoPrecision; | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (isScalarType(valueType)) { | ||
|  |             // scalar
 | ||
|  |             resultId = createBinOp(op, boolType, value1, value2); | ||
|  |         } else { | ||
|  |             // vector
 | ||
|  |             resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); | ||
|  |             setPrecision(resultId, precision); | ||
|  |             // reduce vector compares...
 | ||
|  |             resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); | ||
|  |         } | ||
|  | 
 | ||
|  |         return setPrecision(resultId, precision); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Only structs, arrays, and matrices should be left.
 | ||
|  |     // They share in common the reduction operation across their constituents.
 | ||
|  |     assert(isAggregateType(valueType) || isMatrixType(valueType)); | ||
|  | 
 | ||
|  |     // Compare each pair of constituents
 | ||
|  |     for (int constituent = 0; constituent < numConstituents; ++constituent) { | ||
|  |         std::vector<unsigned> indexes(1, constituent); | ||
|  |         Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); | ||
|  |         Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); | ||
|  |         Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); | ||
|  |         Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); | ||
|  | 
 | ||
|  |         Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); | ||
|  | 
 | ||
|  |         if (constituent == 0) | ||
|  |             resultId = subResultId; | ||
|  |         else | ||
|  |             resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), | ||
|  |                                     precision); | ||
|  |     } | ||
|  | 
 | ||
|  |     return resultId; | ||
|  | } | ||
|  | 
 | ||
|  | // OpCompositeConstruct
 | ||
|  | Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents) | ||
|  | { | ||
|  |     assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && | ||
|  |            getNumTypeConstituents(typeId) == (int)constituents.size())); | ||
|  | 
 | ||
|  |     if (generatingOpCodeForSpecConst) { | ||
|  |         // Sometime, even in spec-constant-op mode, the constant composite to be
 | ||
|  |         // constructed may not be a specialization constant.
 | ||
|  |         // e.g.:
 | ||
|  |         //  const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
 | ||
|  |         // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
 | ||
|  |         // The second column vector should NOT be spec constant, as it does not contain any spec constants.
 | ||
|  |         // To handle such cases, we check the constituents of the constant vector to determine whether this
 | ||
|  |         // vector should be created as a spec constant.
 | ||
|  |         return makeCompositeConstant(typeId, constituents, | ||
|  |                                      std::any_of(constituents.begin(), constituents.end(), | ||
|  |                                                  [&](spv::Id id) { return isSpecConstant(id); })); | ||
|  |     } | ||
|  | 
 | ||
|  |     Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); | ||
|  |     for (int c = 0; c < (int)constituents.size(); ++c) | ||
|  |         op->addIdOperand(constituents[c]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); | ||
|  | 
 | ||
|  |     return op->getResultId(); | ||
|  | } | ||
|  | 
 | ||
|  | // Vector or scalar constructor
 | ||
|  | Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) | ||
|  | { | ||
|  |     Id result = NoResult; | ||
|  |     unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); | ||
|  |     unsigned int targetComponent = 0; | ||
|  | 
 | ||
|  |     // Special case: when calling a vector constructor with a single scalar
 | ||
|  |     // argument, smear the scalar
 | ||
|  |     if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) | ||
|  |         return smearScalar(precision, sources[0], resultTypeId); | ||
|  | 
 | ||
|  |     // accumulate the arguments for OpCompositeConstruct
 | ||
|  |     std::vector<Id> constituents; | ||
|  |     Id scalarTypeId = getScalarTypeId(resultTypeId); | ||
|  | 
 | ||
|  |     // lambda to store the result of visiting an argument component
 | ||
|  |     const auto latchResult = [&](Id comp) { | ||
|  |         if (numTargetComponents > 1) | ||
|  |             constituents.push_back(comp); | ||
|  |         else | ||
|  |             result = comp; | ||
|  |         ++targetComponent; | ||
|  |     }; | ||
|  | 
 | ||
|  |     // lambda to visit a vector argument's components
 | ||
|  |     const auto accumulateVectorConstituents = [&](Id sourceArg) { | ||
|  |         unsigned int sourceSize = getNumComponents(sourceArg); | ||
|  |         unsigned int sourcesToUse = sourceSize; | ||
|  |         if (sourcesToUse + targetComponent > numTargetComponents) | ||
|  |             sourcesToUse = numTargetComponents - targetComponent; | ||
|  | 
 | ||
|  |         for (unsigned int s = 0; s < sourcesToUse; ++s) { | ||
|  |             std::vector<unsigned> swiz; | ||
|  |             swiz.push_back(s); | ||
|  |             latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  |     // lambda to visit a matrix argument's components
 | ||
|  |     const auto accumulateMatrixConstituents = [&](Id sourceArg) { | ||
|  |         unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); | ||
|  |         unsigned int sourcesToUse = sourceSize; | ||
|  |         if (sourcesToUse + targetComponent > numTargetComponents) | ||
|  |             sourcesToUse = numTargetComponents - targetComponent; | ||
|  | 
 | ||
|  |         int col = 0; | ||
|  |         int row = 0; | ||
|  |         for (unsigned int s = 0; s < sourcesToUse; ++s) { | ||
|  |             if (row >= getNumRows(sourceArg)) { | ||
|  |                 row = 0; | ||
|  |                 col++; | ||
|  |             } | ||
|  |             std::vector<Id> indexes; | ||
|  |             indexes.push_back(col); | ||
|  |             indexes.push_back(row); | ||
|  |             latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); | ||
|  |             row++; | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  |     // Go through the source arguments, each one could have either
 | ||
|  |     // a single or multiple components to contribute.
 | ||
|  |     for (unsigned int i = 0; i < sources.size(); ++i) { | ||
|  | 
 | ||
|  |         if (isScalar(sources[i]) || isPointer(sources[i])) | ||
|  |             latchResult(sources[i]); | ||
|  |         else if (isVector(sources[i])) | ||
|  |             accumulateVectorConstituents(sources[i]); | ||
|  |         else if (isMatrix(sources[i])) | ||
|  |             accumulateMatrixConstituents(sources[i]); | ||
|  |         else | ||
|  |             assert(0); | ||
|  | 
 | ||
|  |         if (targetComponent >= numTargetComponents) | ||
|  |             break; | ||
|  |     } | ||
|  | 
 | ||
|  |     // If the result is a vector, make it from the gathered constituents.
 | ||
|  |     if (constituents.size() > 0) | ||
|  |         result = createCompositeConstruct(resultTypeId, constituents); | ||
|  | 
 | ||
|  |     return setPrecision(result, precision); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) | ||
|  | { | ||
|  |     Id componentTypeId = getScalarTypeId(resultTypeId); | ||
|  |     int numCols = getTypeNumColumns(resultTypeId); | ||
|  |     int numRows = getTypeNumRows(resultTypeId); | ||
|  | 
 | ||
|  |     Instruction* instr = module.getInstruction(componentTypeId); | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     const unsigned bitCount = 32; | ||
|  |     assert(bitCount == instr->getImmediateOperand(0)); | ||
|  | #else
 | ||
|  |     const unsigned bitCount = instr->getImmediateOperand(0); | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     // Optimize matrix constructed from a bigger matrix
 | ||
|  |     if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { | ||
|  |         // To truncate the matrix to a smaller number of rows/columns, we need to:
 | ||
|  |         // 1. For each column, extract the column and truncate it to the required size using shuffle
 | ||
|  |         // 2. Assemble the resulting matrix from all columns
 | ||
|  |         Id matrix = sources[0]; | ||
|  |         Id columnTypeId = getContainedTypeId(resultTypeId); | ||
|  |         Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); | ||
|  | 
 | ||
|  |         std::vector<unsigned> channels; | ||
|  |         for (int row = 0; row < numRows; ++row) | ||
|  |             channels.push_back(row); | ||
|  | 
 | ||
|  |         std::vector<Id> matrixColumns; | ||
|  |         for (int col = 0; col < numCols; ++col) { | ||
|  |             std::vector<unsigned> indexes; | ||
|  |             indexes.push_back(col); | ||
|  |             Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); | ||
|  |             setPrecision(colv, precision); | ||
|  | 
 | ||
|  |             if (numRows != getNumRows(matrix)) { | ||
|  |                 matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); | ||
|  |             } else { | ||
|  |                 matrixColumns.push_back(colv); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Otherwise, will use a two step process
 | ||
|  |     // 1. make a compile-time 2D array of values
 | ||
|  |     // 2. construct a matrix from that array
 | ||
|  | 
 | ||
|  |     // Step 1.
 | ||
|  | 
 | ||
|  |     // initialize the array to the identity matrix
 | ||
|  |     Id ids[maxMatrixSize][maxMatrixSize]; | ||
|  |     Id  one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); | ||
|  |     Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); | ||
|  |     for (int col = 0; col < 4; ++col) { | ||
|  |         for (int row = 0; row < 4; ++row) { | ||
|  |             if (col == row) | ||
|  |                 ids[col][row] = one; | ||
|  |             else | ||
|  |                 ids[col][row] = zero; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // modify components as dictated by the arguments
 | ||
|  |     if (sources.size() == 1 && isScalar(sources[0])) { | ||
|  |         // a single scalar; resets the diagonals
 | ||
|  |         for (int col = 0; col < 4; ++col) | ||
|  |             ids[col][col] = sources[0]; | ||
|  |     } else if (isMatrix(sources[0])) { | ||
|  |         // constructing from another matrix; copy over the parts that exist in both the argument and constructee
 | ||
|  |         Id matrix = sources[0]; | ||
|  |         int minCols = std::min(numCols, getNumColumns(matrix)); | ||
|  |         int minRows = std::min(numRows, getNumRows(matrix)); | ||
|  |         for (int col = 0; col < minCols; ++col) { | ||
|  |             std::vector<unsigned> indexes; | ||
|  |             indexes.push_back(col); | ||
|  |             for (int row = 0; row < minRows; ++row) { | ||
|  |                 indexes.push_back(row); | ||
|  |                 ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); | ||
|  |                 indexes.pop_back(); | ||
|  |                 setPrecision(ids[col][row], precision); | ||
|  |             } | ||
|  |         } | ||
|  |     } else { | ||
|  |         // fill in the matrix in column-major order with whatever argument components are available
 | ||
|  |         int row = 0; | ||
|  |         int col = 0; | ||
|  | 
 | ||
|  |         for (int arg = 0; arg < (int)sources.size(); ++arg) { | ||
|  |             Id argComp = sources[arg]; | ||
|  |             for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { | ||
|  |                 if (getNumComponents(sources[arg]) > 1) { | ||
|  |                     argComp = createCompositeExtract(sources[arg], componentTypeId, comp); | ||
|  |                     setPrecision(argComp, precision); | ||
|  |                 } | ||
|  |                 ids[col][row++] = argComp; | ||
|  |                 if (row == numRows) { | ||
|  |                     row = 0; | ||
|  |                     col++; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // Step 2:  Construct a matrix from that array.
 | ||
|  |     // First make the column vectors, then make the matrix.
 | ||
|  | 
 | ||
|  |     // make the column vectors
 | ||
|  |     Id columnTypeId = getContainedTypeId(resultTypeId); | ||
|  |     std::vector<Id> matrixColumns; | ||
|  |     for (int col = 0; col < numCols; ++col) { | ||
|  |         std::vector<Id> vectorComponents; | ||
|  |         for (int row = 0; row < numRows; ++row) | ||
|  |             vectorComponents.push_back(ids[col][row]); | ||
|  |         Id column = createCompositeConstruct(columnTypeId, vectorComponents); | ||
|  |         setPrecision(column, precision); | ||
|  |         matrixColumns.push_back(column); | ||
|  |     } | ||
|  | 
 | ||
|  |     // make the matrix
 | ||
|  |     return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : | ||
|  |     builder(gb), | ||
|  |     condition(cond), | ||
|  |     control(ctrl), | ||
|  |     elseBlock(0) | ||
|  | { | ||
|  |     function = &builder.getBuildPoint()->getParent(); | ||
|  | 
 | ||
|  |     // make the blocks, but only put the then-block into the function,
 | ||
|  |     // the else-block and merge-block will be added later, in order, after
 | ||
|  |     // earlier code is emitted
 | ||
|  |     thenBlock = new Block(builder.getUniqueId(), *function); | ||
|  |     mergeBlock = new Block(builder.getUniqueId(), *function); | ||
|  | 
 | ||
|  |     // Save the current block, so that we can add in the flow control split when
 | ||
|  |     // makeEndIf is called.
 | ||
|  |     headerBlock = builder.getBuildPoint(); | ||
|  | 
 | ||
|  |     function->addBlock(thenBlock); | ||
|  |     builder.setBuildPoint(thenBlock); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::If::makeBeginElse() | ||
|  | { | ||
|  |     // Close out the "then" by having it jump to the mergeBlock
 | ||
|  |     builder.createBranch(mergeBlock); | ||
|  | 
 | ||
|  |     // Make the first else block and add it to the function
 | ||
|  |     elseBlock = new Block(builder.getUniqueId(), *function); | ||
|  |     function->addBlock(elseBlock); | ||
|  | 
 | ||
|  |     // Start building the else block
 | ||
|  |     builder.setBuildPoint(elseBlock); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::If::makeEndIf() | ||
|  | { | ||
|  |     // jump to the merge block
 | ||
|  |     builder.createBranch(mergeBlock); | ||
|  | 
 | ||
|  |     // Go back to the headerBlock and make the flow control split
 | ||
|  |     builder.setBuildPoint(headerBlock); | ||
|  |     builder.createSelectionMerge(mergeBlock, control); | ||
|  |     if (elseBlock) | ||
|  |         builder.createConditionalBranch(condition, thenBlock, elseBlock); | ||
|  |     else | ||
|  |         builder.createConditionalBranch(condition, thenBlock, mergeBlock); | ||
|  | 
 | ||
|  |     // add the merge block to the function
 | ||
|  |     function->addBlock(mergeBlock); | ||
|  |     builder.setBuildPoint(mergeBlock); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues, | ||
|  |                          const std::vector<int>& valueIndexToSegment, int defaultSegment, | ||
|  |                          std::vector<Block*>& segmentBlocks) | ||
|  | { | ||
|  |     Function& function = buildPoint->getParent(); | ||
|  | 
 | ||
|  |     // make all the blocks
 | ||
|  |     for (int s = 0; s < numSegments; ++s) | ||
|  |         segmentBlocks.push_back(new Block(getUniqueId(), function)); | ||
|  | 
 | ||
|  |     Block* mergeBlock = new Block(getUniqueId(), function); | ||
|  | 
 | ||
|  |     // make and insert the switch's selection-merge instruction
 | ||
|  |     createSelectionMerge(mergeBlock, control); | ||
|  | 
 | ||
|  |     // make the switch instruction
 | ||
|  |     Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); | ||
|  |     switchInst->addIdOperand(selector); | ||
|  |     auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; | ||
|  |     switchInst->addIdOperand(defaultOrMerge->getId()); | ||
|  |     defaultOrMerge->addPredecessor(buildPoint); | ||
|  |     for (int i = 0; i < (int)caseValues.size(); ++i) { | ||
|  |         switchInst->addImmediateOperand(caseValues[i]); | ||
|  |         switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); | ||
|  |         segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); | ||
|  |     } | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst)); | ||
|  | 
 | ||
|  |     // push the merge block
 | ||
|  |     switchMerges.push(mergeBlock); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::addSwitchBreak() | ||
|  | { | ||
|  |     // branch to the top of the merge block stack
 | ||
|  |     createBranch(switchMerges.top()); | ||
|  |     createAndSetNoPredecessorBlock("post-switch-break"); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment) | ||
|  | { | ||
|  |     int lastSegment = nextSegment - 1; | ||
|  |     if (lastSegment >= 0) { | ||
|  |         // Close out previous segment by jumping, if necessary, to next segment
 | ||
|  |         if (! buildPoint->isTerminated()) | ||
|  |             createBranch(segmentBlock[nextSegment]); | ||
|  |     } | ||
|  |     Block* block = segmentBlock[nextSegment]; | ||
|  |     block->getParent().addBlock(block); | ||
|  |     setBuildPoint(block); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/) | ||
|  | { | ||
|  |     // Close out previous segment by jumping, if necessary, to next segment
 | ||
|  |     if (! buildPoint->isTerminated()) | ||
|  |         addSwitchBreak(); | ||
|  | 
 | ||
|  |     switchMerges.top()->getParent().addBlock(switchMerges.top()); | ||
|  |     setBuildPoint(switchMerges.top()); | ||
|  | 
 | ||
|  |     switchMerges.pop(); | ||
|  | } | ||
|  | 
 | ||
|  | Block& Builder::makeNewBlock() | ||
|  | { | ||
|  |     Function& function = buildPoint->getParent(); | ||
|  |     auto block = new Block(getUniqueId(), function); | ||
|  |     function.addBlock(block); | ||
|  |     return *block; | ||
|  | } | ||
|  | 
 | ||
|  | Builder::LoopBlocks& Builder::makeNewLoop() | ||
|  | { | ||
|  |     // This verbosity is needed to simultaneously get the same behavior
 | ||
|  |     // everywhere (id's in the same order), have a syntax that works
 | ||
|  |     // across lots of versions of C++, have no warnings from pedantic
 | ||
|  |     // compilation modes, and leave the rest of the code alone.
 | ||
|  |     Block& head            = makeNewBlock(); | ||
|  |     Block& body            = makeNewBlock(); | ||
|  |     Block& merge           = makeNewBlock(); | ||
|  |     Block& continue_target = makeNewBlock(); | ||
|  |     LoopBlocks blocks(head, body, merge, continue_target); | ||
|  |     loops.push(blocks); | ||
|  |     return loops.top(); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createLoopContinue() | ||
|  | { | ||
|  |     createBranch(&loops.top().continue_target); | ||
|  |     // Set up a block for dead code.
 | ||
|  |     createAndSetNoPredecessorBlock("post-loop-continue"); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createLoopExit() | ||
|  | { | ||
|  |     createBranch(&loops.top().merge); | ||
|  |     // Set up a block for dead code.
 | ||
|  |     createAndSetNoPredecessorBlock("post-loop-break"); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::closeLoop() | ||
|  | { | ||
|  |     loops.pop(); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::clearAccessChain() | ||
|  | { | ||
|  |     accessChain.base = NoResult; | ||
|  |     accessChain.indexChain.clear(); | ||
|  |     accessChain.instr = NoResult; | ||
|  |     accessChain.swizzle.clear(); | ||
|  |     accessChain.component = NoResult; | ||
|  |     accessChain.preSwizzleBaseType = NoType; | ||
|  |     accessChain.isRValue = false; | ||
|  |     accessChain.coherentFlags.clear(); | ||
|  |     accessChain.alignment = 0; | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, | ||
|  |     AccessChain::CoherentFlags coherentFlags, unsigned int alignment) | ||
|  | { | ||
|  |     accessChain.coherentFlags |= coherentFlags; | ||
|  |     accessChain.alignment |= alignment; | ||
|  | 
 | ||
|  |     // swizzles can be stacked in GLSL, but simplified to a single
 | ||
|  |     // one here; the base type doesn't change
 | ||
|  |     if (accessChain.preSwizzleBaseType == NoType) | ||
|  |         accessChain.preSwizzleBaseType = preSwizzleBaseType; | ||
|  | 
 | ||
|  |     // if needed, propagate the swizzle for the current access chain
 | ||
|  |     if (accessChain.swizzle.size() > 0) { | ||
|  |         std::vector<unsigned> oldSwizzle = accessChain.swizzle; | ||
|  |         accessChain.swizzle.resize(0); | ||
|  |         for (unsigned int i = 0; i < swizzle.size(); ++i) { | ||
|  |             assert(swizzle[i] < oldSwizzle.size()); | ||
|  |             accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); | ||
|  |         } | ||
|  |     } else | ||
|  |         accessChain.swizzle = swizzle; | ||
|  | 
 | ||
|  |     // determine if we need to track this swizzle anymore
 | ||
|  |     simplifyAccessChainSwizzle(); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) | ||
|  | { | ||
|  |     assert(accessChain.isRValue == false); | ||
|  | 
 | ||
|  |     transferAccessChainSwizzle(true); | ||
|  |     Id base = collapseAccessChain(); | ||
|  |     Id source = rvalue; | ||
|  | 
 | ||
|  |     // dynamic component should be gone
 | ||
|  |     assert(accessChain.component == NoResult); | ||
|  | 
 | ||
|  |     // If swizzle still exists, it is out-of-order or not full, we must load the target vector,
 | ||
|  |     // extract and insert elements to perform writeMask and/or swizzle.
 | ||
|  |     if (accessChain.swizzle.size() > 0) { | ||
|  |         Id tempBaseId = createLoad(base); | ||
|  |         source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); | ||
|  |     } | ||
|  | 
 | ||
|  |     // take LSB of alignment
 | ||
|  |     alignment = alignment & ~(alignment & (alignment-1)); | ||
|  |     if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { | ||
|  |         memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); | ||
|  |     } | ||
|  | 
 | ||
|  |     createStore(source, base, memoryAccess, scope, alignment); | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, | ||
|  |     spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) | ||
|  | { | ||
|  |     Id id; | ||
|  | 
 | ||
|  |     if (accessChain.isRValue) { | ||
|  |         // transfer access chain, but try to stay in registers
 | ||
|  |         transferAccessChainSwizzle(false); | ||
|  |         if (accessChain.indexChain.size() > 0) { | ||
|  |             Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; | ||
|  | 
 | ||
|  |             // if all the accesses are constants, we can use OpCompositeExtract
 | ||
|  |             std::vector<unsigned> indexes; | ||
|  |             bool constant = true; | ||
|  |             for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { | ||
|  |                 if (isConstantScalar(accessChain.indexChain[i])) | ||
|  |                     indexes.push_back(getConstantScalar(accessChain.indexChain[i])); | ||
|  |                 else { | ||
|  |                     constant = false; | ||
|  |                     break; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             if (constant) { | ||
|  |                 id = createCompositeExtract(accessChain.base, swizzleBase, indexes); | ||
|  |             } else { | ||
|  |                 Id lValue = NoResult; | ||
|  |                 if (spvVersion >= Spv_1_4) { | ||
|  |                     // make a new function variable for this r-value, using an initializer,
 | ||
|  |                     // and mark it as NonWritable so that downstream it can be detected as a lookup
 | ||
|  |                     // table
 | ||
|  |                     lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable", | ||
|  |                         accessChain.base); | ||
|  |                     addDecoration(lValue, DecorationNonWritable); | ||
|  |                 } else { | ||
|  |                     lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable"); | ||
|  |                     // store into it
 | ||
|  |                     createStore(accessChain.base, lValue); | ||
|  |                 } | ||
|  |                 // move base to the new variable
 | ||
|  |                 accessChain.base = lValue; | ||
|  |                 accessChain.isRValue = false; | ||
|  | 
 | ||
|  |                 // load through the access chain
 | ||
|  |                 id = createLoad(collapseAccessChain()); | ||
|  |             } | ||
|  |             setPrecision(id, precision); | ||
|  |         } else | ||
|  |             id = accessChain.base;  // no precision, it was set when this was defined
 | ||
|  |     } else { | ||
|  |         transferAccessChainSwizzle(true); | ||
|  | 
 | ||
|  |         // take LSB of alignment
 | ||
|  |         alignment = alignment & ~(alignment & (alignment-1)); | ||
|  |         if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { | ||
|  |             memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); | ||
|  |         } | ||
|  | 
 | ||
|  |         // load through the access chain
 | ||
|  |         id = collapseAccessChain(); | ||
|  |         // Apply nonuniform both to the access chain and the loaded value.
 | ||
|  |         // Buffer accesses need the access chain decorated, and this is where
 | ||
|  |         // loaded image types get decorated. TODO: This should maybe move to
 | ||
|  |         // createImageTextureFunctionCall.
 | ||
|  |         addDecoration(id, nonUniform); | ||
|  |         id = createLoad(id, memoryAccess, scope, alignment); | ||
|  |         setPrecision(id, precision); | ||
|  |         addDecoration(id, nonUniform); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Done, unless there are swizzles to do
 | ||
|  |     if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) | ||
|  |         return id; | ||
|  | 
 | ||
|  |     // Do remaining swizzling
 | ||
|  | 
 | ||
|  |     // Do the basic swizzle
 | ||
|  |     if (accessChain.swizzle.size() > 0) { | ||
|  |         Id swizzledType = getScalarTypeId(getTypeId(id)); | ||
|  |         if (accessChain.swizzle.size() > 1) | ||
|  |             swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); | ||
|  |         id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Do the dynamic component
 | ||
|  |     if (accessChain.component != NoResult) | ||
|  |         id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); | ||
|  | 
 | ||
|  |     addDecoration(id, nonUniform); | ||
|  |     return id; | ||
|  | } | ||
|  | 
 | ||
|  | Id Builder::accessChainGetLValue() | ||
|  | { | ||
|  |     assert(accessChain.isRValue == false); | ||
|  | 
 | ||
|  |     transferAccessChainSwizzle(true); | ||
|  |     Id lvalue = collapseAccessChain(); | ||
|  | 
 | ||
|  |     // If swizzle exists, it is out-of-order or not full, we must load the target vector,
 | ||
|  |     // extract and insert elements to perform writeMask and/or swizzle.  This does not
 | ||
|  |     // go with getting a direct l-value pointer.
 | ||
|  |     assert(accessChain.swizzle.size() == 0); | ||
|  |     assert(accessChain.component == NoResult); | ||
|  | 
 | ||
|  |     return lvalue; | ||
|  | } | ||
|  | 
 | ||
|  | // comment in header
 | ||
|  | Id Builder::accessChainGetInferredType() | ||
|  | { | ||
|  |     // anything to operate on?
 | ||
|  |     if (accessChain.base == NoResult) | ||
|  |         return NoType; | ||
|  |     Id type = getTypeId(accessChain.base); | ||
|  | 
 | ||
|  |     // do initial dereference
 | ||
|  |     if (! accessChain.isRValue) | ||
|  |         type = getContainedTypeId(type); | ||
|  | 
 | ||
|  |     // dereference each index
 | ||
|  |     for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { | ||
|  |         if (isStructType(type)) | ||
|  |             type = getContainedTypeId(type, getConstantScalar(*it)); | ||
|  |         else | ||
|  |             type = getContainedTypeId(type); | ||
|  |     } | ||
|  | 
 | ||
|  |     // dereference swizzle
 | ||
|  |     if (accessChain.swizzle.size() == 1) | ||
|  |         type = getContainedTypeId(type); | ||
|  |     else if (accessChain.swizzle.size() > 1) | ||
|  |         type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); | ||
|  | 
 | ||
|  |     // dereference component selection
 | ||
|  |     if (accessChain.component) | ||
|  |         type = getContainedTypeId(type); | ||
|  | 
 | ||
|  |     return type; | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::dump(std::vector<unsigned int>& out) const | ||
|  | { | ||
|  |     // Header, before first instructions:
 | ||
|  |     out.push_back(MagicNumber); | ||
|  |     out.push_back(spvVersion); | ||
|  |     out.push_back(builderNumber); | ||
|  |     out.push_back(uniqueId + 1); | ||
|  |     out.push_back(0); | ||
|  | 
 | ||
|  |     // Capabilities
 | ||
|  |     for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { | ||
|  |         Instruction capInst(0, 0, OpCapability); | ||
|  |         capInst.addImmediateOperand(*it); | ||
|  |         capInst.dump(out); | ||
|  |     } | ||
|  | 
 | ||
|  |     for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { | ||
|  |         Instruction extInst(0, 0, OpExtension); | ||
|  |         extInst.addStringOperand(it->c_str()); | ||
|  |         extInst.dump(out); | ||
|  |     } | ||
|  | 
 | ||
|  |     dumpInstructions(out, imports); | ||
|  |     Instruction memInst(0, 0, OpMemoryModel); | ||
|  |     memInst.addImmediateOperand(addressModel); | ||
|  |     memInst.addImmediateOperand(memoryModel); | ||
|  |     memInst.dump(out); | ||
|  | 
 | ||
|  |     // Instructions saved up while building:
 | ||
|  |     dumpInstructions(out, entryPoints); | ||
|  |     dumpInstructions(out, executionModes); | ||
|  | 
 | ||
|  |     // Debug instructions
 | ||
|  |     dumpInstructions(out, strings); | ||
|  |     dumpSourceInstructions(out); | ||
|  |     for (int e = 0; e < (int)sourceExtensions.size(); ++e) { | ||
|  |         Instruction sourceExtInst(0, 0, OpSourceExtension); | ||
|  |         sourceExtInst.addStringOperand(sourceExtensions[e]); | ||
|  |         sourceExtInst.dump(out); | ||
|  |     } | ||
|  |     dumpInstructions(out, names); | ||
|  |     dumpModuleProcesses(out); | ||
|  | 
 | ||
|  |     // Annotation instructions
 | ||
|  |     dumpInstructions(out, decorations); | ||
|  | 
 | ||
|  |     dumpInstructions(out, constantsTypesGlobals); | ||
|  |     dumpInstructions(out, externals); | ||
|  | 
 | ||
|  |     // The functions
 | ||
|  |     module.dump(out); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | // Protected methods.
 | ||
|  | //
 | ||
|  | 
 | ||
|  | // Turn the described access chain in 'accessChain' into an instruction(s)
 | ||
|  | // computing its address.  This *cannot* include complex swizzles, which must
 | ||
|  | // be handled after this is called.
 | ||
|  | //
 | ||
|  | // Can generate code.
 | ||
|  | Id Builder::collapseAccessChain() | ||
|  | { | ||
|  |     assert(accessChain.isRValue == false); | ||
|  | 
 | ||
|  |     // did we already emit an access chain for this?
 | ||
|  |     if (accessChain.instr != NoResult) | ||
|  |         return accessChain.instr; | ||
|  | 
 | ||
|  |     // If we have a dynamic component, we can still transfer
 | ||
|  |     // that into a final operand to the access chain.  We need to remap the
 | ||
|  |     // dynamic component through the swizzle to get a new dynamic component to
 | ||
|  |     // update.
 | ||
|  |     //
 | ||
|  |     // This was not done in transferAccessChainSwizzle() because it might
 | ||
|  |     // generate code.
 | ||
|  |     remapDynamicSwizzle(); | ||
|  |     if (accessChain.component != NoResult) { | ||
|  |         // transfer the dynamic component to the access chain
 | ||
|  |         accessChain.indexChain.push_back(accessChain.component); | ||
|  |         accessChain.component = NoResult; | ||
|  |     } | ||
|  | 
 | ||
|  |     // note that non-trivial swizzling is left pending
 | ||
|  | 
 | ||
|  |     // do we have an access chain?
 | ||
|  |     if (accessChain.indexChain.size() == 0) | ||
|  |         return accessChain.base; | ||
|  | 
 | ||
|  |     // emit the access chain
 | ||
|  |     StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); | ||
|  |     accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); | ||
|  | 
 | ||
|  |     return accessChain.instr; | ||
|  | } | ||
|  | 
 | ||
|  | // For a dynamic component selection of a swizzle.
 | ||
|  | //
 | ||
|  | // Turn the swizzle and dynamic component into just a dynamic component.
 | ||
|  | //
 | ||
|  | // Generates code.
 | ||
|  | void Builder::remapDynamicSwizzle() | ||
|  | { | ||
|  |     // do we have a swizzle to remap a dynamic component through?
 | ||
|  |     if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { | ||
|  |         // build a vector of the swizzle for the component to map into
 | ||
|  |         std::vector<Id> components; | ||
|  |         for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) | ||
|  |             components.push_back(makeUintConstant(accessChain.swizzle[c])); | ||
|  |         Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); | ||
|  |         Id map = makeCompositeConstant(mapType, components); | ||
|  | 
 | ||
|  |         // use it
 | ||
|  |         accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); | ||
|  |         accessChain.swizzle.clear(); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // clear out swizzle if it is redundant, that is reselecting the same components
 | ||
|  | // that would be present without the swizzle.
 | ||
|  | void Builder::simplifyAccessChainSwizzle() | ||
|  | { | ||
|  |     // If the swizzle has fewer components than the vector, it is subsetting, and must stay
 | ||
|  |     // to preserve that fact.
 | ||
|  |     if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) | ||
|  |         return; | ||
|  | 
 | ||
|  |     // if components are out of order, it is a swizzle
 | ||
|  |     for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { | ||
|  |         if (i != accessChain.swizzle[i]) | ||
|  |             return; | ||
|  |     } | ||
|  | 
 | ||
|  |     // otherwise, there is no need to track this swizzle
 | ||
|  |     accessChain.swizzle.clear(); | ||
|  |     if (accessChain.component == NoResult) | ||
|  |         accessChain.preSwizzleBaseType = NoType; | ||
|  | } | ||
|  | 
 | ||
|  | // To the extent any swizzling can become part of the chain
 | ||
|  | // of accesses instead of a post operation, make it so.
 | ||
|  | // If 'dynamic' is true, include transferring the dynamic component,
 | ||
|  | // otherwise, leave it pending.
 | ||
|  | //
 | ||
|  | // Does not generate code. just updates the access chain.
 | ||
|  | void Builder::transferAccessChainSwizzle(bool dynamic) | ||
|  | { | ||
|  |     // non existent?
 | ||
|  |     if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) | ||
|  |         return; | ||
|  | 
 | ||
|  |     // too complex?
 | ||
|  |     // (this requires either a swizzle, or generating code for a dynamic component)
 | ||
|  |     if (accessChain.swizzle.size() > 1) | ||
|  |         return; | ||
|  | 
 | ||
|  |     // single component, either in the swizzle and/or dynamic component
 | ||
|  |     if (accessChain.swizzle.size() == 1) { | ||
|  |         assert(accessChain.component == NoResult); | ||
|  |         // handle static component selection
 | ||
|  |         accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); | ||
|  |         accessChain.swizzle.clear(); | ||
|  |         accessChain.preSwizzleBaseType = NoType; | ||
|  |     } else if (dynamic && accessChain.component != NoResult) { | ||
|  |         assert(accessChain.swizzle.size() == 0); | ||
|  |         // handle dynamic component
 | ||
|  |         accessChain.indexChain.push_back(accessChain.component); | ||
|  |         accessChain.preSwizzleBaseType = NoType; | ||
|  |         accessChain.component = NoResult; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Utility method for creating a new block and setting the insert point to
 | ||
|  | // be in it. This is useful for flow-control operations that need a "dummy"
 | ||
|  | // block proceeding them (e.g. instructions after a discard, etc).
 | ||
|  | void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) | ||
|  | { | ||
|  |     Block* block = new Block(getUniqueId(), buildPoint->getParent()); | ||
|  |     block->setUnreachable(); | ||
|  |     buildPoint->getParent().addBlock(block); | ||
|  |     setBuildPoint(block); | ||
|  | 
 | ||
|  |     // if (name)
 | ||
|  |     //    addName(block->getId(), name);
 | ||
|  | } | ||
|  | 
 | ||
|  | // Comments in header
 | ||
|  | void Builder::createBranch(Block* block) | ||
|  | { | ||
|  |     Instruction* branch = new Instruction(OpBranch); | ||
|  |     branch->addIdOperand(block->getId()); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); | ||
|  |     block->addPredecessor(buildPoint); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) | ||
|  | { | ||
|  |     Instruction* merge = new Instruction(OpSelectionMerge); | ||
|  |     merge->addIdOperand(mergeBlock->getId()); | ||
|  |     merge->addImmediateOperand(control); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, | ||
|  |                               const std::vector<unsigned int>& operands) | ||
|  | { | ||
|  |     Instruction* merge = new Instruction(OpLoopMerge); | ||
|  |     merge->addIdOperand(mergeBlock->getId()); | ||
|  |     merge->addIdOperand(continueBlock->getId()); | ||
|  |     merge->addImmediateOperand(control); | ||
|  |     for (int op = 0; op < (int)operands.size(); ++op) | ||
|  |         merge->addImmediateOperand(operands[op]); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) | ||
|  | { | ||
|  |     Instruction* branch = new Instruction(OpBranchConditional); | ||
|  |     branch->addIdOperand(condition); | ||
|  |     branch->addIdOperand(thenBlock->getId()); | ||
|  |     branch->addIdOperand(elseBlock->getId()); | ||
|  |     buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); | ||
|  |     thenBlock->addPredecessor(buildPoint); | ||
|  |     elseBlock->addPredecessor(buildPoint); | ||
|  | } | ||
|  | 
 | ||
|  | // OpSource
 | ||
|  | // [OpSourceContinued]
 | ||
|  | // ...
 | ||
|  | void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, | ||
|  |                                      std::vector<unsigned int>& out) const | ||
|  | { | ||
|  |     const int maxWordCount = 0xFFFF; | ||
|  |     const int opSourceWordCount = 4; | ||
|  |     const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; | ||
|  | 
 | ||
|  |     if (source != SourceLanguageUnknown) { | ||
|  |         // OpSource Language Version File Source
 | ||
|  |         Instruction sourceInst(NoResult, NoType, OpSource); | ||
|  |         sourceInst.addImmediateOperand(source); | ||
|  |         sourceInst.addImmediateOperand(sourceVersion); | ||
|  |         // File operand
 | ||
|  |         if (fileId != NoResult) { | ||
|  |             sourceInst.addIdOperand(fileId); | ||
|  |             // Source operand
 | ||
|  |             if (text.size() > 0) { | ||
|  |                 int nextByte = 0; | ||
|  |                 std::string subString; | ||
|  |                 while ((int)text.size() - nextByte > 0) { | ||
|  |                     subString = text.substr(nextByte, nonNullBytesPerInstruction); | ||
|  |                     if (nextByte == 0) { | ||
|  |                         // OpSource
 | ||
|  |                         sourceInst.addStringOperand(subString.c_str()); | ||
|  |                         sourceInst.dump(out); | ||
|  |                     } else { | ||
|  |                         // OpSourcContinued
 | ||
|  |                         Instruction sourceContinuedInst(OpSourceContinued); | ||
|  |                         sourceContinuedInst.addStringOperand(subString.c_str()); | ||
|  |                         sourceContinuedInst.dump(out); | ||
|  |                     } | ||
|  |                     nextByte += nonNullBytesPerInstruction; | ||
|  |                 } | ||
|  |             } else | ||
|  |                 sourceInst.dump(out); | ||
|  |         } else | ||
|  |             sourceInst.dump(out); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Dump an OpSource[Continued] sequence for the source and every include file
 | ||
|  | void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const | ||
|  | { | ||
|  |     dumpSourceInstructions(sourceFileStringId, sourceText, out); | ||
|  |     for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) | ||
|  |         dumpSourceInstructions(iItr->first, *iItr->second, out); | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::dumpInstructions(std::vector<unsigned int>& out, | ||
|  |     const std::vector<std::unique_ptr<Instruction> >& instructions) const | ||
|  | { | ||
|  |     for (int i = 0; i < (int)instructions.size(); ++i) { | ||
|  |         instructions[i]->dump(out); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const | ||
|  | { | ||
|  |     for (int i = 0; i < (int)moduleProcesses.size(); ++i) { | ||
|  |         Instruction moduleProcessed(OpModuleProcessed); | ||
|  |         moduleProcessed.addStringOperand(moduleProcesses[i]); | ||
|  |         moduleProcessed.dump(out); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | }; // end spv namespace
 |