Skip to content

Commit 348d3cf

Browse files
author
Artem Gindinson
authored
[RFC] Improve code for IVDep backwards translation (#1084)
* [RFC] Improve code for IVDep backwards translation This refactoring phase comes down to moving the translation algorithm out of the already-cluttered `SPIRVToLLVM::setLLVMLoopMetadata()` body. For now, a static member-only class is employed: it provides encapsulation for helper functions while avoiding the unnecessary complexity that "true" class entities would bring. A simple `LoopsEmitted` set is used to guard against duplication of metadata for a particular loop by the callers. Signed-off-by: Artem Gindinson <[email protected]>
1 parent 2eaec4b commit 348d3cf

File tree

1 file changed

+135
-102
lines changed

1 file changed

+135
-102
lines changed

lib/SPIRV/SPIRVReader.cpp

Lines changed: 135 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,135 @@ SPIRVToLLVM::getMetadataFromNameAndParameter(std::string Name,
749749
return llvm::MDNode::get(*Context, Metadata);
750750
}
751751

752+
class IVDepMetadataEmitter {
753+
public:
754+
using PointerSafeLenMapTy = std::map<Value *, unsigned>;
755+
static void emit(LLVMContext *Context, const Loop *LoopObj,
756+
const PointerSafeLenMapTy &PointerSafeLenMap,
757+
std::vector<llvm::Metadata *> &Metadata) {
758+
if (LoopsEmitted.contains(LoopObj))
759+
return;
760+
const auto ArrayGEPMap = mapArrayToGEPs(LoopObj, PointerSafeLenMap);
761+
emitMetadata(Context, ArrayGEPMap, PointerSafeLenMap, Metadata);
762+
LoopsEmitted.insert(LoopObj);
763+
}
764+
765+
private:
766+
static llvm::DenseSet<const Loop *> LoopsEmitted;
767+
768+
using ArrayGEPMapTy = std::map<Value *, std::vector<GetElementPtrInst *>>;
769+
// A single run over the loop to retrieve all GetElementPtr instructions
770+
// that access relevant array variables
771+
static ArrayGEPMapTy
772+
mapArrayToGEPs(const Loop *LoopObj,
773+
const PointerSafeLenMapTy &PointerSafeLenMap) {
774+
ArrayGEPMapTy ArrayGEPMap;
775+
for (const auto &BB : LoopObj->blocks()) {
776+
for (Instruction &I : *BB) {
777+
auto *GEP = dyn_cast<GetElementPtrInst>(&I);
778+
if (!GEP)
779+
continue;
780+
781+
Value *AccessedPointer = GEP->getPointerOperand();
782+
if (auto *LI = dyn_cast<LoadInst>(AccessedPointer))
783+
AccessedPointer = LI->getPointerOperand();
784+
auto PointerSafeLenIt = PointerSafeLenMap.find(AccessedPointer);
785+
if (PointerSafeLenIt != PointerSafeLenMap.end()) {
786+
ArrayGEPMap[AccessedPointer].push_back(GEP);
787+
}
788+
}
789+
}
790+
return ArrayGEPMap;
791+
}
792+
793+
// Create index group metadata nodes - one per each of the array
794+
// variables. Mark each GEP accessing a particular array variable
795+
// into a corresponding index group
796+
static void emitMetadata(LLVMContext *Context,
797+
const ArrayGEPMapTy &ArrayGEPMap,
798+
const PointerSafeLenMapTy &PointerSafeLenMap,
799+
std::vector<llvm::Metadata *> &Metadata) {
800+
using SafeLenIdxGroupMapTy = std::map<unsigned, SmallSet<MDNode *, 4>>;
801+
SafeLenIdxGroupMapTy SafeLenIdxGroupMap;
802+
// Whenever a kernel closure field access is pointed to instead of
803+
// an array/pointer variable, ensure that all GEPs to that memory
804+
// share the same index group by hashing the newly added index groups.
805+
// "Memory offset info" represents a handle to the whole closure block
806+
// + an integer offset to a particular captured parameter.
807+
using MemoryOffsetInfo = std::pair<Value *, unsigned>;
808+
std::map<MemoryOffsetInfo, MDNode *> OffsetIdxGroupMap;
809+
810+
for (auto &ArrayGEPIt : ArrayGEPMap) {
811+
MDNode *CurrentDepthIdxGroup = nullptr;
812+
if (auto *PrecedingGEP = dyn_cast<GetElementPtrInst>(ArrayGEPIt.first)) {
813+
Value *ClosureFieldPointer = PrecedingGEP->getPointerOperand();
814+
unsigned Offset =
815+
cast<ConstantInt>(PrecedingGEP->getOperand(2))->getZExtValue();
816+
MemoryOffsetInfo Info{ClosureFieldPointer, Offset};
817+
auto OffsetIdxGroupIt = OffsetIdxGroupMap.find(Info);
818+
if (OffsetIdxGroupIt == OffsetIdxGroupMap.end()) {
819+
// This is the first GEP encountered for this closure field.
820+
// Emit a distinct index group that will be referenced from
821+
// llvm.loop.parallel_access_indices metadata; hash the new
822+
// MDNode for future accesses to the same memory.
823+
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, None);
824+
OffsetIdxGroupMap.emplace(Info, CurrentDepthIdxGroup);
825+
} else {
826+
// Previous accesses to that field have already been indexed,
827+
// just use the already-existing metadata.
828+
CurrentDepthIdxGroup = OffsetIdxGroupIt->second;
829+
}
830+
} else /* Regular kernel-scope array/pointer variable */ {
831+
// Emit a distinct index group that will be referenced from
832+
// llvm.loop.parallel_access_indices metadata
833+
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, None);
834+
}
835+
836+
unsigned SafeLen = PointerSafeLenMap.find(ArrayGEPIt.first)->second;
837+
SafeLenIdxGroupMap[SafeLen].insert(CurrentDepthIdxGroup);
838+
for (auto *GEP : ArrayGEPIt.second) {
839+
StringRef IdxGroupMDName("llvm.index.group");
840+
llvm::MDNode *PreviousIdxGroup = GEP->getMetadata(IdxGroupMDName);
841+
if (!PreviousIdxGroup) {
842+
GEP->setMetadata(IdxGroupMDName, CurrentDepthIdxGroup);
843+
continue;
844+
}
845+
846+
// If we're dealing with an embedded loop, it may be the case
847+
// that GEP instructions for some of the arrays were already
848+
// marked by the algorithm when it went over the outer level loops.
849+
// In order to retain the IVDep information for each "loop
850+
// dimension", we will mark such GEP's into a separate joined node
851+
// that will refer to the previous levels' index groups AND to the
852+
// index group specific to the current loop.
853+
std::vector<llvm::Metadata *> CurrentDepthOperands(
854+
PreviousIdxGroup->op_begin(), PreviousIdxGroup->op_end());
855+
if (CurrentDepthOperands.empty())
856+
CurrentDepthOperands.push_back(PreviousIdxGroup);
857+
CurrentDepthOperands.push_back(CurrentDepthIdxGroup);
858+
auto *JointIdxGroup = llvm::MDNode::get(*Context, CurrentDepthOperands);
859+
GEP->setMetadata(IdxGroupMDName, JointIdxGroup);
860+
}
861+
}
862+
863+
for (auto &SafeLenIdxGroupIt : SafeLenIdxGroupMap) {
864+
auto *Name = MDString::get(*Context, "llvm.loop.parallel_access_indices");
865+
unsigned SafeLenValue = SafeLenIdxGroupIt.first;
866+
llvm::Metadata *SafeLenMDOp =
867+
SafeLenValue ? ConstantAsMetadata::get(ConstantInt::get(
868+
Type::getInt32Ty(*Context), SafeLenValue))
869+
: nullptr;
870+
std::vector<llvm::Metadata *> Parameters{Name};
871+
for (auto *Node : SafeLenIdxGroupIt.second)
872+
Parameters.push_back(Node);
873+
if (SafeLenMDOp)
874+
Parameters.push_back(SafeLenMDOp);
875+
Metadata.push_back(llvm::MDNode::get(*Context, Parameters));
876+
}
877+
}
878+
};
879+
llvm::DenseSet<const Loop *> IVDepMetadataEmitter::LoopsEmitted;
880+
752881
template <typename LoopInstType>
753882
void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
754883
const Loop *LoopObj) {
@@ -843,7 +972,7 @@ void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
843972
}
844973
if (LC & LoopControlDependencyArrayINTELMask) {
845974
// Collect pointer variable <-> safelen information
846-
std::map<Value *, unsigned> PointerSflnMap;
975+
IVDepMetadataEmitter::PointerSafeLenMapTy PointerSafeLenMap;
847976
unsigned NumOperandPairs = LoopControlParameters[NumParam];
848977
unsigned OperandsEndIndex = NumParam + NumOperandPairs * 2;
849978
assert(OperandsEndIndex <= LoopControlParameters.size() &&
@@ -852,109 +981,13 @@ void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
852981
while (NumParam < OperandsEndIndex) {
853982
SPIRVId ArraySPIRVId = LoopControlParameters[++NumParam];
854983
Value *PointerVar = ValueMap[M->getValue(ArraySPIRVId)];
855-
unsigned Safelen = LoopControlParameters[++NumParam];
856-
PointerSflnMap.emplace(PointerVar, Safelen);
857-
}
858-
859-
// A single run over the loop to retrieve all GetElementPtr instructions
860-
// that access relevant array variables
861-
std::map<Value *, std::vector<GetElementPtrInst *>> ArrayGEPMap;
862-
for (const auto &BB : LoopObj->blocks()) {
863-
for (Instruction &I : *BB) {
864-
auto *GEP = dyn_cast<GetElementPtrInst>(&I);
865-
if (!GEP)
866-
continue;
867-
868-
Value *AccessedPointer = GEP->getPointerOperand();
869-
if (auto *LI = dyn_cast<LoadInst>(AccessedPointer))
870-
AccessedPointer = LI->getPointerOperand();
871-
auto PointerSflnIt = PointerSflnMap.find(AccessedPointer);
872-
if (PointerSflnIt != PointerSflnMap.end()) {
873-
ArrayGEPMap[AccessedPointer].push_back(GEP);
874-
}
875-
}
876-
}
877-
878-
// Create index group metadata nodes - one per each of the array
879-
// variables. Mark each GEP accessing a particular array variable
880-
// into a corresponding index group
881-
std::map<unsigned, SmallSet<MDNode *, 4>> SafelenIdxGroupMap;
882-
// Whenever a kernel closure field access is pointed to instead of
883-
// an array/pointer variable, ensure that all GEPs to that memory
884-
// share the same index group by hashing the newly added index groups.
885-
// "Memory offset info" represents a handle to the whole closure block
886-
// + an integer offset to a particular captured parameter.
887-
using MemoryOffsetInfo = std::pair<Value *, unsigned>;
888-
std::map<MemoryOffsetInfo, MDNode *> OffsetIdxGroupMap;
889-
890-
for (auto &ArrayGEPIt : ArrayGEPMap) {
891-
MDNode *CurrentDepthIdxGroup = nullptr;
892-
if (auto *PrecedingGEP = dyn_cast<GetElementPtrInst>(ArrayGEPIt.first)) {
893-
Value *ClosureFieldPointer = PrecedingGEP->getPointerOperand();
894-
unsigned Offset =
895-
cast<ConstantInt>(PrecedingGEP->getOperand(2))->getZExtValue();
896-
MemoryOffsetInfo Info{ClosureFieldPointer, Offset};
897-
auto OffsetIdxGroupIt = OffsetIdxGroupMap.find(Info);
898-
if (OffsetIdxGroupIt == OffsetIdxGroupMap.end()) {
899-
// This is the first GEP encountered for this closure field.
900-
// Emit a distinct index group that will be referenced from
901-
// llvm.loop.parallel_access_indices metadata; hash the new
902-
// MDNode for future accesses to the same memory.
903-
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, None);
904-
OffsetIdxGroupMap.emplace(Info, CurrentDepthIdxGroup);
905-
} else {
906-
// Previous accesses to that field have already been indexed,
907-
// just use the already-existing metadata.
908-
CurrentDepthIdxGroup = OffsetIdxGroupIt->second;
909-
}
910-
} else /* Regular kernel-scope array/pointer variable */ {
911-
// Emit a distinct index group that will be referenced from
912-
// llvm.loop.parallel_access_indices metadata
913-
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, None);
914-
}
915-
916-
unsigned Safelen = PointerSflnMap.find(ArrayGEPIt.first)->second;
917-
SafelenIdxGroupMap[Safelen].insert(CurrentDepthIdxGroup);
918-
for (auto *GEP : ArrayGEPIt.second) {
919-
StringRef IdxGroupMDName("llvm.index.group");
920-
llvm::MDNode *PreviousIdxGroup = GEP->getMetadata(IdxGroupMDName);
921-
if (!PreviousIdxGroup) {
922-
GEP->setMetadata(IdxGroupMDName, CurrentDepthIdxGroup);
923-
continue;
924-
}
925-
926-
// If we're dealing with an embedded loop, it may be the case
927-
// that GEP instructions for some of the arrays were already
928-
// marked by the algorithm when it went over the outer level loops.
929-
// In order to retain the IVDep information for each "loop
930-
// dimension", we will mark such GEP's into a separate joined node
931-
// that will refer to the previous levels' index groups AND to the
932-
// index group specific to the current loop.
933-
std::vector<llvm::Metadata *> CurrentDepthOperands(
934-
PreviousIdxGroup->op_begin(), PreviousIdxGroup->op_end());
935-
if (CurrentDepthOperands.empty())
936-
CurrentDepthOperands.push_back(PreviousIdxGroup);
937-
CurrentDepthOperands.push_back(CurrentDepthIdxGroup);
938-
auto *JointIdxGroup = llvm::MDNode::get(*Context, CurrentDepthOperands);
939-
GEP->setMetadata(IdxGroupMDName, JointIdxGroup);
940-
}
941-
}
942-
943-
for (auto &SflnIdxGroupIt : SafelenIdxGroupMap) {
944-
auto *Name = MDString::get(*Context, "llvm.loop.parallel_access_indices");
945-
unsigned SflnValue = SflnIdxGroupIt.first;
946-
llvm::Metadata *SafelenMDOp =
947-
SflnValue ? ConstantAsMetadata::get(ConstantInt::get(
948-
Type::getInt32Ty(*Context), SflnValue))
949-
: nullptr;
950-
std::vector<llvm::Metadata *> Parameters{Name};
951-
for (auto *Node : SflnIdxGroupIt.second)
952-
Parameters.push_back(Node);
953-
if (SafelenMDOp)
954-
Parameters.push_back(SafelenMDOp);
955-
Metadata.push_back(llvm::MDNode::get(*Context, Parameters));
984+
unsigned SafeLen = LoopControlParameters[++NumParam];
985+
PointerSafeLenMap.emplace(PointerVar, SafeLen);
956986
}
987+
IVDepMetadataEmitter::emit(Context, LoopObj, PointerSafeLenMap, Metadata);
957988
++NumParam;
989+
assert(NumParam <= LoopControlParameters.size() &&
990+
"Missing loop control parameter!");
958991
}
959992
if (LC & LoopControlPipelineEnableINTELMask) {
960993
Metadata.push_back(llvm::MDNode::get(

0 commit comments

Comments
 (0)