2015-06-26 22:58:36 +00:00
|
|
|
//
|
2017-01-06 19:34:14 +00:00
|
|
|
// Copyright (C) 2014-2015 LunarG, Inc.
|
2018-12-14 17:47:35 +00:00
|
|
|
// Copyright (C) 2015-2018 Google, Inc.
|
2020-03-19 15:09:57 +00:00
|
|
|
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
|
2015-06-26 22:58:36 +00:00
|
|
|
//
|
2017-01-06 19:34:14 +00:00
|
|
|
// All rights reserved.
|
2015-06-26 22:58:36 +00:00
|
|
|
//
|
2017-01-06 19:34:14 +00:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions
|
|
|
|
// are met:
|
2015-06-26 22:58:36 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2017-01-06 19:34:14 +00:00
|
|
|
// 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.
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Helper for making SPIR-V IR. Generally, this is documented in the header
|
|
|
|
// SpvBuilder.h.
|
|
|
|
//
|
|
|
|
|
2016-08-05 20:04:23 +00:00
|
|
|
#include <cassert>
|
|
|
|
#include <cstdlib>
|
2015-06-26 22:58:36 +00:00
|
|
|
|
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"
|
|
|
|
|
2019-08-09 05:29:20 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2018-03-06 23:12:04 +00:00
|
|
|
#include "hex_float.h"
|
2019-08-09 05:29:20 +00:00
|
|
|
#endif
|
Parser: Implement extension GL_AMD_gpu_shader_half_float.
- Add built-in types: float16_t, f16vec, f16mat.
- Add support of half float constant: hf, HF.
- Extend built-in floating-point operators: +, -, *, /, ++, --, +=, -=,
*=, /=, ==, !=, >=, <=, >, <.
- Add support of type conversions: float16_t -> XXX, XXX -> float16_t.
- Add new built-in functions.
2016-07-29 08:00:05 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <cstdio>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace spv {
|
|
|
|
|
2018-02-01 01:35:56 +00:00
|
|
|
Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
|
|
|
|
spvVersion(spvVersion),
|
2015-06-26 22:58:36 +00:00
|
|
|
source(SourceLanguageUnknown),
|
|
|
|
sourceVersion(0),
|
2017-05-31 23:11:16 +00:00
|
|
|
sourceFileStringId(NoResult),
|
2017-06-01 00:50:53 +00:00
|
|
|
currentLine(0),
|
2018-12-08 00:36:33 +00:00
|
|
|
currentFile(nullptr),
|
2017-06-01 00:50:53 +00:00
|
|
|
emitOpLines(false),
|
2015-06-26 22:58:36 +00:00
|
|
|
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),
|
2016-11-26 20:31:47 +00:00
|
|
|
entryPointFunction(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);
|
2018-08-14 19:31:43 +00:00
|
|
|
module.mapInstruction(import);
|
2017-01-06 07:34:48 +00:00
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-12-08 00:36:33 +00:00
|
|
|
// Emit instruction for non-filename-based #line directives (ie. no filename
|
|
|
|
// seen yet): emit an OpLine if we've been asked to emit OpLines and the line
|
|
|
|
// number has changed since the last time, and is a valid line number.
|
2017-06-01 00:50:53 +00:00
|
|
|
void Builder::setLine(int lineNum)
|
|
|
|
{
|
|
|
|
if (lineNum != 0 && lineNum != currentLine) {
|
|
|
|
currentLine = lineNum;
|
|
|
|
if (emitOpLines)
|
|
|
|
addLine(sourceFileStringId, currentLine, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-08 00:36:33 +00:00
|
|
|
// If no filename, do non-filename-based #line emit. Else do filename-based emit.
|
|
|
|
// Emit OpLine if we've been asked to emit OpLines and the line number or filename
|
|
|
|
// has changed since the last time, and line number is valid.
|
|
|
|
void Builder::setLine(int lineNum, const char* filename)
|
|
|
|
{
|
|
|
|
if (filename == nullptr) {
|
|
|
|
setLine(lineNum);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
|
|
|
|
strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) {
|
|
|
|
currentLine = lineNum;
|
|
|
|
currentFile = filename;
|
|
|
|
if (emitOpLines) {
|
2018-12-06 18:13:15 +00:00
|
|
|
spv::Id strId = getStringId(filename);
|
|
|
|
addLine(strId, currentLine, 0);
|
2018-12-08 00:36:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 00:50:53 +00:00
|
|
|
void Builder::addLine(Id fileName, int lineNum, int column)
|
|
|
|
{
|
|
|
|
Instruction* line = new Instruction(OpLine);
|
|
|
|
line->addIdOperand(fileName);
|
|
|
|
line->addImmediateOperand(lineNum);
|
|
|
|
line->addImmediateOperand(column);
|
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2019-01-06 23:58:04 +00:00
|
|
|
Id Builder::makeForwardPointer(StorageClass storageClass)
|
|
|
|
{
|
|
|
|
// Caching/uniquifying doesn't work here, because we don't know the
|
|
|
|
// pointee type and there can be multiple forward pointers of the same
|
|
|
|
// storage type. Somebody higher up in the stack must keep track.
|
|
|
|
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);
|
|
|
|
type->addImmediateOperand(storageClass);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
|
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypePointer][t];
|
|
|
|
if (type->getImmediateOperand(0) == (unsigned)storageClass &&
|
|
|
|
type->getIdOperand(1) == pointee)
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
type = new Instruction(forwardPointerType, NoType, OpTypePointer);
|
|
|
|
type->addImmediateOperand(storageClass);
|
|
|
|
type->addIdOperand(pointee);
|
|
|
|
groupedTypes[OpTypePointer].push_back(type);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
Id Builder::makeIntegerType(int width, bool hasSign)
|
|
|
|
{
|
2019-08-08 16:35:51 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
assert(width == 32);
|
|
|
|
width = 32;
|
|
|
|
#endif
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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) {
|
2018-03-06 23:12:04 +00:00
|
|
|
case 8:
|
2016-02-15 00:11:15 +00:00
|
|
|
case 16:
|
2018-08-15 19:54:09 +00:00
|
|
|
// these are currently handled by storage-type declarations and post processing
|
2016-02-15 00:11:15 +00:00
|
|
|
break;
|
|
|
|
case 64:
|
|
|
|
addCapability(CapabilityInt64);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
Id Builder::makeFloatType(int width)
|
|
|
|
{
|
2019-08-08 16:35:51 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
assert(width == 32);
|
|
|
|
width = 32;
|
|
|
|
#endif
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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:
|
2018-08-15 19:54:09 +00:00
|
|
|
// currently handled by storage-type declarations and post processing
|
2016-02-15 00:11:15 +00:00
|
|
|
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;
|
2017-01-06 07:34:48 +00:00
|
|
|
if (type->getIdOperand(0) != type0 ||
|
2015-11-16 04:33:39 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-02-19 19:10:32 +00:00
|
|
|
Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols)
|
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
|
|
|
for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {
|
|
|
|
type = groupedTypes[OpTypeCooperativeMatrixNV][t];
|
|
|
|
if (type->getIdOperand(0) == component &&
|
|
|
|
type->getIdOperand(1) == scope &&
|
|
|
|
type->getIdOperand(2) == rows &&
|
|
|
|
type->getIdOperand(3) == cols)
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);
|
|
|
|
type->addIdOperand(component);
|
|
|
|
type->addIdOperand(scope);
|
|
|
|
type->addIdOperand(rows);
|
|
|
|
type->addIdOperand(cols);
|
|
|
|
groupedTypes[OpTypeCooperativeMatrixNV].push_back(type);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
2021-11-17 15:32:27 +00:00
|
|
|
Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)
|
|
|
|
{
|
|
|
|
// try to find it
|
|
|
|
Instruction* type;
|
|
|
|
for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {
|
|
|
|
type = groupedTypes[opcode][t];
|
2022-01-05 02:55:41 +00:00
|
|
|
if (static_cast<size_t>(type->getNumOperands()) != operands.size())
|
2021-11-17 15:32:27 +00:00
|
|
|
continue; // Number mismatch, find next
|
|
|
|
|
|
|
|
bool match = true;
|
2022-01-27 18:39:19 +00:00
|
|
|
for (int op = 0; match && op < (int)operands.size(); ++op) {
|
2021-11-17 15:32:27 +00:00
|
|
|
match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;
|
|
|
|
}
|
|
|
|
if (match)
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found, make it
|
|
|
|
type = new Instruction(getUniqueId(), NoType, opcode);
|
2022-01-05 02:55:41 +00:00
|
|
|
for (size_t op = 0; op < operands.size(); ++op) {
|
2021-11-17 15:32:27 +00:00
|
|
|
if (operands[op].isId)
|
|
|
|
type->addIdOperand(operands[op].word);
|
|
|
|
else
|
|
|
|
type->addImmediateOperand(operands[op].word);
|
|
|
|
}
|
|
|
|
groupedTypes[opcode].push_back(type);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
2019-02-19 19:10:32 +00:00
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-03-03 17:25:07 +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
|
|
|
{
|
2017-08-15 04:10:00 +00:00
|
|
|
assert(sampled == 1 || sampled == 2);
|
|
|
|
|
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);
|
|
|
|
|
2019-08-11 13:41:45 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2016-02-15 00:37:30 +00:00
|
|
|
// deal with capabilities
|
|
|
|
switch (dim) {
|
|
|
|
case DimBuffer:
|
2017-08-15 04:10:00 +00:00
|
|
|
if (sampled == 1)
|
2016-02-15 00:37:30 +00:00
|
|
|
addCapability(CapabilitySampledBuffer);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageBuffer);
|
|
|
|
break;
|
|
|
|
case Dim1D:
|
2017-08-15 04:10:00 +00:00
|
|
|
if (sampled == 1)
|
2016-02-15 00:37:30 +00:00
|
|
|
addCapability(CapabilitySampled1D);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImage1D);
|
|
|
|
break;
|
|
|
|
case DimCube:
|
|
|
|
if (arrayed) {
|
2017-08-15 04:10:00 +00:00
|
|
|
if (sampled == 1)
|
2016-02-15 00:37:30 +00:00
|
|
|
addCapability(CapabilitySampledCubeArray);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageCubeArray);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DimRect:
|
2017-08-15 04:10:00 +00:00
|
|
|
if (sampled == 1)
|
2016-02-15 00:37:30 +00:00
|
|
|
addCapability(CapabilitySampledRect);
|
|
|
|
else
|
|
|
|
addCapability(CapabilityImageRect);
|
|
|
|
break;
|
|
|
|
case DimSubpassData:
|
|
|
|
addCapability(CapabilityInputAttachment);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ms) {
|
2017-08-15 04:10:00 +00:00
|
|
|
if (sampled == 2) {
|
2017-10-20 16:56:50 +00:00
|
|
|
// Images used with subpass data are not storage
|
|
|
|
// images, so don't require the capability for them.
|
|
|
|
if (dim != Dim::DimSubpassData)
|
|
|
|
addCapability(CapabilityStorageImageMultisample);
|
2017-08-15 04:10:00 +00:00
|
|
|
if (arrayed)
|
|
|
|
addCapability(CapabilityImageMSArray);
|
|
|
|
}
|
2016-02-15 00:37:30 +00:00
|
|
|
}
|
2019-08-11 13:41:45 +00:00
|
|
|
#endif
|
2016-02-15 00:37:30 +00:00
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-08-06 13:00:58 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2020-03-18 00:42:47 +00:00
|
|
|
Id Builder::makeAccelerationStructureType()
|
2018-09-19 18:42:24 +00:00
|
|
|
{
|
|
|
|
Instruction *type;
|
2020-03-18 00:42:47 +00:00
|
|
|
if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);
|
|
|
|
groupedTypes[OpTypeAccelerationStructureKHR].push_back(type);
|
2018-09-19 18:42:24 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
} else {
|
2020-03-18 00:42:47 +00:00
|
|
|
type = groupedTypes[OpTypeAccelerationStructureKHR].back();
|
2018-09-19 18:42:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
2020-03-19 15:09:57 +00:00
|
|
|
|
|
|
|
Id Builder::makeRayQueryType()
|
|
|
|
{
|
|
|
|
Instruction *type;
|
2020-11-23 20:41:27 +00:00
|
|
|
if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {
|
|
|
|
type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);
|
|
|
|
groupedTypes[OpTypeRayQueryKHR].push_back(type);
|
2020-03-19 15:09:57 +00:00
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
|
|
|
|
module.mapInstruction(type);
|
|
|
|
} else {
|
2020-11-23 20:41:27 +00:00
|
|
|
type = groupedTypes[OpTypeRayQueryKHR].back();
|
2020-03-19 15:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return type->getResultId();
|
|
|
|
}
|
2018-09-19 18:42:24 +00:00
|
|
|
#endif
|
2019-08-06 13:00:58 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
Id Builder::getDerefTypeId(Id resultId) const
|
|
|
|
{
|
|
|
|
Id typeId = getTypeId(resultId);
|
|
|
|
assert(isPointerType(typeId));
|
|
|
|
|
2018-08-14 03:37:59 +00:00
|
|
|
return module.getInstruction(typeId)->getIdOperand(1);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Op Builder::getMostBasicTypeClass(Id typeId) const
|
|
|
|
{
|
|
|
|
Instruction* instr = module.getInstruction(typeId);
|
|
|
|
|
|
|
|
Op typeClass = instr->getOpCode();
|
|
|
|
switch (typeClass)
|
|
|
|
{
|
|
|
|
case OpTypeVector:
|
|
|
|
case OpTypeMatrix:
|
|
|
|
case OpTypeArray:
|
|
|
|
case OpTypeRuntimeArray:
|
|
|
|
return getMostBasicTypeClass(instr->getIdOperand(0));
|
|
|
|
case OpTypePointer:
|
|
|
|
return getMostBasicTypeClass(instr->getIdOperand(1));
|
|
|
|
default:
|
2018-08-15 19:54:09 +00:00
|
|
|
return typeClass;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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:
|
2019-01-06 23:58:04 +00:00
|
|
|
case OpTypePointer:
|
2015-06-26 22:58:36 +00:00
|
|
|
return 1;
|
|
|
|
case OpTypeVector:
|
|
|
|
case OpTypeMatrix:
|
|
|
|
return instr->getImmediateOperand(1);
|
2016-02-16 03:58:50 +00:00
|
|
|
case OpTypeArray:
|
|
|
|
{
|
2018-08-14 03:37:59 +00:00
|
|
|
Id lengthId = instr->getIdOperand(1);
|
2016-02-16 03:58:50 +00:00
|
|
|
return module.getInstruction(lengthId)->getImmediateOperand(0);
|
|
|
|
}
|
2015-12-22 03:54:09 +00:00
|
|
|
case OpTypeStruct:
|
|
|
|
return instr->getNumOperands();
|
2019-02-19 19:10:32 +00:00
|
|
|
case OpTypeCooperativeMatrixNV:
|
|
|
|
// has only one constituent when used with OpCompositeConstruct.
|
|
|
|
return 1;
|
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:
|
2019-02-19 19:10:32 +00:00
|
|
|
case OpTypeCooperativeMatrixNV:
|
2015-06-26 22:58:36 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
// Figure out the final resulting type of the access chain.
|
|
|
|
Id Builder::getResultingAccessChainType() const
|
|
|
|
{
|
|
|
|
assert(accessChain.base != NoResult);
|
|
|
|
Id typeId = getTypeId(accessChain.base);
|
|
|
|
|
|
|
|
assert(isPointerType(typeId));
|
|
|
|
typeId = getContainedTypeId(typeId);
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
|
|
|
|
if (isStructType(typeId)) {
|
|
|
|
assert(isConstantScalar(accessChain.indexChain[i]));
|
|
|
|
typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));
|
|
|
|
} else
|
|
|
|
typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return typeId;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Return the immediately contained type of a given composite type.
|
|
|
|
Id Builder::getContainedTypeId(Id typeId) const
|
|
|
|
{
|
|
|
|
return getContainedTypeId(typeId, 0);
|
|
|
|
}
|
|
|
|
|
2018-08-15 19:54:09 +00:00
|
|
|
// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
|
|
|
|
// of width 'width'. The 'width' is only consumed for int and float types.
|
|
|
|
// Returns false otherwise.
|
2018-09-25 20:32:06 +00:00
|
|
|
bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
|
2018-08-15 19:54:09 +00:00
|
|
|
{
|
|
|
|
const Instruction& instr = *module.getInstruction(typeId);
|
|
|
|
|
|
|
|
Op typeClass = instr.getOpCode();
|
|
|
|
switch (typeClass)
|
|
|
|
{
|
|
|
|
case OpTypeInt:
|
|
|
|
case OpTypeFloat:
|
|
|
|
return typeClass == typeOp && instr.getImmediateOperand(0) == width;
|
|
|
|
case OpTypeStruct:
|
|
|
|
for (int m = 0; m < instr.getNumOperands(); ++m) {
|
|
|
|
if (containsType(instr.getIdOperand(m), typeOp, width))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2019-01-06 23:58:04 +00:00
|
|
|
case OpTypePointer:
|
|
|
|
return false;
|
2018-08-15 19:54:09 +00:00
|
|
|
case OpTypeVector:
|
|
|
|
case OpTypeMatrix:
|
|
|
|
case OpTypeArray:
|
|
|
|
case OpTypeRuntimeArray:
|
|
|
|
return containsType(getContainedTypeId(typeId), typeOp, width);
|
|
|
|
default:
|
|
|
|
return typeClass == typeOp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 23:58:04 +00:00
|
|
|
// return true if the type is a pointer to PhysicalStorageBufferEXT or an
|
|
|
|
// array of such pointers. These require restrict/aliased decorations.
|
|
|
|
bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
|
|
|
|
{
|
|
|
|
const Instruction& instr = *module.getInstruction(typeId);
|
|
|
|
|
|
|
|
Op typeClass = instr.getOpCode();
|
|
|
|
switch (typeClass)
|
|
|
|
{
|
|
|
|
case OpTypePointer:
|
|
|
|
return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
|
|
|
|
case OpTypeArray:
|
|
|
|
return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// See if a scalar constant of this type has already been created, so it
|
|
|
|
// can be reused rather than duplicated. (Required by the specification).
|
2018-02-27 02:20:05 +00:00
|
|
|
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
|
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').
|
2018-02-27 02:20:05 +00:00
|
|
|
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
|
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) {
|
2017-01-06 07:34:48 +00:00
|
|
|
case OpUndef:
|
2015-10-13 16:39:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 14:30:03 +00:00
|
|
|
Id Builder::makeNullConstant(Id typeId)
|
|
|
|
{
|
|
|
|
Instruction* constant;
|
|
|
|
|
|
|
|
// See if we already made it.
|
|
|
|
Id existing = NoResult;
|
|
|
|
for (int i = 0; i < (int)nullConstants.size(); ++i) {
|
|
|
|
constant = nullConstants[i];
|
|
|
|
if (constant->getTypeId() == typeId)
|
|
|
|
existing = constant->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (existing != NoResult)
|
|
|
|
return existing;
|
|
|
|
|
|
|
|
// Make it
|
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
|
|
|
nullConstants.push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2019-08-09 05:29:20 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
assert(0);
|
|
|
|
return NoResult;
|
|
|
|
#else
|
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();
|
2019-08-09 05:29:20 +00:00
|
|
|
#endif
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
Parser: Implement extension GL_AMD_gpu_shader_half_float.
- Add built-in types: float16_t, f16vec, f16mat.
- Add support of half float constant: hf, HF.
- Extend built-in floating-point operators: +, -, *, /, ++, --, +=, -=,
*=, /=, ==, !=, >=, <=, >, <.
- Add support of type conversions: float16_t -> XXX, XXX -> float16_t.
- Add new built-in functions.
2016-07-29 08:00:05 +00:00
|
|
|
Id Builder::makeFloat16Constant(float f16, bool specConstant)
|
|
|
|
{
|
2019-08-09 05:29:20 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
assert(0);
|
|
|
|
return NoResult;
|
|
|
|
#else
|
Parser: Implement extension GL_AMD_gpu_shader_half_float.
- Add built-in types: float16_t, f16vec, f16mat.
- Add support of half float constant: hf, HF.
- Extend built-in floating-point operators: +, -, *, /, ++, --, +=, -=,
*=, /=, ==, !=, >=, <=, >, <.
- Add support of type conversions: float16_t -> XXX, XXX -> float16_t.
- Add new built-in functions.
2016-07-29 08:00:05 +00:00
|
|
|
Op opcode = specConstant ? OpSpecConstant : OpConstant;
|
|
|
|
Id typeId = makeFloatType(16);
|
|
|
|
|
|
|
|
spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
|
|
|
|
spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
|
2016-10-13 17:28:20 +00:00
|
|
|
fVal.castTo(f16Val, spvutils::kRoundToZero);
|
Parser: Implement extension GL_AMD_gpu_shader_half_float.
- Add built-in types: float16_t, f16vec, f16mat.
- Add support of half float constant: hf, HF.
- Extend built-in floating-point operators: +, -, *, /, ++, --, +=, -=,
*=, /=, ==, !=, >=, <=, >, <.
- Add support of type conversions: float16_t -> XXX, XXX -> float16_t.
- Add new built-in functions.
2016-07-29 08:00:05 +00:00
|
|
|
|
|
|
|
unsigned value = f16Val.value().getAsFloat().get_value();
|
|
|
|
|
|
|
|
// See if we already made it. Applies only to regular constants, because specialization constants
|
|
|
|
// must remain distinct for the purpose of applying a SpecId decoration.
|
|
|
|
if (!specConstant) {
|
|
|
|
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
|
|
|
|
c->addImmediateOperand(value);
|
|
|
|
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
|
|
|
|
groupedConstants[OpTypeFloat].push_back(c);
|
|
|
|
module.mapInstruction(c);
|
|
|
|
|
|
|
|
return c->getResultId();
|
2019-08-09 05:29:20 +00:00
|
|
|
#endif
|
Parser: Implement extension GL_AMD_gpu_shader_half_float.
- Add built-in types: float16_t, f16vec, f16mat.
- Add support of half float constant: hf, HF.
- Extend built-in floating-point operators: +, -, *, /, ++, --, +=, -=,
*=, /=, ==, !=, >=, <=, >, <.
- Add support of type conversions: float16_t -> XXX, XXX -> float16_t.
- Add new built-in functions.
2016-07-29 08:00:05 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 09:57:59 +00:00
|
|
|
Id Builder::makeFpConstant(Id type, double d, bool specConstant)
|
|
|
|
{
|
2019-08-11 13:41:45 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
const int width = 32;
|
|
|
|
assert(width == getScalarTypeWidth(type));
|
|
|
|
#else
|
|
|
|
const int width = getScalarTypeWidth(type);
|
|
|
|
#endif
|
2018-03-13 09:57:59 +00:00
|
|
|
|
2019-08-11 13:41:45 +00:00
|
|
|
assert(isFloatType(type));
|
2018-03-13 09:57:59 +00:00
|
|
|
|
2019-08-11 13:41:45 +00:00
|
|
|
switch (width) {
|
|
|
|
case 16:
|
|
|
|
return makeFloat16Constant((float)d, specConstant);
|
|
|
|
case 32:
|
|
|
|
return makeFloatConstant((float)d, specConstant);
|
|
|
|
case 64:
|
|
|
|
return makeDoubleConstant(d, specConstant);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
return NoResult;
|
2018-03-13 09:57:59 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 19:10:32 +00:00
|
|
|
Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* constant = 0;
|
|
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
|
|
|
|
constant = groupedConstants[typeClass][i];
|
|
|
|
|
2019-02-19 19:10:32 +00:00
|
|
|
if (constant->getTypeId() != typeId)
|
2015-06-26 22:58:36 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-27 02:20:05 +00:00
|
|
|
Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
|
|
|
|
{
|
|
|
|
Instruction* constant = 0;
|
|
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
|
|
|
|
constant = groupedStructConstants[typeId][i];
|
|
|
|
|
|
|
|
// same contents?
|
|
|
|
bool mismatch = false;
|
|
|
|
for (int op = 0; op < constant->getNumOperands(); ++op) {
|
|
|
|
if (constant->getIdOperand(op) != comps[op]) {
|
|
|
|
mismatch = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! mismatch) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found ? constant->getResultId() : NoResult;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Comments in header
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::makeCompositeConstant(Id typeId, const 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 OpTypeMatrix:
|
2019-02-19 19:10:32 +00:00
|
|
|
case OpTypeCooperativeMatrixNV:
|
2018-02-27 02:20:05 +00:00
|
|
|
if (! specConstant) {
|
2019-02-19 19:10:32 +00:00
|
|
|
Id existing = findCompositeConstant(typeClass, typeId, members);
|
2018-02-27 02:20:05 +00:00
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OpTypeStruct:
|
|
|
|
if (! specConstant) {
|
|
|
|
Id existing = findStructConstant(typeId, members);
|
|
|
|
if (existing)
|
|
|
|
return existing;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
|
|
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));
|
2018-02-27 02:20:05 +00:00
|
|
|
if (typeClass == OpTypeStruct)
|
|
|
|
groupedStructConstants[typeId].push_back(c);
|
|
|
|
else
|
|
|
|
groupedConstants[typeClass].push_back(c);
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-09-18 13:18:35 +00:00
|
|
|
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
|
|
|
|
{
|
|
|
|
Instruction* instr = new Instruction(OpExecutionMode);
|
|
|
|
instr->addIdOperand(entryPoint->getId());
|
|
|
|
instr->addImmediateOperand(mode);
|
|
|
|
for (auto literal : literals)
|
|
|
|
instr->addImmediateOperand(literal);
|
|
|
|
|
|
|
|
executionModes.push_back(std::unique_ptr<Instruction>(instr));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
|
|
|
|
{
|
|
|
|
Instruction* instr = new Instruction(OpExecutionModeId);
|
|
|
|
instr->addIdOperand(entryPoint->getId());
|
|
|
|
instr->addImmediateOperand(mode);
|
|
|
|
for (auto operandId : operandIds)
|
|
|
|
instr->addIdOperand(operandId);
|
|
|
|
|
|
|
|
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::addDecoration(Id id, Decoration decoration, int num)
|
|
|
|
{
|
2016-07-15 17:53:56 +00:00
|
|
|
if (decoration == spv::DecorationMax)
|
2015-11-16 04:33:39 +00:00
|
|
|
return;
|
2018-03-08 01:05:55 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-03-08 01:05:55 +00:00
|
|
|
void Builder::addDecoration(Id id, Decoration decoration, const char* s)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
2020-09-18 13:18:35 +00:00
|
|
|
Instruction* dec = new Instruction(OpDecorateString);
|
2018-03-08 01:05:55 +00:00
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
dec->addStringOperand(s);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:18:35 +00:00
|
|
|
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpDecorate);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
for (auto literal : literals)
|
|
|
|
dec->addImmediateOperand(literal);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpDecorateString);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
for (auto string : strings)
|
|
|
|
dec->addStringOperand(string);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
2018-03-08 01:05:55 +00:00
|
|
|
void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpDecorateId);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
dec->addIdOperand(idDecoration);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:18:35 +00:00
|
|
|
void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)
|
|
|
|
{
|
|
|
|
if(decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpDecorateId);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
|
|
|
|
for (auto operandId : operandIds)
|
|
|
|
dec->addIdOperand(operandId);
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2018-03-08 01:05:55 +00:00
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-03-08 01:05:55 +00:00
|
|
|
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(member);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
dec->addStringOperand(s);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:18:35 +00:00
|
|
|
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpMemberDecorate);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(member);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
for (auto literal : literals)
|
|
|
|
dec->addImmediateOperand(literal);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)
|
|
|
|
{
|
|
|
|
if (decoration == spv::DecorationMax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Instruction* dec = new Instruction(OpMemberDecorateString);
|
|
|
|
dec->addIdOperand(id);
|
|
|
|
dec->addImmediateOperand(member);
|
|
|
|
dec->addImmediateOperand(decoration);
|
|
|
|
for (auto string : strings)
|
|
|
|
dec->addStringOperand(string);
|
|
|
|
|
|
|
|
decorations.push_back(std::unique_ptr<Instruction>(dec));
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Comments in header
|
2016-09-19 22:01:41 +00:00
|
|
|
Function* Builder::makeEntryPoint(const char* entryPoint)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2016-11-26 20:31:47 +00:00
|
|
|
assert(! entryPointFunction);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
Block* entry;
|
|
|
|
std::vector<Id> params;
|
2017-07-18 08:35:46 +00:00
|
|
|
std::vector<std::vector<Decoration>> decorations;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2017-07-18 08:35:46 +00:00
|
|
|
entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2016-11-26 20:31:47 +00:00
|
|
|
return entryPointFunction;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2016-02-02 19:37:46 +00:00
|
|
|
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
|
2020-03-03 17:25:07 +00:00
|
|
|
const std::vector<Id>& paramTypes,
|
|
|
|
const std::vector<std::vector<Decoration>>& decorations, 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);
|
2020-06-30 07:27:08 +00:00
|
|
|
function->setReturnPrecision(precision);
|
2017-07-18 08:35:46 +00:00
|
|
|
for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
|
2020-06-26 14:37:06 +00:00
|
|
|
for (int d = 0; d < (int)decorations[p].size(); ++d) {
|
2017-07-18 08:35:46 +00:00
|
|
|
addDecoration(firstParamId + p, decorations[p][d]);
|
2020-06-30 07:27:08 +00:00
|
|
|
function->addParamPrecision(p, decorations[p][d]);
|
2020-06-26 14:37:06 +00:00
|
|
|
}
|
2017-07-18 08:35:46 +00:00
|
|
|
}
|
2016-02-02 19:37:46 +00:00
|
|
|
|
|
|
|
// 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
|
2020-11-23 20:41:27 +00:00
|
|
|
void Builder::makeStatementTerminator(spv::Op opcode, const char *name)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2020-11-23 20:41:27 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));
|
|
|
|
createAndSetNoPredecessorBlock(name);
|
2020-11-09 16:30:01 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Comments in header
|
2020-06-30 07:27:08 +00:00
|
|
|
Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Id pointerType = makePointer(storageClass, type);
|
|
|
|
Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
|
|
|
|
inst->addImmediateOperand(storageClass);
|
2019-02-07 15:04:12 +00:00
|
|
|
if (initializer != NoResult)
|
|
|
|
inst->addIdOperand(initializer);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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);
|
2020-06-30 07:27:08 +00:00
|
|
|
setPrecision(inst->getResultId(), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-01-06 23:58:04 +00:00
|
|
|
// av/vis/nonprivate are unnecessary and illegal for some storage classes.
|
2020-03-03 17:25:07 +00:00
|
|
|
spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
|
|
|
|
const
|
2019-01-06 23:58:04 +00:00
|
|
|
{
|
|
|
|
switch (sc) {
|
|
|
|
case spv::StorageClassUniform:
|
|
|
|
case spv::StorageClassWorkgroup:
|
|
|
|
case spv::StorageClassStorageBuffer:
|
|
|
|
case spv::StorageClassPhysicalStorageBufferEXT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
memoryAccess = spv::MemoryAccessMask(memoryAccess &
|
|
|
|
~(spv::MemoryAccessMakePointerAvailableKHRMask |
|
|
|
|
spv::MemoryAccessMakePointerVisibleKHRMask |
|
|
|
|
spv::MemoryAccessNonPrivatePointerKHRMask));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return memoryAccess;
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// Comments in header
|
2020-03-03 17:25:07 +00:00
|
|
|
void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,
|
|
|
|
unsigned int alignment)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* store = new Instruction(OpStore);
|
|
|
|
store->addIdOperand(lValue);
|
|
|
|
store->addIdOperand(rValue);
|
2018-09-05 15:11:41 +00:00
|
|
|
|
2019-01-06 23:58:04 +00:00
|
|
|
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
|
|
|
|
|
2018-09-05 15:11:41 +00:00
|
|
|
if (memoryAccess != MemoryAccessMaskNone) {
|
|
|
|
store->addImmediateOperand(memoryAccess);
|
2019-01-06 23:58:04 +00:00
|
|
|
if (memoryAccess & spv::MemoryAccessAlignedMask) {
|
|
|
|
store->addImmediateOperand(alignment);
|
|
|
|
}
|
2018-09-05 15:11:41 +00:00
|
|
|
if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
|
|
|
|
store->addIdOperand(makeUintConstant(scope));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
2020-06-30 07:27:08 +00:00
|
|
|
Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,
|
|
|
|
spv::Scope scope, unsigned int alignment)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
|
|
|
|
load->addIdOperand(lValue);
|
2018-09-05 15:11:41 +00:00
|
|
|
|
2019-01-06 23:58:04 +00:00
|
|
|
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
|
|
|
|
|
2018-09-05 15:11:41 +00:00
|
|
|
if (memoryAccess != MemoryAccessMaskNone) {
|
|
|
|
load->addImmediateOperand(memoryAccess);
|
2019-01-06 23:58:04 +00:00
|
|
|
if (memoryAccess & spv::MemoryAccessAlignedMask) {
|
|
|
|
load->addImmediateOperand(alignment);
|
|
|
|
}
|
2018-09-05 15:11:41 +00:00
|
|
|
if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
|
|
|
|
load->addIdOperand(makeUintConstant(scope));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-18 14:23:56 +00:00
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
|
2020-06-30 07:27:08 +00:00
|
|
|
setPrecision(load->getResultId(), precision);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
return load->getResultId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
// Figure out the final resulting type.
|
2021-07-16 21:07:16 +00:00
|
|
|
Id typeId = getResultingAccessChainType();
|
2015-06-26 22:58:36 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-11-27 16:19:10 +00:00
|
|
|
spv::Id intType = makeUintType(32);
|
2017-02-13 00:50:28 +00:00
|
|
|
Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
|
2015-09-22 03:50:29 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-02-19 19:10:32 +00:00
|
|
|
Id Builder::createCooperativeMatrixLength(Id type)
|
|
|
|
{
|
|
|
|
spv::Id intType = makeUintType(32);
|
|
|
|
|
|
|
|
// Generate code for spec constants if in spec constant operation
|
|
|
|
// generation mode.
|
|
|
|
if (generatingOpCodeForSpecConst) {
|
|
|
|
return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());
|
|
|
|
}
|
|
|
|
|
|
|
|
Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
|
|
|
|
length->addIdOperand(type);
|
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
|
|
|
|
|
|
|
|
return length->getResultId();
|
|
|
|
}
|
|
|
|
|
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) {
|
2020-03-03 17:25:07 +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();
|
|
|
|
}
|
|
|
|
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
|
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
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-08-14 19:31:43 +00:00
|
|
|
// An opcode that has one id operand, no result id, and no type
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-09-19 18:42:24 +00:00
|
|
|
// An opcode that has one or more operands, no result id, and no type
|
2018-09-05 15:11:41 +00:00
|
|
|
void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
|
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(opCode);
|
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
|
|
|
|
op->addIdOperand(*it);
|
|
|
|
}
|
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
|
|
|
}
|
|
|
|
|
2018-08-14 19:31:43 +00:00
|
|
|
// An opcode that has multiple operands, no result id, and no type
|
|
|
|
void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
|
2015-09-09 08:42:49 +00:00
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(opCode);
|
2018-08-14 19:31:43 +00:00
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
|
|
|
|
if (it->isId)
|
|
|
|
op->addIdOperand(it->word);
|
|
|
|
else
|
|
|
|
op->addImmediateOperand(it->word);
|
|
|
|
}
|
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);
|
2018-08-14 03:37:59 +00:00
|
|
|
op->addIdOperand(makeUintConstant(execution));
|
|
|
|
op->addIdOperand(makeUintConstant(memory));
|
|
|
|
op->addIdOperand(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);
|
2018-08-14 03:37:59 +00:00
|
|
|
op->addIdOperand(makeUintConstant(executionScope));
|
|
|
|
op->addIdOperand(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();
|
|
|
|
}
|
|
|
|
|
2018-08-14 19:31:43 +00:00
|
|
|
Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
|
|
|
|
{
|
|
|
|
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
|
|
|
|
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
|
|
|
|
if (it->isId)
|
|
|
|
op->addIdOperand(it->word);
|
|
|
|
else
|
|
|
|
op->addImmediateOperand(it->word);
|
|
|
|
}
|
|
|
|
buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
|
|
|
|
|
|
|
|
return op->getResultId();
|
|
|
|
}
|
|
|
|
|
2020-03-03 17:25:07 +00:00
|
|
|
Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,
|
|
|
|
const std::vector<unsigned>& literals)
|
2016-03-31 17:57:28 +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
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
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
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const 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
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
|
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);
|
2018-02-05 21:44:14 +00:00
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
assert(isVector(target));
|
|
|
|
swizzle->addIdOperand(target);
|
2018-02-05 21:44:14 +00:00
|
|
|
|
|
|
|
assert(getNumComponents(source) == (int)channels.size());
|
|
|
|
assert(isVector(source));
|
|
|
|
swizzle->addIdOperand(source);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
// 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
|
2018-02-05 21:44:14 +00:00
|
|
|
for (int i = 0; i < (int)channels.size(); ++i)
|
|
|
|
components[channels[i]] = numTargetComponents + i;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
// 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
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const 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.
|
2018-06-05 14:53:36 +00:00
|
|
|
Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
|
2019-03-31 16:51:57 +00:00
|
|
|
bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)
|
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;
|
2016-06-16 18:43:23 +00:00
|
|
|
if (parameters.Dref != NoResult)
|
2015-08-07 04:53:06 +00:00
|
|
|
texArgs[numArgs++] = parameters.Dref;
|
2016-06-16 18:43:23 +00:00
|
|
|
if (parameters.component != NoResult)
|
|
|
|
texArgs[numArgs++] = parameters.component;
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2019-08-07 07:52:20 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2018-09-19 18:41:27 +00:00
|
|
|
if (parameters.granularity != NoResult)
|
|
|
|
texArgs[numArgs++] = parameters.granularity;
|
|
|
|
if (parameters.coarse != NoResult)
|
|
|
|
texArgs[numArgs++] = parameters.coarse;
|
|
|
|
#endif
|
|
|
|
|
2015-08-07 04:53:06 +00:00
|
|
|
//
|
|
|
|
// Set up the optional arguments
|
|
|
|
//
|
2019-03-31 16:51:57 +00:00
|
|
|
int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
|
|
|
|
++numArgs; // speculatively make room for the mask operand
|
2015-08-07 04:53:06 +00:00
|
|
|
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) {
|
2018-06-05 14:53:36 +00:00
|
|
|
addCapability(CapabilityImageGatherExtended);
|
2015-08-07 04:53:06 +00:00
|
|
|
mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
|
|
|
|
texArgs[numArgs++] = parameters.offsets;
|
|
|
|
}
|
2019-08-11 13:41:45 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2015-08-07 04:53:06 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-09-05 15:11:41 +00:00
|
|
|
if (parameters.nonprivate) {
|
|
|
|
mask = mask | ImageOperandsNonPrivateTexelKHRMask;
|
|
|
|
}
|
|
|
|
if (parameters.volatil) {
|
|
|
|
mask = mask | ImageOperandsVolatileTexelKHRMask;
|
|
|
|
}
|
2019-08-11 13:41:45 +00:00
|
|
|
#endif
|
2019-03-31 16:51:57 +00:00
|
|
|
mask = mask | signExtensionMask;
|
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;
|
2019-08-06 13:00:58 +00:00
|
|
|
#ifndef GLSLANG_WEB
|
2018-09-19 18:41:27 +00:00
|
|
|
} else if (parameters.granularity && parameters.coarse) {
|
|
|
|
opCode = OpImageSampleFootprintNV;
|
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;
|
2019-08-08 07:15:24 +00:00
|
|
|
#endif
|
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
|
2017-03-10 19:45:50 +00:00
|
|
|
Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
|
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;
|
2017-03-10 19:45:50 +00:00
|
|
|
|
|
|
|
Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
|
2015-06-26 22:58:36 +00:00
|
|
|
if (numComponents == 1)
|
2017-03-10 19:45:50 +00:00
|
|
|
resultType = intType;
|
2015-06-26 22:58:36 +00:00
|
|
|
else
|
2017-03-10 19:45:50 +00:00
|
|
|
resultType = makeVectorType(intType, numComponents);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2015-08-07 04:53:06 +00:00
|
|
|
case OpImageQueryLod:
|
2016-11-29 09:36:31 +00:00
|
|
|
resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
|
2015-06-26 22:58:36 +00:00
|
|
|
break;
|
2015-08-07 04:53:06 +00:00
|
|
|
case OpImageQueryLevels:
|
|
|
|
case OpImageQuerySamples:
|
2017-03-10 19:45:50 +00:00
|
|
|
resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
|
2015-06-26 22:58:36 +00:00
|
|
|
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));
|
2019-07-28 08:12:10 +00:00
|
|
|
addCapability(CapabilityImageQuery);
|
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:
|
2020-06-05 12:32:51 +00:00
|
|
|
op = equal ? OpFOrdEqual : OpFUnordNotEqual;
|
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
|
2020-03-03 17:25:07 +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
|
2017-01-26 17:46:02 +00:00
|
|
|
Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2020-03-03 17:25:07 +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);
|
|
|
|
|
2017-02-18 02:06:21 +00:00
|
|
|
// accumulate the arguments for OpCompositeConstruct
|
|
|
|
std::vector<Id> constituents;
|
2015-06-26 22:58:36 +00:00
|
|
|
Id scalarTypeId = getScalarTypeId(resultTypeId);
|
2017-02-18 02:06:21 +00:00
|
|
|
|
|
|
|
// lambda to store the result of visiting an argument component
|
|
|
|
const auto latchResult = [&](Id comp) {
|
|
|
|
if (numTargetComponents > 1)
|
|
|
|
constituents.push_back(comp);
|
|
|
|
else
|
|
|
|
result = comp;
|
|
|
|
++targetComponent;
|
|
|
|
};
|
|
|
|
|
|
|
|
// lambda to visit a vector argument's components
|
|
|
|
const auto accumulateVectorConstituents = [&](Id sourceArg) {
|
|
|
|
unsigned int sourceSize = getNumComponents(sourceArg);
|
2015-06-26 22:58:36 +00:00
|
|
|
unsigned int sourcesToUse = sourceSize;
|
|
|
|
if (sourcesToUse + targetComponent > numTargetComponents)
|
|
|
|
sourcesToUse = numTargetComponents - targetComponent;
|
|
|
|
|
|
|
|
for (unsigned int s = 0; s < sourcesToUse; ++s) {
|
2017-02-18 02:06:21 +00:00
|
|
|
std::vector<unsigned> swiz;
|
|
|
|
swiz.push_back(s);
|
|
|
|
latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
|
|
|
|
}
|
|
|
|
};
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2017-02-18 02:06:21 +00:00
|
|
|
// lambda to visit a matrix argument's components
|
|
|
|
const auto accumulateMatrixConstituents = [&](Id sourceArg) {
|
|
|
|
unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
|
|
|
|
unsigned int sourcesToUse = sourceSize;
|
|
|
|
if (sourcesToUse + targetComponent > numTargetComponents)
|
|
|
|
sourcesToUse = numTargetComponents - targetComponent;
|
|
|
|
|
|
|
|
int col = 0;
|
|
|
|
int row = 0;
|
|
|
|
for (unsigned int s = 0; s < sourcesToUse; ++s) {
|
|
|
|
if (row >= getNumRows(sourceArg)) {
|
|
|
|
row = 0;
|
|
|
|
col++;
|
|
|
|
}
|
|
|
|
std::vector<Id> indexes;
|
|
|
|
indexes.push_back(col);
|
|
|
|
indexes.push_back(row);
|
|
|
|
latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
|
|
|
|
row++;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
2017-02-18 02:06:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Go through the source arguments, each one could have either
|
|
|
|
// a single or multiple components to contribute.
|
|
|
|
for (unsigned int i = 0; i < sources.size(); ++i) {
|
2019-01-06 23:58:04 +00:00
|
|
|
|
|
|
|
if (isScalar(sources[i]) || isPointer(sources[i]))
|
2017-02-18 02:06:21 +00:00
|
|
|
latchResult(sources[i]);
|
|
|
|
else if (isVector(sources[i]))
|
|
|
|
accumulateVectorConstituents(sources[i]);
|
|
|
|
else if (isMatrix(sources[i]))
|
|
|
|
accumulateMatrixConstituents(sources[i]);
|
|
|
|
else
|
|
|
|
assert(0);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
|
|
|
if (targetComponent >= numTargetComponents)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-02-18 02:06:21 +00:00
|
|
|
// If the result is a vector, make it from the gathered constituents.
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
|
|
|
|
2016-06-01 14:40:00 +00:00
|
|
|
Instruction* instr = module.getInstruction(componentTypeId);
|
2019-08-11 13:41:45 +00:00
|
|
|
#ifdef GLSLANG_WEB
|
|
|
|
const unsigned bitCount = 32;
|
2019-08-13 14:00:30 +00:00
|
|
|
assert(bitCount == instr->getImmediateOperand(0));
|
2019-08-11 13:41:45 +00:00
|
|
|
#else
|
|
|
|
const unsigned bitCount = instr->getImmediateOperand(0);
|
|
|
|
#endif
|
2016-06-01 14:40:00 +00:00
|
|
|
|
2018-07-20 21:28:38 +00:00
|
|
|
// Optimize matrix constructed from a bigger matrix
|
2018-07-05 20:19:39 +00:00
|
|
|
if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
|
|
|
|
// To truncate the matrix to a smaller number of rows/columns, we need to:
|
|
|
|
// 1. For each column, extract the column and truncate it to the required size using shuffle
|
|
|
|
// 2. Assemble the resulting matrix from all columns
|
|
|
|
Id matrix = sources[0];
|
|
|
|
Id columnTypeId = getContainedTypeId(resultTypeId);
|
|
|
|
Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
|
|
|
|
|
|
|
|
std::vector<unsigned> channels;
|
2018-07-20 21:28:38 +00:00
|
|
|
for (int row = 0; row < numRows; ++row)
|
2018-07-05 20:19:39 +00:00
|
|
|
channels.push_back(row);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2018-07-05 20:19:39 +00:00
|
|
|
std::vector<Id> matrixColumns;
|
|
|
|
for (int col = 0; col < numCols; ++col) {
|
2015-06-26 22:58:36 +00:00
|
|
|
std::vector<unsigned> indexes;
|
|
|
|
indexes.push_back(col);
|
2018-07-05 20:19:39 +00:00
|
|
|
Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
|
|
|
|
setPrecision(colv, precision);
|
|
|
|
|
|
|
|
if (numRows != getNumRows(matrix)) {
|
|
|
|
matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
|
|
|
|
} else {
|
|
|
|
matrixColumns.push_back(colv);
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-05 20:19:39 +00:00
|
|
|
|
|
|
|
return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
|
2018-07-20 21:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, will use a two step process
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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];
|
2016-06-01 14:40:00 +00:00
|
|
|
Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
|
|
|
|
Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
|
2015-06-26 22:58:36 +00:00
|
|
|
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;
|
|
|
|
|
Fix OOB write in matrix constructor
In a matrix constructor that takes a number of components, as many
components as necessary must be taken, with the rest discarded, as GLSL
allows more components than necessary to be specified. For example, the
following:
mat4 m4 = mat4(v4, v4.yzwx, v4.zwx, v4.zwxy, v4.wxyz);
is equivalent to:
mat4 m4 = mat4(v4, v4.yzwx, v4.zwx, v4.zwxy, v4.w);
glslang takes the components from the constructor and builds the single
components of the matrix in a 2D array before constructing the matrix
itself. It however did not check for extra parameters and was thus
writing OOB to said 2D array. This is fixed in this change
Signed-off-by: Shahbaz Youssefi <shabbyx@gmail.com>
2021-06-03 17:42:57 +00:00
|
|
|
for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) {
|
2015-06-26 22:58:36 +00:00
|
|
|
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++;
|
|
|
|
}
|
Fix OOB write in matrix constructor
In a matrix constructor that takes a number of components, as many
components as necessary must be taken, with the rest discarded, as GLSL
allows more components than necessary to be specified. For example, the
following:
mat4 m4 = mat4(v4, v4.yzwx, v4.zwx, v4.zwxy, v4.wxyz);
is equivalent to:
mat4 m4 = mat4(v4, v4.yzwx, v4.zwx, v4.zwxy, v4.w);
glslang takes the components from the constructor and builds the single
components of the matrix in a 2D array before constructing the matrix
itself. It however did not check for extra parameters and was thus
writing OOB to said 2D array. This is fixed in this change
Signed-off-by: Shahbaz Youssefi <shabbyx@gmail.com>
2021-06-03 17:42:57 +00:00
|
|
|
if (col == numCols) {
|
|
|
|
// If more components are provided than fit the matrix, discard the rest.
|
|
|
|
break;
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-07-04 15:23:40 +00:00
|
|
|
Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
|
2015-06-26 22:58:36 +00:00
|
|
|
builder(gb),
|
|
|
|
condition(cond),
|
2017-07-04 15:23:40 +00:00
|
|
|
control(ctrl),
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
2017-07-04 15:23:40 +00:00
|
|
|
builder.createSelectionMerge(mergeBlock, control);
|
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
|
2017-07-04 15:23:40 +00:00
|
|
|
void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
|
2017-01-26 17:46:02 +00:00
|
|
|
const std::vector<int>& valueIndexToSegment, int defaultSegment,
|
2015-06-26 22:58:36 +00:00
|
|
|
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
|
2017-07-04 15:23:40 +00:00
|
|
|
createSelectionMerge(mergeBlock, control);
|
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-07-09 04:09:10 +00:00
|
|
|
// This verbosity is needed to simultaneously get the same behavior
|
|
|
|
// everywhere (id's in the same order), have a syntax that works
|
|
|
|
// across lots of versions of C++, have no warnings from pedantic
|
|
|
|
// compilation modes, and leave the rest of the code alone.
|
|
|
|
Block& head = makeNewBlock();
|
|
|
|
Block& body = makeNewBlock();
|
|
|
|
Block& merge = makeNewBlock();
|
|
|
|
Block& continue_target = makeNewBlock();
|
|
|
|
LoopBlocks blocks(head, body, merge, continue_target);
|
2016-01-20 15:19:25 +00:00
|
|
|
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;
|
2018-09-05 15:11:41 +00:00
|
|
|
accessChain.coherentFlags.clear();
|
2019-01-06 23:58:04 +00:00
|
|
|
accessChain.alignment = 0;
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2020-03-03 17:25:07 +00:00
|
|
|
void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
|
|
|
|
AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
2019-01-06 23:58:04 +00:00
|
|
|
accessChain.coherentFlags |= coherentFlags;
|
|
|
|
accessChain.alignment |= alignment;
|
|
|
|
|
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
|
2018-02-05 21:44:14 +00:00
|
|
|
if (accessChain.swizzle.size() > 0) {
|
2015-06-26 22:58:36 +00:00
|
|
|
std::vector<unsigned> oldSwizzle = accessChain.swizzle;
|
|
|
|
accessChain.swizzle.resize(0);
|
|
|
|
for (unsigned int i = 0; i < swizzle.size(); ++i) {
|
2016-12-15 00:02:32 +00:00
|
|
|
assert(swizzle[i] < oldSwizzle.size());
|
2015-06-26 22:58:36 +00:00
|
|
|
accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
accessChain.swizzle = swizzle;
|
|
|
|
|
|
|
|
// determine if we need to track this swizzle anymore
|
|
|
|
simplifyAccessChainSwizzle();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2020-08-24 21:27:26 +00:00
|
|
|
void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
assert(accessChain.isRValue == false);
|
|
|
|
|
2015-11-16 04:33:39 +00:00
|
|
|
transferAccessChainSwizzle(true);
|
2020-08-24 21:27:26 +00:00
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
// If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.
|
|
|
|
if (accessChain.swizzle.size() > 0 &&
|
|
|
|
getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&
|
|
|
|
accessChain.component == NoResult) {
|
|
|
|
for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
|
|
|
|
accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));
|
2021-08-09 23:52:40 +00:00
|
|
|
accessChain.instr = NoResult;
|
2018-02-05 21:44:14 +00:00
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
Id base = collapseAccessChain();
|
|
|
|
addDecoration(base, nonUniform);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
accessChain.indexChain.pop_back();
|
|
|
|
accessChain.instr = NoResult;
|
|
|
|
|
|
|
|
// dynamic component should be gone
|
|
|
|
assert(accessChain.component == NoResult);
|
|
|
|
|
|
|
|
Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);
|
2015-06-26 22:58:36 +00:00
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
// take LSB of alignment
|
|
|
|
alignment = alignment & ~(alignment & (alignment-1));
|
|
|
|
if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
|
|
|
|
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
createStore(source, base, memoryAccess, scope, alignment);
|
|
|
|
}
|
2019-01-06 23:58:04 +00:00
|
|
|
}
|
2021-07-16 21:07:16 +00:00
|
|
|
else {
|
|
|
|
Id base = collapseAccessChain();
|
|
|
|
addDecoration(base, nonUniform);
|
2019-01-06 23:58:04 +00:00
|
|
|
|
2021-07-16 21:07:16 +00:00
|
|
|
Id source = rvalue;
|
|
|
|
|
|
|
|
// dynamic component should be gone
|
|
|
|
assert(accessChain.component == NoResult);
|
|
|
|
|
|
|
|
// If swizzle still exists, it may be out-of-order, we must load the target vector,
|
|
|
|
// extract and insert elements to perform writeMask and/or swizzle.
|
|
|
|
if (accessChain.swizzle.size() > 0) {
|
|
|
|
Id tempBaseId = createLoad(base, spv::NoPrecision);
|
|
|
|
source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
|
|
|
|
}
|
|
|
|
|
|
|
|
// take LSB of alignment
|
|
|
|
alignment = alignment & ~(alignment & (alignment-1));
|
|
|
|
if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
|
|
|
|
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
createStore(source, base, memoryAccess, scope, alignment);
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Comments in header
|
2020-11-12 18:10:07 +00:00
|
|
|
Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,
|
|
|
|
Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,
|
|
|
|
spv::Scope scope, unsigned int alignment)
|
2015-06-26 22:58:36 +00:00
|
|
|
{
|
|
|
|
Id id;
|
|
|
|
|
|
|
|
if (accessChain.isRValue) {
|
2018-02-05 21:44:14 +00:00
|
|
|
// transfer access chain, but try to stay in registers
|
2015-11-16 04:33:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-19 19:10:32 +00:00
|
|
|
if (constant) {
|
2015-09-13 20:46:30 +00:00
|
|
|
id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
|
2020-06-30 07:27:08 +00:00
|
|
|
setPrecision(id, precision);
|
2019-02-19 19:10:32 +00:00
|
|
|
} else {
|
2019-02-07 15:04:12 +00:00
|
|
|
Id lValue = NoResult;
|
2020-07-13 06:35:58 +00:00
|
|
|
if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) {
|
2019-02-07 15:04:12 +00:00
|
|
|
// make a new function variable for this r-value, using an initializer,
|
|
|
|
// and mark it as NonWritable so that downstream it can be detected as a lookup
|
|
|
|
// table
|
2020-07-13 08:52:11 +00:00
|
|
|
lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
|
|
|
|
"indexable", accessChain.base);
|
2019-02-07 15:04:12 +00:00
|
|
|
addDecoration(lValue, DecorationNonWritable);
|
|
|
|
} else {
|
2020-07-13 08:52:11 +00:00
|
|
|
lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
|
|
|
|
"indexable");
|
2019-02-07 15:04:12 +00:00
|
|
|
// store into it
|
|
|
|
createStore(accessChain.base, lValue);
|
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
// move base to the new variable
|
|
|
|
accessChain.base = lValue;
|
|
|
|
accessChain.isRValue = false;
|
|
|
|
|
|
|
|
// load through the access chain
|
2020-06-30 07:27:08 +00:00
|
|
|
id = createLoad(collapseAccessChain(), 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);
|
2019-01-06 23:58:04 +00:00
|
|
|
|
|
|
|
// take LSB of alignment
|
|
|
|
alignment = alignment & ~(alignment & (alignment-1));
|
|
|
|
if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) {
|
|
|
|
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
// load through the access chain
|
2020-03-09 16:31:15 +00:00
|
|
|
id = collapseAccessChain();
|
|
|
|
// Apply nonuniform both to the access chain and the loaded value.
|
|
|
|
// Buffer accesses need the access chain decorated, and this is where
|
|
|
|
// loaded image types get decorated. TODO: This should maybe move to
|
|
|
|
// createImageTextureFunctionCall.
|
2020-11-12 18:10:07 +00:00
|
|
|
addDecoration(id, l_nonUniform);
|
2020-06-30 07:27:08 +00:00
|
|
|
id = createLoad(id, precision, memoryAccess, scope, alignment);
|
2020-11-12 18:10:07 +00:00
|
|
|
addDecoration(id, r_nonUniform);
|
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
|
2018-02-05 21:44:14 +00:00
|
|
|
|
|
|
|
// Do the basic swizzle
|
|
|
|
if (accessChain.swizzle.size() > 0) {
|
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
|
|
|
}
|
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// Do the dynamic component
|
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
|
|
|
|
2020-11-12 18:10:07 +00:00
|
|
|
addDecoration(id, r_nonUniform);
|
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;
|
|
|
|
}
|
|
|
|
|
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);
|
2018-02-01 01:35:56 +00:00
|
|
|
out.push_back(spvVersion);
|
2015-06-26 22:58:36 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-09-21 10:56:12 +00:00
|
|
|
for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
|
2016-05-05 04:30:44 +00:00
|
|
|
Instruction extInst(0, 0, OpExtension);
|
2016-10-14 09:22:23 +00:00
|
|
|
extInst.addStringOperand(it->c_str());
|
2016-05-05 04:30:44 +00:00
|
|
|
extInst.dump(out);
|
|
|
|
}
|
2015-11-16 04:33:39 +00:00
|
|
|
|
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
|
2017-05-31 23:11:16 +00:00
|
|
|
dumpInstructions(out, strings);
|
|
|
|
dumpSourceInstructions(out);
|
2016-05-05 04:30:44 +00:00
|
|
|
for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
|
|
|
|
Instruction sourceExtInst(0, 0, OpSourceExtension);
|
|
|
|
sourceExtInst.addStringOperand(sourceExtensions[e]);
|
|
|
|
sourceExtInst.dump(out);
|
2015-11-16 04:33:39 +00:00
|
|
|
}
|
2015-06-26 22:58:36 +00:00
|
|
|
dumpInstructions(out, names);
|
2018-02-23 02:36:18 +00:00
|
|
|
dumpModuleProcesses(out);
|
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.
|
|
|
|
//
|
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// Turn the described access chain in 'accessChain' into an instruction(s)
|
2015-11-16 04:33:39 +00:00
|
|
|
// computing its address. This *cannot* include complex swizzles, which must
|
2018-02-05 21:44:14 +00:00
|
|
|
// be handled after this is called.
|
|
|
|
//
|
|
|
|
// Can generate code.
|
2015-06-26 22:58:36 +00:00
|
|
|
Id Builder::collapseAccessChain()
|
|
|
|
{
|
|
|
|
assert(accessChain.isRValue == false);
|
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// did we already emit an access chain for this?
|
|
|
|
if (accessChain.instr != NoResult)
|
2015-06-26 22:58:36 +00:00
|
|
|
return accessChain.instr;
|
2018-02-05 21:44:14 +00:00
|
|
|
|
|
|
|
// If we have a dynamic component, we can still transfer
|
|
|
|
// that into a final operand to the access chain. We need to remap the
|
|
|
|
// dynamic component through the swizzle to get a new dynamic component to
|
|
|
|
// update.
|
|
|
|
//
|
|
|
|
// This was not done in transferAccessChainSwizzle() because it might
|
|
|
|
// generate code.
|
|
|
|
remapDynamicSwizzle();
|
|
|
|
if (accessChain.component != NoResult) {
|
|
|
|
// transfer the dynamic component to the access chain
|
|
|
|
accessChain.indexChain.push_back(accessChain.component);
|
|
|
|
accessChain.component = NoResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// note that non-trivial swizzling is left pending
|
|
|
|
|
|
|
|
// do we have an access chain?
|
|
|
|
if (accessChain.indexChain.size() == 0)
|
2015-06-26 22:58:36 +00:00
|
|
|
return accessChain.base;
|
2015-11-16 04:33:39 +00:00
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// emit the access chain
|
|
|
|
StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
|
|
|
|
accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
|
|
|
|
|
|
|
|
return accessChain.instr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For a dynamic component selection of a swizzle.
|
|
|
|
//
|
|
|
|
// Turn the swizzle and dynamic component into just a dynamic component.
|
|
|
|
//
|
|
|
|
// Generates code.
|
|
|
|
void Builder::remapDynamicSwizzle()
|
|
|
|
{
|
|
|
|
// do we have a swizzle to remap a dynamic component through?
|
|
|
|
if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
|
|
|
|
// build a vector of the swizzle for the component to map into
|
|
|
|
std::vector<Id> components;
|
2018-03-13 21:06:51 +00:00
|
|
|
for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
|
2018-02-05 21:44:14 +00:00
|
|
|
components.push_back(makeUintConstant(accessChain.swizzle[c]));
|
|
|
|
Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
|
|
|
|
Id map = makeCompositeConstant(mapType, components);
|
|
|
|
|
|
|
|
// use it
|
|
|
|
accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
|
|
|
|
accessChain.swizzle.clear();
|
|
|
|
}
|
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.
|
2018-02-05 21:44:14 +00:00
|
|
|
// If 'dynamic' is true, include transferring the dynamic component,
|
|
|
|
// otherwise, leave it pending.
|
2015-11-16 04:33:39 +00:00
|
|
|
//
|
2018-02-05 21:44:14 +00:00
|
|
|
// Does not generate code. just updates the access chain.
|
2015-11-16 04:33:39 +00:00
|
|
|
void Builder::transferAccessChainSwizzle(bool dynamic)
|
|
|
|
{
|
|
|
|
// non existent?
|
|
|
|
if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
|
2015-06-26 22:58:36 +00:00
|
|
|
return;
|
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// too complex?
|
|
|
|
// (this requires either a swizzle, or generating code for a dynamic component)
|
|
|
|
if (accessChain.swizzle.size() > 1)
|
2015-11-16 04:33:39 +00:00
|
|
|
return;
|
|
|
|
|
2018-02-05 21:44:14 +00:00
|
|
|
// single component, either in the swizzle and/or dynamic component
|
2015-11-16 04:33:39 +00:00
|
|
|
if (accessChain.swizzle.size() == 1) {
|
2018-02-05 21:44:14 +00:00
|
|
|
assert(accessChain.component == NoResult);
|
|
|
|
// handle static component selection
|
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();
|
|
|
|
accessChain.preSwizzleBaseType = NoType;
|
|
|
|
} else if (dynamic && accessChain.component != NoResult) {
|
2018-02-05 21:44:14 +00:00
|
|
|
assert(accessChain.swizzle.size() == 0);
|
2015-11-16 04:33:39 +00:00
|
|
|
// 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);
|
|
|
|
|
2017-01-06 19:34:14 +00:00
|
|
|
// if (name)
|
2015-06-26 22:58:36 +00:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2018-01-31 15:11:18 +00:00
|
|
|
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
|
2019-01-12 10:31:41 +00:00
|
|
|
const std::vector<unsigned int>& operands)
|
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);
|
2019-01-12 10:31:41 +00:00
|
|
|
for (int op = 0; op < (int)operands.size(); ++op)
|
|
|
|
merge->addImmediateOperand(operands[op]);
|
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);
|
|
|
|
}
|
|
|
|
|
2017-05-31 23:11:16 +00:00
|
|
|
// OpSource
|
|
|
|
// [OpSourceContinued]
|
|
|
|
// ...
|
2018-12-06 18:13:15 +00:00
|
|
|
void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
|
|
|
|
std::vector<unsigned int>& out) const
|
2017-05-31 23:11:16 +00:00
|
|
|
{
|
|
|
|
const int maxWordCount = 0xFFFF;
|
|
|
|
const int opSourceWordCount = 4;
|
|
|
|
const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
|
|
|
|
|
|
|
|
if (source != SourceLanguageUnknown) {
|
|
|
|
// OpSource Language Version File Source
|
|
|
|
Instruction sourceInst(NoResult, NoType, OpSource);
|
|
|
|
sourceInst.addImmediateOperand(source);
|
|
|
|
sourceInst.addImmediateOperand(sourceVersion);
|
|
|
|
// File operand
|
2018-12-06 18:13:15 +00:00
|
|
|
if (fileId != NoResult) {
|
|
|
|
sourceInst.addIdOperand(fileId);
|
2017-05-31 23:11:16 +00:00
|
|
|
// Source operand
|
2018-12-06 18:13:15 +00:00
|
|
|
if (text.size() > 0) {
|
2017-05-31 23:11:16 +00:00
|
|
|
int nextByte = 0;
|
|
|
|
std::string subString;
|
2018-12-06 18:13:15 +00:00
|
|
|
while ((int)text.size() - nextByte > 0) {
|
|
|
|
subString = text.substr(nextByte, nonNullBytesPerInstruction);
|
2017-05-31 23:11:16 +00:00
|
|
|
if (nextByte == 0) {
|
|
|
|
// OpSource
|
|
|
|
sourceInst.addStringOperand(subString.c_str());
|
|
|
|
sourceInst.dump(out);
|
|
|
|
} else {
|
|
|
|
// OpSourcContinued
|
|
|
|
Instruction sourceContinuedInst(OpSourceContinued);
|
|
|
|
sourceContinuedInst.addStringOperand(subString.c_str());
|
|
|
|
sourceContinuedInst.dump(out);
|
|
|
|
}
|
|
|
|
nextByte += nonNullBytesPerInstruction;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sourceInst.dump(out);
|
|
|
|
} else
|
|
|
|
sourceInst.dump(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-06 18:13:15 +00:00
|
|
|
// Dump an OpSource[Continued] sequence for the source and every include file
|
|
|
|
void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
|
|
|
|
{
|
|
|
|
dumpSourceInstructions(sourceFileStringId, sourceText, out);
|
|
|
|
for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
|
|
|
|
dumpSourceInstructions(iItr->first, *iItr->second, out);
|
|
|
|
}
|
|
|
|
|
2020-03-03 17:25:07 +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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 02:00:36 +00:00
|
|
|
void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
|
|
|
|
Instruction moduleProcessed(OpModuleProcessed);
|
|
|
|
moduleProcessed.addStringOperand(moduleProcesses[i]);
|
|
|
|
moduleProcessed.dump(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-26 22:58:36 +00:00
|
|
|
}; // end spv namespace
|