@@ -42,6 +42,9 @@ constexpr uint32_t SPIRV_HOST_ACCESS_DEFAULT_VALUE = 2; // Read/Write
4242constexpr uint32_t SPIRV_INITIATION_INTERVAL_DECOR = 5917 ;
4343constexpr uint32_t SPIRV_PIPELINE_ENABLE_DECOR = 5919 ;
4444
45+ constexpr uint32_t SPIRV_CACHE_CONTROL_READ_DECOR = 6442 ;
46+ constexpr uint32_t SPIRV_CACHE_CONTROL_WRITE_DECOR = 6443 ;
47+
4548enum class DecorValueTy {
4649 uint32,
4750 boolean,
@@ -97,6 +100,72 @@ MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
97100 return MDNode::get (Ctx, MD);
98101}
99102
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+
100169// / Builds a metadata node for a SPIR-V decoration (decoration code
101170// / is \c uint32_t integer and value is a string).
102171// /
@@ -625,9 +694,12 @@ bool CompileTimePropertiesPass::transformSYCLPropertiesAnnotation(
625694 // check alignment annotation and apply it to load/store
626695 parseAlignmentAndApply (M, IntrInst);
627696
628- // Read the annotation values and create the new annotation string .
697+ // Read the annotation values and create new annotation strings .
629698 std::string NewAnnotString = " " ;
630699 auto Properties = parseSYCLPropertiesString (M, IntrInst);
700+ SmallVector<Metadata *, 8 > MDOpsCacheProp;
701+ bool CacheProp = false ;
702+ bool FPGAProp = false ;
631703 for (const auto &[PropName, PropVal] : Properties) {
632704 // sycl-alignment is converted to align on
633705 // previous parseAlignmentAndApply(), dropping here
@@ -639,59 +711,118 @@ bool CompileTimePropertiesPass::transformSYCLPropertiesAnnotation(
639711 continue ;
640712 uint32_t DecorCode = DecorIt->second .Code ;
641713
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+ }
658765 }
659766
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) {
663771 IntrInst->replaceAllUsesWith (IntrInst->getOperand (0 ));
664772 RemovableAnnotations.push_back (IntrInst);
665773 return true ;
666774 }
667775
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));
684806 }
685807
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+ }
690826
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));
696827 return true ;
697828}
0 commit comments