yum-mirror/glslang
git clone https://git.yummers.dev/yum-mirror/glslang
6309d623
main
1// 2// Copyright (C) 2014 LunarG, Inc. 3// Copyright (C) 2015-2018 Google, Inc. 4// 5// All rights reserved. 6// 7// Redistribution and use in source and binary forms, with or without 8// modification, are permitted provided that the following conditions 9// are met: 10// 11// Redistributions of source code must retain the above copyright 12// notice, this list of conditions and the following disclaimer. 13// 14// Redistributions in binary form must reproduce the above 15// copyright notice, this list of conditions and the following 16// disclaimer in the documentation and/or other materials provided 17// with the distribution. 18// 19// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20// contributors may be used to endorse or promote products derived 21// from this software without specific prior written permission. 22// 23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34// POSSIBILITY OF SUCH DAMAGE. 35 36// SPIRV-IR 37// 38// Simple in-memory representation (IR) of SPIRV. Just for holding 39// Each function's CFG of blocks. Has this hierarchy: 40// - Module, which is a list of 41// - Function, which is a list of 42// - Block, which is a list of 43// - Instruction 44// 45 46#pragma once 47#ifndef spvIR_H 48#define spvIR_H 49 50#include "spirv.hpp11" 51 52#include <algorithm> 53#include <cassert> 54#include <functional> 55#include <iostream> 56#include <memory> 57#include <vector> 58#include <set> 59#include <optional> 60 61namespace spv { 62 63class Block ; 64class Function ; 65class Module ; 66 67const Id NoResult = 0 ; 68const Id NoType = 0 ; 69 70const Decoration NoPrecision = Decoration ::Max ; 71 72#ifdef __GNUC__ 73# define POTENTIALLY_UNUSED __attribute__((unused)) 74#else 75# define POTENTIALLY_UNUSED 76#endif 77 78POTENTIALLY_UNUSED 79const MemorySemanticsMask MemorySemanticsAllMemory = 80 (MemorySemanticsMask )(MemorySemanticsMask ::UniformMemory | 81MemorySemanticsMask ::WorkgroupMemory | 82MemorySemanticsMask ::AtomicCounterMemory | 83MemorySemanticsMask ::ImageMemory ); 84 85struct IdImmediate { 86bool isId ;// true if word is an Id, false if word is an immediate 87unsigned word ; 88IdImmediate (bool i ,unsigned w ) :isId (i ),word (w ) {} 89IdImmediate (bool i ,spv ::MemoryAccessMask w ) :isId (i ),word (( unsigned )w) {} 90IdImmediate ( bool i, spv ::TensorAddressingOperandsMask w) : isId (i), word (( unsigned )w) {} 91IdImmediate ( bool i, spv ::ImageOperandsMask w) : isId (i), word (( unsigned )w) {} 92IdImmediate ( bool i, spv ::CooperativeMatrixOperandsMask w) : isId (i), word (( unsigned )w) {} 93}; 94 95// 96// SPIR-V IR instruction. 97// 98 99class Instruction { 100public : 101Instruction (Id resultId, Id typeId, Op opCode) : resultId (resultId), typeId ( typeId ), opCode ( opCode ), block ( nullptr ) { } 102explicit Instruction ( Op opCode) : resultId (NoResult), typeId ( NoType ), opCode ( opCode ), block ( nullptr ) { } 103virtual ~ Instruction () {} 104void reserveOperands ( size_t count) { 105operands . reserve ( count ); 106idOperand . reserve ( count ); 107} 108void addIdOperand ( Id id) { 109// ids can't be 0 110assert (id); 111operands. push_back (id); 112idOperand. push_back (true); 113} 114// This method is potentially dangerous as it can break assumptions 115// about SSA and lack of forward references. 116void setIdOperand ( unsigned idx, Id id) { 117assert (id); 118assert (idOperand[idx]); 119operands[idx] = id; 120} 121 122void addImmediateOperand ( unsigned int immediate) { 123operands. push_back (immediate); 124idOperand. push_back (false); 125} 126 127void addImmediateOperand ( spv ::StorageClass immediate) { 128addImmediateOperand (( unsigned )immediate); 129} 130 131void addImmediateOperand ( spv ::ExecutionMode immediate) { 132addImmediateOperand (( unsigned )immediate); 133} 134 135void addImmediateOperand ( spv ::ExecutionModel immediate) { 136addImmediateOperand (( unsigned )immediate); 137} 138 139void addImmediateOperand ( spv ::Decoration immediate) { 140addImmediateOperand (( unsigned )immediate); 141} 142 143void addImmediateOperand ( spv ::LinkageType immediate) { 144addImmediateOperand (( unsigned )immediate); 145} 146 147void addImmediateOperand ( spv ::MemoryAccessMask immediate) { 148addImmediateOperand (( unsigned )immediate); 149} 150 151void addImmediateOperand ( spv ::Capability immediate) { 152addImmediateOperand (( unsigned )immediate); 153} 154 155void addImmediateOperand ( spv ::AddressingModel immediate) { 156addImmediateOperand (( unsigned )immediate); 157} 158 159void addImmediateOperand ( spv ::MemoryModel immediate) { 160addImmediateOperand (( unsigned )immediate); 161} 162 163void addImmediateOperand ( spv ::FPEncoding immediate) { 164addImmediateOperand (( unsigned )immediate); 165} 166 167void addImmediateOperand ( spv ::SourceLanguage immediate) { 168addImmediateOperand (( unsigned )immediate); 169} 170 171void addImmediateOperand ( spv ::Dim immediate) { 172addImmediateOperand (( unsigned )immediate); 173} 174 175void addImmediateOperand ( spv ::FunctionControlMask immediate){ 176addImmediateOperand (( unsigned )immediate); 177} 178 179void addImmediateOperand ( spv ::SelectionControlMask immediate) { 180addImmediateOperand (( unsigned )immediate); 181} 182 183void addImmediateOperand ( spv ::LoopControlMask immediate) { 184addImmediateOperand (( unsigned )immediate); 185} 186 187void setImmediateOperand ( unsigned idx, unsigned int immediate) { 188assert (!idOperand[idx]); 189operands[idx] = immediate; 190} 191 192void clearOperands () { 193operands. clear (); 194idOperand. clear (); 195} 196 197void addStringOperand ( const char * str) 198{ 199unsigned int word = 0 ; 200unsigned int shiftAmount = 0 ; 201unsigned char c; 202 203do { 204c = * (str ++ ); 205word |= (( unsigned int )c) << shiftAmount; 206shiftAmount += 8 ; 207if (shiftAmount == 32 ) { 208addImmediateOperand (word); 209word = 0 ; 210shiftAmount = 0 ; 211} 212} while (c != 0 ); 213 214// deal with partial last word 215if (shiftAmount > 0 ) { 216addImmediateOperand (word); 217} 218} 219bool isIdOperand ( int op) const { return idOperand[op]; } 220void setBlock ( Block * b) { block = b; } 221Block * getBlock () const { return block; } 222Op getOpCode () const { return opCode; } 223int getNumOperands () const 224{ 225assert (operands. size () == idOperand. size ()); 226return ( int )operands. size (); 227} 228Id getResultId () const { return resultId; } 229Id getTypeId () const { return typeId; } 230void setTypeId ( Id tId) { typeId = tId; } 231Id getIdOperand ( int op) const { 232assert (idOperand[op]); 233return operands[op]; 234} 235unsigned int getImmediateOperand ( int op) const { 236assert (!idOperand[op]); 237return operands[op]; 238} 239 240// Write out the binary form. 241void dump ( std ::vector < unsigned int >& out) const 242{ 243// Compute the wordCount 244unsigned int wordCount = 1 ; 245if (typeId) 246++ wordCount; 247if (resultId) 248++ wordCount; 249wordCount += ( unsigned int )operands. size (); 250 251// Write out the beginning of the instruction 252out. push_back (((wordCount) << WordCountShift) | ( unsigned )opCode); 253if (typeId) 254out. push_back (typeId); 255if (resultId) 256out. push_back (resultId); 257 258// Write out the operands 259for ( int op = 0 ; op < ( int )operands. size (); ++ op) 260out. push_back (operands[op]); 261} 262 263const char * getNameString () const { 264if (opCode == Op::OpString) { 265return ( const char * ) & operands[ 0 ]; 266} else { 267assert (opCode == Op::OpName); 268return ( const char * ) & operands[ 1 ]; 269} 270} 271 272protected : 273Instruction( const Instruction & ); 274Id resultId; 275Id typeId; 276Op opCode; 277std ::vector < Id > operands; // operands, both <id> and immediates (both are unsigned int) 278std ::vector < bool > idOperand; // true for operands that are <id>, false for immediates 279Block * block; 280}; 281 282// 283// SPIR-V IR block. 284// 285 286struct DebugSourceLocation { 287int line ; 288int column ; 289spv :: Id fileId ; 290}; 291 292class Block { 293public : 294Block( Id id, Function & parent); 295virtual ~ Block () 296{ 297} 298 299Id getId () { return instructions. front () -> getResultId (); } 300 301Function & getParent () const { return parent; } 302// Returns true if the source location is actually updated. 303// Note we still need the builder to insert the line marker instruction. This is just a tracker. 304bool updateDebugSourceLocation ( int line, int column, spv ::Id fileId) { 305if (currentSourceLoc && currentSourceLoc -> line == line && currentSourceLoc -> column == column && 306currentSourceLoc -> fileId == fileId) { 307return false; 308} 309 310currentSourceLoc = DebugSourceLocation{line, column, fileId}; 311return true; 312} 313// Returns true if the scope is actually updated. 314// Note we still need the builder to insert the debug scope instruction. This is just a tracker. 315bool updateDebugScope ( spv ::Id scopeId) { 316assert (scopeId); 317if (currentDebugScope && * currentDebugScope == scopeId) { 318return false; 319} 320 321currentDebugScope = scopeId; 322return true; 323} 324void addInstruction ( std ::unique_ptr < Instruction > inst); 325void addPredecessor ( Block * pred) { predecessors. push_back (pred); pred -> successors . push_back (this);} 326void addLocalVariable ( std ::unique_ptr < Instruction > inst) { localVariables. push_back (std:: move (inst)); } 327const std ::vector < Block *>& getPredecessors () const { return predecessors; } 328const std ::vector < Block *>& getSuccessors () const { return successors; } 329std::vector < std::unique_ptr < Instruction > >& getInstructions () { 330return instructions; 331} 332const std ::vector < std::unique_ptr < Instruction > >& getLocalVariables () const { return localVariables; } 333void setUnreachable () { unreachable = true; } 334bool isUnreachable () const { return unreachable; } 335// Returns the block's merge instruction, if one exists (otherwise null). 336const Instruction * getMergeInstruction () const { 337if (instructions. size () < 2 ) return nullptr ; 338const Instruction * nextToLast = (instructions. cend () - 2 ) -> get (); 339switch (nextToLast -> getOpCode ()) { 340case Op:: OpSelectionMerge : 341case Op:: OpLoopMerge : 342return nextToLast; 343default : 344return nullptr ; 345} 346return nullptr ; 347} 348 349// Change this block into a canonical dead merge block. Delete instructions 350// as necessary. A canonical dead merge block has only an OpLabel and an 351// OpUnreachable. 352void rewriteAsCanonicalUnreachableMerge () { 353assert (localVariables. empty ()); 354// Delete all instructions except for the label. 355assert (instructions. size () > 0 ); 356instructions. resize ( 1 ); 357successors. clear (); 358addInstruction ( std ::unique_ptr < Instruction > (new Instruction( Op :: OpUnreachable ))); 359} 360// Change this block into a canonical dead continue target branching to the 361// given header ID. Delete instructions as necessary. A canonical dead continue 362// target has only an OpLabel and an unconditional branch back to the corresponding 363// header. 364void rewriteAsCanonicalUnreachableContinue ( Block * header) { 365assert (localVariables. empty ()); 366// Delete all instructions except for the label. 367assert (instructions. size () > 0 ); 368instructions. resize ( 1 ); 369successors. clear (); 370// Add OpBranch back to the header. 371assert (header != nullptr ); 372Instruction * branch = new Instruction (Op::OpBranch); 373branch -> addIdOperand (header -> getId ()); 374addInstruction ( std ::unique_ptr < Instruction > (branch)); 375successors. push_back (header); 376} 377 378bool isTerminated () const 379{ 380switch (instructions. back () -> getOpCode ()) { 381case Op:: OpBranch : 382case Op:: OpBranchConditional : 383case Op:: OpSwitch : 384case Op:: OpKill : 385case Op:: OpTerminateInvocation : 386case Op:: OpReturn : 387case Op:: OpReturnValue : 388case Op:: OpUnreachable : 389case Op:: OpAbortKHR : 390return true; 391default : 392return false; 393} 394} 395 396void dump ( std ::vector < unsigned int >& out) const 397{ 398instructions[ 0 ] -> dump (out); 399for ( int i = 0 ; i < ( int )localVariables. size (); ++ i) 400localVariables[i] -> dump (out); 401for ( int i = 1 ; i < ( int )instructions. size (); ++ i) 402instructions[i] -> dump (out); 403} 404 405protected : 406Block( const Block & ); 407Block & operator = (Block & ); 408 409// To enforce keeping parent and ownership in sync: 410friend Function; 411 412std::vector < std::unique_ptr < Instruction > > instructions; 413std ::vector < Block *> predecessors, successors; 414std::vector < std::unique_ptr < Instruction > > localVariables; 415Function & parent; 416 417// Track source location of the last source location marker instruction. 418std ::optional < DebugSourceLocation > currentSourceLoc; 419 420// Track scope of the last debug scope instruction. 421std ::optional < spv::Id > currentDebugScope; 422 423// track whether this block is known to be uncreachable (not necessarily 424// true for all unreachable blocks, but should be set at least 425// for the extraneous ones introduced by the builder). 426bool unreachable; 427}; 428 429// The different reasons for reaching a block in the inReadableOrder traversal. 430enum ReachReason { 431// Reachable from the entry block via transfers of control, i.e. branches. 432ReachViaControlFlow = 0 , 433// A continue target that is not reachable via control flow. 434ReachDeadContinue, 435// A merge block that is not reachable via control flow. 436ReachDeadMerge 437}; 438 439// Traverses the control-flow graph rooted at root in an order suited for 440// readable code generation. Invokes callback at every node in the traversal 441// order. The callback arguments are: 442// - the block, 443// - the reason we reached the block, 444// - if the reason was that block is an unreachable continue or unreachable merge block 445// then the last parameter is the corresponding header block. 446void inReadableOrder ( Block * root, std ::function < void (Block * , ReachReason , Block * header) > callback); 447 448// 449// SPIR-V IR Function. 450// 451 452class Function { 453public : 454Function( Id id, Id resultType, Id functionType, Id firstParam, LinkageType linkage, const std::string & name, Module & parent); 455virtual ~ Function () 456{ 457for ( int i = 0 ; i < ( int )parameterInstructions. size (); ++ i) 458delete parameterInstructions[i]; 459 460for ( int i = 0 ; i < ( int )blocks. size (); ++ i) 461delete blocks[i]; 462} 463Id getId () const { return functionInstruction. getResultId (); } 464Id getParamId ( int p) const { return parameterInstructions[p] -> getResultId (); } 465Id getParamType ( int p) const { return parameterInstructions[p] -> getTypeId (); } 466 467void addBlock ( Block * block) { blocks. push_back (block); } 468void removeBlock ( Block * block) 469{ 470auto found = find (blocks. begin (), blocks. end (), block); 471assert (found != blocks. end ()); 472blocks. erase (found); 473delete block; 474} 475 476Module & getParent () const { return parent; } 477Block * getEntryBlock () const { return blocks. front (); } 478Block * getLastBlock () const { return blocks. back (); } 479const std ::vector < Block *>& getBlocks () const { return blocks; } 480void addLocalVariable ( std ::unique_ptr < Instruction > inst); 481Id getReturnType () const { return functionInstruction. getTypeId (); } 482Id getFuncId () const { return functionInstruction. getResultId (); } 483Id getFuncTypeId () const { return functionInstruction. getIdOperand ( 1 ); } 484void setFunctionControl ( FunctionControlMask functionControl) 485{ 486functionInstruction. setImmediateOperand ( 0 , static_cast < unsigned > (functionControl)); 487} 488void setReturnPrecision ( Decoration precision) 489{ 490if (precision == Decoration::RelaxedPrecision) 491reducedPrecisionReturn = true; 492} 493Decoration getReturnPrecision () const 494{ return reducedPrecisionReturn ? Decoration::RelaxedPrecision : NoPrecision; } 495 496void setDebugLineInfo ( Id fileName, int line, int column) { 497lineInstruction = std::unique_ptr < Instruction > { new Instruction ( Op ::OpLine)}; 498lineInstruction -> reserveOperands ( 3 ); 499lineInstruction -> addIdOperand (fileName); 500lineInstruction -> addImmediateOperand (line); 501lineInstruction -> addImmediateOperand (column); 502} 503bool hasDebugLineInfo () const { return lineInstruction != nullptr ; } 504 505void setImplicitThis () { implicitThis = true; } 506bool hasImplicitThis () const { return implicitThis; } 507 508void addParamPrecision ( unsigned param, Decoration precision) 509{ 510if (precision == Decoration::RelaxedPrecision) 511reducedPrecisionParams. insert (param); 512} 513Decoration getParamPrecision ( unsigned param) const 514{ 515return reducedPrecisionParams. find (param) != reducedPrecisionParams. end () ? 516Decoration::RelaxedPrecision : NoPrecision; 517} 518 519void dump ( std ::vector < unsigned int >& out) const 520{ 521// OpLine 522if (lineInstruction != nullptr ) { 523lineInstruction -> dump (out); 524} 525 526// OpFunction 527functionInstruction. dump (out); 528 529// OpFunctionParameter 530for ( int p = 0 ; p < ( int )parameterInstructions. size (); ++ p) 531parameterInstructions[p] -> dump (out); 532 533// Blocks 534inReadableOrder (blocks[ 0 ], [ & out](const Block * b, ReachReason, Block * ) { b -> dump (out); }); 535Instruction end( 0 , 0 , Op::OpFunctionEnd); 536end. dump (out); 537} 538 539LinkageType getLinkType () const { return linkType; } 540const char * getExportName () const { return exportName. c_str (); } 541 542protected : 543Function( const Function & ); 544Function & operator = (Function & ); 545 546Module & parent; 547std ::unique_ptr < Instruction > lineInstruction; 548Instruction functionInstruction; 549std ::vector < Instruction *> parameterInstructions; 550std ::vector < Block *> blocks; 551bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument 552bool reducedPrecisionReturn; 553std ::set < int > reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg 554LinkageType linkType; 555std :: string exportName; 556}; 557 558// 559// SPIR-V IR Module. 560// 561 562class Module { 563public : 564Module () {} 565virtual ~ Module () 566{ 567// TODO delete things 568} 569 570void addFunction ( Function * fun) { functions. push_back (fun); } 571 572void mapInstruction ( Instruction * instruction) 573{ 574spv :: Id resultId = instruction -> getResultId (); 575// map the instruction's result id 576if (resultId >= idToInstruction. size ()) 577idToInstruction. resize (resultId + 16 ); 578idToInstruction[resultId] = instruction; 579} 580 581Instruction * getInstruction ( Id id) const { return idToInstruction[id]; } 582const std ::vector < Function *>& getFunctions () const { return functions; } 583spv :: Id getTypeId ( Id resultId) const { 584return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId] -> getTypeId (); 585} 586StorageClass getStorageClass ( Id typeId) const 587{ 588assert (idToInstruction[typeId] -> getOpCode () == spv::Op::OpTypePointer || 589idToInstruction[typeId] -> getOpCode () == spv::Op::OpTypeUntypedPointerKHR); 590return ( StorageClass )idToInstruction[typeId] -> getImmediateOperand ( 0 ); 591} 592 593void dump ( std ::vector < unsigned int >& out) const 594{ 595for ( int f = 0 ; f < ( int )functions. size (); ++ f) 596functions[f] -> dump (out); 597} 598 599protected : 600Module( const Module & ); 601std ::vector < Function *> functions; 602 603// map from result id to instruction having that result id 604std ::vector < Instruction *> idToInstruction; 605 606// map from a result id to its type id 607}; 608 609// 610// Implementation (it's here due to circular type definitions). 611// 612 613// Add both 614// - the OpFunction instruction 615// - all the OpFunctionParameter instructions 616__inline Function :: Function ( Id id, Id resultType, Id functionType, Id firstParamId, LinkageType linkage, const std ::string & name, Module & parent) 617: parent (parent), lineInstruction ( nullptr ), 618functionInstruction ( id , resultType , Op ::OpFunction), implicitThis ( false ), 619reducedPrecisionReturn ( false ), 620linkType ( linkage ) 621{ 622// OpFunction 623functionInstruction. reserveOperands ( 2 ); 624functionInstruction. addImmediateOperand (FunctionControlMask::MaskNone); 625functionInstruction. addIdOperand (functionType); 626parent. mapInstruction ( & functionInstruction); 627parent. addFunction (this); 628 629// OpFunctionParameter 630Instruction * typeInst = parent. getInstruction (functionType); 631int numParams = typeInst -> getNumOperands () - 1 ; 632for ( int p = 0 ; p < numParams; ++ p) { 633Instruction * param = new Instruction (firstParamId + p, typeInst -> getIdOperand (p + 1 ), Op::OpFunctionParameter); 634parent. mapInstruction (param); 635parameterInstructions. push_back (param); 636} 637 638// If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration 639if (linkType != LinkageType::Max) { 640exportName = name. substr ( 0 , name. find_first_of ( '(' )); 641} 642} 643 644__inline void Function:: addLocalVariable ( std ::unique_ptr < Instruction > inst) 645{ 646Instruction * raw_instruction = inst. get (); 647blocks[ 0 ] -> addLocalVariable (std:: move (inst)); 648parent. mapInstruction (raw_instruction); 649} 650 651__inline Block :: Block ( Id id, Function & parent) : parent (parent), unreachable ( false ) 652{ 653instructions. push_back (std::unique_ptr < Instruction > (new Instruction (id, NoType, Op::OpLabel))); 654instructions. back () -> setBlock (this); 655parent. getParent (). mapInstruction (instructions. back (). get ()); 656} 657 658__inline void Block:: addInstruction ( std ::unique_ptr < Instruction > inst) 659{ 660Instruction * raw_instruction = inst. get (); 661instructions. push_back (std:: move (inst)); 662raw_instruction -> setBlock (this); 663if (raw_instruction -> getResultId ()) 664parent. getParent (). mapInstruction (raw_instruction); 665} 666 667} // end spv namespace 668 669#endif // spvIR_H