glslang/SPIRV/SpvBuilder.cpp
David Neto c22f37cfb4 Generate correctly structured do-while loops.
The loop test is always emitted before the loop body.

For do-while loops, use a phi node to track whether we're
on the first loop iteration, and only check the loop test
on the second and subsequent iterations.

For do-while loops, the loop test branch no longer occurs
at the top of the loop, so it must get its own selection
merge instruction.

A block can't be the target of more than one merge instruction.
So when the loop test executes after the body (as in do-while in GLSL)
we need to introduce a dummy block to be the target of the selection
merge just before the loop test conditional branch.

The other arm of the branch exits the loop and hence is the
"break block" exception in the structured control flow rules.
2015-07-17 17:51:51 -04:00

2202 lines
69 KiB
C++

//
//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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "SpvBuilder.h"
#ifndef _WIN32
#include <cstdio>
#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) == (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(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) == (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(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) == (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(type);
module.mapInstruction(type);
return type->getResultId();
}
Id Builder::makeStructType(std::vector<Id>& 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) == (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(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(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<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(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) == (unsigned int)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<Id>& comps) const
{
Instruction* constant = 0;
bool found = false;
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
constant = groupedConstants[typeClass][i];
// same shape?
if (constant->getNumOperands() != (int)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<Id>& 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<Id> 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<Id>& paramTypes, Block **entry)
{
Id typeId = makeFunctionType(returnType, paramTypes);
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)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<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(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<unsigned>& 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<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(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(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(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<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(op);
return op->getResultId();
}
// Comments in header
Id Builder::createRvalueSwizzle(Id typeId, Id source, std::vector<unsigned>& channels)
{
if (channels.size() == 1)
return createCompositeExtract(source, typeId, channels.front());
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(swizzle);
return swizzle->getResultId();
}
// Comments in header
Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& 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);
assert(isVector(source));
assert(isVector(target));
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<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(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 = NoType;
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)
{
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<llvm::ArrayType>(value1->getType());
//if (arrayType)
// numElements = (int)arrayType->getNumElements();
//else {
// // better be structure
// const llvm::StructType* structType = llvm::dyn_cast<llvm::StructType>(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<Id>& 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<Id>& sources, Id resultTypeId)
{
Id result = 0;
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<Id> 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<unsigned> 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<Id>& 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<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]);
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<int>& caseValues, 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
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<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();
}
// Comments in header
void Builder::makeNewLoop(bool loopTestFirst)
{
loops.push({ });
Loop& loop = loops.top();
loop.function = &getBuildPoint()->getParent();
loop.header = new Block(getUniqueId(), *loop.function);
loop.merge = new Block(getUniqueId(), *loop.function);
// Delaying creation of the loop body perturbs test results less,
// which makes for easier patch review.
// TODO(dneto): Create the loop body block here, instead of
// upon first use.
loop.body = 0;
loop.testFirst = loopTestFirst;
loop.isFirstIteration = 0;
// The loop test is always emitted before the loop body.
// But if the loop test executes at the bottom of the loop, then
// execute the test only on the second and subsequent iterations.
// Remember the block that branches to the loop header. This
// is required for the test-after-body case.
Block* preheader = getBuildPoint();
// Branch into the loop
createBranch(loop.header);
// Set ourselves inside the loop
loop.function->addBlock(loop.header);
setBuildPoint(loop.header);
if (!loopTestFirst) {
// Generate code to defer the loop test until the second and
// subsequent iterations.
// A phi node determines whether we're on the first iteration.
loop.isFirstIteration = new Instruction(getUniqueId(), makeBoolType(), OpPhi);
// It's always the first iteration when coming from the preheader.
// All other branches to this loop header will need to indicate "false",
// but we don't yet know where they will come from.
loop.isFirstIteration->addIdOperand(makeBoolConstant(true));
loop.isFirstIteration->addIdOperand(preheader->getId());
getBuildPoint()->addInstruction(loop.isFirstIteration);
// Mark the end of the structured loop. This must exist in the loop header block.
createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone);
// Generate code to see if this is the first iteration of the loop.
// It needs to be in its own block, since the loop merge and
// the selection merge instructions can't both be in the same
// (header) block.
Block* firstIterationCheck = new Block(getUniqueId(), *loop.function);
createBranch(firstIterationCheck);
loop.function->addBlock(firstIterationCheck);
setBuildPoint(firstIterationCheck);
loop.body = new Block(getUniqueId(), *loop.function);
// Control flow after this "if" normally reconverges at the loop body.
// However, the loop test has a "break branch" out of this selection
// construct because it can transfer control to the loop merge block.
createMerge(OpSelectionMerge, loop.body, SelectionControlMaskNone);
Block* loopTest = new Block(getUniqueId(), *loop.function);
createConditionalBranch(loop.isFirstIteration->getResultId(), loop.body, loopTest);
loop.function->addBlock(loopTest);
setBuildPoint(loopTest);
}
}
void Builder::createLoopTestBranch(Id condition)
{
Loop& loop = loops.top();
// Generate the merge instruction. If the loop test executes before
// the body, then this is a loop merge. Otherwise the loop merge
// has already been generated and this is a conditional merge.
if (loop.testFirst) {
createMerge(OpLoopMerge, loop.merge, LoopControlMaskNone);
loop.body = new Block(getUniqueId(), *loop.function);
// Branching to the "body" block will keep control inside
// the loop.
createConditionalBranch(condition, loop.body, loop.merge);
loop.function->addBlock(loop.body);
setBuildPoint(loop.body);
} else {
// The branch to the loop merge block is the allowed exception
// to the structured control flow. Otherwise, control flow will
// continue to loop.body block. Since that is already the target
// of a merge instruction, and a block can't be the target of more
// than one merge instruction, we need to make an intermediate block.
Block* stayInLoopBlock = new Block(getUniqueId(), *loop.function);
createMerge(OpSelectionMerge, stayInLoopBlock, SelectionControlMaskNone);
// This is the loop test.
createConditionalBranch(condition, stayInLoopBlock, loop.merge);
// The dummy block just branches to the real loop body.
loop.function->addBlock(stayInLoopBlock);
setBuildPoint(stayInLoopBlock);
createBranchToBody();
}
}
void Builder::createBranchToBody()
{
Loop& loop = loops.top();
assert(loop.body);
// This is a reconvergence of control flow, so no merge instruction
// is required.
createBranch(loop.body);
loop.function->addBlock(loop.body);
setBuildPoint(loop.body);
}
void Builder::createLoopContinue()
{
createBranchToLoopHeaderFromInside(loops.top());
// Set up a block for dead code.
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);
// Set up a block for dead code.
createAndSetNoPredecessorBlock("post-loop-break");
}
// Close the innermost loop
void Builder::closeLoop()
{
Loop& loop = loops.top();
// Branch back to the top
createBranchToLoopHeaderFromInside(loop);
// Add the merge block and set the build point to it
loop.function->addBlock(loop.merge);
setBuildPoint(loop.merge);
loops.pop();
}
// Create a branch to the header of the given loop, from inside
// the loop body.
// Adjusts the phi node for the first-iteration value if needeed.
void Builder::createBranchToLoopHeaderFromInside(const Loop& loop)
{
createBranch(loop.header);
if (loop.isFirstIteration) {
loop.isFirstIteration->addIdOperand(makeBoolConstant(false));
loop.isFirstIteration->addIdOperand(getBuildPoint()->getId());
}
}
void Builder::clearAccessChain()
{
accessChain.base = 0;
accessChain.indexChain.clear();
accessChain.instr = 0;
accessChain.swizzle.clear();
accessChain.component = 0;
accessChain.resultType = NoType;
accessChain.isRValue = false;
}
// Comments in header
void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle)
{
// if needed, propagate the swizzle for the current access chain
if (accessChain.swizzle.size()) {
std::vector<unsigned> 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;
// 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 (accessChain.swizzle.size() && accessChain.component)
MissingFunctionality("simultaneous l-value swizzle and dynamic component selection");
// 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 = NoResult;
if (accessChain.swizzle.size()) {
Id tempBaseId = createLoad(base);
source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, rvalue, accessChain.swizzle);
}
// dynamic component selection
if (accessChain.component) {
Id tempBaseId = (source == NoResult) ? createLoad(base) : source;
source = createVectorInsertDynamic(tempBaseId, getTypeId(tempBaseId), rvalue, accessChain.component);
}
if (source == NoResult)
source = rvalue;
createStore(source, base);
}
// Comments in header
Id Builder::accessChainLoad(Decoration /*precision*/)
{
Id id;
if (accessChain.isRValue) {
if (accessChain.indexChain.size() > 0) {
mergeAccessChainSwizzle(); // TODO: optimization: look at applying this optimization more widely
// 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, 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());
}
// Done, unless there are swizzles to do
if (accessChain.swizzle.size() == 0 && accessChain.component == 0)
return id;
Id componentType = getScalarTypeId(accessChain.resultType);
// Do remaining swizzling
// First, static swizzling
if (accessChain.swizzle.size()) {
// static swizzle
Id resultType = componentType;
if (accessChain.swizzle.size() > 1)
resultType = makeVectorType(componentType, (int)accessChain.swizzle.size());
id = createRvalueSwizzle(resultType, id, accessChain.swizzle);
}
// dynamic single-component selection
if (accessChain.component)
id = createVectorExtractDynamic(id, componentType, accessChain.component);
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<unsigned int>& 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 the swizzle has fewer components than the vector, it is subsetting, and must stay
// to preserve that fact.
if (getNumTypeComponents(accessChain.resultType) > (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();
}
// clear out swizzle if it can become part of the indexes
void Builder::mergeAccessChainSwizzle()
{
// is there even a chance of doing something? Need a single-component swizzle
if ((accessChain.swizzle.size() > 1) ||
(accessChain.swizzle.size() == 0 && accessChain.component == 0))
return;
// TODO: optimization: remove this, but for now confine this to non-dynamic accesses
// (the above test is correct when this is removed.)
if (accessChain.component)
return;
// move the swizzle over to the indexes
if (accessChain.swizzle.size() == 1)
accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
else
accessChain.indexChain.push_back(accessChain.component);
accessChain.resultType = getScalarTypeId(accessChain.resultType);
// now there is no need to track this swizzle
accessChain.component = NoResult;
accessChain.swizzle.clear();
}
// 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<unsigned int>& out, const std::vector<Instruction*>& 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