2015-06-26 22:58:36 +00:00
|
|
|
//
|
2016-02-16 03:58:50 +00:00
|
|
|
//Copyright (C) 2014-2015 LunarG, Inc.
|
|
|
|
//Copyright (C) 2015-2016 Google, Inc.
|
2015-06-26 22:58:36 +00:00
|
|
|
//
|
|
|
|
//All rights reserved.
|
|
|
|
//
|
|
|
|
//Redistribution and use in source and binary forms, with or without
|
|
|
|
//modification, are permitted provided that the following conditions
|
|
|
|
//are met:
|
|
|
|
//
|
|
|
|
// Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
//
|
|
|
|
// Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
//
|
|
|
|
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
//POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
//
|
|
|
|
// Helper for making SPIR-V IR. Generally, this is documented in the header
|
|
|
|
// SpvBuilder.h.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2015-07-23 16:22:48 +00:00
|
|
|
#include <unordered_set>
|
2016-04-14 22:34:27 +00:00
|
|
|
#include <algorithm>
|
2015-07-23 16:22:48 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
#include "SpvBuilder.h"
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <cstdio>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace spv {
|
|
|
|
|
2016-05-04 19:55:59 +00:00
|
|
|
Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) :
|
2015-06-26 22:58:36 +00:00
|
|
|
source(SourceLanguageUnknown),
|
|
|
|
sourceVersion(0),
|
|
|
|
addressModel(AddressingModelLogical),
|
|
|
|
memoryModel(MemoryModelGLSL450),
|
2015-11-16 04:33:39 +00:00
|
|
|
builderNumber(magicNumber),
|
2015-06-26 22:58:36 +00:00
|
|
|
buildPoint(0),
|
|
|
|
uniqueId(0),
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
mainFunction(0),
|
2016-05-02 22:11:54 +00:00
|
|
|
generatingOpCodeForSpecConst(false),
|
2016-05-04 19:55:59 +00:00
|
|
|
logger(buildLogger)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
clearAccessChain();
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder::~Builder()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::import(const char* name)
|
|
|
|
{
|
|
|
|
Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
|
|
|
|
import->addStringOperand(name);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
imports.push_back(std::unique_ptr<Instruction>(import));
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
} else
|
|
|
|
type = groupedTypes[OpTypeBool].back();
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::makeSamplerType()
|
|
|
|
{
|
|
|
|
Instruction* type;
|
|
|
|
if (groupedTypes[OpTypeSampler].size() == 0) {
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
|
|
|
|
groupedTypes[OpTypeSampler].push_back(type);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-11-16 04:33:39 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
} else
|
|
|
|
type = groupedTypes[OpTypeSampler].back();
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
2016-02-15 00:11:15 +00:00
|
|
|
// deal with capabilities
|
|
|
|
switch (width) {
|
|
|
|
case 16:
|
|
|
|
addCapability(CapabilityInt16);
|
|
|
|
break;
|
|
|
|
case 64:
|
|
|
|
addCapability(CapabilityInt64);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
2016-02-15 00:11:15 +00:00
|
|
|
// deal with capabilities
|
|
|
|
switch (width) {
|
|
|
|
case 16:
|
|
|
|
addCapability(CapabilityFloat16);
|
|
|
|
break;
|
|
|
|
case 64:
|
|
|
|
addCapability(CapabilityFloat64);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// Make a struct without checking for duplication.
|
|
|
|
// See makeStructResultType() for non-decorated structs
|
|
|
|
// needed as the result of some instructions, which does
|
|
|
|
// check for duplicates.
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
// Don't look for previous one, because in the general case,
|
|
|
|
// structs can be duplicated except for decorations.
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
addName(type->getResultId(), name);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// Make a struct for the simple results of several instructions,
|
|
|
|
// checking for duplication.
|
|
|
|
Id Builder::makeStructResultType(Id type0, Id type1)
|
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypeStruct][t];
|
|
|
|
if (type->getNumOperands() != 2)
|
|
|
|
continue;
|
|
|
|
if (type->getIdOperand(0) != type0 ||
|
|
|
|
type->getIdOperand(1) != type1)
|
|
|
|
continue;
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
|
|
|
std::vector<spv::Id> members;
|
|
|
|
members.push_back(type0);
|
|
|
|
members.push_back(type1);
|
|
|
|
|
|
|
|
return makeStructType(members, "ResType");
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-12-30 04:27:24 +00:00
|
|
|
// TODO: performance: track arrays per stride
|
|
|
|
// If a stride is supplied (non-zero) make an array.
|
|
|
|
// If no stride (0), reuse previous array types.
|
2016-02-16 03:58:50 +00:00
|
|
|
// 'size' is an Id of a constant or specialization constant of the array size
|
|
|
|
Id Builder::makeArrayType(Id element, Id sizeId, int stride)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* type;
|
2015-12-30 04:27:24 +00:00
|
|
|
if (stride == 0) {
|
|
|
|
// try to find existing type
|
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypeArray][t];
|
|
|
|
if (type->getIdOperand(0) == element &&
|
|
|
|
type->getIdOperand(1) == sizeId)
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeArray);
|
|
|
|
type->addIdOperand(element);
|
|
|
|
type->addIdOperand(sizeId);
|
|
|
|
groupedTypes[OpTypeArray].push_back(type);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-09-12 18:17:44 +00:00
|
|
|
Id Builder::makeRuntimeArray(Id element)
|
|
|
|
{
|
|
|
|
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
|
|
|
|
type->addIdOperand(element);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-09-12 18:17:44 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
// 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)
|
2015-09-12 18:17:44 +00:00
|
|
|
return type->getResultId();
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
2015-08-07 04:53:06 +00:00
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypeImage][t];
|
2015-06-26 22:58:36 +00:00
|
|
|
if (type->getIdOperand(0) == sampledType &&
|
|
|
|
type->getImmediateOperand(1) == (unsigned int)dim &&
|
2015-08-07 04:53:06 +00:00
|
|
|
type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
|
2015-06-26 22:58:36 +00:00
|
|
|
type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
|
2015-08-07 04:53:06 +00:00
|
|
|
type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
|
|
|
|
type->getImmediateOperand(5) == sampled &&
|
|
|
|
type->getImmediateOperand(6) == (unsigned int)format)
|
2015-06-26 22:58:36 +00:00
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
2015-08-07 04:53:06 +00:00
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeImage);
|
2015-06-26 22:58:36 +00:00
|
|
|
type->addIdOperand(sampledType);
|
|
|
|
type->addImmediateOperand( dim);
|
2015-08-07 04:53:06 +00:00
|
|
|
type->addImmediateOperand( depth ? 1 : 0);
|
2015-06-26 22:58:36 +00:00
|
|
|
type->addImmediateOperand(arrayed ? 1 : 0);
|
|
|
|
type->addImmediateOperand( ms ? 1 : 0);
|
2015-08-07 04:53:06 +00:00
|
|
|
type->addImmediateOperand(sampled);
|
|
|
|
type->addImmediateOperand((unsigned int)format);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
groupedTypes[OpTypeImage].push_back(type);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-08-07 04:53:06 +00:00
|
|
|
module.mapInstruction(type);
|
|
|
|
|
2016-02-15 00:37:30 +00:00
|
|
|
// deal with capabilities
|
|
|
|
switch (dim) {
|
|
|
|
case DimBuffer:
|
|
|
|
if (sampled)
|
|
|
|
addCapability(CapabilitySampledBuffer);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageBuffer);
|
|
|
|
break;
|
|
|
|
case Dim1D:
|
|
|
|
if (sampled)
|
|
|
|
addCapability(CapabilitySampled1D);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImage1D);
|
|
|
|
break;
|
|
|
|
case DimCube:
|
|
|
|
if (arrayed) {
|
|
|
|
if (sampled)
|
|
|
|
addCapability(CapabilitySampledCubeArray);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageCubeArray);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DimRect:
|
|
|
|
if (sampled)
|
|
|
|
addCapability(CapabilitySampledRect);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageRect);
|
|
|
|
break;
|
|
|
|
case DimSubpassData:
|
|
|
|
addCapability(CapabilityInputAttachment);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ms) {
|
|
|
|
if (arrayed)
|
|
|
|
addCapability(CapabilityImageMSArray);
|
|
|
|
if (! sampled)
|
|
|
|
addCapability(CapabilityStorageImageMultisample);
|
|
|
|
}
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::makeSampledImageType(Id imageType)
|
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypeSampledImage][t];
|
|
|
|
if (type->getIdOperand(0) == imageType)
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
|
|
|
|
type->addIdOperand(imageType);
|
|
|
|
|
|
|
|
groupedTypes[OpTypeSampledImage].push_back(type);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
2015-06-26 22:58:36 +00:00
|
|
|
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:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
return OpTypeFloat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
int Builder::getNumTypeConstituents(Id typeId) const
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* instr = module.getInstruction(typeId);
|
|
|
|
|
|
|
|
switch (instr->getOpCode())
|
|
|
|
{
|
|
|
|
case OpTypeBool:
|
|
|
|
case OpTypeInt:
|
|
|
|
case OpTypeFloat:
|
|
|
|
return 1;
|
|
|
|
case OpTypeVector:
|
|
|
|
case OpTypeMatrix:
|
|
|
|
return instr->getImmediateOperand(1);
|
2016-02-16 03:58:50 +00:00
|
|
|
case OpTypeArray:
|
|
|
|
{
|
|
|
|
Id lengthId = instr->getImmediateOperand(1);
|
|
|
|
return module.getInstruction(lengthId)->getImmediateOperand(0);
|
|
|
|
}
|
2015-12-22 03:54:09 +00:00
|
|
|
case OpTypeStruct:
|
|
|
|
return instr->getNumOperands();
|
2015-06-26 22:58:36 +00:00
|
|
|
default:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
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:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
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:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
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).
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) const
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* constant;
|
|
|
|
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
|
|
|
|
constant = groupedConstants[typeClass][i];
|
2015-11-16 04:33:39 +00:00
|
|
|
if (constant->getOpCode() == opcode &&
|
2015-06-26 22:58:36 +00:00
|
|
|
constant->getTypeId() == typeId &&
|
|
|
|
constant->getImmediateOperand(0) == value)
|
|
|
|
return constant->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-22 08:51:45 +00:00
|
|
|
// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) const
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* constant;
|
|
|
|
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
|
|
|
|
constant = groupedConstants[typeClass][i];
|
2015-11-16 04:33:39 +00:00
|
|
|
if (constant->getOpCode() == opcode &&
|
2015-06-26 22:58:36 +00:00
|
|
|
constant->getTypeId() == typeId &&
|
|
|
|
constant->getImmediateOperand(0) == v1 &&
|
|
|
|
constant->getImmediateOperand(1) == v2)
|
|
|
|
return constant->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-13 16:55:08 +00:00
|
|
|
// Return true if consuming 'opcode' means consuming a constant.
|
|
|
|
// "constant" here means after final transform to executable code,
|
|
|
|
// the value consumed will be a constant, so includes specialization.
|
2015-10-13 16:39:19 +00:00
|
|
|
bool Builder::isConstantOpCode(Op opcode) const
|
|
|
|
{
|
|
|
|
switch (opcode) {
|
|
|
|
case OpUndef:
|
|
|
|
case OpConstantTrue:
|
|
|
|
case OpConstantFalse:
|
|
|
|
case OpConstant:
|
|
|
|
case OpConstantComposite:
|
|
|
|
case OpConstantSampler:
|
|
|
|
case OpConstantNull:
|
|
|
|
case OpSpecConstantTrue:
|
|
|
|
case OpSpecConstantFalse:
|
|
|
|
case OpSpecConstant:
|
|
|
|
case OpSpecConstantComposite:
|
|
|
|
case OpSpecConstantOp:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-14 20:40:20 +00:00
|
|
|
// Return true if consuming 'opcode' means consuming a specialization constant.
|
|
|
|
bool Builder::isSpecConstantOpCode(Op opcode) const
|
|
|
|
{
|
|
|
|
switch (opcode) {
|
|
|
|
case OpSpecConstantTrue:
|
|
|
|
case OpSpecConstantFalse:
|
|
|
|
case OpSpecConstant:
|
|
|
|
case OpSpecConstantComposite:
|
|
|
|
case OpSpecConstantOp:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::makeBoolConstant(bool b, bool specConstant)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Id typeId = makeBoolType();
|
|
|
|
Instruction* constant;
|
2015-11-16 04:33:39 +00:00
|
|
|
Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-16 03:58:50 +00:00
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = 0;
|
|
|
|
for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
|
|
|
|
constant = groupedConstants[OpTypeBool][i];
|
|
|
|
if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
|
|
|
|
existing = constant->getResultId();
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-16 03:58:50 +00:00
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
// Make it
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
2015-06-26 22:58:36 +00:00
|
|
|
groupedConstants[OpTypeBool].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
Op opcode = specConstant ? OpSpecConstant : OpConstant;
|
2016-02-16 03:58:50 +00:00
|
|
|
|
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
2015-06-26 22:58:36 +00:00
|
|
|
c->addImmediateOperand(value);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
2015-06-26 22:58:36 +00:00
|
|
|
groupedConstants[OpTypeInt].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
2016-04-22 08:51:45 +00:00
|
|
|
Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
|
|
|
|
{
|
|
|
|
Op opcode = specConstant ? OpSpecConstant : OpConstant;
|
|
|
|
|
|
|
|
unsigned op1 = value & 0xFFFFFFFF;
|
|
|
|
unsigned op2 = value >> 32;
|
|
|
|
|
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
|
|
|
c->addImmediateOperand(op1);
|
|
|
|
c->addImmediateOperand(op2);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
|
|
|
groupedConstants[OpTypeInt].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::makeFloatConstant(float f, bool specConstant)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
Op opcode = specConstant ? OpSpecConstant : OpConstant;
|
2015-06-26 22:58:36 +00:00
|
|
|
Id typeId = makeFloatType(32);
|
2016-02-23 17:17:11 +00:00
|
|
|
union { float fl; unsigned int ui; } u;
|
|
|
|
u.fl = f;
|
|
|
|
unsigned value = u.ui;
|
2016-02-16 03:58:50 +00:00
|
|
|
|
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
2015-06-26 22:58:36 +00:00
|
|
|
c->addImmediateOperand(value);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
2015-06-26 22:58:36 +00:00
|
|
|
groupedConstants[OpTypeFloat].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Id Builder::makeDoubleConstant(double d, bool specConstant)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
Op opcode = specConstant ? OpSpecConstant : OpConstant;
|
2015-06-26 22:58:36 +00:00
|
|
|
Id typeId = makeFloatType(64);
|
2016-02-23 17:17:11 +00:00
|
|
|
union { double db; unsigned long long ull; } u;
|
|
|
|
u.db = d;
|
|
|
|
unsigned long long value = u.ull;
|
2015-06-26 22:58:36 +00:00
|
|
|
unsigned op1 = value & 0xFFFFFFFF;
|
|
|
|
unsigned op2 = value >> 32;
|
2016-02-16 03:58:50 +00:00
|
|
|
|
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
2015-06-26 22:58:36 +00:00
|
|
|
c->addImmediateOperand(op1);
|
|
|
|
c->addImmediateOperand(op2);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
2016-02-16 03:58:50 +00:00
|
|
|
Id Builder::makeCompositeConstant(Id typeId, std::vector<Id>& members, bool specConstant)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2016-02-16 03:58:50 +00:00
|
|
|
Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
|
2015-06-26 22:58:36 +00:00
|
|
|
assert(typeId);
|
|
|
|
Op typeClass = getTypeClass(typeId);
|
|
|
|
|
|
|
|
switch (typeClass) {
|
|
|
|
case OpTypeVector:
|
|
|
|
case OpTypeArray:
|
|
|
|
case OpTypeStruct:
|
|
|
|
case OpTypeMatrix:
|
|
|
|
break;
|
|
|
|
default:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
return makeFloatConstant(0.0);
|
|
|
|
}
|
|
|
|
|
2016-02-16 03:58:50 +00:00
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findCompositeConstant(typeClass, members);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-16 03:58:50 +00:00
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
2015-06-26 22:58:36 +00:00
|
|
|
for (int op = 0; op < (int)members.size(); ++op)
|
|
|
|
c->addIdOperand(members[op]);
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
2015-06-26 22:58:36 +00:00
|
|
|
groupedConstants[typeClass].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* entryPoint = new Instruction(OpEntryPoint);
|
|
|
|
entryPoint->addImmediateOperand(model);
|
|
|
|
entryPoint->addIdOperand(function->getId());
|
2015-08-07 04:53:06 +00:00
|
|
|
entryPoint->addStringOperand(name);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
|
2015-11-16 04:33:39 +00:00
|
|
|
|
|
|
|
return entryPoint;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
2015-09-16 22:04:05 +00:00
|
|
|
// Currently relying on the fact that all 'value' of interest are small non-negative values.
|
|
|
|
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* instr = new Instruction(OpExecutionMode);
|
|
|
|
instr->addIdOperand(entryPoint->getId());
|
|
|
|
instr->addImmediateOperand(mode);
|
2015-09-16 22:04:05 +00:00
|
|
|
if (value1 >= 0)
|
|
|
|
instr->addImmediateOperand(value1);
|
|
|
|
if (value2 >= 0)
|
|
|
|
instr->addImmediateOperand(value2);
|
|
|
|
if (value3 >= 0)
|
|
|
|
instr->addImmediateOperand(value3);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
executionModes.push_back(std::unique_ptr<Instruction>(instr));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addName(Id id, const char* string)
|
|
|
|
{
|
|
|
|
Instruction* name = new Instruction(OpName);
|
|
|
|
name->addIdOperand(id);
|
|
|
|
name->addStringOperand(string);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
names.push_back(std::unique_ptr<Instruction>(name));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addMemberName(Id id, int memberNumber, const char* string)
|
|
|
|
{
|
|
|
|
Instruction* name = new Instruction(OpMemberName);
|
|
|
|
name->addIdOperand(id);
|
|
|
|
name->addImmediateOperand(memberNumber);
|
|
|
|
name->addStringOperand(string);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
names.push_back(std::unique_ptr<Instruction>(name));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
lines.push_back(std::unique_ptr<Instruction>(line));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addDecoration(Id id, Decoration decoration, int num)
|
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
if (decoration == (spv::Decoration)spv::BadValue)
|
|
|
|
return;
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* dec = new Instruction(OpDecorate);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
if (num >= 0)
|
|
|
|
dec->addImmediateOperand(num);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-03-13 01:17:47 +00:00
|
|
|
Function* Builder::makeEntrypoint(const char* entryPoint)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
assert(! mainFunction);
|
|
|
|
|
|
|
|
Block* entry;
|
|
|
|
std::vector<Id> params;
|
2016-02-02 19:37:46 +00:00
|
|
|
std::vector<Decoration> precisions;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-03-13 01:17:47 +00:00
|
|
|
mainFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, precisions, &entry);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return mainFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-02-02 19:37:46 +00:00
|
|
|
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
|
|
|
|
const std::vector<Id>& paramTypes, const std::vector<Decoration>& precisions, Block **entry)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2016-02-02 19:37:46 +00:00
|
|
|
// Make the function and initial instructions in it
|
2015-06-26 22:58:36 +00:00
|
|
|
Id typeId = makeFunctionType(returnType, paramTypes);
|
2015-07-12 09:32:58 +00:00
|
|
|
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
|
2015-06-26 22:58:36 +00:00
|
|
|
Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
|
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
// Set up the precisions
|
|
|
|
setPrecision(function->getId(), precision);
|
|
|
|
for (unsigned p = 0; p < (unsigned)precisions.size(); ++p)
|
|
|
|
setPrecision(firstParamId + p, precisions[p]);
|
|
|
|
|
|
|
|
// CFG
|
2015-06-26 22:58:36 +00:00
|
|
|
if (entry) {
|
|
|
|
*entry = new Block(getUniqueId(), *function);
|
|
|
|
function->addBlock(*entry);
|
|
|
|
setBuildPoint(*entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
addName(function->getId(), name);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
functions.push_back(std::unique_ptr<Function>(function));
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2015-09-15 02:58:02 +00:00
|
|
|
void Builder::makeReturn(bool implicit, Id retVal)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-09-15 02:58:02 +00:00
|
|
|
if (retVal) {
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
|
|
|
|
inst->addIdOperand(retVal);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
|
2015-06-26 22:58:36 +00:00
|
|
|
} else
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
if (! implicit)
|
|
|
|
createAndSetNoPredecessorBlock("post-return");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2015-09-15 02:58:02 +00:00
|
|
|
void Builder::leaveFunction()
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
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()) {
|
2016-01-20 02:13:38 +00:00
|
|
|
if (function.getReturnType() == makeVoidType())
|
|
|
|
makeReturn(true);
|
|
|
|
else {
|
|
|
|
makeReturn(true, createUndefined(function.getReturnType()));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
|
|
|
void Builder::makeDiscard()
|
|
|
|
{
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill)));
|
2015-06-26 22:58:36 +00:00
|
|
|
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 StorageClassFunction:
|
|
|
|
// Validation rules require the declaration in the entry block
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
|
2015-06-26 22:58:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-01-18 14:23:56 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
|
2015-11-16 04:33:39 +00:00
|
|
|
module.mapInstruction(inst);
|
2015-06-26 22:58:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
addName(inst->getResultId(), name);
|
|
|
|
|
|
|
|
return inst->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-08-11 00:45:24 +00:00
|
|
|
// Comments in header
|
|
|
|
Id Builder::createUndefined(Id type)
|
|
|
|
{
|
|
|
|
Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
|
2015-08-11 00:45:24 +00:00
|
|
|
return inst->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Comments in header
|
|
|
|
void Builder::createStore(Id rValue, Id lValue)
|
|
|
|
{
|
|
|
|
Instruction* store = new Instruction(OpStore);
|
|
|
|
store->addIdOperand(lValue);
|
|
|
|
store->addIdOperand(rValue);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
|
|
|
Id Builder::createLoad(Id lValue)
|
|
|
|
{
|
|
|
|
Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
|
|
|
|
load->addIdOperand(lValue);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return chain->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-09-22 03:50:29 +00:00
|
|
|
Id Builder::createArrayLength(Id base, unsigned int member)
|
|
|
|
{
|
|
|
|
Instruction* length = new Instruction(getUniqueId(), makeIntType(32), OpArrayLength);
|
|
|
|
length->addIdOperand(base);
|
|
|
|
length->addImmediateOperand(member);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
|
2015-09-22 03:50:29 +00:00
|
|
|
|
|
|
|
return length->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
|
|
|
|
{
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-02 11:38:28 +00:00
|
|
|
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), std::vector<Id>(1, index));
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
|
|
|
|
extract->addIdOperand(composite);
|
|
|
|
extract->addImmediateOperand(index);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return extract->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::createCompositeExtract(Id composite, Id typeId, std::vector<unsigned>& indexes)
|
|
|
|
{
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-02 11:38:28 +00:00
|
|
|
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
|
|
|
|
extract->addIdOperand(composite);
|
|
|
|
for (int i = 0; i < (int)indexes.size(); ++i)
|
|
|
|
extract->addImmediateOperand(indexes[i]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-09-09 08:42:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// An opcode that has one operand, no result id, and no type
|
|
|
|
void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
|
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(opCode);
|
2016-02-23 21:17:38 +00:00
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
|
|
|
|
op->addIdOperand(*it);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(OpControlBarrier);
|
2015-08-07 04:53:06 +00:00
|
|
|
op->addImmediateOperand(makeUintConstant(execution));
|
|
|
|
op->addImmediateOperand(makeUintConstant(memory));
|
|
|
|
op->addImmediateOperand(makeUintConstant(semantics));
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
|
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(OpMemoryBarrier);
|
2015-08-07 04:53:06 +00:00
|
|
|
op->addImmediateOperand(makeUintConstant(executionScope));
|
|
|
|
op->addImmediateOperand(makeUintConstant(memorySemantics));
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// An opcode that has one operands, a result id, and a type
|
|
|
|
Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
|
|
|
|
{
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-02 11:38:28 +00:00
|
|
|
return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
|
|
|
|
op->addIdOperand(operand);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
|
|
|
|
{
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-02 11:38:28 +00:00
|
|
|
std::vector<Id> operands(2);
|
|
|
|
operands[0] = left; operands[1] = right;
|
|
|
|
return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
|
|
|
|
op->addIdOperand(left);
|
|
|
|
op->addIdOperand(right);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
|
|
|
|
{
|
2016-04-07 19:40:27 +00:00
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
|
|
|
std::vector<Id> operands(3);
|
2016-04-13 03:16:20 +00:00
|
|
|
operands[0] = op1;
|
|
|
|
operands[1] = op2;
|
|
|
|
operands[2] = op3;
|
2016-04-07 19:40:27 +00:00
|
|
|
return createSpecConstantOp(
|
|
|
|
opCode, typeId, operands, std::vector<Id>());
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
|
|
|
|
op->addIdOperand(op1);
|
|
|
|
op->addIdOperand(op2);
|
|
|
|
op->addIdOperand(op3);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
|
2016-02-23 21:17:38 +00:00
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
|
|
|
|
op->addIdOperand(*it);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
2016-03-31 17:57:28 +00:00
|
|
|
Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& literals)
|
|
|
|
{
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
|
|
|
|
op->addImmediateOperand((unsigned) opCode);
|
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
|
|
|
|
op->addIdOperand(*it);
|
|
|
|
for (auto it = literals.cbegin(); it != literals.cend(); ++it)
|
|
|
|
op->addImmediateOperand(*it);
|
|
|
|
module.mapInstruction(op);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
|
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, std::vector<unsigned>& channels)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
if (channels.size() == 1)
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-02 11:38:28 +00:00
|
|
|
std::vector<Id> operands(2);
|
|
|
|
operands[0] = operands[1] = source;
|
|
|
|
return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
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]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(swizzle->getResultId(), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
|
|
|
Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& channels)
|
|
|
|
{
|
2015-08-07 04:02:24 +00:00
|
|
|
assert(getNumComponents(source) == (int)channels.size());
|
2015-06-26 22:58:36 +00:00
|
|
|
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]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return swizzle->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
|
|
|
void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
|
|
|
|
{
|
|
|
|
int direction = getNumComponents(right) - getNumComponents(left);
|
|
|
|
|
|
|
|
if (direction > 0)
|
2015-12-10 02:08:42 +00:00
|
|
|
left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
|
2015-06-26 22:58:36 +00:00
|
|
|
else if (direction < 0)
|
2015-12-10 02:08:42 +00:00
|
|
|
right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
assert(getNumComponents(scalar) == 1);
|
2015-12-10 02:08:42 +00:00
|
|
|
assert(getTypeId(scalar) == getScalarTypeId(vectorType));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
int numComponents = getNumTypeComponents(vectorType);
|
|
|
|
if (numComponents == 1)
|
|
|
|
return scalar;
|
|
|
|
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
Instruction* smear = nullptr;
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
|
|
|
auto members = std::vector<spv::Id>(numComponents, scalar);
|
2016-04-14 22:34:27 +00:00
|
|
|
// Sometime even in spec-constant-op mode, the temporary vector created by
|
|
|
|
// promoting a scalar might not be a spec constant. This should depend on
|
|
|
|
// the scalar.
|
|
|
|
// e.g.:
|
|
|
|
// const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
|
|
|
|
// In such cases, the temporary vector created from a_front_end_const_scalar
|
|
|
|
// is not a spec constant vector, even though the binary operation node is marked
|
|
|
|
// as 'specConstant' and we are in spec-constant-op mode.
|
2016-04-14 20:40:20 +00:00
|
|
|
auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
|
Spec Constant Operations
Approach:
Add a flag in `Builder` to indicate 'spec constant mode' and 'normal
mode'. When the builder is in 'normal mode', nothing changed. When the
builder is in 'spec constant mode', binary, unary and other instruction
creation rountines will be redirected to `createSpecConstantOp()` to
create instrution at module level with `OpSpecConstantOp <original
opcode> <operands>`.
'spec constant mode' should be enabled if and only if we are creating
spec constants. So a flager setter/recover guard is added when handling
binary/unary nodes in `createSpvConstantsFromConstSubTree()`.
Note when handling spec constants which are represented as ConstantUnion
Node, we should not use `OpSpecConstantOp` to initialize the composite
constant, so builder is set to 'normal mode'.
Tests:
Tests are added in Test/spv.specConstantOperations.vert, including:
1) Arithmetic, shift opeations for both scalar and composite type spec constants.
2) Size conversion from/to float and double for both scalar and vector.
3) Bitwise and/or/xor for both scalar and vector.
4) Unary negate/not for both scalar and vector.
5) Vector swizzles.
6) Comparisons for scalars.
7) == and != for composite type spec constants
Issues:
1) To implement == and != for composite type spec constants, the Spec needs
to allow OpAll, OpAny, OpFOrdEqual, OpFUnordEqual, OpOrdNotEqual,
OpFUnordNotEqual. Currently none of them are allowed in the Spec.
2016-03-21 13:51:37 +00:00
|
|
|
smear = module.getInstruction(result_id);
|
|
|
|
} else {
|
|
|
|
smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
|
|
|
|
for (int c = 0; c < numComponents; ++c)
|
|
|
|
smear->addIdOperand(scalar);
|
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(smear));
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(smear->getResultId(), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, std::vector<Id>& args)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
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]);
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
|
2016-02-02 19:37:46 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
return inst->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept all parameters needed to create a texture instruction.
|
|
|
|
// Create the correct instruction based on the inputs, and make the call.
|
2016-02-15 22:40:42 +00:00
|
|
|
Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, bool noImplicitLod, const TextureParameters& parameters)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-08-07 04:53:06 +00:00
|
|
|
static const int maxTextureArgs = 10;
|
2015-06-26 22:58:36 +00:00
|
|
|
Id texArgs[maxTextureArgs] = {};
|
|
|
|
|
|
|
|
//
|
2015-08-07 04:53:06 +00:00
|
|
|
// Set up the fixed arguments
|
2015-06-26 22:58:36 +00:00
|
|
|
//
|
|
|
|
int numArgs = 0;
|
2016-02-15 22:40:42 +00:00
|
|
|
bool explicitLod = false;
|
2015-06-26 22:58:36 +00:00
|
|
|
texArgs[numArgs++] = parameters.sampler;
|
|
|
|
texArgs[numArgs++] = parameters.coords;
|
2015-08-07 04:53:06 +00:00
|
|
|
if (parameters.Dref)
|
|
|
|
texArgs[numArgs++] = parameters.Dref;
|
2015-11-16 04:33:39 +00:00
|
|
|
if (parameters.comp)
|
|
|
|
texArgs[numArgs++] = parameters.comp;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
//
|
|
|
|
// Set up the optional arguments
|
|
|
|
//
|
|
|
|
int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
|
|
|
|
++numArgs; // speculatively make room for the mask operand
|
|
|
|
ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
|
|
|
|
if (parameters.bias) {
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
|
|
|
|
texArgs[numArgs++] = parameters.bias;
|
|
|
|
}
|
|
|
|
if (parameters.lod) {
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
|
|
|
|
texArgs[numArgs++] = parameters.lod;
|
2016-02-15 22:40:42 +00:00
|
|
|
explicitLod = true;
|
|
|
|
} else if (parameters.gradX) {
|
2015-08-07 04:53:06 +00:00
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
|
2015-06-26 22:58:36 +00:00
|
|
|
texArgs[numArgs++] = parameters.gradX;
|
|
|
|
texArgs[numArgs++] = parameters.gradY;
|
2016-02-15 22:40:42 +00:00
|
|
|
explicitLod = true;
|
|
|
|
} else if (noImplicitLod && ! fetch && ! gather) {
|
|
|
|
// have to explicitly use lod of 0 if not allowed to have them be implicit, and
|
|
|
|
// we would otherwise be about to issue an implicit instruction
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
|
|
|
|
texArgs[numArgs++] = makeFloatConstant(0.0);
|
|
|
|
explicitLod = true;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
2015-08-07 04:53:06 +00:00
|
|
|
if (parameters.offset) {
|
2015-10-11 11:37:48 +00:00
|
|
|
if (isConstant(parameters.offset))
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
|
2016-05-11 08:38:50 +00:00
|
|
|
else {
|
|
|
|
addCapability(CapabilityImageGatherExtended);
|
2015-10-11 11:37:48 +00:00
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
|
2016-05-11 08:38:50 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
texArgs[numArgs++] = parameters.offset;
|
2015-08-07 04:53:06 +00:00
|
|
|
}
|
|
|
|
if (parameters.offsets) {
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
|
|
|
|
texArgs[numArgs++] = parameters.offsets;
|
|
|
|
}
|
|
|
|
if (parameters.sample) {
|
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
|
|
|
|
texArgs[numArgs++] = parameters.sample;
|
|
|
|
}
|
2015-12-31 08:11:41 +00:00
|
|
|
if (parameters.lodClamp) {
|
2016-02-15 18:09:46 +00:00
|
|
|
// capability if this bit is used
|
|
|
|
addCapability(CapabilityMinLod);
|
|
|
|
|
2015-12-31 08:11:41 +00:00
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
|
|
|
|
texArgs[numArgs++] = parameters.lodClamp;
|
|
|
|
}
|
2015-08-07 04:53:06 +00:00
|
|
|
if (mask == ImageOperandsMaskNone)
|
|
|
|
--numArgs; // undo speculative reservation for the mask argument
|
|
|
|
else
|
|
|
|
texArgs[optArgNum] = mask;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Set up the instruction
|
|
|
|
//
|
2016-02-15 22:40:42 +00:00
|
|
|
Op opCode = OpNop; // All paths below need to set this
|
2015-09-05 21:14:48 +00:00
|
|
|
if (fetch) {
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseFetch;
|
|
|
|
else
|
|
|
|
opCode = OpImageFetch;
|
2015-11-16 04:33:39 +00:00
|
|
|
} else if (gather) {
|
|
|
|
if (parameters.Dref)
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseDrefGather;
|
|
|
|
else
|
|
|
|
opCode = OpImageDrefGather;
|
2015-11-16 04:33:39 +00:00
|
|
|
else
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseGather;
|
|
|
|
else
|
|
|
|
opCode = OpImageGather;
|
2016-02-15 22:40:42 +00:00
|
|
|
} else if (explicitLod) {
|
2015-08-07 04:53:06 +00:00
|
|
|
if (parameters.Dref) {
|
|
|
|
if (proj)
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleProjDrefExplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleProjDrefExplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
else
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleDrefExplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleDrefExplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
} else {
|
|
|
|
if (proj)
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleProjExplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleProjExplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
else
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleExplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleExplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (parameters.Dref) {
|
|
|
|
if (proj)
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleProjDrefImplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleProjDrefImplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
else
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleDrefImplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleDrefImplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
} else {
|
|
|
|
if (proj)
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleProjImplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleProjImplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
else
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse)
|
|
|
|
opCode = OpImageSparseSampleImplicitLod;
|
|
|
|
else
|
|
|
|
opCode = OpImageSampleImplicitLod;
|
2015-08-07 04:53:06 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-09-15 04:08:12 +00:00
|
|
|
// See if the result type is expecting a smeared result.
|
|
|
|
// This happens when a legacy shadow*() call is made, which
|
|
|
|
// gets a vec4 back instead of a float.
|
|
|
|
Id smearedType = resultType;
|
|
|
|
if (! isScalarType(resultType)) {
|
|
|
|
switch (opCode) {
|
|
|
|
case OpImageSampleDrefImplicitLod:
|
|
|
|
case OpImageSampleDrefExplicitLod:
|
|
|
|
case OpImageSampleProjDrefImplicitLod:
|
|
|
|
case OpImageSampleProjDrefExplicitLod:
|
|
|
|
resultType = getScalarTypeId(resultType);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-31 08:11:41 +00:00
|
|
|
Id typeId0 = 0;
|
|
|
|
Id typeId1 = 0;
|
|
|
|
|
|
|
|
if (sparse) {
|
|
|
|
typeId0 = resultType;
|
|
|
|
typeId1 = getDerefTypeId(parameters.texelOut);
|
|
|
|
resultType = makeStructResultType(typeId0, typeId1);
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// Build the SPIR-V instruction
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
|
2015-08-07 04:53:06 +00:00
|
|
|
for (int op = 0; op < optArgNum; ++op)
|
|
|
|
textureInst->addIdOperand(texArgs[op]);
|
|
|
|
if (optArgNum < numArgs)
|
|
|
|
textureInst->addImmediateOperand(texArgs[optArgNum]);
|
|
|
|
for (int op = optArgNum + 1; op < numArgs; ++op)
|
2015-06-26 22:58:36 +00:00
|
|
|
textureInst->addIdOperand(texArgs[op]);
|
|
|
|
setPrecision(textureInst->getResultId(), precision);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-09-15 04:08:12 +00:00
|
|
|
Id resultId = textureInst->getResultId();
|
|
|
|
|
2015-12-31 08:11:41 +00:00
|
|
|
if (sparse) {
|
2016-02-15 18:09:46 +00:00
|
|
|
// set capability
|
|
|
|
addCapability(CapabilitySparseResidency);
|
|
|
|
|
2015-12-31 08:11:41 +00:00
|
|
|
// Decode the return type that was a special structure
|
|
|
|
createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
|
|
|
|
resultId = createCompositeExtract(resultId, typeId0, 0);
|
2016-02-02 19:37:46 +00:00
|
|
|
setPrecision(resultId, precision);
|
2015-12-31 08:11:41 +00:00
|
|
|
} else {
|
|
|
|
// When a smear is needed, do it, as per what was computed
|
|
|
|
// above when resultType was changed to a scalar type.
|
|
|
|
if (resultType != smearedType)
|
|
|
|
resultId = smearScalar(precision, resultId, smearedType);
|
|
|
|
}
|
2015-09-15 04:08:12 +00:00
|
|
|
|
|
|
|
return resultId;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
|
|
|
Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters)
|
|
|
|
{
|
2016-02-15 18:09:46 +00:00
|
|
|
// All these need a capability
|
|
|
|
addCapability(CapabilityImageQuery);
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Figure out the result type
|
2015-08-07 04:53:06 +00:00
|
|
|
Id resultType = 0;
|
2015-06-26 22:58:36 +00:00
|
|
|
switch (opCode) {
|
2015-08-07 04:53:06 +00:00
|
|
|
case OpImageQuerySize:
|
|
|
|
case OpImageQuerySizeLod:
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2016-01-06 18:41:02 +00:00
|
|
|
int numComponents = 0;
|
2015-08-07 04:53:06 +00:00
|
|
|
switch (getTypeDimensionality(getImageType(parameters.sampler))) {
|
2015-06-26 22:58:36 +00:00
|
|
|
case Dim1D:
|
|
|
|
case DimBuffer:
|
|
|
|
numComponents = 1;
|
|
|
|
break;
|
|
|
|
case Dim2D:
|
|
|
|
case DimCube:
|
|
|
|
case DimRect:
|
2015-12-22 04:21:11 +00:00
|
|
|
case DimSubpassData:
|
2015-06-26 22:58:36 +00:00
|
|
|
numComponents = 2;
|
|
|
|
break;
|
|
|
|
case Dim3D:
|
|
|
|
numComponents = 3;
|
|
|
|
break;
|
2015-11-16 04:33:39 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
default:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-08-07 04:53:06 +00:00
|
|
|
if (isArrayedImageType(getImageType(parameters.sampler)))
|
2015-06-26 22:58:36 +00:00
|
|
|
++numComponents;
|
|
|
|
if (numComponents == 1)
|
|
|
|
resultType = makeIntType(32);
|
|
|
|
else
|
|
|
|
resultType = makeVectorType(makeIntType(32), numComponents);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2015-08-07 04:53:06 +00:00
|
|
|
case OpImageQueryLod:
|
2015-06-26 22:58:36 +00:00
|
|
|
resultType = makeVectorType(makeFloatType(32), 2);
|
|
|
|
break;
|
2015-08-07 04:53:06 +00:00
|
|
|
case OpImageQueryLevels:
|
|
|
|
case OpImageQuerySamples:
|
2015-06-26 22:58:36 +00:00
|
|
|
resultType = makeIntType(32);
|
|
|
|
break;
|
|
|
|
default:
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(0);
|
|
|
|
break;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(query));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return query->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
// External comments in header.
|
|
|
|
// Operates recursively to visit the composite's hierarchy.
|
|
|
|
Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Id boolType = makeBoolType();
|
|
|
|
Id valueType = getTypeId(value1);
|
|
|
|
|
2016-02-02 03:13:06 +00:00
|
|
|
Id resultId = NoResult;
|
2015-12-22 03:54:09 +00:00
|
|
|
|
|
|
|
int numConstituents = getNumTypeConstituents(valueType);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
// Scalars and Vectors
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
if (isScalarType(valueType) || isVectorType(valueType)) {
|
2016-01-05 06:49:03 +00:00
|
|
|
assert(valueType == getTypeId(value2));
|
2015-12-22 03:54:09 +00:00
|
|
|
// These just need a single comparison, just have
|
|
|
|
// to figure out what it is.
|
2015-06-26 22:58:36 +00:00
|
|
|
Op op;
|
2015-12-22 03:54:09 +00:00
|
|
|
switch (getMostBasicTypeClass(valueType)) {
|
|
|
|
case OpTypeFloat:
|
2015-06-26 22:58:36 +00:00
|
|
|
op = equal ? OpFOrdEqual : OpFOrdNotEqual;
|
2015-12-22 03:54:09 +00:00
|
|
|
break;
|
|
|
|
case OpTypeInt:
|
2016-02-02 03:13:06 +00:00
|
|
|
default:
|
2015-06-26 22:58:36 +00:00
|
|
|
op = equal ? OpIEqual : OpINotEqual;
|
2015-12-22 03:54:09 +00:00
|
|
|
break;
|
|
|
|
case OpTypeBool:
|
|
|
|
op = equal ? OpLogicalEqual : OpLogicalNotEqual;
|
|
|
|
precision = NoPrecision;
|
|
|
|
break;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
if (isScalarType(valueType)) {
|
|
|
|
// scalar
|
|
|
|
resultId = createBinOp(op, boolType, value1, value2);
|
|
|
|
} else {
|
|
|
|
// vector
|
|
|
|
resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
|
|
|
|
setPrecision(resultId, precision);
|
|
|
|
// reduce vector compares...
|
|
|
|
resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(resultId, precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
// Only structs, arrays, and matrices should be left.
|
|
|
|
// They share in common the reduction operation across their constituents.
|
|
|
|
assert(isAggregateType(valueType) || isMatrixType(valueType));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
// Compare each pair of constituents
|
|
|
|
for (int constituent = 0; constituent < numConstituents; ++constituent) {
|
|
|
|
std::vector<unsigned> indexes(1, constituent);
|
2016-01-05 06:49:03 +00:00
|
|
|
Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
|
|
|
|
Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
|
|
|
|
Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
|
|
|
|
Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
if (constituent == 0)
|
|
|
|
resultId = subResultId;
|
|
|
|
else
|
2016-02-02 19:37:46 +00:00
|
|
|
resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), precision);
|
2015-12-22 03:54:09 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-12-22 03:54:09 +00:00
|
|
|
return resultId;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// OpCompositeConstruct
|
|
|
|
Id Builder::createCompositeConstruct(Id typeId, std::vector<Id>& constituents)
|
|
|
|
{
|
2015-12-22 03:54:09 +00:00
|
|
|
assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size()));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-04-14 20:40:20 +00:00
|
|
|
if (generatingOpCodeForSpecConst) {
|
2016-04-14 22:34:27 +00:00
|
|
|
// Sometime, even in spec-constant-op mode, the constant composite to be
|
|
|
|
// constructed may not be a specialization constant.
|
|
|
|
// e.g.:
|
|
|
|
// const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
|
|
|
|
// The first column vector should be a spec constant one, as a_spec_const is a spec constant.
|
|
|
|
// The second column vector should NOT be spec constant, as it does not contain any spec constants.
|
|
|
|
// To handle such cases, we check the constituents of the constant vector to determine whether this
|
|
|
|
// vector should be created as a spec constant.
|
|
|
|
return makeCompositeConstant(typeId, constituents,
|
|
|
|
std::any_of(constituents.begin(), constituents.end(),
|
|
|
|
[&](spv::Id id) { return isSpecConstant(id); }));
|
2016-04-14 20:40:20 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
|
|
|
|
for (int c = 0; c < (int)constituents.size(); ++c)
|
|
|
|
op->addIdOperand(constituents[c]);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vector or scalar constructor
|
|
|
|
Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
|
|
|
|
{
|
2016-02-02 19:37:46 +00:00
|
|
|
Id result = NoResult;
|
2015-06-26 22:58:36 +00:00
|
|
|
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) {
|
2015-11-16 04:33:39 +00:00
|
|
|
assert(! isAggregate(sources[i]));
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2016-02-02 19:37:46 +00:00
|
|
|
arg = createRvalueSwizzle(precision, scalarTypeId, arg, swiz);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (numTargetComponents > 1)
|
|
|
|
constituents.push_back(arg);
|
|
|
|
else
|
|
|
|
result = arg;
|
|
|
|
++targetComponent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetComponent >= numTargetComponents)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (constituents.size() > 0)
|
|
|
|
result = createCompositeConstruct(resultTypeId, constituents);
|
|
|
|
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(result, precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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]);
|
2016-02-02 19:37:46 +00:00
|
|
|
Id column = createCompositeConstruct(columnTypeId, vectorComponents);
|
|
|
|
setPrecision(column, precision);
|
|
|
|
matrixColumns.push_back(column);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// make the matrix
|
2016-02-02 19:37:46 +00:00
|
|
|
return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2015-11-16 04:33:39 +00:00
|
|
|
builder.createSelectionMerge(mergeBlock, SelectionControlMaskNone);
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
2015-11-16 04:33:39 +00:00
|
|
|
createSelectionMerge(mergeBlock, SelectionControlMaskNone);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
// make the switch instruction
|
|
|
|
Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
|
|
|
|
switchInst->addIdOperand(selector);
|
2016-01-18 21:18:01 +00:00
|
|
|
auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
|
|
|
|
switchInst->addIdOperand(defaultOrMerge->getId());
|
|
|
|
defaultOrMerge->addPredecessor(buildPoint);
|
2015-06-26 22:58:36 +00:00
|
|
|
for (int i = 0; i < (int)caseValues.size(); ++i) {
|
|
|
|
switchInst->addImmediateOperand(caseValues[i]);
|
|
|
|
switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
|
2016-01-18 21:18:01 +00:00
|
|
|
segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst));
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2016-01-10 17:15:13 +00:00
|
|
|
Block& Builder::makeNewBlock()
|
|
|
|
{
|
|
|
|
Function& function = buildPoint->getParent();
|
|
|
|
auto block = new Block(getUniqueId(), function);
|
|
|
|
function.addBlock(block);
|
|
|
|
return *block;
|
|
|
|
}
|
|
|
|
|
2016-01-11 14:35:22 +00:00
|
|
|
Builder::LoopBlocks& Builder::makeNewLoop()
|
2016-01-10 17:15:13 +00:00
|
|
|
{
|
2016-01-20 15:19:25 +00:00
|
|
|
// Older MSVC versions don't allow inlining of blocks below.
|
|
|
|
LoopBlocks blocks = {makeNewBlock(), makeNewBlock(), makeNewBlock(), makeNewBlock()};
|
|
|
|
loops.push(blocks);
|
2016-01-11 14:35:22 +00:00
|
|
|
return loops.top();
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::createLoopContinue()
|
|
|
|
{
|
2016-01-11 14:35:22 +00:00
|
|
|
createBranch(&loops.top().continue_target);
|
2015-06-26 22:58:36 +00:00
|
|
|
// Set up a block for dead code.
|
|
|
|
createAndSetNoPredecessorBlock("post-loop-continue");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::createLoopExit()
|
|
|
|
{
|
2016-01-11 14:35:22 +00:00
|
|
|
createBranch(&loops.top().merge);
|
2015-06-26 22:58:36 +00:00
|
|
|
// Set up a block for dead code.
|
|
|
|
createAndSetNoPredecessorBlock("post-loop-break");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::closeLoop()
|
|
|
|
{
|
|
|
|
loops.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::clearAccessChain()
|
|
|
|
{
|
2015-09-13 20:46:30 +00:00
|
|
|
accessChain.base = NoResult;
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.indexChain.clear();
|
2015-09-13 20:46:30 +00:00
|
|
|
accessChain.instr = NoResult;
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.swizzle.clear();
|
2015-09-13 20:46:30 +00:00
|
|
|
accessChain.component = NoResult;
|
|
|
|
accessChain.preSwizzleBaseType = NoType;
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.isRValue = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2015-09-13 20:46:30 +00:00
|
|
|
void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-09-13 20:46:30 +00:00
|
|
|
// swizzles can be stacked in GLSL, but simplified to a single
|
|
|
|
// one here; the base type doesn't change
|
|
|
|
if (accessChain.preSwizzleBaseType == NoType)
|
|
|
|
accessChain.preSwizzleBaseType = preSwizzleBaseType;
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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);
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
transferAccessChainSwizzle(true);
|
2015-06-26 22:58:36 +00:00
|
|
|
Id base = collapseAccessChain();
|
|
|
|
|
2015-09-13 20:46:30 +00:00
|
|
|
if (accessChain.swizzle.size() && accessChain.component != NoResult)
|
2016-05-04 19:55:59 +00:00
|
|
|
logger->missingFunctionality("simultaneous l-value swizzle and dynamic component selection");
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// If swizzle still exists, it is out-of-order or not full, we must load the target vector,
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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
|
2015-09-13 20:46:30 +00:00
|
|
|
if (accessChain.component != NoResult) {
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
2016-02-02 19:37:46 +00:00
|
|
|
Id Builder::accessChainLoad(Decoration precision, Id resultType)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Id id;
|
|
|
|
|
|
|
|
if (accessChain.isRValue) {
|
2015-11-16 04:33:39 +00:00
|
|
|
// transfer access chain, but keep it static, so we can stay in registers
|
|
|
|
transferAccessChainSwizzle(false);
|
2015-06-26 22:58:36 +00:00
|
|
|
if (accessChain.indexChain.size() > 0) {
|
2015-09-13 20:46:30 +00:00
|
|
|
Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
|
2016-02-02 19:37:46 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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)
|
2015-09-13 20:46:30 +00:00
|
|
|
id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
|
2015-06-26 22:58:36 +00:00
|
|
|
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());
|
|
|
|
}
|
2016-02-02 19:37:46 +00:00
|
|
|
setPrecision(id, precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
} else
|
2016-02-02 19:37:46 +00:00
|
|
|
id = accessChain.base; // no precision, it was set when this was defined
|
2015-06-26 22:58:36 +00:00
|
|
|
} else {
|
2015-11-16 04:33:39 +00:00
|
|
|
transferAccessChainSwizzle(true);
|
2015-06-26 22:58:36 +00:00
|
|
|
// load through the access chain
|
|
|
|
id = createLoad(collapseAccessChain());
|
2016-02-02 19:37:46 +00:00
|
|
|
setPrecision(id, precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Done, unless there are swizzles to do
|
2015-09-13 20:46:30 +00:00
|
|
|
if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
|
2015-06-26 22:58:36 +00:00
|
|
|
return id;
|
|
|
|
|
|
|
|
// Do remaining swizzling
|
|
|
|
// First, static swizzling
|
|
|
|
if (accessChain.swizzle.size()) {
|
|
|
|
// static swizzle
|
2015-09-13 20:46:30 +00:00
|
|
|
Id swizzledType = getScalarTypeId(getTypeId(id));
|
2015-06-26 22:58:36 +00:00
|
|
|
if (accessChain.swizzle.size() > 1)
|
2015-09-13 20:46:30 +00:00
|
|
|
swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
|
2016-02-02 19:37:46 +00:00
|
|
|
id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// dynamic single-component selection
|
2015-09-13 20:46:30 +00:00
|
|
|
if (accessChain.component != NoResult)
|
2016-02-02 19:37:46 +00:00
|
|
|
id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::accessChainGetLValue()
|
|
|
|
{
|
|
|
|
assert(accessChain.isRValue == false);
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
transferAccessChainSwizzle(true);
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2015-09-13 20:46:30 +00:00
|
|
|
assert(accessChain.component == NoResult);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return lvalue;
|
|
|
|
}
|
|
|
|
|
2016-02-09 04:38:15 +00:00
|
|
|
// comment in header
|
|
|
|
Id Builder::accessChainGetInferredType()
|
|
|
|
{
|
|
|
|
// anything to operate on?
|
|
|
|
if (accessChain.base == NoResult)
|
|
|
|
return NoType;
|
|
|
|
Id type = getTypeId(accessChain.base);
|
|
|
|
|
|
|
|
// do initial dereference
|
|
|
|
if (! accessChain.isRValue)
|
|
|
|
type = getContainedTypeId(type);
|
|
|
|
|
|
|
|
// dereference each index
|
2016-02-23 21:17:38 +00:00
|
|
|
for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
|
2016-02-09 04:38:15 +00:00
|
|
|
if (isStructType(type))
|
2016-02-23 21:17:38 +00:00
|
|
|
type = getContainedTypeId(type, getConstantScalar(*it));
|
2016-02-09 04:38:15 +00:00
|
|
|
else
|
|
|
|
type = getContainedTypeId(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// dereference swizzle
|
|
|
|
if (accessChain.swizzle.size() == 1)
|
|
|
|
type = getContainedTypeId(type);
|
|
|
|
else if (accessChain.swizzle.size() > 1)
|
2016-02-10 18:41:29 +00:00
|
|
|
type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
|
2016-02-09 04:38:15 +00:00
|
|
|
|
|
|
|
// dereference component selection
|
|
|
|
if (accessChain.component)
|
|
|
|
type = getContainedTypeId(type);
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2016-03-10 00:54:03 +00:00
|
|
|
// comment in header
|
|
|
|
void Builder::eliminateDeadDecorations() {
|
|
|
|
std::unordered_set<const Block*> reachable_blocks;
|
|
|
|
std::unordered_set<Id> unreachable_definitions;
|
|
|
|
// Collect IDs defined in unreachable blocks. For each function, label the
|
|
|
|
// reachable blocks first. Then for each unreachable block, collect the
|
|
|
|
// result IDs of the instructions in it.
|
2016-03-10 02:40:41 +00:00
|
|
|
for (std::vector<Function*>::const_iterator fi = module.getFunctions().cbegin();
|
|
|
|
fi != module.getFunctions().cend(); fi++) {
|
|
|
|
Function* f = *fi;
|
2016-03-10 00:54:03 +00:00
|
|
|
Block* entry = f->getEntryBlock();
|
|
|
|
inReadableOrder(entry, [&reachable_blocks](const Block* b) {
|
|
|
|
reachable_blocks.insert(b);
|
|
|
|
});
|
2016-03-10 02:40:41 +00:00
|
|
|
for (std::vector<Block*>::const_iterator bi = f->getBlocks().cbegin();
|
|
|
|
bi != f->getBlocks().cend(); bi++) {
|
|
|
|
Block* b = *bi;
|
2016-03-10 00:54:03 +00:00
|
|
|
if (!reachable_blocks.count(b)) {
|
2016-03-10 02:40:41 +00:00
|
|
|
for (std::vector<std::unique_ptr<Instruction> >::const_iterator
|
|
|
|
ii = b->getInstructions().cbegin();
|
|
|
|
ii != b->getInstructions().cend(); ii++) {
|
|
|
|
Instruction* i = ii->get();
|
2016-03-10 00:54:03 +00:00
|
|
|
unreachable_definitions.insert(i->getResultId());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
decorations.erase(std::remove_if(decorations.begin(), decorations.end(),
|
2016-04-02 11:38:42 +00:00
|
|
|
[&unreachable_definitions](std::unique_ptr<Instruction>& I) -> bool {
|
2016-03-10 00:54:03 +00:00
|
|
|
Instruction* inst = I.get();
|
|
|
|
Id decoration_id = inst->getIdOperand(0);
|
|
|
|
return unreachable_definitions.count(decoration_id) != 0;
|
|
|
|
}),
|
|
|
|
decorations.end());
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
// Capabilities
|
2016-02-23 21:17:38 +00:00
|
|
|
for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
|
2015-08-07 04:53:06 +00:00
|
|
|
Instruction capInst(0, 0, OpCapability);
|
2016-02-23 21:17:38 +00:00
|
|
|
capInst.addImmediateOperand(*it);
|
2015-08-07 04:53:06 +00:00
|
|
|
capInst.dump(out);
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// TBD: OpExtension ...
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2015-11-16 04:33:39 +00:00
|
|
|
|
|
|
|
// Debug instructions
|
|
|
|
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);
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
dumpInstructions(out, names);
|
|
|
|
dumpInstructions(out, lines);
|
2015-11-16 04:33:39 +00:00
|
|
|
|
|
|
|
// Annotation instructions
|
2015-06-26 22:58:36 +00:00
|
|
|
dumpInstructions(out, decorations);
|
2015-11-16 04:33:39 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
dumpInstructions(out, constantsTypesGlobals);
|
|
|
|
dumpInstructions(out, externals);
|
|
|
|
|
|
|
|
// The functions
|
|
|
|
module.dump(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Protected methods.
|
|
|
|
//
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// Turn the described access chain in 'accessChain' into an instruction
|
|
|
|
// computing its address. This *cannot* include complex swizzles, which must
|
|
|
|
// be handled after this is called, but it does include swizzles that select
|
|
|
|
// an individual element, as a single address of a scalar type can be
|
|
|
|
// computed by an OpAccessChain instruction.
|
2015-06-26 22:58:36 +00:00
|
|
|
Id Builder::collapseAccessChain()
|
|
|
|
{
|
|
|
|
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;
|
2015-11-16 04:33:39 +00:00
|
|
|
|
|
|
|
// note that non-trivial swizzling is left pending...
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// clear out swizzle if it is redundant, that is reselecting the same components
|
|
|
|
// that would be present without the swizzle.
|
2015-06-26 22:58:36 +00:00
|
|
|
void Builder::simplifyAccessChainSwizzle()
|
|
|
|
{
|
|
|
|
// If the swizzle has fewer components than the vector, it is subsetting, and must stay
|
|
|
|
// to preserve that fact.
|
2015-09-13 20:46:30 +00:00
|
|
|
if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
|
2015-06-26 22:58:36 +00:00
|
|
|
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();
|
2015-09-13 20:46:30 +00:00
|
|
|
if (accessChain.component == NoResult)
|
|
|
|
accessChain.preSwizzleBaseType = NoType;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// To the extent any swizzling can become part of the chain
|
|
|
|
// of accesses instead of a post operation, make it so.
|
2016-04-03 00:17:13 +00:00
|
|
|
// If 'dynamic' is true, include transferring a non-static component index,
|
2015-11-16 04:33:39 +00:00
|
|
|
// otherwise, only transfer static indexes.
|
|
|
|
//
|
|
|
|
// Also, Boolean vectors are likely to be special. While
|
|
|
|
// for external storage, they should only be integer types,
|
|
|
|
// function-local bool vectors could use sub-word indexing,
|
|
|
|
// so keep that as a separate Insert/Extract on a loaded vector.
|
|
|
|
void Builder::transferAccessChainSwizzle(bool dynamic)
|
|
|
|
{
|
|
|
|
// too complex?
|
|
|
|
if (accessChain.swizzle.size() > 1)
|
2015-06-26 22:58:36 +00:00
|
|
|
return;
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// non existent?
|
|
|
|
if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
|
2015-06-26 22:58:36 +00:00
|
|
|
return;
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
// single component...
|
|
|
|
|
|
|
|
// skip doing it for Boolean vectors
|
|
|
|
if (isBoolType(getContainedTypeId(accessChain.preSwizzleBaseType)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (accessChain.swizzle.size() == 1) {
|
|
|
|
// handle static component
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
|
2015-11-16 04:33:39 +00:00
|
|
|
accessChain.swizzle.clear();
|
|
|
|
// note, the only valid remaining dynamic access would be to this one
|
|
|
|
// component, so don't bother even looking at accessChain.component
|
|
|
|
accessChain.preSwizzleBaseType = NoType;
|
|
|
|
accessChain.component = NoResult;
|
|
|
|
} else if (dynamic && accessChain.component != NoResult) {
|
|
|
|
// handle dynamic component
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.indexChain.push_back(accessChain.component);
|
2015-11-16 04:33:39 +00:00
|
|
|
accessChain.preSwizzleBaseType = NoType;
|
|
|
|
accessChain.component = NoResult;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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());
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
|
2015-06-26 22:58:36 +00:00
|
|
|
block->addPredecessor(buildPoint);
|
|
|
|
}
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
|
|
|
|
{
|
|
|
|
Instruction* merge = new Instruction(OpSelectionMerge);
|
|
|
|
merge->addIdOperand(mergeBlock->getId());
|
|
|
|
merge->addImmediateOperand(control);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
|
2015-11-16 04:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2015-11-16 04:33:39 +00:00
|
|
|
Instruction* merge = new Instruction(OpLoopMerge);
|
2015-06-26 22:58:36 +00:00
|
|
|
merge->addIdOperand(mergeBlock->getId());
|
2015-11-16 04:33:39 +00:00
|
|
|
merge->addIdOperand(continueBlock->getId());
|
2015-06-26 22:58:36 +00:00
|
|
|
merge->addImmediateOperand(control);
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
|
2015-06-26 22:58:36 +00:00
|
|
|
thenBlock->addPredecessor(buildPoint);
|
|
|
|
elseBlock->addPredecessor(buildPoint);
|
|
|
|
}
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
for (int i = 0; i < (int)instructions.size(); ++i) {
|
|
|
|
instructions[i]->dump(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}; // end spv namespace
|