diff --git a/llvm/lib/SYCLLowerIR/SYCLPropagateAspectsUsage.cpp b/llvm/lib/SYCLLowerIR/SYCLPropagateAspectsUsage.cpp index 6d8c248a81607..a2e1f3d72b1e6 100644 --- a/llvm/lib/SYCLLowerIR/SYCLPropagateAspectsUsage.cpp +++ b/llvm/lib/SYCLLowerIR/SYCLPropagateAspectsUsage.cpp @@ -349,7 +349,14 @@ AspectsSetTy getAspectsUsedByInstruction(const Instruction &I, } using FunctionToAspectsMapTy = DenseMap; -using CallGraphTy = DenseMap>; +using ConditionsSetTy = SmallSet; +using FunctionToConditionExprsMapTy = DenseMap; +using ConditionExprsAndConditionalAspectsTy = + SmallVector>; +using FuncToConditionExprsAndConditionalAspectsTy = + DenseMap; +using FunctionSetTy = SmallPtrSet; +using CallGraphTy = DenseMap; // Finds the first function in a list that uses a given aspect. Returns nullptr // if none of the functions satisfy the criteria. @@ -441,6 +448,96 @@ void createUsedAspectsMetadataForFunctions( } } +void createConditionallyUsedAspectsMetadataForFunctions( + FuncToConditionExprsAndConditionalAspectsTy + &FunctionToConditionExpressionsAndConditionalAspects, + const AspectsSetTy &ExcludeAspectVals) { + for (auto &[F, ConditionsAndAspectsVec] : + FunctionToConditionExpressionsAndConditionalAspects) { + LLVMContext &C = F->getContext(); + + // Create a set of unique conditions and aspects. First we add the ones from + // the found aspects that have not been excluded. + ConditionExprsAndConditionalAspectsTy UniqueConditionsAndAspects; + for (const auto &ConditionsAndAspectsPair : ConditionsAndAspectsVec) { + AspectsSetTy FilteredAspects; + for (const auto &A : ConditionsAndAspectsPair.second) + if (!ExcludeAspectVals.contains(A)) + FilteredAspects.insert(A); + + if (FilteredAspects.empty()) + continue; + + UniqueConditionsAndAspects.push_back( + std::make_pair(ConditionsAndAspectsPair.first, FilteredAspects)); + } + + // If there is new metadata, merge it with the old aspects. We preserve + // the excluded ones. + + if (const MDNode *ExistingAspects = + F->getMetadata("sycl_conditionally_used_aspects")) { + for (const MDOperand &MDOp : ExistingAspects->operands()) { + if (MDNode *PairNode = dyn_cast(MDOp)) { + if (PairNode->getNumOperands() != 2) + continue; + + MDNode *ConditionNode = dyn_cast(PairNode->getOperand(0)); + ConditionsSetTy ConditionsSet; + if (ConditionNode) { + for (const MDOperand &CondOp : ConditionNode->operands()) { + const Constant *C = cast(CondOp)->getValue(); + ConditionsSet.insert(cast(C)->getSExtValue()); + } + } + + MDNode *AspectsNode = dyn_cast(PairNode->getOperand(1)); + AspectsSetTy AspectsSet; + if (AspectsNode) { + for (const MDOperand &AspOp : AspectsNode->operands()) { + const Constant *C = cast(AspOp)->getValue(); + AspectsSet.insert(cast(C)->getSExtValue()); + } + } + if (ConditionsSet.empty() || AspectsSet.empty()) + continue; + + UniqueConditionsAndAspects.push_back( + std::make_pair(ConditionsSet, AspectsSet)); + } + } + } + + // Create new metadata. + SmallVector AspectsMetadata; + + for (const auto &Pair : ConditionsAndAspectsVec) { + // Metadata node for conditions + SmallVector ConditionsMD; + for (int Condition : Pair.first) { + ConditionsMD.push_back(ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt32Ty(C), Condition))); + } + MDNode *ConditionNode = MDNode::get(C, ConditionsMD); + + // Metadata node for aspects + SmallVector AspectsMD; + for (int Aspect : Pair.second) { + AspectsMD.push_back(ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt32Ty(C), Aspect))); + } + MDNode *AspectsNode = MDNode::get(C, AspectsMD); + + // Create the pair metadata node that holds the condition and aspects + // nodes + AspectsMetadata.push_back(MDNode::get(C, {ConditionNode, AspectsNode})); + } + + MDNode *MDN = MDNode::get(C, AspectsMetadata); + F->setMetadata("sycl_conditionally_used_aspects", MDN); + } +} + /// Checks that all aspects determined to be used by a given function are in /// that function's sycl_declared_aspects metadata if present. A warning /// diagnostic is produced for each aspect this check fails for. @@ -511,12 +608,40 @@ void validateUsedAspectsForFunctions(const FunctionToAspectsMapTy &Map, } } +struct FunctionsClassifiedByTheirFunctionCall { + FunctionSetTy CalledUnconditionally; + FunctionSetTy CalledConditionally; +}; + +void classifyFuncsInCallGraph(Function *F, const CallGraphTy &CG, + FunctionsClassifiedByTheirFunctionCall &Funcs, + SmallPtrSet &Visited) { + const auto It = CG.find(F); + if (It == CG.end()) + return; + bool FCalledUnconditionally = Funcs.CalledUnconditionally.contains(F); + bool FCalledConditionally = Funcs.CalledConditionally.contains(F); + for (Function *Callee : It->second) { + bool CalleeIsCalledOnDeviceConditionally = + Callee->hasFnAttribute("sycl-call-if-on-device-conditionally"); + if (!CalleeIsCalledOnDeviceConditionally && FCalledUnconditionally) { + Funcs.CalledUnconditionally.insert(Callee); + } + if (CalleeIsCalledOnDeviceConditionally || FCalledConditionally) { + Funcs.CalledConditionally.insert(Callee); + } + if (Visited.insert(Callee).second) + classifyFuncsInCallGraph(Callee, CG, Funcs, Visited); + } +} + /// Propagates aspects from leaves up to the top of call graph. /// NB! Call graph corresponds to call graph of SYCL code which /// can't contain recursive calls. So there can't be loops in /// a call graph. But there can be path's intersections. -void propagateAspectsThroughCG(Function *F, CallGraphTy &CG, +void propagateAspectsThroughCG(Function *F, const CallGraphTy &CG, FunctionToAspectsMapTy &AspectsMap, + FunctionSetTy FunctionsCalledUnconditionally, SmallPtrSet &Visited) { const auto It = CG.find(F); if (It == CG.end()) @@ -525,26 +650,149 @@ void propagateAspectsThroughCG(Function *F, CallGraphTy &CG, AspectsSetTy LocalAspects; for (Function *Callee : It->second) { if (Visited.insert(Callee).second) - propagateAspectsThroughCG(Callee, CG, AspectsMap, Visited); + propagateAspectsThroughCG(Callee, CG, AspectsMap, + FunctionsCalledUnconditionally, Visited); const auto &CalleeAspects = AspectsMap[Callee]; LocalAspects.insert(CalleeAspects.begin(), CalleeAspects.end()); } + if (FunctionsCalledUnconditionally.contains(F)) + AspectsMap[F].insert(LocalAspects.begin(), LocalAspects.end()); +} + +using PathsContainingConditionalCallersTy = + std::vector>; + +void identifyPathsContainingConditionalCallers( + Function *F, const CallGraphTy &CG, std::vector &CurrentPath, + PathsContainingConditionalCallersTy &Paths) { + const auto It = CG.find(F); + if (It == CG.end()) { + CurrentPath.push_back(F); + for (auto Func : CurrentPath) + if (Func->hasFnAttribute("sycl-call-if-on-device-conditionally")) { + Paths.push_back(CurrentPath); + break; + } + CurrentPath.pop_back(); + return; + } - AspectsMap[F].insert(LocalAspects.begin(), LocalAspects.end()); + CurrentPath.push_back(F); + + for (Function *Callee : It->second) { + identifyPathsContainingConditionalCallers(Callee, CG, CurrentPath, Paths); + } + + CurrentPath.pop_back(); +} + +void propagateConditionExpressionsAndConditionalAspectsThroughCG( + const PathsContainingConditionalCallersTy &Paths, + FuncToConditionExprsAndConditionalAspectsTy &ConditionsAndAspectsMap, + FunctionToConditionExprsMapTy &ConditionsMap, + FunctionToAspectsMapTy &AspectsMap) { + auto Contains = + [](const ConditionExprsAndConditionalAspectsTy &ConditionsAndAspectsVec, + const std::pair &Search) { + return std::any_of( + ConditionsAndAspectsVec.begin(), ConditionsAndAspectsVec.end(), + [&Search](const auto ConditionAndAspectsPair) { + return (ConditionAndAspectsPair.first == Search.first) && + (ConditionAndAspectsPair.second == Search.second); + }); + }; + + // TODO: need to optimize finding duplicates and combining pairs with the same + // Condition Expressions or Aspects + for (const auto &Path : Paths) + for (int E = Path.size() - 1, I = E; I >= 0; --I) { + if (I != E) { + const auto &CalleeAspects = AspectsMap[Path[I + 1]]; + AspectsMap[Path[I]].insert(CalleeAspects.begin(), CalleeAspects.end()); + if (ConditionsMap[Path[I]].empty()) + ConditionsMap[Path[I]] = ConditionsMap[Path[I + 1]]; + } + auto NewPair = + std::make_pair(ConditionsMap[Path[I]], AspectsMap[Path[I]]); + if (!NewPair.first.empty() && !NewPair.second.empty()) { + if (ConditionsAndAspectsMap[Path[I]].empty()) + ConditionsAndAspectsMap[Path[I]].push_back(NewPair); + else if (!Contains(ConditionsAndAspectsMap[Path[I]], NewPair)) + ConditionsAndAspectsMap[Path[I]].push_back(NewPair); + } + if (I != E) + for (const auto &ConditionAndAspectsPair : + ConditionsAndAspectsMap[Path[I + 1]]) + if (!Contains(ConditionsAndAspectsMap[Path[I]], + ConditionAndAspectsPair)) + ConditionsAndAspectsMap[Path[I]].push_back(ConditionAndAspectsPair); + } +} + +void propagateConditionExpressionsThroughCG( + Function *F, const CallGraphTy &CG, + FunctionToConditionExprsMapTy &ConditionsMap) { + // TODO: re-write using Paths from identifyPathsContainingConditionalCallers + // func + const auto It = CG.find(F); + if (It == CG.end()) + return; + + FunctionToConditionExprsMapTy LocalConditions; + for (Instruction &I : instructions(F)) { + const auto *CI = dyn_cast(&I); + if (!CI) + continue; + + Function *CalledFunction = CI->getCalledFunction(); + if (CI->isIndirectCall() || !CalledFunction) + continue; + + if (!CalledFunction->hasFnAttribute("sycl-call-if-on-device-conditionally")) + continue; + + // Start the loop with the 2nd argument (counting from 0) as 0 arg by + // design doc is Conditional Action, 1st arg is "this" pointer for the + // application's callable object, and all Condition Expressions start + // with 2nd argument + for (unsigned J = 2; J < CI->arg_size(); ++J) { + Value *Arg = CI->getArgOperand(J); + if (auto *ConstInt = dyn_cast(Arg)) { + // Get the integer value and add it to the set + LocalConditions[CalledFunction].insert( + static_cast(ConstInt->getSExtValue())); + } + } + } + + for (Function *Callee : It->second) { + auto NewConditions = ConditionsMap[F]; + if (Callee->hasFnAttribute("sycl-call-if-on-device-conditionally")) { + NewConditions.insert(LocalConditions[Callee].begin(), + LocalConditions[Callee].end()); + } + ConditionsMap[Callee] = NewConditions; + propagateConditionExpressionsThroughCG(Callee, CG, ConditionsMap); + } } /// Processes a function: /// - checks if return and argument types are using any aspects /// - checks if instructions are using any aspects /// - updates call graph information -/// - checks if function has "!sycl_used_aspects" and "!sycl_declared_aspects" -/// metadata and if so collects aspects from this metadata +/// - checks if function has "!sycl_used_aspects", +/// "!sycl_conditionally_used_aspects" and "!sycl_declared_aspects" +/// metadata and if so collects aspects from this metadata void processFunction(Function &F, FunctionToAspectsMapTy &FunctionToUsedAspects, + FunctionToAspectsMapTy &FunctionToConditionallyUsedAspects, FunctionToAspectsMapTy &FunctionToDeclaredAspects, - TypeToAspectsMapTy &TypesWithAspects, CallGraphTy &CG, + const FunctionsClassifiedByTheirFunctionCall &Funcs, + TypeToAspectsMapTy &TypesWithAspects, const AspectValueToNameMapTy &AspectValues, bool FP64ConvEmu) { + bool FunctionIsUnconditional = Funcs.CalledUnconditionally.contains(&F); + bool FunctionIsConditional = Funcs.CalledConditionally.contains(&F); auto FP64AspectIt = AspectValues.find("fp64"); assert(FP64AspectIt != AspectValues.end() && "fp64 aspect was not found in the aspect values."); @@ -553,15 +801,23 @@ void processFunction(Function &F, FunctionToAspectsMapTy &FunctionToUsedAspects, getAspectsFromType(F.getReturnType(), TypesWithAspects); for (const auto &Aspect : RetTyAspects) if (!FP64ConvEmu || (Aspect != FP64Aspect) || - !hasDoubleType(F.getReturnType())) - FunctionToUsedAspects[&F].insert(Aspect); + !hasDoubleType(F.getReturnType())) { + if (FunctionIsUnconditional) + FunctionToUsedAspects[&F].insert(Aspect); + if (FunctionIsConditional) + FunctionToConditionallyUsedAspects[&F].insert(Aspect); + } for (Argument &Arg : F.args()) { const AspectsSetTy ArgAspects = getAspectsFromType(Arg.getType(), TypesWithAspects); for (const auto &Aspect : ArgAspects) if (!FP64ConvEmu || (Aspect != FP64Aspect) || - !hasDoubleType(Arg.getType())) - FunctionToUsedAspects[&F].insert(Aspect); + !hasDoubleType(Arg.getType())) { + if (FunctionIsUnconditional) + FunctionToUsedAspects[&F].insert(Aspect); + if (FunctionIsConditional) + FunctionToConditionallyUsedAspects[&F].insert(Aspect); + } } for (Instruction &I : instructions(F)) { @@ -569,12 +825,12 @@ void processFunction(Function &F, FunctionToAspectsMapTy &FunctionToUsedAspects, getAspectsUsedByInstruction(I, TypesWithAspects); for (const auto &Aspect : Aspects) if (!FP64ConvEmu || (Aspect != FP64Aspect) || !hasDoubleType(I) || - !isFP64ConversionInstruction(I)) - FunctionToUsedAspects[&F].insert(Aspect); - if (const auto *CI = dyn_cast(&I)) { - if (!CI->isIndirectCall() && CI->getCalledFunction()) - CG[&F].insert(CI->getCalledFunction()); - } + !isFP64ConversionInstruction(I)) { + if (FunctionIsUnconditional) + FunctionToUsedAspects[&F].insert(Aspect); + if (FunctionIsConditional) + FunctionToConditionallyUsedAspects[&F].insert(Aspect); + } } auto CollectAspectsFromMD = [&F](const char* MDName, FunctionToAspectsMapTy &Map) { @@ -647,25 +903,92 @@ void setSyclFixedTargetsMD(const std::vector &EntryPoints, F->setMetadata("sycl_fixed_targets", MDN); } +CallGraphTy getCallGraph(Module &M) { + CallGraphTy CG; + for (Function &F : M.functions()) { + for (Instruction &I : instructions(F)) { + const auto *CI = dyn_cast(&I); + if (!CI) + continue; + if (Function *CalledFunction = CI->getCalledFunction(); + !CI->isIndirectCall() && CalledFunction) { + if (CalledFunction->hasFnAttribute( + "sycl-call-if-on-device-conditionally")) { + if (Function *CallableObj = + dyn_cast(CI->getArgOperand(0))) { + CG[&F].insert(CalledFunction); + CG[CalledFunction].insert(CallableObj); + } + } else + CG[&F].insert(CalledFunction); + } + } + } + return CG; +} + /// Returns a map of functions with corresponding used aspects. -std::pair +std::tuple buildFunctionsToAspectsMap(Module &M, TypeToAspectsMapTy &TypesWithAspects, const AspectValueToNameMapTy &AspectValues, const std::vector &EntryPoints, bool ValidateAspects, bool FP64ConvEmu) { FunctionToAspectsMapTy FunctionToUsedAspects; + FunctionToAspectsMapTy FunctionToConditionallyUsedAspects; FunctionToAspectsMapTy FunctionToDeclaredAspects; - CallGraphTy CG; + + // Create call graph which includes conditional actions + CallGraphTy CG = getCallGraph(M); + + // Separate functions which called conditionally and unconditionally + FunctionsClassifiedByTheirFunctionCall FuncsClassifiedByTheirFunctionCall; + auto addFunctionToFunctionsClassifiedByTheirFunctionCallStruct = + [&FuncsClassifiedByTheirFunctionCall](Function *F) { + if (!F->hasFnAttribute("sycl-call-if-on-device-conditionally")) + FuncsClassifiedByTheirFunctionCall.CalledUnconditionally.insert(F); + else + FuncsClassifiedByTheirFunctionCall.CalledConditionally.insert(F); + }; + + SmallPtrSet Visited; + for (Function *F : EntryPoints) { + addFunctionToFunctionsClassifiedByTheirFunctionCallStruct(F); + classifyFuncsInCallGraph(F, CG, FuncsClassifiedByTheirFunctionCall, + Visited); + } + for (Function &F : M.functions()) + if (!Visited.contains(&F)) + addFunctionToFunctionsClassifiedByTheirFunctionCallStruct(&F); for (Function &F : M.functions()) { - processFunction(F, FunctionToUsedAspects, FunctionToDeclaredAspects, - TypesWithAspects, CG, AspectValues, FP64ConvEmu); + processFunction( + F, FunctionToUsedAspects, FunctionToConditionallyUsedAspects, + FunctionToDeclaredAspects, FuncsClassifiedByTheirFunctionCall, + TypesWithAspects, AspectValues, FP64ConvEmu); } - SmallPtrSet Visited; + Visited.clear(); for (Function *F : EntryPoints) - propagateAspectsThroughCG(F, CG, FunctionToUsedAspects, Visited); + propagateAspectsThroughCG( + F, CG, FunctionToUsedAspects, + FuncsClassifiedByTheirFunctionCall.CalledUnconditionally, Visited); + + Visited.clear(); + + FunctionToConditionExprsMapTy FunctionToConditionExpressions; + std::vector> Paths; + for (Function *F : EntryPoints) { + propagateConditionExpressionsThroughCG(F, CG, + FunctionToConditionExpressions); + std::vector CurrentPath; + identifyPathsContainingConditionalCallers(F, CG, CurrentPath, Paths); + } + FuncToConditionExprsAndConditionalAspectsTy FunctionToCondExpsAndCondAspects; + propagateConditionExpressionsAndConditionalAspectsThroughCG( + Paths, FunctionToCondExpsAndCondAspects, FunctionToConditionExpressions, + FunctionToConditionallyUsedAspects); if (ValidateAspects) validateUsedAspectsForFunctions(FunctionToUsedAspects, AspectValues, EntryPoints, CG); @@ -675,9 +998,12 @@ buildFunctionsToAspectsMap(Module &M, TypeToAspectsMapTy &TypesWithAspects, // avoid errors during validation. Visited.clear(); for (Function *F : EntryPoints) - propagateAspectsThroughCG(F, CG, FunctionToDeclaredAspects, Visited); + propagateAspectsThroughCG( + F, CG, FunctionToDeclaredAspects, + FuncsClassifiedByTheirFunctionCall.CalledUnconditionally, Visited); return {std::move(FunctionToUsedAspects), + std::move(FunctionToCondExpsAndCondAspects), std::move(FunctionToDeclaredAspects)}; } @@ -717,7 +1043,9 @@ SYCLPropagateAspectsUsagePass::run(Module &M, ModuleAnalysisManager &MAM) { propagateAspectsToOtherTypesInModule(M, TypesWithAspects, AspectValues); - auto [FunctionToUsedAspects, FunctionToDeclaredAspects] = + auto [FunctionToUsedAspects, + FunctionToConditionExpressionsAndConditionalAspects, + FunctionToDeclaredAspects] = buildFunctionsToAspectsMap(M, TypesWithAspects, AspectValues, EntryPoints, ValidateAspectUsage, FP64ConvEmu); @@ -732,6 +1060,8 @@ SYCLPropagateAspectsUsagePass::run(Module &M, ModuleAnalysisManager &MAM) { createUsedAspectsMetadataForFunctions( FunctionToUsedAspects, FunctionToDeclaredAspects, ExcludedAspectVals); + createConditionallyUsedAspectsMetadataForFunctions( + FunctionToConditionExpressionsAndConditionalAspects, ExcludedAspectVals); setSyclFixedTargetsMD(EntryPoints, TargetFixedAspects, AspectValues); return PreservedAnalyses::all(); diff --git a/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-1.ll b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-1.ll new file mode 100644 index 0000000000000..f47c2f6ce2209 --- /dev/null +++ b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-1.ll @@ -0,0 +1,70 @@ +; RUN: opt -passes=sycl-propagate-aspects-usage < %s -S | FileCheck %s +; +; Test checks that the pass is able to propagate information about conditionally +; and unconditionally used aspects through a call graph +; +; K +; / \ +; F1 (A) F2 (B) +; | +; CF1 +; | +; F3 (C, fp64) +; + +%Optional.A = type { i32 } +%Optional.B = type { i32 } +%Optional.C = type { i32 } + +%class.anon = type { ptr addrspace(4) } + +; CHECK: define spir_kernel void @kernel() !sycl_used_aspects ![[#ID1:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_kernel void @kernel() { + call spir_func void @func1() + call spir_func void @func2() + ret void +} + +; CHECK: define spir_func void @func1() !sycl_used_aspects ![[#ID6:]] +define spir_func void @func1() { + %tmp = alloca %Optional.A + ret void +} + +; CHECK: define spir_func void @func2() !sycl_used_aspects ![[#ID7:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_func void @func2() { + %tmp = alloca %Optional.B + %agg.tmp = alloca %class.anon, align 8 + call spir_func void @cond_func1(ptr @func3, ptr %agg.tmp, i32 0, i32 0) + ret void +} + +; CHECK: define spir_func void @func3() !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_func void @func3() { + %tmp1 = alloca %Optional.C + %tmp2 = alloca double + ret void +} + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID2:]] spir_func void @cond_func1 +declare spir_func void @cond_func1(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +%fn = type { ptr addrspace(4) } + +attributes #1 = { "sycl-call-if-on-device-conditionally"="true" } + +!sycl_types_that_use_aspects = !{!0, !1, !2} +!0 = !{!"Optional.A", i32 1} +!1 = !{!"Optional.B", i32 2} +!2 = !{!"Optional.C", i32 3} + +!sycl_aspects = !{!3} +!3 = !{!"fp64", i32 6} + +; CHECK: ![[#ID1]] = !{i32 1, i32 2} +; CHECK: ![[#ID2]] = !{![[#ID3:]]} +; CHECK: ![[#ID3]] = !{![[#ID4:]], ![[#ID5:]]} +; CHECK: ![[#ID4]] = !{i32 0} +; CHECK: ![[#ID5]] = !{i32 3, i32 6} +; CHECK: ![[#ID6]] = !{i32 1} +; CHECK: ![[#ID7]] = !{i32 2} diff --git a/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-2.ll b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-2.ll new file mode 100644 index 0000000000000..9cae3afa5da90 --- /dev/null +++ b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-2.ll @@ -0,0 +1,66 @@ +; RUN: opt -passes=sycl-propagate-aspects-usage < %s -S | FileCheck %s +; +; Test checks that the pass is able to propagate information about conditionally +; and unconditionally used aspects through a call graph +; +; K +; / \ +; CF1 CF2 +; | | +; F1 (A) F2 (B) +; + +%Optional.A = type { i32 } +%Optional.B = type { i32 } + +%class.anon = type { ptr addrspace(4) } + +; CHECK: define spir_kernel void @kernel() !sycl_conditionally_used_aspects ![[#ID1:]] +define spir_kernel void @kernel() { + %agg.tmp = alloca %class.anon, align 8 + call spir_func void @cond_func1(ptr @func1, ptr %agg.tmp, i32 0, i32 0) + call spir_func void @cond_func2(ptr @func2, ptr %agg.tmp, i32 0, i32 0) + ret void +} + +; CHECK: define spir_func void @func1() !sycl_conditionally_used_aspects ![[#ID9:]] +define spir_func void @func1() { + %tmp = alloca %Optional.A + ret void +} + +; CHECK: define spir_func void @func2() !sycl_conditionally_used_aspects ![[#ID10:]] +define spir_func void @func2() { + %tmp = alloca %Optional.B + ret void +} + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID9:]] spir_func void @cond_func1 +declare spir_func void @cond_func1(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID10:]] spir_func void @cond_func2 +declare spir_func void @cond_func2(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +%fn = type { ptr addrspace(4) } + +attributes #1 = { "sycl-call-if-on-device-conditionally"="true" } + +!sycl_types_that_use_aspects = !{!0, !1} +!0 = !{!"Optional.A", i32 1} +!1 = !{!"Optional.B", i32 2} + +!sycl_aspects = !{!2} +!2 = !{!"fp64", i32 6} + +; TODO: need to optimize Conditions-Aspects pairs to combine Aspects from different pairs which have the same Conditions + +; CHECK: ![[#ID1]] = !{![[#ID2:]], ![[#ID5:]], ![[#ID7:]]} +; CHECK: ![[#ID2]] = !{![[#ID3:]], ![[#ID4:]]} +; CHECK: ![[#ID3]] = !{i32 0} +; CHECK: ![[#ID4]] = !{i32 1} +; CHECK: ![[#ID5]] = !{![[#ID3:]], ![[#ID6:]]} +; CHECK: ![[#ID6]] = !{i32 1, i32 2} +; CHECK: ![[#ID7]] = !{![[#ID3:]], ![[#ID8:]]} +; CHECK: ![[#ID8]] = !{i32 2} +; CHECK: ![[#ID9]] = !{![[#ID2:]]} +; CHECK: ![[#ID10]] = !{![[#ID7:]]} diff --git a/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-3.ll b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-3.ll new file mode 100644 index 0000000000000..8d06fd60e4acf --- /dev/null +++ b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-3.ll @@ -0,0 +1,57 @@ +; RUN: opt -passes=sycl-propagate-aspects-usage < %s -S | FileCheck %s +; +; Test checks that the pass is able to propagate information about conditionally +; and unconditionally used aspects through a call graph +; +; K +; / \ +; F1 (B) CF1 +; \ / +; F2 (A) +; + +%Optional.A = type { i32 } +%Optional.B = type { i32 } + +%class.anon = type { ptr addrspace(4) } + +; CHECK: define spir_kernel void @kernel() !sycl_used_aspects ![[#ID1:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_kernel void @kernel() { + call spir_func void @func1() + %agg.tmp = alloca %class.anon, align 8 + call spir_func void @cond_func1(ptr @func2, ptr %agg.tmp, i32 0, i32 0) + ret void +} + +; CHECK: define spir_func void @func1() !sycl_used_aspects ![[#ID1:]] +define spir_func void @func1() { + %tmp = alloca %Optional.B + call spir_func void @func2() + ret void +} + +; CHECK: define spir_func void @func2() !sycl_used_aspects ![[#ID5:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_func void @func2() { + %tmp = alloca %Optional.A + ret void +} + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID2:]] spir_func void @cond_func1 +declare spir_func void @cond_func1(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +%fn = type { ptr addrspace(4) } + +attributes #1 = { "sycl-call-if-on-device-conditionally"="true" } + +!sycl_types_that_use_aspects = !{!0, !1} +!0 = !{!"Optional.A", i32 1} +!1 = !{!"Optional.B", i32 2} + +!sycl_aspects = !{!2} +!2 = !{!"fp64", i32 6} + +; CHECK: ![[#ID1]] = !{i32 2, i32 1} +; CHECK: ![[#ID2]] = !{![[#ID3:]]} +; CHECK: ![[#ID3]] = !{![[#ID4:]], ![[#ID5:]]} +; CHECK: ![[#ID4]] = !{i32 0} +; CHECK: ![[#ID5]] = !{i32 1} \ No newline at end of file diff --git a/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-4.ll b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-4.ll new file mode 100644 index 0000000000000..fad673810844d --- /dev/null +++ b/llvm/test/SYCLLowerIR/PropagateAspectsUsage/PropogateConditionalAspects/conditional-aspects-4.ll @@ -0,0 +1,89 @@ +; RUN: opt -passes=sycl-propagate-aspects-usage < %s -S | FileCheck %s +; +; Test checks that the pass is able to propagate information about conditionally +; and unconditionally used aspects through a call graph +; +; K +; / \ +; F1 (A) F2 (B) +; | +; CF1 +; | +; F3 (C) +; | +; CF2 +; | +; F4 (D) +; + +%Optional.A = type { i32 } +%Optional.B = type { i32 } +%Optional.C = type { i32 } +%Optional.D = type { i32 } + +%class.anon = type { ptr addrspace(4) } + +; CHECK: define spir_kernel void @kernel() !sycl_used_aspects ![[#ID1:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_kernel void @kernel() { + call spir_func void @func1() + call spir_func void @func2() + ret void +} + +; CHECK: define spir_func void @func1() !sycl_used_aspects ![[#ID8:]] +define spir_func void @func1() { + %tmp = alloca %Optional.A + ret void +} + +; CHECK: define spir_func void @func2() !sycl_used_aspects ![[#ID9:]] !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_func void @func2() { + %tmp = alloca %Optional.B + %agg.tmp = alloca %class.anon, align 8 + call spir_func void @cond_func1(ptr @func3, ptr %agg.tmp, i32 1, i32 2) + ret void +} + +; CHECK: define spir_func void @func3() !sycl_conditionally_used_aspects ![[#ID2:]] +define spir_func void @func3() { + %tmp = alloca %Optional.C + %agg.tmp = alloca %class.anon, align 8 + call spir_func void @cond_func2(ptr @func4, ptr %agg.tmp, i32 3, i32 4) + ret void +} + +; CHECK: define spir_func void @func4() !sycl_conditionally_used_aspects ![[#ID10:]] +define spir_func void @func4() { + %tmp = alloca %Optional.D + ret void +} + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID2:]] spir_func void @cond_func1 +declare spir_func void @cond_func1(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +; CHECK: declare !sycl_conditionally_used_aspects ![[#ID10:]] spir_func void @cond_func2 +declare spir_func void @cond_func2(ptr noundef byval(%fn) align 8, ptr noundef, i32 noundef, i32) #1 + +%fn = type { ptr addrspace(4) } + +attributes #1 = { "sycl-call-if-on-device-conditionally"="true" } + +!sycl_types_that_use_aspects = !{!0, !1, !2, !3} +!0 = !{!"Optional.A", i32 1} +!1 = !{!"Optional.B", i32 2} +!2 = !{!"Optional.C", i32 3} +!3 = !{!"Optional.D", i32 4} + +!sycl_aspects = !{!4} +!4 = !{!"fp64", i32 6} + +; CHECK: ![[#ID1]] = !{i32 1, i32 2} +; CHECK: ![[#ID2]] = !{![[#ID3:]], ![[#ID5:]]} +; CHECK: ![[#ID3]] = !{![[#ID1:]], ![[#ID4:]]} +; CHECK: ![[#ID4]] = !{i32 3, i32 4} +; CHECK: ![[#ID5]] = !{![[#ID6:]], ![[#ID7:]]} +; CHECK: ![[#ID6]] = !{i32 1, i32 2, i32 3, i32 4} +; CHECK: ![[#ID7]] = !{i32 4} +; CHECK: ![[#ID8]] = !{i32 1} +; CHECK: ![[#ID9]] = !{i32 2} +; CHECK: ![[#ID10]] = !{![[#ID5:]]}