-
Notifications
You must be signed in to change notification settings - Fork 852
Expand file tree
/
Copy pathSpirvEmitter.h
More file actions
1665 lines (1399 loc) · 78.6 KB
/
SpirvEmitter.h
File metadata and controls
1665 lines (1399 loc) · 78.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===------- SpirvEmitter.h - SPIR-V Binary Code Emitter --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a SPIR-V emitter class that takes in HLSL AST and emits
// SPIR-V binary words.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_SPIRV_SPIRVEMITTER_H
#define LLVM_CLANG_LIB_SPIRV_SPIRVEMITTER_H
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include "dxc/DXIL/DxilShaderModel.h"
#include "dxc/HlslIntrinsicOp.h"
#include "spirv/unified1/GLSL.std.450.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/SPIRV/FeatureManager.h"
#include "clang/SPIRV/SpirvBuilder.h"
#include "clang/SPIRV/SpirvContext.h"
#include "llvm/ADT/STLExtras.h"
#include "ConstEvaluator.h"
#include "DeclResultIdMapper.h"
#include "spirv-tools/optimizer.hpp"
namespace spvtools {
namespace opt {
// A struct for a pair of descriptor set and binding.
struct DescriptorSetAndBinding {
uint32_t descriptor_set;
uint32_t binding;
};
} // namespace opt
} // namespace spvtools
namespace clang {
namespace spirv {
/// SPIR-V emitter class. It consumes the HLSL AST and emits SPIR-V words.
///
/// This class only overrides the HandleTranslationUnit() method; Traversing
/// through the AST is done manually instead of using ASTConsumer's harness.
class SpirvEmitter : public ASTConsumer {
public:
SpirvEmitter(CompilerInstance &ci);
void HandleTranslationUnit(ASTContext &context) override;
ASTContext &getASTContext() { return astContext; }
SpirvBuilder &getSpirvBuilder() { return spvBuilder; }
SpirvContext &getSpirvContext() { return spvContext; }
DiagnosticsEngine &getDiagnosticsEngine() { return diags; }
CompilerInstance &getCompilerInstance() { return theCompilerInstance; }
SpirvCodeGenOptions &getSpirvOptions() { return spirvOptions; }
/// \brief If DebugSource and DebugCompilationUnit for loc are already
/// created, we just return RichDebugInfo containing it. Otherwise,
/// create DebugSource and DebugCompilationUnit for loc and return it.
RichDebugInfo *getOrCreateRichDebugInfo(const SourceLocation &loc);
RichDebugInfo *getOrCreateRichDebugInfoImpl(llvm::StringRef file);
void doDecl(const Decl *decl);
void doStmt(const Stmt *stmt, llvm::ArrayRef<const Attr *> attrs = {});
SpirvInstruction *doExpr(const Expr *expr, SourceRange rangeOverride = {});
SpirvInstruction *doExprEnsuringRValue(const Expr *expr,
SourceLocation location,
SourceRange range);
/// Processes the given expression and emits SPIR-V instructions. If the
/// result is a GLValue, does an additional load.
///
/// This method is useful for cases where ImplicitCastExpr (LValueToRValue) is
/// missing when using an lvalue as rvalue in the AST, e.g., DeclRefExpr will
/// not be wrapped in ImplicitCastExpr (LValueToRValue) when appearing in
/// HLSLVectorElementExpr since the generated HLSLVectorElementExpr itself can
/// be lvalue or rvalue.
SpirvInstruction *loadIfGLValue(const Expr *expr,
SourceRange rangeOverride = {});
/// Casts the given value from fromType to toType. fromType and toType should
/// both be scalar or vector types of the same size.
SpirvInstruction *castToType(SpirvInstruction *value, QualType fromType,
QualType toType, SourceLocation,
SourceRange range = {});
/// Returns true if the given VarDecl will be translated into a SPIR-V
/// variable not in the Private or Function storage class.
static inline bool isExternalVar(const VarDecl *var) {
// Class static variables should be put in the Private storage class.
// groupshared variables are allowed to be declared as "static". But we
// still need to put them in the Workgroup storage class. That is, when
// seeing "static groupshared", ignore "static".
return var->hasExternalFormalLinkage()
? !var->isStaticDataMember()
: (var->getAttr<HLSLGroupSharedAttr>() != nullptr);
}
/// Create SpirvIntrinsicInstruction for arbitrary SPIR-V instructions
/// specified by [[vk::ext_instruction(..)]] or [[vk::ext_type_def(..)]]
SpirvInstruction *
createSpirvIntrInstExt(llvm::ArrayRef<const Attr *> attrs, QualType retType,
llvm::ArrayRef<SpirvInstruction *> spvArgs,
bool isInstr, SourceLocation loc);
/// \brief Negates to get the additive inverse of SV_Position.y if requested.
SpirvInstruction *invertYIfRequested(SpirvInstruction *position,
SourceLocation loc,
SourceRange range = {});
private:
bool handleNodePayloadArrayType(const ParmVarDecl *decl,
SpirvInstruction *instr);
void doFunctionDecl(const FunctionDecl *decl);
void doVarDecl(const VarDecl *decl);
void doRecordDecl(const RecordDecl *decl);
void doClassTemplateDecl(const ClassTemplateDecl *classTemplateDecl);
void doEnumDecl(const EnumDecl *decl);
void doHLSLBufferDecl(const HLSLBufferDecl *decl);
void doImplicitDecl(const Decl *decl);
void doBreakStmt(const BreakStmt *stmt);
void doDiscardStmt(const DiscardStmt *stmt);
inline void doDeclStmt(const DeclStmt *stmt);
void doForStmt(const ForStmt *, llvm::ArrayRef<const Attr *> attrs = {});
void doIfStmt(const IfStmt *ifStmt, llvm::ArrayRef<const Attr *> attrs = {});
void doReturnStmt(const ReturnStmt *stmt);
void doSwitchStmt(const SwitchStmt *stmt,
llvm::ArrayRef<const Attr *> attrs = {});
void doWhileStmt(const WhileStmt *, llvm::ArrayRef<const Attr *> attrs = {});
void doDoStmt(const DoStmt *, llvm::ArrayRef<const Attr *> attrs = {});
void doContinueStmt(const ContinueStmt *);
SpirvInstruction *doArraySubscriptExpr(const ArraySubscriptExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doBinaryOperator(const BinaryOperator *expr);
SpirvInstruction *doCallExpr(const CallExpr *callExpr,
SourceRange rangeOverride = {});
SpirvInstruction *doCastExpr(const CastExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doCompoundAssignOperator(const CompoundAssignOperator *);
SpirvInstruction *doConditionalOperator(const ConditionalOperator *expr);
SpirvInstruction *doConditional(const Expr *expr, const Expr *cond,
const Expr *falseExpr, const Expr *trueExpr);
SpirvInstruction *
doShortCircuitedConditionalOperator(const ConditionalOperator *expr);
SpirvInstruction *doCXXMemberCallExpr(const CXXMemberCallExpr *expr);
SpirvInstruction *doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doExtMatrixElementExpr(const ExtMatrixElementExpr *expr);
SpirvInstruction *doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doInitListExpr(const InitListExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doMemberExpr(const MemberExpr *expr,
SourceRange rangeOverride = {});
SpirvInstruction *doUnaryOperator(const UnaryOperator *expr);
SpirvInstruction *
doUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *expr);
/// Overload with pre computed SpirvEvalInfo.
///
/// The given expr will not be evaluated again.
SpirvInstruction *loadIfGLValue(const Expr *expr, SpirvInstruction *info,
SourceRange rangeOverride = {});
/// Loads the pointer of the aliased-to-variable if the given expression is a
/// DeclRefExpr referencing an alias variable. See DeclResultIdMapper for
/// more explanation regarding this.
///
/// Note: legalization specific code
SpirvInstruction *loadIfAliasVarRef(const Expr *expr,
SourceRange rangeOverride = {});
/// Loads the pointer of the aliased-to-variable and ajusts aliasVarInfo
/// accordingly if aliasVarExpr is referencing an alias variable. Returns true
/// if aliasVarInfo is changed, false otherwise.
///
/// Note: legalization specific code
bool loadIfAliasVarRef(const Expr *aliasVarExpr,
SpirvInstruction **aliasVarInstr,
SourceRange rangeOverride = {});
/// Check whether a member value has a nointerpolation qualifier in its type
/// declaration or any parents' type declaration recursively.
bool isNoInterpMemberExpr(const MemberExpr *expr);
private:
/// Translates the given frontend binary operator into its SPIR-V equivalent
/// taking consideration of the operand type.
spv::Op translateOp(BinaryOperator::Opcode op, QualType type);
spv::Op translateWaveOp(hlsl::IntrinsicOp op, QualType type, SourceLocation);
/// Generates SPIR-V instructions for the given normal (non-intrinsic and
/// non-operator) standalone or member function call.
SpirvInstruction *processCall(const CallExpr *expr);
/// Generates the necessary instructions for assigning rhs to lhs. If lhsPtr
/// is not zero, it will be used as the pointer from lhs instead of evaluating
/// lhs again.
SpirvInstruction *processAssignment(const Expr *lhs, SpirvInstruction *rhs,
bool isCompoundAssignment,
SpirvInstruction *lhsPtr = nullptr,
SourceRange range = {});
/// Generates SPIR-V instructions to store rhsVal into lhsPtr. This will be
/// recursive if lhsValType is a composite type. rhsExpr will be used as a
/// reference to adjust the CodeGen if not nullptr.
void storeValue(SpirvInstruction *lhsPtr, SpirvInstruction *rhsVal,
QualType lhsValType, SourceLocation loc,
SourceRange range = {});
bool canUseOpCopyLogical(QualType type) const;
/// Decomposes and reconstructs the given srcVal of the given valType to meet
/// the requirements of the dstLR layout rule.
SpirvInstruction *reconstructValue(SpirvInstruction *srcVal, QualType valType,
SpirvLayoutRule dstLR, SourceLocation loc,
SourceRange range = {});
/// Generates the necessary instructions for conducting the given binary
/// operation on lhs and rhs.
///
/// computationType is the type for LHS and RHS when doing computation, while
/// resultType is the type of the whole binary operation. They can be
/// different for compound assignments like <some-int-value> *=
/// <some-float-value>, where computationType is float and resultType is int.
///
/// If lhsResultId is not nullptr, the evaluated pointer from lhs during the
/// process will be written into it. If mandateGenOpcode is not spv::Op::Max,
/// it will used as the SPIR-V opcode instead of deducing from Clang frontend
/// opcode.
SpirvInstruction *
processBinaryOp(const Expr *lhs, const Expr *rhs, BinaryOperatorKind opcode,
QualType computationType, QualType resultType, SourceRange,
SourceLocation, SpirvInstruction **lhsInfo = nullptr,
spv::Op mandateGenOpcode = spv::Op::Max);
/// Generates SPIR-V instructions to initialize the given variable once.
void initOnce(QualType varType, std::string varName, SpirvVariable *,
const Expr *varInit);
/// Returns true if the given expression will be translated into a vector
/// shuffle instruction in SPIR-V.
///
/// We emit a vector shuffle instruction iff
/// * We are not selecting only one element from the vector (OpAccessChain
/// or OpCompositeExtract for such case);
/// * We are not selecting all elements in their original order (essentially
/// the original vector, no shuffling needed).
bool isVectorShuffle(const Expr *expr);
/// Returns true if the given expression is a short-circuited operator.
bool isShortCircuitedOp(const Expr *expr);
/// Returns true if the given statement or any of its children are a
/// short-circuited operator.
bool stmtTreeContainsShortCircuitedOp(const Stmt *stmt);
/// \brief Returns true if the given CXXOperatorCallExpr is indexing into a
/// Buffer/RWBuffer/Texture/RWTexture using operator[].
/// On success, writes the base buffer into *base if base is not nullptr, and
/// writes the index into *index if index is not nullptr.
bool isBufferTextureIndexing(const CXXOperatorCallExpr *,
const Expr **base = nullptr,
const Expr **index = nullptr);
bool isDescriptorHeap(const Expr *expr);
void getDescriptorHeapOperands(const Expr *expr, const Expr **base,
const Expr **index);
/// \brief Returns true if the given CXXOperatorCallExpr is the .mips[][]
/// access into a Texture or .sample[][] access into Texture2DMS(Array). On
/// success, writes base texture object into *base if base is not nullptr,
/// writes the index into *index if index is not nullptr, and writes the mip
/// level (lod) to *lod if lod is not nullptr.
bool isTextureMipsSampleIndexing(const CXXOperatorCallExpr *indexExpr,
const Expr **base = nullptr,
const Expr **index = nullptr,
const Expr **lod = nullptr);
/// Condenses a sequence of HLSLVectorElementExpr starting from the given
/// expr into one. Writes the original base into *basePtr and the condensed
/// accessor into *flattenedAccessor.
void condenseVectorElementExpr(
const HLSLVectorElementExpr *expr, const Expr **basePtr,
hlsl::VectorMemberAccessPositions *flattenedAccessor);
/// Generates necessary SPIR-V instructions to create a vector splat out of
/// the given scalarExpr. The generated vector will have the same element
/// type as scalarExpr and of the given size. If resultIsConstant is not
/// nullptr, writes true to it if the generated instruction is a constant.
SpirvInstruction *createVectorSplat(const Expr *scalarExpr, uint32_t size,
SourceRange rangeOverride = {});
/// Splits the given vector into the last element and the rest (as a new
/// vector).
void splitVecLastElement(QualType vecType, SpirvInstruction *vec,
SpirvInstruction **residual,
SpirvInstruction **lastElement, SourceLocation loc);
/// Converts a vector value into the given struct type with its element type's
/// <result-id> as elemTypeId.
///
/// Assumes the vector and the struct have matching number of elements. Panics
/// otherwise.
SpirvInstruction *convertVectorToStruct(QualType structType,
QualType elemType,
SpirvInstruction *vector,
SourceLocation loc,
SourceRange range = {});
/// Translates a floatN * float multiplication into SPIR-V instructions and
/// returns the <result-id>. Returns 0 if the given binary operation is not
/// floatN * float.
SpirvInstruction *tryToGenFloatVectorScale(const BinaryOperator *expr);
/// Translates a floatMxN * float multiplication into SPIR-V instructions and
/// returns the <result-id>. Returns 0 if the given binary operation is not
/// floatMxN * float.
SpirvInstruction *tryToGenFloatMatrixScale(const BinaryOperator *expr);
/// Tries to emit instructions for assigning to the given vector element
/// accessing expression. Returns 0 if the trial fails and no instructions
/// are generated.
SpirvInstruction *tryToAssignToVectorElements(const Expr *lhs,
SpirvInstruction *rhs,
SourceRange range = {});
/// Tries to emit instructions for assigning to the given matrix element
/// accessing expression. Returns 0 if the trial fails and no instructions
/// are generated.
SpirvInstruction *tryToAssignToMatrixElements(const Expr *lhs,
SpirvInstruction *rhs,
SourceRange range = {});
/// Tries to emit instructions for assigning to the given RWBuffer/RWTexture
/// object. Returns 0 if the trial fails and no instructions are generated.
SpirvInstruction *tryToAssignToRWBufferRWTexture(const Expr *lhs,
SpirvInstruction *rhs,
SourceRange range = {});
/// Tries to emit instructions for assigning to the given mesh out attribute
/// or indices object. Returns 0 if the trial fails and no instructions are
/// generated.
SpirvInstruction *
tryToAssignToMSOutAttrsOrIndices(const Expr *lhs, SpirvInstruction *rhs,
SpirvInstruction *vecComponent = nullptr,
bool noWriteBack = false);
/// Emit instructions for assigning to the given mesh out attribute.
void assignToMSOutAttribute(
const DeclaratorDecl *decl, SpirvInstruction *value,
const llvm::SmallVector<SpirvInstruction *, 4> &indices);
/// Emit instructions for assigning to the given mesh out indices object.
void
assignToMSOutIndices(const DeclaratorDecl *decl, SpirvInstruction *value,
const llvm::SmallVector<SpirvInstruction *, 4> &indices);
/// Processes each vector within the given matrix by calling actOnEachVector.
/// matrixVal should be the loaded value of the matrix. actOnEachVector takes
/// three parameters for the current vector: the index, the <type-id>, and
/// the value. It returns the <result-id> of the processed vector.
SpirvInstruction *processEachVectorInMatrix(
const Expr *matrix, SpirvInstruction *matrixVal,
llvm::function_ref<SpirvInstruction *(uint32_t, QualType, QualType,
SpirvInstruction *)>
actOnEachVector,
SourceLocation loc = {}, SourceRange range = {});
SpirvInstruction *processEachVectorInMatrix(
const Expr *matrix, QualType outputType, SpirvInstruction *matrixVal,
llvm::function_ref<SpirvInstruction *(uint32_t, QualType, QualType,
SpirvInstruction *)>
actOnEachVector,
SourceLocation loc = {}, SourceRange range = {});
/// Translates the given varDecl into a spec constant.
void createSpecConstant(const VarDecl *varDecl);
/// Generates the necessary instructions for conducting the given binary
/// operation on lhs and rhs.
///
/// This method expects that both lhs and rhs are SPIR-V acceptable matrices.
SpirvInstruction *processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
const BinaryOperatorKind opcode,
SourceRange, SourceLocation);
/// Creates a temporary local variable in the current function of the given
/// varType and varName. Initializes the variable with the given initValue.
/// Returns the instruction pointer for the variable.
SpirvVariable *createTemporaryVar(QualType varType, llvm::StringRef varName,
SpirvInstruction *initValue,
SourceLocation loc);
/// Collects all indices from consecutive MemberExprs, ArraySubscriptExprs and
/// CXXOperatorCallExprs. Also special handles all mesh shader out attributes
/// to return the entire expression in order for caller to extract the member
/// expression.
const Expr *
collectArrayStructIndices(const Expr *expr, bool rawIndex,
llvm::SmallVectorImpl<uint32_t> *rawIndices,
llvm::SmallVectorImpl<SpirvInstruction *> *indices,
bool *isMSOutAttribute = nullptr,
bool *isNointerp = nullptr);
/// For L-values, creates an access chain to index into the given SPIR-V
/// evaluation result and returns the new SPIR-V evaluation result.
/// For R-values, stores it in a variable, then create the access chain and
/// return the evaluation result.
SpirvInstruction *derefOrCreatePointerToValue(
QualType baseType, SpirvInstruction *base, QualType elemType,
const llvm::SmallVector<SpirvInstruction *, 4> &indices,
SourceLocation loc, SourceRange range = {});
SpirvVariable *turnIntoLValue(QualType type, SpirvInstruction *source,
SourceLocation loc);
private:
/// Validates that vk::* attributes are used correctly and returns false if
/// errors are found.
bool validateVKAttributes(const NamedDecl *decl);
/// Records any Spir-V capabilities and extensions for the given varDecl so
/// they will be added to the SPIR-V module.
void registerCapabilitiesAndExtensionsForVarDecl(const VarDecl *varDecl);
private:
/// Converts the given value from the bitwidth of 'fromType' to the bitwidth
/// of 'toType'. If the two have the same bitwidth, returns the value itself.
/// If resultType is not nullptr, the resulting value's type will be written
/// to resultType. Panics if the given types are not scalar or vector of
/// float/integer type.
SpirvInstruction *convertBitwidth(SpirvInstruction *value, SourceLocation loc,
QualType fromType, QualType toType,
QualType *resultType = nullptr,
SourceRange range = {});
/// Processes the given expr, casts the result into the given bool (vector)
/// type and returns the <result-id> of the casted value.
SpirvInstruction *castToBool(SpirvInstruction *value, QualType fromType,
QualType toType, SourceLocation loc,
SourceRange range = {});
/// Processes the given expr, casts the result into the given integer (vector)
/// type and returns the <result-id> of the casted value.
SpirvInstruction *castToInt(SpirvInstruction *value, QualType fromType,
QualType toType, SourceLocation,
SourceRange srcRange = {});
/// Processes the given expr, casts the result into the given float (vector)
/// type and returns the <result-id> of the casted value.
SpirvInstruction *castToFloat(SpirvInstruction *value, QualType fromType,
QualType toType, SourceLocation,
SourceRange range = {});
private:
/// Processes HLSL instrinsic functions.
SpirvInstruction *processIntrinsicCallExpr(const CallExpr *);
/// Processes the 'clip' intrinsic function. Discards the current pixel if the
/// specified value is less than zero.
SpirvInstruction *processIntrinsicClip(const CallExpr *);
/// Processes the 'dst' intrinsic function.
SpirvInstruction *processIntrinsicDst(const CallExpr *);
/// Processes the 'clamp' intrinsic function.
SpirvInstruction *processIntrinsicClamp(const CallExpr *);
/// Processes the 'frexp' intrinsic function.
SpirvInstruction *processIntrinsicFrexp(const CallExpr *);
/// Processes the 'ldexp' intrinsic function.
SpirvInstruction *processIntrinsicLdexp(const CallExpr *);
/// Processes the 'D3DCOLORtoUBYTE4' intrinsic function.
SpirvInstruction *processD3DCOLORtoUBYTE4(const CallExpr *);
/// Processes the 'lit' intrinsic function.
SpirvInstruction *processIntrinsicLit(const CallExpr *);
/// Processes the 'vk::static_pointer_cast' and 'vk_reinterpret_pointer_cast'
/// intrinsic functions.
SpirvInstruction *processIntrinsicPointerCast(const CallExpr *,
bool isStatic);
/// Processes the vk::BufferPointer intrinsic function 'Get'.
SpirvInstruction *
processIntrinsicGetBufferContents(const CXXMemberCallExpr *);
/// Processes the 'Barrier' intrinsic function.
SpirvInstruction *processIntrinsicBarrier(const CallExpr *);
/// Processes the 'GroupMemoryBarrier', 'GroupMemoryBarrierWithGroupSync',
/// 'DeviceMemoryBarrier', 'DeviceMemoryBarrierWithGroupSync',
/// 'AllMemoryBarrier', and 'AllMemoryBarrierWithGroupSync' intrinsic
/// functions.
SpirvInstruction *processIntrinsicMemoryBarrier(const CallExpr *,
bool isDevice, bool groupSync,
bool isAllBarrier);
/// Processes the 'GetRemainingRecursionLevels' intrinsic function.
SpirvInstruction *
processIntrinsicGetRemainingRecursionLevels(const CallExpr *callExpr);
/// Processes the 'IsValid' intrinsic function.
SpirvInstruction *processIntrinsicIsValid(const CXXMemberCallExpr *callExpr);
/// Processes the 'Get' intrinsic function for (arrays of) node records and
/// the array subscript operator for node record arrays.
SpirvInstruction *
processIntrinsicExtractRecordStruct(const CXXMemberCallExpr *callExpr);
/// Processes the 'GetGroupNodeOutputRecords' and 'GetThreadNodeOutputRecords'
/// intrinsic functions.
SpirvInstruction *
processIntrinsicGetNodeOutputRecords(const CXXMemberCallExpr *callExpr,
bool isGroupShared);
/// Processes the 'IncrementOutputCount' intrinsic function.
SpirvInstruction *
processIntrinsicIncrementOutputCount(const CXXMemberCallExpr *callExpr,
bool isGroupShared);
/// Processes the 'Count' intrinsic function for node input record arrays.
SpirvInstruction *
processIntrinsicGetRecordCount(const CXXMemberCallExpr *callExpr);
/// Processes the 'OutputComplete' intrinsic function.
void processIntrinsicOutputComplete(const CXXMemberCallExpr *callExpr);
/// Processes the 'FinishedCrossGroupSharing' intrinsic function.
SpirvInstruction *
processIntrinsicFinishedCrossGroupSharing(const CXXMemberCallExpr *callExpr);
/// Processes the 'mad' intrinsic function.
SpirvInstruction *processIntrinsicMad(const CallExpr *);
/// Processes the 'modf' intrinsic function.
SpirvInstruction *processIntrinsicModf(const CallExpr *);
/// Processes the 'msad4' intrinsic function.
SpirvInstruction *processIntrinsicMsad4(const CallExpr *);
/// Processes the 'mul' intrinsic function.
SpirvInstruction *processIntrinsicMul(const CallExpr *);
/// Processes the 'printf' intrinsic function.
SpirvInstruction *processIntrinsicPrintf(const CallExpr *);
/// Transposes a non-floating point matrix and returns the result-id of the
/// transpose.
SpirvInstruction *processNonFpMatrixTranspose(QualType matType,
SpirvInstruction *matrix,
SourceLocation loc,
SourceRange range = {});
/// Processes the dot product of two non-floating point vectors. The SPIR-V
/// OpDot only accepts float vectors. Assumes that the two vectors are of the
/// same size and have the same element type (elemType).
SpirvInstruction *processNonFpDot(SpirvInstruction *vec1Id,
SpirvInstruction *vec2Id, uint32_t vecSize,
QualType elemType, SourceLocation loc,
SourceRange range = {});
/// Processes the multiplication of a *non-floating point* matrix by a scalar.
/// Assumes that the matrix element type and the scalar type are the same.
SpirvInstruction *
processNonFpScalarTimesMatrix(QualType scalarType, SpirvInstruction *scalar,
QualType matType, SpirvInstruction *matrix,
SourceLocation loc, SourceRange range = {});
/// Processes the multiplication of a *non-floating point* matrix by a vector.
/// Assumes the matrix element type and the vector element type are the same.
/// Notice that the vector in this case is a "row vector" and will be
/// multiplied by the matrix columns (dot product). As a result, the given
/// matrix must be transposed in order to easily get each column. If
/// 'matrixTranspose' is non-zero, it will be used as the transpose matrix
/// result-id; otherwise the function will perform the transpose itself.
SpirvInstruction *processNonFpVectorTimesMatrix(
QualType vecType, SpirvInstruction *vector, QualType matType,
SpirvInstruction *matrix, SourceLocation loc,
SpirvInstruction *matrixTranspose = nullptr, SourceRange range = {});
/// Processes the multiplication of a vector by a *non-floating point* matrix.
/// Assumes the matrix element type and the vector element type are the same.
SpirvInstruction *
processNonFpMatrixTimesVector(QualType matType, SpirvInstruction *matrix,
QualType vecType, SpirvInstruction *vector,
SourceLocation loc, SourceRange range = {});
/// Processes a non-floating point matrix multiplication. Assumes that the
/// number of columns in lhs matrix is the same as number of rows in the rhs
/// matrix. Also assumes that the two matrices have the same element type.
SpirvInstruction *
processNonFpMatrixTimesMatrix(QualType lhsType, SpirvInstruction *lhs,
QualType rhsType, SpirvInstruction *rhs,
SourceLocation loc, SourceRange range = {});
/// Processes the 'dot' intrinsic function.
SpirvInstruction *processIntrinsicDot(const CallExpr *);
/// Processes the 'log10' intrinsic function.
SpirvInstruction *processIntrinsicLog10(const CallExpr *);
/// Processes the 'all' and 'any' intrinsic functions.
SpirvInstruction *processIntrinsicAllOrAny(const CallExpr *, spv::Op);
/// Processes the 'asfloat', 'asint', and 'asuint' intrinsic functions.
SpirvInstruction *processIntrinsicAsType(const CallExpr *);
/// Processes the 'saturate' intrinsic function.
SpirvInstruction *processIntrinsicSaturate(const CallExpr *);
/// Processes the 'sincos' intrinsic function.
SpirvInstruction *processIntrinsicSinCos(const CallExpr *);
/// Processes the 'isFinite' intrinsic function.
SpirvInstruction *processIntrinsicIsFinite(const CallExpr *);
/// Processes the 'isNormal' intrinsic function.
SpirvInstruction *processIntrinsicIsNormal(const CallExpr *);
/// Processes the 'rcp' intrinsic function.
SpirvInstruction *processIntrinsicRcp(const CallExpr *);
/// Processes the 'ReadClock' intrinsic function.
SpirvInstruction *processIntrinsicReadClock(const CallExpr *);
/// Processes the 'sign' intrinsic function for unsigned integer types.
SpirvInstruction *processIntrinsicSignUnsignedInt(const CallExpr *callExpr);
/// Processes the 'sign' intrinsic function for float types.
/// The FSign instruction in the GLSL instruction set returns a floating point
/// result. The HLSL sign function, however, returns an integer. An extra
/// casting from float to integer is therefore performed by this method.
SpirvInstruction *processIntrinsicFloatSign(const CallExpr *);
/// Processes the 'f16to32' intrinsic function.
SpirvInstruction *processIntrinsicF16ToF32(const CallExpr *);
/// Processes the 'f32tof16' intrinsic function.
SpirvInstruction *processIntrinsicF32ToF16(const CallExpr *);
/// Processes the given intrinsic function call using the given GLSL
/// extended instruction. If the given instruction cannot operate on matrices,
/// it performs the instruction on each row of the matrix and uses composite
/// construction to generate the resulting matrix.
SpirvInstruction *processIntrinsicUsingGLSLInst(const CallExpr *,
GLSLstd450 instr,
bool canOperateOnMatrix,
SourceLocation,
SourceRange range = {});
/// Processes the given intrinsic function call using the given SPIR-V
/// instruction. If the given instruction cannot operate on matrices, it
/// performs the instruction on each row of the matrix and uses composite
/// construction to generate the resulting matrix.
SpirvInstruction *processIntrinsicUsingSpirvInst(const CallExpr *, spv::Op,
bool canOperateOnMatrix);
/// Processes the given intrinsic member call.
SpirvInstruction *processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
hlsl::IntrinsicOp opcode);
/// Processes EvaluateAttributeAt* intrinsic calls.
SpirvInstruction *processEvaluateAttributeAt(const CallExpr *expr,
hlsl::IntrinsicOp opcode,
SourceLocation loc,
SourceRange range);
/// Processes Interlocked* intrinsic functions.
SpirvInstruction *processIntrinsicInterlockedMethod(const CallExpr *,
hlsl::IntrinsicOp);
/// Processes SM6.0 wave query intrinsic calls.
SpirvInstruction *processWaveQuery(const CallExpr *, spv::Op opcode);
/// Processes SM6.6 IsHelperLane intrisic calls.
SpirvInstruction *processIsHelperLane(const CallExpr *, SourceLocation loc,
SourceRange range);
/// Processes SM6.0 wave vote intrinsic calls.
SpirvInstruction *processWaveVote(const CallExpr *, spv::Op opcode);
/// Processes SM6.0 wave active/prefix count bits.
SpirvInstruction *processWaveCountBits(const CallExpr *,
spv::GroupOperation groupOp);
/// Processes SM6.0 wave reduction or scan/prefix and SM6.5 wave multiprefix
/// intrinsic calls.
SpirvInstruction *processWaveReductionOrPrefix(const CallExpr *, spv::Op op,
spv::GroupOperation groupOp);
/// Processes SM6.0 wave broadcast intrinsic calls.
SpirvInstruction *processWaveBroadcast(const CallExpr *);
/// Processes SM6.0 quad-wide shuffle.
SpirvInstruction *processWaveQuadWideShuffle(const CallExpr *,
hlsl::IntrinsicOp op);
/// Processes SM6.7 quad any/all.
SpirvInstruction *processWaveQuadAnyAll(const CallExpr *,
hlsl::IntrinsicOp op);
/// Generates the Spir-V instructions needed to implement the given call to
/// WaveActiveAllEqual. Returns a pointer to the instruction that produces the
/// final result.
SpirvInstruction *processWaveActiveAllEqual(const CallExpr *);
/// Generates the Spir-V instructions needed to implement WaveActiveAllEqual
/// with the scalar input `arg`. Returns a pointer to the instruction that
/// produces the final result. srcLoc should be the source location of the
/// original call.
SpirvInstruction *
processWaveActiveAllEqualScalar(SpirvInstruction *arg,
clang::SourceLocation srcLoc);
/// Generates the Spir-V instructions needed to implement WaveActiveAllEqual
/// with the vector input `arg`. Returns a pointer to the instruction that
/// produces the final result. srcLoc should be the source location of the
/// original call.
SpirvInstruction *
processWaveActiveAllEqualVector(SpirvInstruction *arg,
clang::SourceLocation srcLoc);
/// Generates the Spir-V instructions needed to implement WaveActiveAllEqual
/// with the matrix input `arg`. Returns a pointer to the instruction that
/// produces the final result. srcLoc should be the source location of the
/// original call.
SpirvInstruction *
processWaveActiveAllEqualMatrix(SpirvInstruction *arg, QualType,
clang::SourceLocation srcLoc);
/// Processes SM6.5 WaveMatch function.
SpirvInstruction *processWaveMatch(const CallExpr *);
/// Processes the NonUniformResourceIndex intrinsic function.
SpirvInstruction *processIntrinsicNonUniformResourceIndex(const CallExpr *);
/// Processes the SM 6.4 dot4add_{i|u}8packed intrinsic functions.
SpirvInstruction *processIntrinsicDP4a(const CallExpr *callExpr,
hlsl::IntrinsicOp op);
/// Processes the SM 6.4 dot2add intrinsic function.
SpirvInstruction *processIntrinsicDP2a(const CallExpr *callExpr);
/// Processes the SM 6.6 pack_{s|u}8 and pack_clamp_{s|u}8 intrinsic
/// functions.
SpirvInstruction *processIntrinsic8BitPack(const CallExpr *,
hlsl::IntrinsicOp);
/// Processes the SM 6.6 unpack_{s|u}8{s|u}{16|32} intrinsic functions.
SpirvInstruction *processIntrinsic8BitUnpack(const CallExpr *,
hlsl::IntrinsicOp);
/// Process builtins specific to raytracing.
SpirvInstruction *processRayBuiltins(const CallExpr *, hlsl::IntrinsicOp op);
/// Process raytracing intrinsics.
SpirvInstruction *processReportHit(const CallExpr *);
void processCallShader(const CallExpr *callExpr);
void processTraceRay(const CallExpr *callExpr);
/// Process amplification shader intrinsics.
void processDispatchMesh(const CallExpr *callExpr);
/// Process mesh shader intrinsics.
void processMeshOutputCounts(const CallExpr *callExpr);
/// Process GetAttributeAtVertex for barycentrics.
SpirvInstruction *processGetAttributeAtVertex(const CallExpr *expr);
/// Process ray query traceinline intrinsics.
SpirvInstruction *processTraceRayInline(const CXXMemberCallExpr *expr);
/// Process ray query intrinsics
SpirvInstruction *processRayQueryIntrinsics(const CXXMemberCallExpr *expr,
hlsl::IntrinsicOp opcode);
/// Process spirv intrinsic instruction
SpirvInstruction *processSpvIntrinsicCallExpr(const CallExpr *expr);
/// Process spirv intrinsic type definition
SpirvInstruction *processSpvIntrinsicTypeDef(const CallExpr *expr);
/// Process `T vk::RawBufferLoad<T>(in uint64_t address
/// [, in uint alignment])` that loads data from a given device address.
SpirvInstruction *processRawBufferLoad(const CallExpr *callExpr);
SpirvInstruction *loadDataFromRawAddress(SpirvInstruction *addressInUInt64,
QualType bufferType,
uint32_t alignment,
SourceLocation loc);
/// Process `void vk::RawBufferStore<T>(in uint64_t address, in T value
/// [, in uint alignment])` that stores data to a given device address.
SpirvInstruction *processRawBufferStore(const CallExpr *callExpr);
SpirvInstruction *storeDataToRawAddress(SpirvInstruction *addressInUInt64,
SpirvInstruction *value,
QualType bufferType,
uint32_t alignment,
SourceLocation loc,
SourceRange range);
/// Returns the value of the alignment argument for `vk::RawBufferLoad()` and
/// `vk::RawBufferStore()`.
uint32_t getRawBufferAlignment(const Expr *expr);
/// Returns a spirv OpCooperativeMatrixLengthKHR instruction generated from a
/// call to __builtin_spv_CooperativeMatrixLengthKHR.
SpirvInstruction *processCooperativeMatrixGetLength(const CallExpr *call);
/// Process vk::ext_execution_mode intrinsic
SpirvInstruction *processIntrinsicExecutionMode(const CallExpr *expr);
/// Process vk::ext_execution_mode_id intrinsic
SpirvInstruction *processIntrinsicExecutionModeId(const CallExpr *expr);
/// Processes the 'firstbit{high|low}' intrinsic functions.
SpirvInstruction *processIntrinsicFirstbit(const CallExpr *,
GLSLstd450 glslOpcode);
SpirvInstruction *
processMatrixDerivativeIntrinsic(hlsl::IntrinsicOp hlslOpcode,
const Expr *arg, SourceLocation loc,
SourceRange range);
SpirvInstruction *processDerivativeIntrinsic(hlsl::IntrinsicOp hlslOpcode,
const Expr *arg,
SourceLocation loc,
SourceRange range);
SpirvInstruction *processDerivativeIntrinsic(hlsl::IntrinsicOp hlslOpcode,
SpirvInstruction *arg,
SourceLocation loc,
SourceRange range);
SpirvInstruction *processCountBitsIntrinsic(const CallExpr *callExpr,
clang::SourceLocation srcLoc);
SpirvInstruction *generateCountBits16(const CallExpr *callExpr,
clang::SourceLocation srcLoc);
SpirvInstruction *generateCountBits64(const CallExpr *callExpr,
clang::SourceLocation srcLoc);
// Processes the `reversebits` intrinsic
SpirvInstruction *processReverseBitsIntrinsic(const CallExpr *expr,
clang::SourceLocation srcLoc);
// Processes the `reversebits` intrinsic for 16-bit integer types
SpirvInstruction *generate16BitReverse(const CallExpr *expr,
clang::SourceLocation srcLoc);
// Processes the `reversebits` intrinsic for 64-bit integer types
SpirvInstruction *generate64BitReverse(const CallExpr *expr,
clang::SourceLocation srcLoc);
private:
/// Returns the <result-id> for constant value 0 of the given type.
SpirvConstant *getValueZero(QualType type);
/// Returns the <result-id> for a constant zero vector of the given size and
/// element type.
SpirvConstant *getVecValueZero(QualType elemType, uint32_t size);
/// Returns the <result-id> for constant value 1 of the given type.
SpirvConstant *getValueOne(QualType type);
/// Returns the <result-id> for a constant one vector of the given size and
/// element type.
SpirvConstant *getVecValueOne(QualType elemType, uint32_t size);
/// Returns the <result-id> for a constant one (vector) having the same
/// element type as the given matrix type.
///
/// If a 1x1 matrix is given, the returned value one will be a scalar;
/// if a Mx1 or 1xN matrix is given, the returned value one will be a
/// vector of size M or N; if a MxN matrix is given, the returned value
/// one will be a vector of size N.
SpirvConstant *getMatElemValueOne(QualType type);
/// Returns a SPIR-V constant equal to the bitwdith of the given type minus
/// one. The returned constant has the same component count and bitwidth as
/// the given type.
SpirvConstant *getMaskForBitwidthValue(QualType type);
private:
/// \brief Performs a FlatConversion implicit cast. Fills an instance of the
/// given type with initializer <result-id>.
SpirvInstruction *processFlatConversion(const QualType type,
SpirvInstruction *initId,
SourceLocation,
SourceRange range = {});
private:
/// Translates the given HLSL loop attribute into SPIR-V loop control mask.
/// Emits an error if the given attribute is not a loop attribute.
spv::LoopControlMask translateLoopAttribute(const Stmt *, const Attr &);
static hlsl::ShaderModel::Kind getShaderModelKind(StringRef stageName);
static spv::ExecutionModel getSpirvShaderStage(hlsl::ShaderModel::Kind smk,
bool);
void checkForWaveSizeAttr(const FunctionDecl *decl);
/// \brief Handle inline SPIR-V attributes for the entry function.
void processInlineSpirvAttributes(const FunctionDecl *entryFunction);
/// \brief Adds necessary execution modes for the hull/domain shaders based on
/// the HLSL attributes of the entry point function.
/// In the case of hull shaders, also writes the number of output control
/// points to *numOutputControlPoints. Returns true on success, and false on
/// failure.
bool processTessellationShaderAttributes(const FunctionDecl *entryFunction,
uint32_t *numOutputControlPoints);
/// \brief Adds necessary execution modes for the geometry shader based on the
/// HLSL attributes of the entry point function. Also writes the array size of
/// the input, which depends on the primitive type, to *arraySize.
bool processGeometryShaderAttributes(const FunctionDecl *entryFunction,
uint32_t *arraySize);
/// \brief Adds necessary execution modes for the pixel shader based on the
/// HLSL attributes of the entry point function.
void processPixelShaderAttributes(const FunctionDecl *decl);
/// \brief Adds necessary execution modes for the compute shader based on the
/// HLSL attributes of the entry point function.
void processComputeShaderAttributes(const FunctionDecl *entryFunction);
/// \brief Adds necessary execution modes for the node shader based on the
/// HLSL attributes of the entry point function.
void processNodeShaderAttributes(const FunctionDecl *entryFunction);
/// \brief Adds necessary execution modes for the mesh/amplification shader
/// based on the HLSL attributes of the entry point function.
bool
processMeshOrAmplificationShaderAttributes(const FunctionDecl *decl,
uint32_t *outVerticesArraySize);
/// \brief Emits a SpirvDebugFunction to match given SpirvFunction, and
/// returns a pointer to it.
SpirvDebugFunction *emitDebugFunction(const FunctionDecl *decl,
SpirvFunction *func,
RichDebugInfo **info, std::string name);
/// \brief Emits a wrapper function for the entry function and returns a
/// pointer to the wrapper SpirvFunction on success.
///
/// The wrapper function loads the values of all stage input variables and
/// creates composites as expected by the source code entry function. It then
/// calls the source code entry point and writes out stage output variables
/// by extracting sub-values from the return value. In this way, we can handle
/// the source code entry point as a normal function.
///
/// The wrapper function is also responsible for initializing global static
/// variables for some cases.
SpirvFunction *emitEntryFunctionWrapper(const FunctionDecl *entryFunction,
RichDebugInfo **info,
SpirvDebugFunction **debugFunction,
SpirvFunction *entryFuncId);
/// \brief Emits a wrapper function for the entry functions for raytracing
/// stages and returns true on success.
///
/// Wrapper is specific to raytracing stages since for specific stages we
/// create specific module scoped stage variables and perform copies to them.
/// The wrapper function is also responsible for initializing global static
/// variables for some cases.
bool emitEntryFunctionWrapperForRayTracing(const FunctionDecl *entryFunction,
RichDebugInfo **info,
SpirvDebugFunction *debugFunction,
SpirvFunction *entryFuncId);
/// \brief Performs the following operations for the Hull shader:
/// * Creates an output variable which is an Array containing results for all
/// control points.
///
/// * If the Patch Constant Function (PCF) takes the Hull main entry function
/// results (OutputPatch), it creates a temporary function-scope variable that
/// is then passed to the PCF.
///
/// * Adds a control barrier (OpControlBarrier) to ensure all invocations are
/// done before PCF is called.
///