@@ -42,6 +42,9 @@ constexpr uint32_t SPIRV_HOST_ACCESS_DEFAULT_VALUE = 2; // Read/Write
42
42
constexpr uint32_t SPIRV_INITIATION_INTERVAL_DECOR = 5917 ;
43
43
constexpr uint32_t SPIRV_PIPELINE_ENABLE_DECOR = 5919 ;
44
44
45
+ constexpr uint32_t SPIRV_CACHE_CONTROL_READ_DECOR = 6442 ;
46
+ constexpr uint32_t SPIRV_CACHE_CONTROL_WRITE_DECOR = 6443 ;
47
+
45
48
enum class DecorValueTy {
46
49
uint32,
47
50
boolean,
@@ -97,6 +100,72 @@ MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
97
100
return MDNode::get (Ctx, MD);
98
101
}
99
102
103
+ // / Builds a metadata node for a SPIR-V decoration for cache controls
104
+ // / where decoration code and value are both uint32_t integers.
105
+ // / The value encodes a cache level and a cache control type.
106
+ // /
107
+ // / @param Ctx [in] the LLVM Context.
108
+ // / @param Name [in] the SPIR-V property string name.
109
+ // / @param OpCode [in] the SPIR-V opcode.
110
+ // / @param CacheMode [in] whether read or write.
111
+ // / @param CacheLevel [in] the cache level.
112
+ // /
113
+ // / @returns a pointer to the metadata node created for the required decoration
114
+ // / and its values.
115
+ MDNode *buildSpirvDecorCacheProp (LLVMContext &Ctx, StringRef Name,
116
+ uint32_t OpCode, uint32_t CacheMode,
117
+ uint32_t CacheLevel) {
118
+ // SPIR-V encodings of read control
119
+ enum cache_control_read_type {
120
+ read_uncached = 0 ,
121
+ read_cached = 1 ,
122
+ read_streaming = 2 ,
123
+ read_invalidate = 3 ,
124
+ read_const_cached = 4
125
+ };
126
+ // SPIR-V encodings of write control
127
+ enum cache_control_write_type {
128
+ write_uncached = 0 ,
129
+ write_through = 1 ,
130
+ write_back = 2 ,
131
+ write_streaming = 3
132
+ };
133
+ // SYCL encodings of read/write control. Definition of cache_mode should match
134
+ // definition in SYCL header file cache_control_properties.hpp.
135
+ enum class cache_mode {
136
+ uncached,
137
+ cached,
138
+ streaming,
139
+ invalidate,
140
+ constant,
141
+ write_through,
142
+ write_back
143
+ };
144
+ static uint32_t SPIRVReadControl[] = {read_uncached, read_cached,
145
+ read_streaming, read_invalidate,
146
+ read_const_cached};
147
+ static uint32_t SPIRVWriteControl[] = {
148
+ write_uncached, write_uncached, write_streaming, write_uncached,
149
+ write_uncached, write_through, write_back};
150
+
151
+ // Map SYCL encoding to SPIR-V
152
+ uint32_t CacheProp;
153
+ if (Name.starts_with (" sycl-cache-read" ))
154
+ CacheProp = SPIRVReadControl[CacheMode];
155
+ else
156
+ CacheProp = SPIRVWriteControl[CacheMode];
157
+
158
+ auto *Ty = Type::getInt32Ty (Ctx);
159
+ SmallVector<Metadata *, 3 > MD;
160
+ MD.push_back (ConstantAsMetadata::get (
161
+ Constant::getIntegerValue (Ty, APInt (32 , OpCode))));
162
+ MD.push_back (ConstantAsMetadata::get (
163
+ Constant::getIntegerValue (Ty, APInt (32 , CacheLevel))));
164
+ MD.push_back (ConstantAsMetadata::get (
165
+ Constant::getIntegerValue (Ty, APInt (32 , CacheProp))));
166
+ return MDNode::get (Ctx, MD);
167
+ }
168
+
100
169
// / Builds a metadata node for a SPIR-V decoration (decoration code
101
170
// / is \c uint32_t integer and value is a string).
102
171
// /
@@ -625,9 +694,12 @@ bool CompileTimePropertiesPass::transformSYCLPropertiesAnnotation(
625
694
// check alignment annotation and apply it to load/store
626
695
parseAlignmentAndApply (M, IntrInst);
627
696
628
- // Read the annotation values and create the new annotation string .
697
+ // Read the annotation values and create new annotation strings .
629
698
std::string NewAnnotString = " " ;
630
699
auto Properties = parseSYCLPropertiesString (M, IntrInst);
700
+ SmallVector<Metadata *, 8 > MDOpsCacheProp;
701
+ bool CacheProp = false ;
702
+ bool FPGAProp = false ;
631
703
for (const auto &[PropName, PropVal] : Properties) {
632
704
// sycl-alignment is converted to align on
633
705
// previous parseAlignmentAndApply(), dropping here
@@ -639,59 +711,118 @@ bool CompileTimePropertiesPass::transformSYCLPropertiesAnnotation(
639
711
continue ;
640
712
uint32_t DecorCode = DecorIt->second .Code ;
641
713
642
- // Expected format is '{X}' or '{X:Y}' where X is decoration ID and
643
- // Y is the value if present. It encloses Y in " to ensure that
644
- // string values are handled correctly. Note that " around values are
645
- // always valid, even if the decoration parameters are not strings.
646
- NewAnnotString += " {" + std::to_string (DecorCode);
647
- if (PropVal)
648
- NewAnnotString += " :\" " + PropVal->str ();
649
-
650
- if (PropName == " sycl-prefetch-hint" )
651
- NewAnnotString += " ,1" ; // CachedINTEL
652
- if (PropName == " sycl-prefetch-hint-nt" )
653
- NewAnnotString += " ,3" ; // InvalidateAfterReadINTEL
654
-
655
- if (PropVal)
656
- NewAnnotString += " \" " ;
657
- NewAnnotString += " }" ;
714
+ // Handle cache control properties
715
+ if ((*PropName).starts_with (" sycl-cache-" )) {
716
+ CacheProp = true ;
717
+ auto DecorValue = PropVal;
718
+ uint32_t AttrVal;
719
+ DecorValue->getAsInteger (0 , AttrVal);
720
+ // Format is:
721
+ // !Annot = !{!CC1, !CC2, ...}
722
+ // !CC1 = !{i32 Load/Store, i32 Level, i32 Control}
723
+ // !CC2 = !{i32 Load/Store, i32 Level, i32 Control}
724
+ // ...
725
+ LLVMContext &Ctx = M.getContext ();
726
+ uint32_t CacheMode = 0 ;
727
+ while (AttrVal) {
728
+ // The attribute value encodes cache control and levels.
729
+ // Low-order to high-order nibbles hold cache levels specified for the
730
+ // enumerated SYCL cache modes. Lowest order nibble for uncached, next
731
+ // for cached, and so on.
732
+ // In each nibble cache levels are encoded as L1=1, L2=2, L3=4 and L4=8.
733
+ // The SPIR-V encoding of cache levels L1..L4 uses values 0..3.
734
+ uint32_t CacheLevel = 0 ;
735
+ uint32_t LevelMask = AttrVal & 0xf ;
736
+ while (LevelMask) {
737
+ if (LevelMask & 1 )
738
+ MDOpsCacheProp.push_back (buildSpirvDecorCacheProp (
739
+ Ctx, *PropName, DecorCode, CacheMode, CacheLevel));
740
+ ++CacheLevel;
741
+ LevelMask >>= 1 ;
742
+ }
743
+ ++CacheMode;
744
+ AttrVal >>= 4 ;
745
+ }
746
+ } else {
747
+ FPGAProp = true ;
748
+ // Expected format is '{X}' or '{X:Y}' where X is decoration ID and
749
+ // Y is the value if present. It encloses Y in " to ensure that
750
+ // string values are handled correctly. Note that " around values are
751
+ // always valid, even if the decoration parameters are not strings.
752
+ NewAnnotString += " {" + std::to_string (DecorCode);
753
+ if (PropVal)
754
+ NewAnnotString += " :\" " + PropVal->str ();
755
+
756
+ if (PropName == " sycl-prefetch-hint" )
757
+ NewAnnotString += " ,1" ; // CachedINTEL
758
+ if (PropName == " sycl-prefetch-hint-nt" )
759
+ NewAnnotString += " ,3" ; // InvalidateAfterReadINTEL
760
+
761
+ if (PropVal)
762
+ NewAnnotString += " \" " ;
763
+ NewAnnotString += " }" ;
764
+ }
658
765
}
659
766
660
- // If the new annotation string is empty there is no reason to keep it, so
661
- // replace it with the first operand and mark it for removal.
662
- if (NewAnnotString.empty ()) {
767
+ // If there are no other annotations (except "alignment") then there is no
768
+ // reason to keep the original intrinsic, so replace it with the first operand
769
+ // and mark it for removal.
770
+ if (!CacheProp && !FPGAProp) {
663
771
IntrInst->replaceAllUsesWith (IntrInst->getOperand (0 ));
664
772
RemovableAnnotations.push_back (IntrInst);
665
773
return true ;
666
774
}
667
775
668
- // Either reuse a previously generated one or create a new global variable
669
- // with the new annotation string.
670
- GlobalVariable *NewAnnotStringGV = nullptr ;
671
- auto ExistingNewAnnotStringIt = ReusableAnnotStrings.find (NewAnnotString);
672
- if (ExistingNewAnnotStringIt != ReusableAnnotStrings.end ()) {
673
- NewAnnotStringGV = ExistingNewAnnotStringIt->second ;
674
- } else {
675
- Constant *NewAnnotStringData =
676
- ConstantDataArray::getString (M.getContext (), NewAnnotString);
677
- NewAnnotStringGV = new GlobalVariable (
678
- M, NewAnnotStringData->getType (), true , GlobalValue::PrivateLinkage,
679
- NewAnnotStringData, " .str" , nullptr , llvm::GlobalValue::NotThreadLocal,
680
- IntrAnnotStringArg->getType ()->getPointerAddressSpace ());
681
- NewAnnotStringGV->setSection (AnnotStrArgGV->getSection ());
682
- NewAnnotStringGV->setUnnamedAddr (GlobalValue::UnnamedAddr::Global);
683
- ReusableAnnotStrings.insert ({NewAnnotString, NewAnnotStringGV});
776
+ if (FPGAProp) {
777
+ // Either reuse a previously generated one or create a new global variable
778
+ // with the new annotation string.
779
+ GlobalVariable *NewAnnotStringGV = nullptr ;
780
+ auto ExistingNewAnnotStringIt = ReusableAnnotStrings.find (NewAnnotString);
781
+ if (ExistingNewAnnotStringIt != ReusableAnnotStrings.end ()) {
782
+ NewAnnotStringGV = ExistingNewAnnotStringIt->second ;
783
+ } else {
784
+ Constant *NewAnnotStringData =
785
+ ConstantDataArray::getString (M.getContext (), NewAnnotString);
786
+ NewAnnotStringGV = new GlobalVariable (
787
+ M, NewAnnotStringData->getType (), true , GlobalValue::PrivateLinkage,
788
+ NewAnnotStringData, " .str" , nullptr ,
789
+ llvm::GlobalValue::NotThreadLocal,
790
+ IntrAnnotStringArg->getType ()->getPointerAddressSpace ());
791
+ NewAnnotStringGV->setSection (AnnotStrArgGV->getSection ());
792
+ NewAnnotStringGV->setUnnamedAddr (GlobalValue::UnnamedAddr::Global);
793
+ ReusableAnnotStrings.insert ({NewAnnotString, NewAnnotStringGV});
794
+ }
795
+
796
+ // Replace the annotation string with a bitcast of the new global variable.
797
+ IntrInst->setArgOperand (
798
+ 1 , ConstantExpr::getBitCast (NewAnnotStringGV,
799
+ IntrAnnotStringArg->getType ()));
800
+
801
+ // The values are now in the annotation string, so we can remove the
802
+ // original annotation value.
803
+ PointerType *Arg4PtrTy =
804
+ cast<PointerType>(IntrInst->getArgOperand (4 )->getType ());
805
+ IntrInst->setArgOperand (4 , ConstantPointerNull::get (Arg4PtrTy));
684
806
}
685
807
686
- // Replace the annotation string with a bitcast of the new global variable.
687
- IntrInst->setArgOperand (
688
- 1 , ConstantExpr::getBitCast (NewAnnotStringGV,
689
- IntrAnnotStringArg->getType ()));
808
+ if (CacheProp) {
809
+ LLVMContext &Ctx = M.getContext ();
810
+ unsigned MDKindID = Ctx.getMDKindID (SPIRV_DECOR_MD_KIND);
811
+ if (!FPGAProp) {
812
+ // If there are no annotations other than cache controls we can apply the
813
+ // controls to the pointer and remove the intrinsic.
814
+ auto PtrInstr = cast<Instruction>(IntrInst->getArgOperand (0 ));
815
+ PtrInstr->setMetadata (MDKindID, MDTuple::get (Ctx, MDOpsCacheProp));
816
+ // Replace all uses of IntrInst with first operand
817
+ IntrInst->replaceAllUsesWith (PtrInstr);
818
+ // Delete the original IntrInst
819
+ RemovableAnnotations.push_back (IntrInst);
820
+ } else {
821
+ // If there were FPGA annotations then we retain the original intrinsic
822
+ // and apply the cache control properties to its result.
823
+ IntrInst->setMetadata (MDKindID, MDTuple::get (Ctx, MDOpsCacheProp));
824
+ }
825
+ }
690
826
691
- // The values are not in the annotation string, so we can remove the original
692
- // annotation value.
693
- PointerType *Arg4PtrTy =
694
- cast<PointerType>(IntrInst->getArgOperand (4 )->getType ());
695
- IntrInst->setArgOperand (4 , ConstantPointerNull::get (Arg4PtrTy));
696
827
return true ;
697
828
}
0 commit comments