// //Copyright (C) 2014 LunarG, Inc. // //All rights reserved. // //Redistribution and use in source and binary forms, with or without //modification, are permitted provided that the following conditions //are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // Neither the name of 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. // // Author: John Kessenich, LunarG // // // Helper for making SPIR-V IR. Generally, this is documented in the header // SpvBuilder.h. // #include #include #include #include "SpvBuilder.h" #ifndef _WIN32 #include #endif namespace spv { const int SpvBuilderMagic = 0xBB; Builder::Builder(unsigned int userNumber) : source(SourceLanguageUnknown), sourceVersion(0), addressModel(AddressingModelLogical), memoryModel(MemoryModelGLSL450), builderNumber(userNumber << 16 | SpvBuilderMagic), buildPoint(0), uniqueId(0), mainFunction(0), stageExit(0) { clearAccessChain(); } Builder::~Builder() { } Id Builder::import(const char* name) { Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); import->addStringOperand(name); imports.push_back(import); return import->getResultId(); } // 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(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(type); module.mapInstruction(type); } else type = groupedTypes[OpTypeBool].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) == 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeIntegerType(int width, bool hasSign) { // try to find it Instruction* type; for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { type = groupedTypes[OpTypeInt][t]; if (type->getImmediateOperand(0) == width && type->getImmediateOperand(1) == (hasSign ? 1 : 0)) 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeFloatType(int width) { // try to find it Instruction* type; for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { type = groupedTypes[OpTypeFloat][t]; if (type->getImmediateOperand(0) == 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeStructType(std::vector& members, const char* name) { // 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(type); module.mapInstruction(type); addName(type->getResultId(), name); return type->getResultId(); } 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) == 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(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) == 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeArrayType(Id element, unsigned size) { // First, we need a constant instruction for the size Id sizeId = makeUintConstant(size); // try to find existing type Instruction* 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeFunctionType(Id returnType, std::vector& 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 || 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(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::makeSampler(Id sampledType, Dim dim, samplerContent content, bool arrayed, bool shadow, bool ms) { // try to find it Instruction* type; for (int t = 0; t < (int)groupedTypes[OpTypeSampler].size(); ++t) { type = groupedTypes[OpTypeSampler][t]; if (type->getIdOperand(0) == sampledType && type->getImmediateOperand(1) == (unsigned int)dim && type->getImmediateOperand(2) == content && type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && type->getImmediateOperand(4) == ( shadow ? 1u : 0u) && type->getImmediateOperand(5) == ( ms ? 1u : 0u)) return type->getResultId(); } // not found, make it type = new Instruction(getUniqueId(), NoType, OpTypeSampler); type->addIdOperand(sampledType); type->addImmediateOperand( dim); type->addImmediateOperand(content); type->addImmediateOperand(arrayed ? 1 : 0); type->addImmediateOperand( shadow ? 1 : 0); type->addImmediateOperand( ms ? 1 : 0); groupedTypes[OpTypeSampler].push_back(type); constantsTypesGlobals.push_back(type); module.mapInstruction(type); return type->getResultId(); } Id Builder::getDerefTypeId(Id resultId) const { Id typeId = getTypeId(resultId); assert(isPointerType(typeId)); return module.getInstruction(typeId)->getImmediateOperand(1); } Op Builder::getMostBasicTypeClass(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 typeClass; case OpTypeVector: case OpTypeMatrix: case OpTypeArray: case OpTypeRuntimeArray: return getMostBasicTypeClass(instr->getIdOperand(0)); case OpTypePointer: return getMostBasicTypeClass(instr->getIdOperand(1)); default: MissingFunctionality("getMostBasicTypeClass"); return OpTypeFloat; } } int Builder::getNumTypeComponents(Id typeId) const { Instruction* instr = module.getInstruction(typeId); switch (instr->getOpCode()) { case OpTypeBool: case OpTypeInt: case OpTypeFloat: return 1; case OpTypeVector: case OpTypeMatrix: return instr->getImmediateOperand(1); default: MissingFunctionality("getNumTypeComponents on non bool/int/float/vector/matrix"); 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: MissingFunctionality("getScalarTypeId"); 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: return instr->getIdOperand(0); case OpTypePointer: return instr->getIdOperand(1); case OpTypeStruct: return instr->getIdOperand(member); default: MissingFunctionality("getContainedTypeId"); return NoResult; } } // Return the immediately contained type of a given composite type. Id Builder::getContainedTypeId(Id typeId) const { return getContainedTypeId(typeId, 0); } // 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, Id typeId, unsigned value) const { Instruction* constant; for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { constant = groupedConstants[typeClass][i]; if (constant->getNumOperands() == 1 && 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'). Id Builder::findScalarConstant(Op typeClass, Id typeId, unsigned v1, unsigned v2) const { Instruction* constant; for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { constant = groupedConstants[typeClass][i]; if (constant->getNumOperands() == 2 && constant->getTypeId() == typeId && constant->getImmediateOperand(0) == v1 && constant->getImmediateOperand(1) == v2) return constant->getResultId(); } return 0; } Id Builder::makeBoolConstant(bool b) { Id typeId = makeBoolType(); Instruction* constant; // See if we already made it Id existing = 0; for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { constant = groupedConstants[OpTypeBool][i]; if (constant->getTypeId() == typeId && (b ? (constant->getOpCode() == OpConstantTrue) : (constant->getOpCode() == OpConstantFalse))) existing = constant->getResultId(); } if (existing) return existing; // Make it Instruction* c = new Instruction(getUniqueId(), typeId, b ? OpConstantTrue : OpConstantFalse); constantsTypesGlobals.push_back(c); groupedConstants[OpTypeBool].push_back(c); module.mapInstruction(c); return c->getResultId(); } Id Builder::makeIntConstant(Id typeId, unsigned value) { Id existing = findScalarConstant(OpTypeInt, typeId, value); if (existing) return existing; Instruction* c = new Instruction(getUniqueId(), typeId, OpConstant); c->addImmediateOperand(value); constantsTypesGlobals.push_back(c); groupedConstants[OpTypeInt].push_back(c); module.mapInstruction(c); return c->getResultId(); } Id Builder::makeFloatConstant(float f) { Id typeId = makeFloatType(32); unsigned value = *(unsigned int*)&f; Id existing = findScalarConstant(OpTypeFloat, typeId, value); if (existing) return existing; Instruction* c = new Instruction(getUniqueId(), typeId, OpConstant); c->addImmediateOperand(value); constantsTypesGlobals.push_back(c); groupedConstants[OpTypeFloat].push_back(c); module.mapInstruction(c); return c->getResultId(); } Id Builder::makeDoubleConstant(double d) { Id typeId = makeFloatType(64); unsigned long long value = *(unsigned long long*)&d; unsigned op1 = value & 0xFFFFFFFF; unsigned op2 = value >> 32; Id existing = findScalarConstant(OpTypeFloat, typeId, op1, op2); if (existing) return existing; Instruction* c = new Instruction(getUniqueId(), typeId, OpConstant); c->addImmediateOperand(op1); c->addImmediateOperand(op2); constantsTypesGlobals.push_back(c); groupedConstants[OpTypeFloat].push_back(c); module.mapInstruction(c); return c->getResultId(); } Id Builder::findCompositeConstant(Op typeClass, std::vector& comps) const { Instruction* constant; bool found = false; for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { constant = groupedConstants[typeClass][i]; // same shape? if (constant->getNumOperands() != comps.size()) 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; } // Comments in header Id Builder::makeCompositeConstant(Id typeId, std::vector& members) { assert(typeId); Op typeClass = getTypeClass(typeId); switch (typeClass) { case OpTypeVector: case OpTypeArray: case OpTypeStruct: case OpTypeMatrix: break; default: MissingFunctionality("Constant composite type in Builder"); return makeFloatConstant(0.0); } Id existing = findCompositeConstant(typeClass, members); if (existing) return existing; Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantComposite); for (int op = 0; op < (int)members.size(); ++op) c->addIdOperand(members[op]); constantsTypesGlobals.push_back(c); groupedConstants[typeClass].push_back(c); module.mapInstruction(c); return c->getResultId(); } void Builder::addEntryPoint(ExecutionModel model, Function* function) { Instruction* entryPoint = new Instruction(OpEntryPoint); entryPoint->addImmediateOperand(model); entryPoint->addIdOperand(function->getId()); entryPoints.push_back(entryPoint); } void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value) { // TODO: handle multiple optional arguments Instruction* instr = new Instruction(OpExecutionMode); instr->addIdOperand(entryPoint->getId()); instr->addImmediateOperand(mode); if (value >= 0) instr->addImmediateOperand(value); executionModes.push_back(instr); } void Builder::addName(Id id, const char* string) { Instruction* name = new Instruction(OpName); name->addIdOperand(id); name->addStringOperand(string); names.push_back(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(name); } void Builder::addLine(Id target, Id fileName, int lineNum, int column) { Instruction* line = new Instruction(OpLine); line->addIdOperand(target); line->addIdOperand(fileName); line->addImmediateOperand(lineNum); line->addImmediateOperand(column); lines.push_back(line); } void Builder::addDecoration(Id id, Decoration decoration, int num) { Instruction* dec = new Instruction(OpDecorate); dec->addIdOperand(id); dec->addImmediateOperand(decoration); if (num >= 0) dec->addImmediateOperand(num); decorations.push_back(dec); } void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) { Instruction* dec = new Instruction(OpMemberDecorate); dec->addIdOperand(id); dec->addImmediateOperand(member); dec->addImmediateOperand(decoration); if (num >= 0) dec->addImmediateOperand(num); decorations.push_back(dec); } // Comments in header Function* Builder::makeMain() { assert(! mainFunction); Block* entry; std::vector params; mainFunction = makeFunctionEntry(makeVoidType(), "main", params, &entry); stageExit = new Block(getUniqueId(), *mainFunction); return mainFunction; } // Comments in header void Builder::closeMain() { setBuildPoint(stageExit); stageExit->addInstruction(new Instruction(NoResult, NoType, OpReturn)); mainFunction->addBlock(stageExit); } // Comments in header Function* Builder::makeFunctionEntry(Id returnType, const char* name, std::vector& paramTypes, Block **entry) { Id typeId = makeFunctionType(returnType, paramTypes); Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds(paramTypes.size()); Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module); if (entry) { *entry = new Block(getUniqueId(), *function); function->addBlock(*entry); setBuildPoint(*entry); } if (name) addName(function->getId(), name); return function; } // Comments in header void Builder::makeReturn(bool implicit, Id retVal, bool isMain) { if (isMain && retVal) MissingFunctionality("return value from main()"); if (isMain) createBranch(stageExit); else if (retVal) { Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); inst->addIdOperand(retVal); buildPoint->addInstruction(inst); } else buildPoint->addInstruction(new Instruction(NoResult, NoType, OpReturn)); if (! implicit) createAndSetNoPredecessorBlock("post-return"); } // Comments in header void Builder::leaveFunction(bool main) { 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()) { // Whether we're in an unreachable (non-entry) block. bool unreachable = function.getEntryBlock() != block && block->getNumPredecessors() == 0; if (unreachable) { // Given that this block is at the end of a function, it must be right after an // explicit return, just remove it. function.popBlock(block); } else if (main) makeMainReturn(true); else { // We're get a return instruction at the end of the current block, // which for a non-void function is really error recovery (?), as the source // being translated should have had an explicit return, which would have been // followed by an unreachable block, which was handled above. if (function.getReturnType() == makeVoidType()) makeReturn(true); else { Id retStorage = createVariable(StorageClassFunction, function.getReturnType(), "dummyReturn"); Id retValue = createLoad(retStorage); makeReturn(true, retValue); } } } if (main) closeMain(); } // Comments in header void Builder::makeDiscard() { buildPoint->addInstruction(new Instruction(OpKill)); createAndSetNoPredecessorBlock("post-discard"); } // Comments in header Id Builder::createVariable(StorageClass storageClass, Id type, const char* name) { Id pointerType = makePointer(storageClass, type); Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); inst->addImmediateOperand(storageClass); switch (storageClass) { case StorageClassUniformConstant: case StorageClassUniform: case StorageClassInput: case StorageClassOutput: case StorageClassWorkgroupLocal: case StorageClassPrivateGlobal: case StorageClassWorkgroupGlobal: constantsTypesGlobals.push_back(inst); module.mapInstruction(inst); break; case StorageClassFunction: // Validation rules require the declaration in the entry block buildPoint->getParent().addLocalVariable(inst); break; default: MissingFunctionality("storage class in createVariable"); break; } if (name) addName(inst->getResultId(), name); return inst->getResultId(); } // Comments in header void Builder::createStore(Id rValue, Id lValue) { Instruction* store = new Instruction(OpStore); store->addIdOperand(lValue); store->addIdOperand(rValue); buildPoint->addInstruction(store); } // Comments in header Id Builder::createLoad(Id lValue) { Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); load->addIdOperand(lValue); buildPoint->addInstruction(load); return load->getResultId(); } // Comments in header Id Builder::createAccessChain(StorageClass storageClass, Id base, std::vector& 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(chain); return chain->getResultId(); } Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) { Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); extract->addIdOperand(composite); extract->addImmediateOperand(index); buildPoint->addInstruction(extract); return extract->getResultId(); } Id Builder::createCompositeExtract(Id composite, Id typeId, std::vector& 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(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(insert); return insert->getResultId(); } Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, std::vector& 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(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(op); } // An opcode that has one operand, no result id, and no type void Builder::createNoResultOp(Op opCode, Id operand) { Instruction* op = new Instruction(opCode); op->addIdOperand(operand); buildPoint->addInstruction(op); } void Builder::createControlBarrier(unsigned executionScope) { Instruction* op = new Instruction(OpControlBarrier); op->addImmediateOperand(executionScope); buildPoint->addInstruction(op); } void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) { Instruction* op = new Instruction(OpMemoryBarrier); op->addImmediateOperand(executionScope); op->addImmediateOperand(memorySemantics); buildPoint->addInstruction(op); } // An opcode that has one operands, a result id, and a type Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(operand); buildPoint->addInstruction(op); return op->getResultId(); } Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(left); op->addIdOperand(right); buildPoint->addInstruction(op); return op->getResultId(); } Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(op1); op->addIdOperand(op2); op->addIdOperand(op3); buildPoint->addInstruction(op); return op->getResultId(); } Id Builder::createTernaryOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(op1); op->addIdOperand(op2); op->addIdOperand(op3); buildPoint->addInstruction(op); return op->getResultId(); } Id Builder::createFunctionCall(spv::Function* function, std::vector& 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(op); return op->getResultId(); } // Comments in header Id Builder::createRvalueSwizzle(Id typeId, Id source, std::vector& channels) { if (channels.size() == 1) return createCompositeExtract(source, typeId, channels.front()); Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); swizzle->addIdOperand(source); swizzle->addIdOperand(source); for (int i = 0; i < (int)channels.size(); ++i) swizzle->addImmediateOperand(channels[i]); buildPoint->addInstruction(swizzle); return swizzle->getResultId(); } // Comments in header Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, std::vector& channels) { assert(getNumComponents(source) == channels.size()); if (channels.size() == 1 && getNumComponents(source) == 1) return createCompositeInsert(source, target, typeId, channels.front()); Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); swizzle->addIdOperand(target); 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(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, getTypeId(right)); else if (direction < 0) right = smearScalar(precision, right, getTypeId(left)); return; } // Comments in header Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) { assert(getNumComponents(scalar) == 1); int numComponents = getNumTypeComponents(vectorType); if (numComponents == 1) return scalar; Instruction* smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); for (int c = 0; c < numComponents; ++c) smear->addIdOperand(scalar); buildPoint->addInstruction(smear); return smear->getResultId(); } // Comments in header Id Builder::createBuiltinCall(Decoration precision, Id resultType, Id builtins, int entryPoint, std::vector& 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(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 proj, const TextureParameters& parameters) { static const int maxTextureArgs = 5; Id texArgs[maxTextureArgs] = {}; // // Set up the arguments // int numArgs = 0; texArgs[numArgs++] = parameters.sampler; texArgs[numArgs++] = parameters.coords; if (parameters.gradX) { texArgs[numArgs++] = parameters.gradX; texArgs[numArgs++] = parameters.gradY; } if (parameters.lod) texArgs[numArgs++] = parameters.lod; if (parameters.offset) texArgs[numArgs++] = parameters.offset; if (parameters.bias) texArgs[numArgs++] = parameters.bias; if (parameters.Dref) texArgs[numArgs++] = parameters.Dref; // // Set up the instruction // Op opCode; if (proj && parameters.gradX && parameters.offset) opCode = OpTextureSampleProjGradOffset; else if (proj && parameters.lod && parameters.offset) opCode = OpTextureSampleProjLodOffset; else if (parameters.gradX && parameters.offset) opCode = OpTextureSampleGradOffset; else if (proj && parameters.offset) opCode = OpTextureSampleProjOffset; else if (parameters.lod && parameters.offset) opCode = OpTextureSampleLodOffset; else if (proj && parameters.gradX) opCode = OpTextureSampleProjGrad; else if (proj && parameters.lod) opCode = OpTextureSampleProjLod; else if (parameters.offset) opCode = OpTextureSampleOffset; else if (parameters.gradX) opCode = OpTextureSampleGrad; else if (proj) opCode = OpTextureSampleProj; else if (parameters.lod) opCode = OpTextureSampleLod; else if (parameters.Dref) opCode = OpTextureSampleDref; else opCode = OpTextureSample; Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); for (int op = 0; op < numArgs; ++op) textureInst->addIdOperand(texArgs[op]); setPrecision(textureInst->getResultId(), precision); buildPoint->addInstruction(textureInst); return textureInst->getResultId(); } // Comments in header Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters) { // Figure out the result type Id resultType; switch (opCode) { case OpTextureQuerySize: case OpTextureQuerySizeLod: { int numComponents; switch (getDimensionality(parameters.sampler)) { case Dim1D: case DimBuffer: numComponents = 1; break; case Dim2D: case DimCube: case DimRect: numComponents = 2; break; case Dim3D: numComponents = 3; break; default: MissingFunctionality("texture query dimensionality"); break; } if (isArrayedSampler(parameters.sampler)) ++numComponents; if (numComponents == 1) resultType = makeIntType(32); else resultType = makeVectorType(makeIntType(32), numComponents); break; } case OpTextureQueryLod: resultType = makeVectorType(makeFloatType(32), 2); break; case OpTextureQueryLevels: case OpTextureQuerySamples: resultType = makeIntType(32); break; default: MissingFunctionality("Texture query op code"); } 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(query); return query->getResultId(); } // Comments in header //Id Builder::createSamplePositionCall(Decoration precision, Id returnType, Id sampleIdx) //{ // // Return type is only flexible type // Function* opCode = (fSamplePosition, returnType); // // Instruction* instr = (opCode, sampleIdx); // setPrecision(instr, precision); // // return instr; //} // Comments in header //Id Builder::createBitFieldExtractCall(Decoration precision, Id id, Id offset, Id bits, bool isSigned) //{ // Op opCode = isSigned ? sBitFieldExtract // : uBitFieldExtract; // // if (isScalar(offset) == false || isScalar(bits) == false) // MissingFunctionality("bitFieldExtract operand types"); // // // Dest and value are matching flexible types // Function* opCode = (opCode, id->getType(), id->getType()); // // assert(opCode); // // Instruction* instr = (opCode, id, offset, bits); // setPrecision(instr, precision); // // return instr; //} // Comments in header //Id Builder::createBitFieldInsertCall(Decoration precision, Id base, Id insert, Id offset, Id bits) //{ // Op opCode = bitFieldInsert; // // if (isScalar(offset) == false || isScalar(bits) == false) // MissingFunctionality("bitFieldInsert operand types"); // // // Dest, base, and insert are matching flexible types // Function* opCode = (opCode, base->getType(), base->getType(), base->getType()); // // assert(opCode); // // Instruction* instr = (opCode, base, insert, offset, bits); // setPrecision(instr, precision); // // return instr; //} // Comments in header Id Builder::createCompare(Decoration precision, Id value1, Id value2, bool equal) { Instruction* compare = 0; spv::Op binOp = spv::OpNop; Id boolType = makeBoolType(); Id valueType = getTypeId(value1); assert(valueType == getTypeId(value2)); assert(! isScalar(value1)); // Vectors if (isVectorType(valueType)) { Id boolVectorType = makeVectorType(boolType, getNumTypeComponents(valueType)); Id boolVector; Op op; if (getMostBasicTypeClass(valueType) == OpTypeFloat) op = equal ? OpFOrdEqual : OpFOrdNotEqual; else op = equal ? OpIEqual : OpINotEqual; boolVector = createBinOp(op, boolVectorType, value1, value2); setPrecision(boolVector, precision); // Reduce vector compares with any() and all(). op = equal ? OpAll : OpAny; return createUnaryOp(op, boolType, boolVector); } spv::MissingFunctionality("Composite comparison of non-vectors"); return NoResult; // Recursively handle aggregates, which include matrices, arrays, and structures // and accumulate the results. // Matrices // Arrays //int numElements; //const llvm::ArrayType* arrayType = llvm::dyn_cast(value1->getType()); //if (arrayType) // numElements = (int)arrayType->getNumElements(); //else { // // better be structure // const llvm::StructType* structType = llvm::dyn_cast(value1->getType()); // assert(structType); // numElements = structType->getNumElements(); //} //assert(numElements > 0); //for (int element = 0; element < numElements; ++element) { // // Get intermediate comparison values // llvm::Value* element1 = builder.CreateExtractValue(value1, element, "element1"); // setInstructionPrecision(element1, precision); // llvm::Value* element2 = builder.CreateExtractValue(value2, element, "element2"); // setInstructionPrecision(element2, precision); // llvm::Value* subResult = createCompare(precision, element1, element2, equal, "comp"); // // Accumulate intermediate comparison // if (element == 0) // result = subResult; // else { // if (equal) // result = builder.CreateAnd(result, subResult); // else // result = builder.CreateOr(result, subResult); // setInstructionPrecision(result, precision); // } //} //return result; } // Comments in header //Id Builder::createOperation(Decoration precision, Op opCode, Id operand) //{ // Op* opCode = 0; // // // Handle special return types here. Things that don't have same result type as parameter // switch (opCode) { // case fIsNan: // case fIsInf: // break; // case fFloatBitsToInt: // break; // case fIntBitsTofloat: // break; // case fPackSnorm2x16: // case fPackUnorm2x16: // case fPackHalf2x16: // break; // case fUnpackUnorm2x16: // case fUnpackSnorm2x16: // case fUnpackHalf2x16: // break; // // case fFrexp: // case fLdexp: // case fPackUnorm4x8: // case fPackSnorm4x8: // case fUnpackUnorm4x8: // case fUnpackSnorm4x8: // case fPackDouble2x32: // case fUnpackDouble2x32: // break; // case fLength: // // scalar result type // break; // case any: // case all: // // fixed result type // break; // case fModF: // // modf() will return a struct that the caller must decode // break; // default: // // Unary operations that have operand and dest with same flexible type // break; // } // // assert(opCode); // // Instruction* instr = (opCode, operand); // setPrecision(instr, precision); // // return instr; //} // //// Comments in header //Id Builder::createOperation(Decoration precision, Op opCode, Id operand0, Id operand1) //{ // Function* opCode = 0; // // // Handle special return types here. Things that don't have same result type as parameter // switch (opCode) { // case fDistance: // case fDot2: // case fDot3: // case fDot4: // // scalar result type // break; // case fStep: // // first argument can be scalar, return and second argument match // break; // case fSmoothStep: // // first argument can be scalar, return and second argument match // break; // default: // // Binary operations that have operand and dest with same flexible type // break; // } // // assert(opCode); // // Instruction* instr = (opCode, operand0, operand1); // setPrecision(instr, precision); // // return instr; //} // //Id Builder::createOperation(Decoration precision, Op opCode, Id operand0, Id operand1, Id operand2) //{ // Function* opCode; // // // Handle special return types here. Things that don't have same result type as parameter // switch (opCode) { // case fSmoothStep: // // first argument can be scalar, return and second argument match // break; // default: // // Use operand0 type as result type // break; // } // // assert(opCode); // // Instruction* instr = (opCode, operand0, operand1, operand2); // setPrecision(instr, precision); // // return instr; //} // OpCompositeConstruct Id Builder::createCompositeConstruct(Id typeId, std::vector& constituents) { assert(isAggregateType(typeId) || getNumTypeComponents(typeId) > 1 && getNumTypeComponents(typeId) == constituents.size()); Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); for (int c = 0; c < (int)constituents.size(); ++c) op->addIdOperand(constituents[c]); buildPoint->addInstruction(op); return op->getResultId(); } // Vector or scalar constructor Id Builder::createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) { Id result; 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); Id scalarTypeId = getScalarTypeId(resultTypeId); std::vector constituents; // accumulate the arguments for OpCompositeConstruct for (unsigned int i = 0; i < sources.size(); ++i) { if (isAggregate(sources[i])) MissingFunctionality("aggregate in vector constructor"); unsigned int sourceSize = getNumComponents(sources[i]); unsigned int sourcesToUse = sourceSize; if (sourcesToUse + targetComponent > numTargetComponents) sourcesToUse = numTargetComponents - targetComponent; for (unsigned int s = 0; s < sourcesToUse; ++s) { Id arg = sources[i]; if (sourceSize > 1) { std::vector swiz; swiz.push_back(s); arg = createRvalueSwizzle(scalarTypeId, arg, swiz); } if (numTargetComponents > 1) constituents.push_back(arg); else result = arg; ++targetComponent; } if (targetComponent >= numTargetComponents) break; } if (constituents.size() > 0) result = createCompositeConstruct(resultTypeId, constituents); setPrecision(result, precision); return result; } // Comments in header Id Builder::createMatrixConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) { Id componentTypeId = getScalarTypeId(resultTypeId); int numCols = getTypeNumColumns(resultTypeId); int numRows = getTypeNumRows(resultTypeId); // 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 = makeFloatConstant(1.0); Id zero = 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 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 matrixColumns; for (int col = 0; col < numCols; ++col) { std::vector vectorComponents; for (int row = 0; row < numRows; ++row) vectorComponents.push_back(ids[col][row]); matrixColumns.push_back(createCompositeConstruct(columnTypeId, vectorComponents)); } // make the matrix return createCompositeConstruct(resultTypeId, matrixColumns); } // Comments in header Builder::If::If(Id cond, Builder& gb) : builder(gb), condition(cond), 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.createMerge(OpSelectionMerge, mergeBlock, SelectionControlMaskNone); 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, int numSegments, std::vector& caseValues, std::vector& valueIndexToSegment, int defaultSegment, std::vector& 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 createMerge(OpSelectionMerge, mergeBlock, SelectionControlMaskNone); // make the switch instruction Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); switchInst->addIdOperand(selector); switchInst->addIdOperand(defaultSegment >= 0 ? segmentBlocks[defaultSegment]->getId() : mergeBlock->getId()); for (int i = 0; i < (int)caseValues.size(); ++i) { switchInst->addImmediateOperand(caseValues[i]); switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); } buildPoint->addInstruction(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& 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& 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(); } // Comments in header void Builder::makeNewLoop() { Loop loop = { }; loop.function = &getBuildPoint()->getParent(); loop.header = new Block(getUniqueId(), *loop.function); loop.merge = new Block(getUniqueId(), *loop.function); loops.push(loop); // Branch into the loop createBranch(loop.header); // Set ourselves inside the loop loop.function->addBlock(loop.header); setBuildPoint(loop.header); } void Builder::createLoopHeaderBranch(Id condition) { Loop loop = loops.top(); Block* body = new Block(getUniqueId(), *loop.function); createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone); createConditionalBranch(condition, body, loop.merge); loop.function->addBlock(body); setBuildPoint(body); } // Add a back-edge (e.g "continue") for the innermost loop that you're in void Builder::createLoopBackEdge(bool implicit) { Loop loop = loops.top(); // Just branch back, and set up a block for dead code if it's a user continue createBranch(loop.header); if (! implicit) createAndSetNoPredecessorBlock("post-loop-continue"); } // Add an exit (e.g. "break") for the innermost loop that you're in void Builder::createLoopExit() { createBranch(loops.top().merge); createAndSetNoPredecessorBlock("post-loop-break"); } // Close the innermost loop void Builder::closeLoop() { // Branch back to the top createLoopBackEdge(true); // Add the merge block and set the build point to it Loop loop = loops.top(); loop.function->addBlock(loop.merge); setBuildPoint(loop.merge); loops.pop(); } void Builder::clearAccessChain() { accessChain.base = 0; accessChain.indexChain.clear(); accessChain.instr = 0; accessChain.swizzle.clear(); accessChain.component = 0; accessChain.swizzleTargetWidth = 0; accessChain.resultType = NoType; accessChain.isRValue = false; } // Comments in header void Builder::accessChainPushSwizzle(std::vector& swizzle, int width, Id type) { // if needed, propagate the swizzle for the current access chain if (accessChain.swizzle.size()) { std::vector oldSwizzle = accessChain.swizzle; accessChain.swizzle.resize(0); for (unsigned int i = 0; i < swizzle.size(); ++i) { accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); } } else { accessChain.swizzle = swizzle; } // track width the swizzle operates on; once known, it does not change if (accessChain.swizzleTargetWidth == 0) accessChain.swizzleTargetWidth = width; accessChain.resultType = type; // determine if we need to track this swizzle anymore simplifyAccessChainSwizzle(); } // Comments in header void Builder::accessChainStore(Id rvalue) { assert(accessChain.isRValue == false); Id base = 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. Id source; if (accessChain.swizzle.size()) { Id tempBaseId = createLoad(base); source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, rvalue, accessChain.swizzle); } else if (accessChain.component) { Instruction* vectorInsert = new Instruction(getUniqueId(), getTypeId(rvalue), OpVectorInsertDynamic); vectorInsert->addIdOperand(createLoad(base)); vectorInsert->addIdOperand(rvalue); vectorInsert->addIdOperand(accessChain.component); buildPoint->addInstruction(vectorInsert); source = vectorInsert->getResultId(); } else source = rvalue; createStore(source, base); } // Comments in header Id Builder::accessChainLoad(Decoration precision) { Id id; if (accessChain.isRValue) { if (accessChain.indexChain.size() > 0) { // if all the accesses are constants, we can use OpCompositeExtract std::vector 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, accessChain.resultType, indexes); else { // make a new function variable for this r-value Id 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()); } } else id = accessChain.base; } else { // load through the access chain id = createLoad(collapseAccessChain()); } if (accessChain.component) { Instruction* vectorExtract = new Instruction(getUniqueId(), getScalarTypeId(getTypeId(id)), OpVectorExtractDynamic); vectorExtract->addIdOperand(id); vectorExtract->addIdOperand(accessChain.component); buildPoint->addInstruction(vectorExtract); id = vectorExtract->getResultId(); } else if (accessChain.swizzle.size()) id = createRvalueSwizzle(accessChain.resultType, id, accessChain.swizzle); return id; } Id Builder::accessChainGetLValue() { assert(accessChain.isRValue == false); 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 == spv::NoResult); return lvalue; } void Builder::dump(std::vector& out) const { // Header, before first instructions: out.push_back(MagicNumber); out.push_back(Version); out.push_back(builderNumber); out.push_back(uniqueId + 1); out.push_back(0); // First instructions, some created on the spot here: if (source != SourceLanguageUnknown) { Instruction sourceInst(0, 0, OpSource); sourceInst.addImmediateOperand(source); sourceInst.addImmediateOperand(sourceVersion); sourceInst.dump(out); } for (int e = 0; e < (int)extensions.size(); ++e) { Instruction extInst(0, 0, OpSourceExtension); extInst.addStringOperand(extensions[e]); extInst.dump(out); } // TBD: OpExtension ... 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); dumpInstructions(out, names); dumpInstructions(out, lines); dumpInstructions(out, decorations); dumpInstructions(out, constantsTypesGlobals); dumpInstructions(out, externals); // The functions module.dump(out); } // // Protected methods. // Id Builder::collapseAccessChain() { // TODO: bring in an individual component swizzle here, so that a pointer // all the way to the component level can be created. assert(accessChain.isRValue == false); if (accessChain.indexChain.size() > 0) { if (accessChain.instr == 0) { StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); } return accessChain.instr; } else return accessChain.base; } // clear out swizzle if it is redundant void Builder::simplifyAccessChainSwizzle() { // if swizzle has fewer components than our target, it is a writemask if (accessChain.swizzleTargetWidth > (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(); accessChain.swizzleTargetWidth = 0; } // 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(branch); block->addPredecessor(buildPoint); } void Builder::createMerge(Op mergeCode, Block* mergeBlock, unsigned int control) { Instruction* merge = new Instruction(mergeCode); merge->addIdOperand(mergeBlock->getId()); merge->addImmediateOperand(control); buildPoint->addInstruction(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(branch); thenBlock->addPredecessor(buildPoint); elseBlock->addPredecessor(buildPoint); } void Builder::dumpInstructions(std::vector& out, const std::vector& instructions) const { for (int i = 0; i < (int)instructions.size(); ++i) { instructions[i]->dump(out); } } void MissingFunctionality(const char* fun) { printf("Missing functionality: %s\n", fun); exit(1); } void ValidationError(const char* error) { printf("Validation Error: %s\n", error); } }; // end spv namespace