2019-02-20 20:21:57 +00:00
//
// Copyright (C) 2014-2015 LunarG, Inc.
2019-04-15 02:53:43 +00:00
// Copyright (C) 2015-2018 Google, Inc.
2019-02-20 20:21:57 +00:00
// Copyright (C) 2017 ARM Limited.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// "Builder" is an interface to fully build SPIR-V IR. Allocate one of
// these to build (a thread safe) internal SPIR-V representation (IR),
// and then dump it as a binary stream according to the SPIR-V specification.
//
// A Builder has a 1:1 relationship with a SPIR-V module.
//
# pragma once
# ifndef SpvBuilder_H
# define SpvBuilder_H
# include "Logger.h"
# include "spirv.hpp"
# include "spvIR.h"
# include <algorithm>
# include <map>
# include <memory>
# include <set>
# include <sstream>
# include <stack>
# include <unordered_map>
2019-04-15 02:53:43 +00:00
# include <map>
2019-02-20 20:21:57 +00:00
namespace spv {
2020-02-15 07:51:03 +00:00
typedef enum {
Spv_1_0 = ( 1 < < 16 ) ,
Spv_1_1 = ( 1 < < 16 ) | ( 1 < < 8 ) ,
Spv_1_2 = ( 1 < < 16 ) | ( 2 < < 8 ) ,
Spv_1_3 = ( 1 < < 16 ) | ( 3 < < 8 ) ,
Spv_1_4 = ( 1 < < 16 ) | ( 4 < < 8 ) ,
Spv_1_5 = ( 1 < < 16 ) | ( 5 < < 8 ) ,
} SpvVersion ;
2019-02-20 20:21:57 +00:00
class Builder {
public :
Builder ( unsigned int spvVersion , unsigned int userNumber , SpvBuildLogger * logger ) ;
virtual ~ Builder ( ) ;
static const int maxMatrixSize = 4 ;
unsigned int getSpvVersion ( ) const { return spvVersion ; }
void setSource ( spv : : SourceLanguage lang , int version )
{
source = lang ;
sourceVersion = version ;
}
2019-04-15 02:53:43 +00:00
spv : : Id getStringId ( const std : : string & str )
2019-02-20 20:21:57 +00:00
{
2019-04-15 02:53:43 +00:00
auto sItr = stringIds . find ( str ) ;
if ( sItr ! = stringIds . end ( ) )
return sItr - > second ;
spv : : Id strId = getUniqueId ( ) ;
Instruction * fileString = new Instruction ( strId , NoType , OpString ) ;
const char * file_c_str = str . c_str ( ) ;
fileString - > addStringOperand ( file_c_str ) ;
2019-02-20 20:21:57 +00:00
strings . push_back ( std : : unique_ptr < Instruction > ( fileString ) ) ;
2019-04-15 02:53:43 +00:00
stringIds [ file_c_str ] = strId ;
return strId ;
}
void setSourceFile ( const std : : string & file )
{
sourceFileStringId = getStringId ( file ) ;
2019-02-20 20:21:57 +00:00
}
void setSourceText ( const std : : string & text ) { sourceText = text ; }
void addSourceExtension ( const char * ext ) { sourceExtensions . push_back ( ext ) ; }
void addModuleProcessed ( const std : : string & p ) { moduleProcesses . push_back ( p . c_str ( ) ) ; }
void setEmitOpLines ( ) { emitOpLines = true ; }
void addExtension ( const char * ext ) { extensions . insert ( ext ) ; }
2020-02-15 07:51:03 +00:00
void removeExtension ( const char * ext )
{
extensions . erase ( ext ) ;
}
void addIncorporatedExtension ( const char * ext , SpvVersion incorporatedVersion )
{
if ( getSpvVersion ( ) < static_cast < unsigned > ( incorporatedVersion ) )
addExtension ( ext ) ;
}
void promoteIncorporatedExtension ( const char * baseExt , const char * promoExt , SpvVersion incorporatedVersion )
{
removeExtension ( baseExt ) ;
addIncorporatedExtension ( promoExt , incorporatedVersion ) ;
}
2019-04-15 02:53:43 +00:00
void addInclude ( const std : : string & name , const std : : string & text )
{
spv : : Id incId = getStringId ( name ) ;
includeFiles [ incId ] = & text ;
}
2019-02-20 20:21:57 +00:00
Id import ( const char * ) ;
void setMemoryModel ( spv : : AddressingModel addr , spv : : MemoryModel mem )
{
addressModel = addr ;
memoryModel = mem ;
}
void addCapability ( spv : : Capability cap ) { capabilities . insert ( cap ) ; }
// To get a new <id> for anything needing a new one.
Id getUniqueId ( ) { return + + uniqueId ; }
// To get a set of new <id>s, e.g., for a set of function parameters
Id getUniqueIds ( int numIds )
{
Id id = uniqueId + 1 ;
uniqueId + = numIds ;
return id ;
}
2019-04-15 02:53:43 +00:00
// Generate OpLine for non-filename-based #line directives (ie no filename
// seen yet): Log the current line, and if different than the last one,
// issue a new OpLine using the new line and current source file name.
2019-02-20 20:21:57 +00:00
void setLine ( int line ) ;
2019-04-15 02:53:43 +00:00
// If filename null, generate OpLine for non-filename-based line directives,
// else do filename-based: Log the current line and file, and if different
// than the last one, issue a new OpLine using the new line and file
// name.
void setLine ( int line , const char * filename ) ;
2019-02-20 20:21:57 +00:00
// Low-level OpLine. See setLine() for a layered helper.
void addLine ( Id fileName , int line , int column ) ;
// For creating new types (will return old type if the requested one was already made).
Id makeVoidType ( ) ;
Id makeBoolType ( ) ;
2019-04-15 02:53:43 +00:00
Id makePointer ( StorageClass , Id pointee ) ;
Id makeForwardPointer ( StorageClass ) ;
Id makePointerFromForwardPointer ( StorageClass , Id forwardPointerType , Id pointee ) ;
2019-02-20 20:21:57 +00:00
Id makeIntegerType ( int width , bool hasSign ) ; // generic
Id makeIntType ( int width ) { return makeIntegerType ( width , true ) ; }
Id makeUintType ( int width ) { return makeIntegerType ( width , false ) ; }
Id makeFloatType ( int width ) ;
Id makeStructType ( const std : : vector < Id > & members , const char * ) ;
Id makeStructResultType ( Id type0 , Id type1 ) ;
Id makeVectorType ( Id component , int size ) ;
Id makeMatrixType ( Id component , int cols , int rows ) ;
Id makeArrayType ( Id element , Id sizeId , int stride ) ; // 0 stride means no stride decoration
Id makeRuntimeArray ( Id element ) ;
Id makeFunctionType ( Id returnType , const std : : vector < Id > & paramTypes ) ;
Id makeImageType ( Id sampledType , Dim , bool depth , bool arrayed , bool ms , unsigned sampled , ImageFormat format ) ;
Id makeSamplerType ( ) ;
Id makeSampledImageType ( Id imageType ) ;
2020-02-15 07:51:03 +00:00
Id makeCooperativeMatrixType ( Id component , Id scope , Id rows , Id cols ) ;
2019-02-20 20:21:57 +00:00
2019-04-15 02:53:43 +00:00
// accelerationStructureNV type
Id makeAccelerationStructureNVType ( ) ;
2019-02-20 20:21:57 +00:00
// For querying about types.
Id getTypeId ( Id resultId ) const { return module . getTypeId ( resultId ) ; }
Id getDerefTypeId ( Id resultId ) const ;
Op getOpCode ( Id id ) const { return module . getInstruction ( id ) - > getOpCode ( ) ; }
Op getTypeClass ( Id typeId ) const { return getOpCode ( typeId ) ; }
Op getMostBasicTypeClass ( Id typeId ) const ;
int getNumComponents ( Id resultId ) const { return getNumTypeComponents ( getTypeId ( resultId ) ) ; }
int getNumTypeConstituents ( Id typeId ) const ;
int getNumTypeComponents ( Id typeId ) const { return getNumTypeConstituents ( typeId ) ; }
Id getScalarTypeId ( Id typeId ) const ;
Id getContainedTypeId ( Id typeId ) const ;
Id getContainedTypeId ( Id typeId , int ) const ;
StorageClass getTypeStorageClass ( Id typeId ) const { return module . getStorageClass ( typeId ) ; }
ImageFormat getImageTypeFormat ( Id typeId ) const { return ( ImageFormat ) module . getInstruction ( typeId ) - > getImmediateOperand ( 6 ) ; }
bool isPointer ( Id resultId ) const { return isPointerType ( getTypeId ( resultId ) ) ; }
bool isScalar ( Id resultId ) const { return isScalarType ( getTypeId ( resultId ) ) ; }
bool isVector ( Id resultId ) const { return isVectorType ( getTypeId ( resultId ) ) ; }
bool isMatrix ( Id resultId ) const { return isMatrixType ( getTypeId ( resultId ) ) ; }
2020-02-15 07:51:03 +00:00
bool isCooperativeMatrix ( Id resultId ) const { return isCooperativeMatrixType ( getTypeId ( resultId ) ) ; }
2019-02-20 20:21:57 +00:00
bool isAggregate ( Id resultId ) const { return isAggregateType ( getTypeId ( resultId ) ) ; }
bool isSampledImage ( Id resultId ) const { return isSampledImageType ( getTypeId ( resultId ) ) ; }
bool isBoolType ( Id typeId ) { return groupedTypes [ OpTypeBool ] . size ( ) > 0 & & typeId = = groupedTypes [ OpTypeBool ] . back ( ) - > getResultId ( ) ; }
bool isIntType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeInt & & module . getInstruction ( typeId ) - > getImmediateOperand ( 1 ) ! = 0 ; }
bool isUintType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeInt & & module . getInstruction ( typeId ) - > getImmediateOperand ( 1 ) = = 0 ; }
bool isFloatType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeFloat ; }
bool isPointerType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypePointer ; }
bool isScalarType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeFloat | | getTypeClass ( typeId ) = = OpTypeInt | | getTypeClass ( typeId ) = = OpTypeBool ; }
bool isVectorType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeVector ; }
bool isMatrixType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeMatrix ; }
bool isStructType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeStruct ; }
bool isArrayType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeArray ; }
2020-02-15 07:51:03 +00:00
# ifdef GLSLANG_WEB
bool isCooperativeMatrixType ( Id typeId ) const { return false ; }
# else
bool isCooperativeMatrixType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeCooperativeMatrixNV ; }
# endif
bool isAggregateType ( Id typeId ) const { return isArrayType ( typeId ) | | isStructType ( typeId ) | | isCooperativeMatrixType ( typeId ) ; }
2019-02-20 20:21:57 +00:00
bool isImageType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeImage ; }
bool isSamplerType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeSampler ; }
bool isSampledImageType ( Id typeId ) const { return getTypeClass ( typeId ) = = OpTypeSampledImage ; }
2019-04-15 02:53:43 +00:00
bool containsType ( Id typeId , Op typeOp , unsigned int width ) const ;
bool containsPhysicalStorageBufferOrArray ( Id typeId ) const ;
2019-02-20 20:21:57 +00:00
bool isConstantOpCode ( Op opcode ) const ;
bool isSpecConstantOpCode ( Op opcode ) const ;
bool isConstant ( Id resultId ) const { return isConstantOpCode ( getOpCode ( resultId ) ) ; }
bool isConstantScalar ( Id resultId ) const { return getOpCode ( resultId ) = = OpConstant ; }
bool isSpecConstant ( Id resultId ) const { return isSpecConstantOpCode ( getOpCode ( resultId ) ) ; }
unsigned int getConstantScalar ( Id resultId ) const { return module . getInstruction ( resultId ) - > getImmediateOperand ( 0 ) ; }
StorageClass getStorageClass ( Id resultId ) const { return getTypeStorageClass ( getTypeId ( resultId ) ) ; }
int getScalarTypeWidth ( Id typeId ) const
{
Id scalarTypeId = getScalarTypeId ( typeId ) ;
assert ( getTypeClass ( scalarTypeId ) = = OpTypeInt | | getTypeClass ( scalarTypeId ) = = OpTypeFloat ) ;
return module . getInstruction ( scalarTypeId ) - > getImmediateOperand ( 0 ) ;
}
int getTypeNumColumns ( Id typeId ) const
{
assert ( isMatrixType ( typeId ) ) ;
return getNumTypeConstituents ( typeId ) ;
}
int getNumColumns ( Id resultId ) const { return getTypeNumColumns ( getTypeId ( resultId ) ) ; }
int getTypeNumRows ( Id typeId ) const
{
assert ( isMatrixType ( typeId ) ) ;
return getNumTypeComponents ( getContainedTypeId ( typeId ) ) ;
}
int getNumRows ( Id resultId ) const { return getTypeNumRows ( getTypeId ( resultId ) ) ; }
Dim getTypeDimensionality ( Id typeId ) const
{
assert ( isImageType ( typeId ) ) ;
return ( Dim ) module . getInstruction ( typeId ) - > getImmediateOperand ( 1 ) ;
}
Id getImageType ( Id resultId ) const
{
Id typeId = getTypeId ( resultId ) ;
assert ( isImageType ( typeId ) | | isSampledImageType ( typeId ) ) ;
return isSampledImageType ( typeId ) ? module . getInstruction ( typeId ) - > getIdOperand ( 0 ) : typeId ;
}
bool isArrayedImageType ( Id typeId ) const
{
assert ( isImageType ( typeId ) ) ;
return module . getInstruction ( typeId ) - > getImmediateOperand ( 3 ) ! = 0 ;
}
// For making new constants (will return old constant if the requested one was already made).
Id makeBoolConstant ( bool b , bool specConstant = false ) ;
Id makeInt8Constant ( int i , bool specConstant = false ) { return makeIntConstant ( makeIntType ( 8 ) , ( unsigned ) i , specConstant ) ; }
Id makeUint8Constant ( unsigned u , bool specConstant = false ) { return makeIntConstant ( makeUintType ( 8 ) , u , specConstant ) ; }
Id makeInt16Constant ( int i , bool specConstant = false ) { return makeIntConstant ( makeIntType ( 16 ) , ( unsigned ) i , specConstant ) ; }
Id makeUint16Constant ( unsigned u , bool specConstant = false ) { return makeIntConstant ( makeUintType ( 16 ) , u , specConstant ) ; }
Id makeIntConstant ( int i , bool specConstant = false ) { return makeIntConstant ( makeIntType ( 32 ) , ( unsigned ) i , specConstant ) ; }
Id makeUintConstant ( unsigned u , bool specConstant = false ) { return makeIntConstant ( makeUintType ( 32 ) , u , specConstant ) ; }
Id makeInt64Constant ( long long i , bool specConstant = false ) { return makeInt64Constant ( makeIntType ( 64 ) , ( unsigned long long ) i , specConstant ) ; }
Id makeUint64Constant ( unsigned long long u , bool specConstant = false ) { return makeInt64Constant ( makeUintType ( 64 ) , u , specConstant ) ; }
Id makeFloatConstant ( float f , bool specConstant = false ) ;
Id makeDoubleConstant ( double d , bool specConstant = false ) ;
Id makeFloat16Constant ( float f16 , bool specConstant = false ) ;
Id makeFpConstant ( Id type , double d , bool specConstant = false ) ;
// Turn the array of constants into a proper spv constant of the requested type.
Id makeCompositeConstant ( Id type , const std : : vector < Id > & comps , bool specConst = false ) ;
// Methods for adding information outside the CFG.
Instruction * addEntryPoint ( ExecutionModel , Function * , const char * name ) ;
void addExecutionMode ( Function * , ExecutionMode mode , int value1 = - 1 , int value2 = - 1 , int value3 = - 1 ) ;
void addName ( Id , const char * name ) ;
void addMemberName ( Id , int member , const char * name ) ;
void addDecoration ( Id , Decoration , int num = - 1 ) ;
void addDecoration ( Id , Decoration , const char * ) ;
void addDecorationId ( Id id , Decoration , Id idDecoration ) ;
void addMemberDecoration ( Id , unsigned int member , Decoration , int num = - 1 ) ;
void addMemberDecoration ( Id , unsigned int member , Decoration , const char * ) ;
// At the end of what block do the next create*() instructions go?
void setBuildPoint ( Block * bp ) { buildPoint = bp ; }
Block * getBuildPoint ( ) const { return buildPoint ; }
// Make the entry-point function. The returned pointer is only valid
// for the lifetime of this builder.
Function * makeEntryPoint ( const char * ) ;
// Make a shader-style function, and create its entry block if entry is non-zero.
// Return the function, pass back the entry.
// The returned pointer is only valid for the lifetime of this builder.
Function * makeFunctionEntry ( Decoration precision , Id returnType , const char * name , const std : : vector < Id > & paramTypes ,
const std : : vector < std : : vector < Decoration > > & precisions , Block * * entry = 0 ) ;
// Create a return. An 'implicit' return is one not appearing in the source
// code. In the case of an implicit return, no post-return block is inserted.
void makeReturn ( bool implicit , Id retVal = 0 ) ;
// Generate all the code needed to finish up a function.
void leaveFunction ( ) ;
// Create a discard.
void makeDiscard ( ) ;
// Create a global or function local or IO variable.
2020-02-15 07:51:03 +00:00
Id createVariable ( StorageClass , Id type , const char * name = 0 , Id initializer = NoResult ) ;
2019-02-20 20:21:57 +00:00
// Create an intermediate with an undefined value.
Id createUndefined ( Id type ) ;
// Store into an Id and return the l-value
2019-04-15 02:53:43 +00:00
void createStore ( Id rValue , Id lValue , spv : : MemoryAccessMask memoryAccess = spv : : MemoryAccessMaskNone , spv : : Scope scope = spv : : ScopeMax , unsigned int alignment = 0 ) ;
2019-02-20 20:21:57 +00:00
// Load from an Id and return it
2019-04-15 02:53:43 +00:00
Id createLoad ( Id lValue , spv : : MemoryAccessMask memoryAccess = spv : : MemoryAccessMaskNone , spv : : Scope scope = spv : : ScopeMax , unsigned int alignment = 0 ) ;
2019-02-20 20:21:57 +00:00
// Create an OpAccessChain instruction
Id createAccessChain ( StorageClass , Id base , const std : : vector < Id > & offsets ) ;
// Create an OpArrayLength instruction
Id createArrayLength ( Id base , unsigned int member ) ;
2020-02-15 07:51:03 +00:00
// Create an OpCooperativeMatrixLengthNV instruction
Id createCooperativeMatrixLength ( Id type ) ;
2019-02-20 20:21:57 +00:00
// Create an OpCompositeExtract instruction
Id createCompositeExtract ( Id composite , Id typeId , unsigned index ) ;
Id createCompositeExtract ( Id composite , Id typeId , const std : : vector < unsigned > & indexes ) ;
Id createCompositeInsert ( Id object , Id composite , Id typeId , unsigned index ) ;
Id createCompositeInsert ( Id object , Id composite , Id typeId , const std : : vector < unsigned > & indexes ) ;
Id createVectorExtractDynamic ( Id vector , Id typeId , Id componentIndex ) ;
Id createVectorInsertDynamic ( Id vector , Id typeId , Id component , Id componentIndex ) ;
void createNoResultOp ( Op ) ;
void createNoResultOp ( Op , Id operand ) ;
void createNoResultOp ( Op , const std : : vector < Id > & operands ) ;
2019-04-15 02:53:43 +00:00
void createNoResultOp ( Op , const std : : vector < IdImmediate > & operands ) ;
2019-02-20 20:21:57 +00:00
void createControlBarrier ( Scope execution , Scope memory , MemorySemanticsMask ) ;
void createMemoryBarrier ( unsigned executionScope , unsigned memorySemantics ) ;
Id createUnaryOp ( Op , Id typeId , Id operand ) ;
Id createBinOp ( Op , Id typeId , Id operand1 , Id operand2 ) ;
Id createTriOp ( Op , Id typeId , Id operand1 , Id operand2 , Id operand3 ) ;
Id createOp ( Op , Id typeId , const std : : vector < Id > & operands ) ;
2019-04-15 02:53:43 +00:00
Id createOp ( Op , Id typeId , const std : : vector < IdImmediate > & operands ) ;
2019-02-20 20:21:57 +00:00
Id createFunctionCall ( spv : : Function * , const std : : vector < spv : : Id > & ) ;
Id createSpecConstantOp ( Op , Id typeId , const std : : vector < spv : : Id > & operands , const std : : vector < unsigned > & literals ) ;
// Take an rvalue (source) and a set of channels to extract from it to
// make a new rvalue, which is returned.
Id createRvalueSwizzle ( Decoration precision , Id typeId , Id source , const std : : vector < unsigned > & channels ) ;
// Take a copy of an lvalue (target) and a source of components, and set the
// source components into the lvalue where the 'channels' say to put them.
// An updated version of the target is returned.
// (No true lvalue or stores are used.)
Id createLvalueSwizzle ( Id typeId , Id target , Id source , const std : : vector < unsigned > & channels ) ;
// If both the id and precision are valid, the id
// gets tagged with the requested precision.
// The passed in id is always the returned id, to simplify use patterns.
Id setPrecision ( Id id , Decoration precision )
{
if ( precision ! = NoPrecision & & id ! = NoResult )
addDecoration ( id , precision ) ;
return id ;
}
// Can smear a scalar to a vector for the following forms:
// - promoteScalar(scalar, vector) // smear scalar to width of vector
// - promoteScalar(vector, scalar) // smear scalar to width of vector
// - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
// - promoteScalar(scalar, scalar) // do nothing
// Other forms are not allowed.
//
// Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
// The type of the created vector is a vector of components of the same type as the scalar.
//
// Note: One of the arguments will change, with the result coming back that way rather than
// through the return value.
void promoteScalar ( Decoration precision , Id & left , Id & right ) ;
// Make a value by smearing the scalar to fill the type.
// vectorType should be the correct type for making a vector of scalarVal.
// (No conversions are done.)
Id smearScalar ( Decoration precision , Id scalarVal , Id vectorType ) ;
// Create a call to a built-in function.
Id createBuiltinCall ( Id resultType , Id builtins , int entryPoint , const std : : vector < Id > & args ) ;
// List of parameters used to create a texture operation
struct TextureParameters {
Id sampler ;
Id coords ;
Id bias ;
Id lod ;
Id Dref ;
Id offset ;
Id offsets ;
Id gradX ;
Id gradY ;
Id sample ;
Id component ;
Id texelOut ;
Id lodClamp ;
2019-04-15 02:53:43 +00:00
Id granularity ;
Id coarse ;
bool nonprivate ;
bool volatil ;
2019-02-20 20:21:57 +00:00
} ;
// Select the correct texture operation based on all inputs, and emit the correct instruction
2020-02-15 07:51:03 +00:00
Id createTextureCall ( Decoration precision , Id resultType , bool sparse , bool fetch , bool proj , bool gather ,
bool noImplicit , const TextureParameters & , ImageOperandsMask ) ;
2019-02-20 20:21:57 +00:00
// Emit the OpTextureQuery* instruction that was passed in.
// Figure out the right return value and type, and return it.
Id createTextureQueryCall ( Op , const TextureParameters & , bool isUnsignedResult ) ;
Id createSamplePositionCall ( Decoration precision , Id , Id ) ;
Id createBitFieldExtractCall ( Decoration precision , Id , Id , Id , bool isSigned ) ;
Id createBitFieldInsertCall ( Decoration precision , Id , Id , Id , Id ) ;
// Reduction comparison for composites: For equal and not-equal resulting in a scalar.
Id createCompositeCompare ( Decoration precision , Id , Id , bool /* true if for equal, false if for not-equal */ ) ;
// OpCompositeConstruct
Id createCompositeConstruct ( Id typeId , const std : : vector < Id > & constituents ) ;
// vector or scalar constructor
Id createConstructor ( Decoration precision , const std : : vector < Id > & sources , Id resultTypeId ) ;
// matrix constructor
Id createMatrixConstructor ( Decoration precision , const std : : vector < Id > & sources , Id constructee ) ;
// Helper to use for building nested control flow with if-then-else.
class If {
public :
If ( Id condition , unsigned int ctrl , Builder & builder ) ;
~ If ( ) { }
void makeBeginElse ( ) ;
void makeEndIf ( ) ;
private :
If ( const If & ) ;
If & operator = ( If & ) ;
Builder & builder ;
Id condition ;
unsigned int control ;
Function * function ;
Block * headerBlock ;
Block * thenBlock ;
Block * elseBlock ;
Block * mergeBlock ;
} ;
// Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
// any case/default labels, all separated by one or more case/default labels. Each possible
// case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
// number space. How to compute the value is given by 'condition', as in switch(condition).
//
// The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
//
// Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
//
// Returns the right set of basic blocks to start each code segment with, so that the caller's
// recursion stack can hold the memory for it.
//
void makeSwitch ( Id condition , unsigned int control , int numSegments , const std : : vector < int > & caseValues ,
const std : : vector < int > & valueToSegment , int defaultSegment , std : : vector < Block * > & segmentBB ) ; // return argument
// Add a branch to the innermost switch's merge block.
void addSwitchBreak ( ) ;
// Move to the next code segment, passing in the return argument in makeSwitch()
void nextSwitchSegment ( std : : vector < Block * > & segmentBB , int segment ) ;
// Finish off the innermost switch.
void endSwitch ( std : : vector < Block * > & segmentBB ) ;
struct LoopBlocks {
LoopBlocks ( Block & head , Block & body , Block & merge , Block & continue_target ) :
head ( head ) , body ( body ) , merge ( merge ) , continue_target ( continue_target ) { }
Block & head , & body , & merge , & continue_target ;
private :
LoopBlocks ( ) ;
LoopBlocks & operator = ( const LoopBlocks & ) ;
} ;
// Start a new loop and prepare the builder to generate code for it. Until
// closeLoop() is called for this loop, createLoopContinue() and
// createLoopExit() will target its corresponding blocks.
LoopBlocks & makeNewLoop ( ) ;
// Create a new block in the function containing the build point. Memory is
// owned by the function object.
Block & makeNewBlock ( ) ;
// Add a branch to the continue_target of the current (innermost) loop.
void createLoopContinue ( ) ;
2020-02-15 07:51:03 +00:00
// Add an exit (e.g. "break") from the innermost loop that we're currently
2019-02-20 20:21:57 +00:00
// in.
void createLoopExit ( ) ;
// Close the innermost loop that you're in
void closeLoop ( ) ;
//
// Access chain design for an R-Value vs. L-Value:
//
// There is a single access chain the builder is building at
// any particular time. Such a chain can be used to either to a load or
// a store, when desired.
//
// Expressions can be r-values, l-values, or both, or only r-values:
// a[b.c].d = .... // l-value
// ... = a[b.c].d; // r-value, that also looks like an l-value
// ++a[b.c].d; // r-value and l-value
// (x + y)[2]; // r-value only, can't possibly be l-value
//
// Computing an r-value means generating code. Hence,
// r-values should only be computed when they are needed, not speculatively.
//
// Computing an l-value means saving away information for later use in the compiler,
// no code is generated until the l-value is later dereferenced. It is okay
// to speculatively generate an l-value, just not okay to speculatively dereference it.
//
// The base of the access chain (the left-most variable or expression
// from which everything is based) can be set either as an l-value
// or as an r-value. Most efficient would be to set an l-value if one
// is available. If an expression was evaluated, the resulting r-value
// can be set as the chain base.
//
// The users of this single access chain can save and restore if they
// want to nest or manage multiple chains.
//
struct AccessChain {
Id base ; // for l-values, pointer to the base object, for r-values, the base object
std : : vector < Id > indexChain ;
Id instr ; // cache the instruction that generates this access chain
std : : vector < unsigned > swizzle ; // each std::vector element selects the next GLSL component number
Id component ; // a dynamic component index, can coexist with a swizzle, done after the swizzle, NoResult if not present
Id preSwizzleBaseType ; // dereferenced type, before swizzle or component is applied; NoType unless a swizzle or component is present
bool isRValue ; // true if 'base' is an r-value, otherwise, base is an l-value
2019-04-15 02:53:43 +00:00
unsigned int alignment ; // bitwise OR of alignment values passed in. Accumulates worst alignment. Only tracks base and (optional) component selection alignment.
// Accumulate whether anything in the chain of structures has coherent decorations.
struct CoherentFlags {
2020-02-15 07:51:03 +00:00
CoherentFlags ( ) { clear ( ) ; }
# ifdef GLSLANG_WEB
void clear ( ) { }
bool isVolatile ( ) const { return false ; }
CoherentFlags operator | = ( const CoherentFlags & other ) { return * this ; }
# else
bool isVolatile ( ) const { return volatil ; }
2019-04-15 02:53:43 +00:00
unsigned coherent : 1 ;
unsigned devicecoherent : 1 ;
unsigned queuefamilycoherent : 1 ;
unsigned workgroupcoherent : 1 ;
unsigned subgroupcoherent : 1 ;
unsigned nonprivate : 1 ;
unsigned volatil : 1 ;
unsigned isImage : 1 ;
void clear ( ) {
coherent = 0 ;
devicecoherent = 0 ;
queuefamilycoherent = 0 ;
workgroupcoherent = 0 ;
subgroupcoherent = 0 ;
nonprivate = 0 ;
volatil = 0 ;
isImage = 0 ;
}
CoherentFlags operator | = ( const CoherentFlags & other ) {
coherent | = other . coherent ;
devicecoherent | = other . devicecoherent ;
queuefamilycoherent | = other . queuefamilycoherent ;
workgroupcoherent | = other . workgroupcoherent ;
subgroupcoherent | = other . subgroupcoherent ;
nonprivate | = other . nonprivate ;
volatil | = other . volatil ;
isImage | = other . isImage ;
return * this ;
}
2020-02-15 07:51:03 +00:00
# endif
2019-04-15 02:53:43 +00:00
} ;
CoherentFlags coherentFlags ;
2019-02-20 20:21:57 +00:00
} ;
//
// the SPIR-V builder maintains a single active chain that
// the following methods operate on
//
// for external save and restore
AccessChain getAccessChain ( ) { return accessChain ; }
void setAccessChain ( AccessChain newChain ) { accessChain = newChain ; }
// clear accessChain
void clearAccessChain ( ) ;
// set new base as an l-value base
void setAccessChainLValue ( Id lValue )
{
assert ( isPointer ( lValue ) ) ;
accessChain . base = lValue ;
}
// set new base value as an r-value
void setAccessChainRValue ( Id rValue )
{
accessChain . isRValue = true ;
accessChain . base = rValue ;
}
// push offset onto the end of the chain
2019-04-15 02:53:43 +00:00
void accessChainPush ( Id offset , AccessChain : : CoherentFlags coherentFlags , unsigned int alignment )
2019-02-20 20:21:57 +00:00
{
accessChain . indexChain . push_back ( offset ) ;
2019-04-15 02:53:43 +00:00
accessChain . coherentFlags | = coherentFlags ;
accessChain . alignment | = alignment ;
2019-02-20 20:21:57 +00:00
}
// push new swizzle onto the end of any existing swizzle, merging into a single swizzle
2019-04-15 02:53:43 +00:00
void accessChainPushSwizzle ( std : : vector < unsigned > & swizzle , Id preSwizzleBaseType , AccessChain : : CoherentFlags coherentFlags , unsigned int alignment ) ;
2019-02-20 20:21:57 +00:00
// push a dynamic component selection onto the access chain, only applicable with a
// non-trivial swizzle or no swizzle
2019-04-15 02:53:43 +00:00
void accessChainPushComponent ( Id component , Id preSwizzleBaseType , AccessChain : : CoherentFlags coherentFlags , unsigned int alignment )
2019-02-20 20:21:57 +00:00
{
if ( accessChain . swizzle . size ( ) ! = 1 ) {
accessChain . component = component ;
if ( accessChain . preSwizzleBaseType = = NoType )
accessChain . preSwizzleBaseType = preSwizzleBaseType ;
}
2019-04-15 02:53:43 +00:00
accessChain . coherentFlags | = coherentFlags ;
accessChain . alignment | = alignment ;
2019-02-20 20:21:57 +00:00
}
// use accessChain and swizzle to store value
2019-04-15 02:53:43 +00:00
void accessChainStore ( Id rvalue , spv : : MemoryAccessMask memoryAccess = spv : : MemoryAccessMaskNone , spv : : Scope scope = spv : : ScopeMax , unsigned int alignment = 0 ) ;
2019-02-20 20:21:57 +00:00
// use accessChain and swizzle to load an r-value
2019-04-15 02:53:43 +00:00
Id accessChainLoad ( Decoration precision , Decoration nonUniform , Id ResultType , spv : : MemoryAccessMask memoryAccess = spv : : MemoryAccessMaskNone , spv : : Scope scope = spv : : ScopeMax , unsigned int alignment = 0 ) ;
2019-02-20 20:21:57 +00:00
// get the direct pointer for an l-value
Id accessChainGetLValue ( ) ;
// Get the inferred SPIR-V type of the result of the current access chain,
// based on the type of the base and the chain of dereferences.
Id accessChainGetInferredType ( ) ;
2020-02-15 07:51:03 +00:00
// Add capabilities, extensions, remove unneeded decorations, etc.,
2019-04-15 02:53:43 +00:00
// based on the resulting SPIR-V.
void postProcess ( ) ;
2020-02-15 07:51:03 +00:00
// Prune unreachable blocks in the CFG and remove unneeded decorations.
void postProcessCFG ( ) ;
# ifndef GLSLANG_WEB
// Add capabilities, extensions based on instructions in the module.
void postProcessFeatures ( ) ;
2019-04-15 02:53:43 +00:00
// Hook to visit each instruction in a block in a function
void postProcess ( Instruction & ) ;
// Hook to visit each non-32-bit sized float/int operation in a block.
void postProcessType ( const Instruction & , spv : : Id typeId ) ;
2020-02-15 07:51:03 +00:00
# endif
2019-04-15 02:53:43 +00:00
2019-02-20 20:21:57 +00:00
void dump ( std : : vector < unsigned int > & ) const ;
void createBranch ( Block * block ) ;
void createConditionalBranch ( Id condition , Block * thenBlock , Block * elseBlock ) ;
2020-02-15 07:51:03 +00:00
void createLoopMerge ( Block * mergeBlock , Block * continueBlock , unsigned int control , const std : : vector < unsigned int > & operands ) ;
2019-02-20 20:21:57 +00:00
// Sets to generate opcode for specialization constants.
void setToSpecConstCodeGenMode ( ) { generatingOpCodeForSpecConst = true ; }
// Sets to generate opcode for non-specialization constants (normal mode).
void setToNormalCodeGenMode ( ) { generatingOpCodeForSpecConst = false ; }
// Check if the builder is generating code for spec constants.
bool isInSpecConstCodeGenMode ( ) { return generatingOpCodeForSpecConst ; }
protected :
Id makeIntConstant ( Id typeId , unsigned value , bool specConstant ) ;
Id makeInt64Constant ( Id typeId , unsigned long long value , bool specConstant ) ;
Id findScalarConstant ( Op typeClass , Op opcode , Id typeId , unsigned value ) ;
Id findScalarConstant ( Op typeClass , Op opcode , Id typeId , unsigned v1 , unsigned v2 ) ;
2020-02-15 07:51:03 +00:00
Id findCompositeConstant ( Op typeClass , Id typeId , const std : : vector < Id > & comps ) ;
2019-02-20 20:21:57 +00:00
Id findStructConstant ( Id typeId , const std : : vector < Id > & comps ) ;
Id collapseAccessChain ( ) ;
void remapDynamicSwizzle ( ) ;
void transferAccessChainSwizzle ( bool dynamic ) ;
void simplifyAccessChainSwizzle ( ) ;
void createAndSetNoPredecessorBlock ( const char * ) ;
void createSelectionMerge ( Block * mergeBlock , unsigned int control ) ;
void dumpSourceInstructions ( std : : vector < unsigned int > & ) const ;
2019-04-15 02:53:43 +00:00
void dumpSourceInstructions ( const spv : : Id fileId , const std : : string & text , std : : vector < unsigned int > & ) const ;
2019-02-20 20:21:57 +00:00
void dumpInstructions ( std : : vector < unsigned int > & , const std : : vector < std : : unique_ptr < Instruction > > & ) const ;
void dumpModuleProcesses ( std : : vector < unsigned int > & ) const ;
2019-04-15 02:53:43 +00:00
spv : : MemoryAccessMask sanitizeMemoryAccessForStorageClass ( spv : : MemoryAccessMask memoryAccess , StorageClass sc ) const ;
2019-02-20 20:21:57 +00:00
unsigned int spvVersion ; // the version of SPIR-V to emit in the header
SourceLanguage source ;
int sourceVersion ;
spv : : Id sourceFileStringId ;
std : : string sourceText ;
int currentLine ;
2019-04-15 02:53:43 +00:00
const char * currentFile ;
2019-02-20 20:21:57 +00:00
bool emitOpLines ;
std : : set < std : : string > extensions ;
std : : vector < const char * > sourceExtensions ;
std : : vector < const char * > moduleProcesses ;
AddressingModel addressModel ;
MemoryModel memoryModel ;
std : : set < spv : : Capability > capabilities ;
int builderNumber ;
Module module ;
Block * buildPoint ;
Id uniqueId ;
Function * entryPointFunction ;
bool generatingOpCodeForSpecConst ;
AccessChain accessChain ;
// special blocks of instructions for output
std : : vector < std : : unique_ptr < Instruction > > strings ;
std : : vector < std : : unique_ptr < Instruction > > imports ;
std : : vector < std : : unique_ptr < Instruction > > entryPoints ;
std : : vector < std : : unique_ptr < Instruction > > executionModes ;
std : : vector < std : : unique_ptr < Instruction > > names ;
std : : vector < std : : unique_ptr < Instruction > > decorations ;
std : : vector < std : : unique_ptr < Instruction > > constantsTypesGlobals ;
std : : vector < std : : unique_ptr < Instruction > > externals ;
std : : vector < std : : unique_ptr < Function > > functions ;
// not output, internally used for quick & dirty canonical (unique) creation
std : : unordered_map < unsigned int , std : : vector < Instruction * > > groupedConstants ; // map type opcodes to constant inst.
std : : unordered_map < unsigned int , std : : vector < Instruction * > > groupedStructConstants ; // map struct-id to constant instructions
std : : unordered_map < unsigned int , std : : vector < Instruction * > > groupedTypes ; // map type opcodes to type instructions
// stack of switches
std : : stack < Block * > switchMerges ;
// Our loop stack.
std : : stack < LoopBlocks > loops ;
2019-04-15 02:53:43 +00:00
// map from strings to their string ids
std : : unordered_map < std : : string , spv : : Id > stringIds ;
// map from include file name ids to their contents
std : : map < spv : : Id , const std : : string * > includeFiles ;
2019-02-20 20:21:57 +00:00
// The stream for outputting warnings and errors.
SpvBuildLogger * logger ;
} ; // end Builder class
} ; // end spv namespace
# endif // SpvBuilder_H