-
Notifications
You must be signed in to change notification settings - Fork 797
[SYCL][ESIMD]Limit propagation of ESIMD attributes #7191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
5767c50
56854ed
38c1541
a0a9c53
dedf2ed
497aa64
a1aadf5
8f089a6
9d33f38
209532f
c6fed9f
992fcda
04737a2
cf0b167
9b59931
ac0422b
0ae282d
87d0f46
a073628
f1cad2f
53fd818
5a4eae3
70ae7a0
2618034
a7c301d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,12 +9,18 @@ | |
//===----------------------------------------------------------------------===// | ||
|
||
#include "llvm/ADT/SmallPtrSet.h" | ||
#include "llvm/ADT/SmallVector.h" | ||
#include "llvm/Demangle/ItaniumDemangle.h" | ||
#include "llvm/IR/Function.h" | ||
|
||
namespace llvm { | ||
namespace esimd { | ||
|
||
constexpr char ESIMD_MARKER_MD[] = "sycl_explicit_simd"; | ||
// This is the prefixes of the names generated from | ||
// sycl/ext/oneapi/experimental/invoke_simd.hpp::__builtin_invoke_simd | ||
// overloads instantiations: | ||
constexpr char INVOKE_SIMD_PREF[] = "_Z33__regcall3____builtin_invoke_simd"; | ||
|
||
// Tells whether given function is a ESIMD kernel. | ||
bool isESIMDKernel(const Function &F); | ||
|
@@ -68,5 +74,40 @@ void collectUsesLookThroughCastsAndZeroGEPs(const Value *V, | |
/// in it. Returns nullptr if failed to do so. | ||
Type *getVectorTyOrNull(StructType *STy); | ||
|
||
/// Tracks the use of a function pointer being stored in a memory. | ||
/// Returns false if the function pointer is used as an argument for invoke_simd | ||
/// function call, true otherwise. | ||
bool filterFunctionPointer(Value *address); | ||
|
||
// Simplest possible implementation of an allocator for the Itanium demangler | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this change (and other ones related to demangling) is no longer needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Upon second thought, it is good to have the allocator defined in shared header, as others may need to use it too. |
||
class SimpleAllocator { | ||
protected: | ||
SmallVector<void *, 128> Ptrs; | ||
|
||
public: | ||
void reset() { | ||
for (void *Ptr : Ptrs) { | ||
// Destructors are not called, but that is OK for the | ||
// itanium_demangle::Node subclasses | ||
std::free(Ptr); | ||
} | ||
Ptrs.resize(0); | ||
} | ||
|
||
template <typename T, typename... Args> T *makeNode(Args &&...args) { | ||
void *Ptr = std::calloc(1, sizeof(T)); | ||
Ptrs.push_back(Ptr); | ||
return new (Ptr) T(std::forward<Args>(args)...); | ||
} | ||
|
||
void *allocateNodeArray(size_t sz) { | ||
void *Ptr = std::calloc(sz, sizeof(itanium_demangle::Node *)); | ||
Ptrs.push_back(Ptr); | ||
return Ptr; | ||
} | ||
|
||
~SimpleAllocator() { reset(); } | ||
}; | ||
|
||
} // namespace esimd | ||
} // namespace llvm |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ namespace llvm { | |
namespace sycl { | ||
namespace utils { | ||
using CallGraphNodeAction = std::function<void(Function *)>; | ||
using CallGraphFunctionFilter = std::function<bool(Value *)>; | ||
|
||
// Traverses call graph starting from given function up the call chain applying | ||
// given action to each function met on the way. If \c ErrorOnNonCallUse | ||
|
@@ -26,24 +27,33 @@ using CallGraphNodeAction = std::function<void(Function *)>; | |
// call graph as if the use was a call. | ||
// Functions which are part of the visited set ('Visited' parameter) are not | ||
// traversed. | ||
void traverseCallgraphUp(llvm::Function *F, CallGraphNodeAction NodeF, | ||
SmallPtrSetImpl<Function *> &Visited, | ||
bool ErrorOnNonCallUse); | ||
void traverseCallgraphUp( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment how functionFilter parameter is used. |
||
llvm::Function *F, CallGraphNodeAction NodeF, | ||
SmallPtrSetImpl<Function *> &Visited, bool ErrorOnNonCallUse, | ||
const CallGraphFunctionFilter &functionFilter = [](Value *) { | ||
return true; | ||
}); | ||
|
||
template <class CallGraphNodeActionF> | ||
void traverseCallgraphUp(Function *F, CallGraphNodeActionF ActionF, | ||
SmallPtrSetImpl<Function *> &Visited, | ||
bool ErrorOnNonCallUse) { | ||
void traverseCallgraphUp( | ||
Function *F, CallGraphNodeActionF ActionF, | ||
SmallPtrSetImpl<Function *> &Visited, bool ErrorOnNonCallUse, | ||
const CallGraphFunctionFilter &functionFilter = [](Value *) { | ||
return true; | ||
}) { | ||
traverseCallgraphUp(F, CallGraphNodeAction(ActionF), Visited, | ||
ErrorOnNonCallUse); | ||
ErrorOnNonCallUse, functionFilter); | ||
} | ||
|
||
template <class CallGraphNodeActionF> | ||
void traverseCallgraphUp(Function *F, CallGraphNodeActionF ActionF, | ||
bool ErrorOnNonCallUse = true) { | ||
void traverseCallgraphUp( | ||
Function *F, CallGraphNodeActionF ActionF, bool ErrorOnNonCallUse = true, | ||
const CallGraphFunctionFilter &functionFilter = [](Value *) { | ||
return true; | ||
}) { | ||
SmallPtrSet<Function *, 32> Visited; | ||
traverseCallgraphUp(F, CallGraphNodeAction(ActionF), Visited, | ||
ErrorOnNonCallUse); | ||
ErrorOnNonCallUse, functionFilter); | ||
} | ||
} // namespace utils | ||
} // namespace sycl | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,5 +123,62 @@ Type *getVectorTyOrNull(StructType *STy) { | |
return Res; | ||
} | ||
|
||
bool isInvokeSimdBuiltinCall(const CallInst *CI) { | ||
Function *F = CI->getCalledFunction(); | ||
|
||
if (F && F->getName().startswith(INVOKE_SIMD_PREF)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
// Tracks the use of a function pointer being stored in a memory. | ||
// Returns false if the function pointer is used as an argument for invoke_simd | ||
// function call, true otherwise. | ||
bool filterFunctionPointer(Value *address) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (address == nullptr) { | ||
return true; | ||
} | ||
|
||
SmallPtrSet<const Use *, 4> Uses; | ||
collectUsesLookThroughCasts(address, Uses); | ||
|
||
for (const Use *U : Uses) { | ||
Value *V = U->getUser(); | ||
|
||
if (auto *StI = dyn_cast<StoreInst>(V)) { | ||
if (U != &StI->getOperandUse(StoreInst::getPointerOperandIndex())) { | ||
// this is double indirection - not supported | ||
return false; | ||
} | ||
V = stripCasts(StI->getPointerOperand()); | ||
if (!isa<AllocaInst>(V)) { | ||
return false; // unsupported case of data flow through non-local memory | ||
} | ||
|
||
if (auto *LI = dyn_cast<LoadInst>(V)) { | ||
// A value loaded from another address is stored at this address - | ||
// recurse into the other address | ||
if (!filterFunctionPointer(LI->getPointerOperand())) { | ||
return false; | ||
} | ||
} | ||
} else if (const auto *CI = dyn_cast<CallInst>(V)) { | ||
// if __builtin_invoke_simd uses the pointer, do not traverse the function | ||
if (isInvokeSimdBuiltinCall(CI)) { | ||
return false; | ||
} | ||
} else if (isa<LoadInst>(V)) { | ||
if (!filterFunctionPointer(V)) { | ||
return false; | ||
} | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
} // namespace esimd | ||
} // namespace llvm |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -8,16 +8,18 @@ | |||||
// Finds and adds sycl_explicit_simd attributes to wrapper functions that wrap | ||||||
// ESIMD kernel functions | ||||||
|
||||||
#include "llvm/Demangle/Demangle.h" | ||||||
#include "llvm/Demangle/ItaniumDemangle.h" | ||||||
#include "llvm/IR/Module.h" | ||||||
#include "llvm/Pass.h" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are these still needed? |
||||||
#include "llvm/SYCLLowerIR/ESIMD/ESIMDUtils.h" | ||||||
#include "llvm/SYCLLowerIR/ESIMD/LowerESIMD.h" | ||||||
#include "llvm/SYCLLowerIR/SYCLUtils.h" | ||||||
|
||||||
#include "llvm/IR/Module.h" | ||||||
#include "llvm/Pass.h" | ||||||
|
||||||
#define DEBUG_TYPE "LowerESIMDKernelAttrs" | ||||||
|
||||||
using namespace llvm; | ||||||
using namespace llvm::esimd; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
namespace llvm { | ||||||
PreservedAnalyses | ||||||
|
@@ -37,7 +39,7 @@ SYCLFixupESIMDKernelWrapperMDPass::run(Module &M, ModuleAnalysisManager &MAM) { | |||||
Modified = true; | ||||||
} | ||||||
}, | ||||||
false); | ||||||
false, filterFunctionPointer); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
} | ||||||
return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all(); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,7 +39,6 @@ using namespace llvm::esimd; | |
|
||
namespace { | ||
|
||
constexpr char ESIMD_MARKER_MD[] = "sycl_explicit_simd"; | ||
constexpr char REQD_SUB_GROUP_SIZE_MD[] = "intel_reqd_sub_group_size"; | ||
|
||
class SYCLLowerInvokeSimdLegacyPass : public ModulePass { | ||
|
@@ -73,10 +72,6 @@ ModulePass *llvm::createSYCLLowerInvokeSimdPass() { | |
|
||
namespace { | ||
// TODO support lambda and functor overloads | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to move this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO is unrelated to the removed code |
||
// This is the prefixes of the names generated from | ||
// sycl/ext/oneapi/experimental/invoke_simd.hpp::__builtin_invoke_simd | ||
// overloads instantiations: | ||
constexpr char INVOKE_SIMD_PREF[] = "_Z33__regcall3____builtin_invoke_simd"; | ||
|
||
using ValueSetImpl = SmallPtrSetImpl<Value *>; | ||
using ValueSet = SmallPtrSet<Value *, 4>; | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,4 @@ | ||||||||||||
//===------------ SYCLUtils.cpp - SYCL utility functions | ||||||||||||
//------------------===// | ||||||||||||
//===------------ SYCLUtils.cpp - SYCL utility functions ------------------===// | ||||||||||||
// | ||||||||||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||||||||||
// See https://llvm.org/LICENSE.txt for license information. | ||||||||||||
|
@@ -10,13 +9,18 @@ | |||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||
#include "llvm/SYCLLowerIR/SYCLUtils.h" | ||||||||||||
#include "llvm/IR/Instructions.h" | ||||||||||||
#include "llvm/SYCLLowerIR/ESIMD/ESIMDUtils.h" | ||||||||||||
|
||||||||||||
namespace llvm { | ||||||||||||
namespace sycl { | ||||||||||||
namespace utils { | ||||||||||||
|
||||||||||||
using namespace llvm::esimd; | ||||||||||||
|
||||||||||||
void traverseCallgraphUp(llvm::Function *F, CallGraphNodeAction ActionF, | ||||||||||||
SmallPtrSetImpl<Function *> &FunctionsVisited, | ||||||||||||
bool ErrorOnNonCallUse) { | ||||||||||||
bool ErrorOnNonCallUse, | ||||||||||||
const CallGraphFunctionFilter &functionFilter) { | ||||||||||||
SmallVector<Function *, 32> Worklist; | ||||||||||||
|
||||||||||||
if (FunctionsVisited.count(F) == 0) | ||||||||||||
|
@@ -43,6 +47,13 @@ void traverseCallgraphUp(llvm::Function *F, CallGraphNodeAction ActionF, | |||||||||||
} else { | ||||||||||||
// ... non-call is OK - add using function to the worklist | ||||||||||||
if (auto *I = dyn_cast<Instruction>(FCall)) { | ||||||||||||
if (auto *SI = dyn_cast<StoreInst>(I)) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as we talked, such filtering within void traverseCallgraphUp(llvm::Function *F, CallGraphNodeAction ActionF,
SmallPtrSetImpl<Function *> &FunctionsVisited,
bool ErrorOnNonCallUse, std::function<bool(const Instruction *I, const Function *F)> && FuncUseFilter); and the LowerESIMDKernelAttrs pass would invoke the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. However, I believe we are going to encounter similar problems in other cases where traverseCallgraphUp is used with invoke_simd and therefore embedding the filtering functionality to traverseCallgraphUp would make more sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add comment why this special branch is needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The StoreInst check should be moved into the filter as it has nothing to do with this generic traversal functionality. That's why I suggested
Suggested change
|
||||||||||||
Value *addr = SI->getPointerOperand(); | ||||||||||||
if (!functionFilter(addr)) { | ||||||||||||
continue; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
auto UseF = I->getFunction(); | ||||||||||||
|
||||||||||||
if (FunctionsVisited.count(UseF) == 0) { | ||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.