Skip to content

Commit c02e5a0

Browse files
[SYCL] Add property-based device_has diagnostics (#7297)
The device_has property should have behavior similar to that of the sycl::device_has attribute. This commit makes the aspect propagation pass issue the same diagnostic as done for when an application uses aspects that are not in sycl::device_has, albeit with a small change to the diagnostics message to differentiate the origin of the device_has, be it property or attribute. Signed-off-by: Larsen, Steffen <[email protected]>
1 parent d824127 commit c02e5a0

File tree

9 files changed

+339
-47
lines changed

9 files changed

+339
-47
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ def err_avx_calling_convention : Error<warn_avx_calling_convention.Text>;
283283

284284
def warn_sycl_device_has_aspect_mismatch
285285
: Warning<"function '%0' uses aspect '%1' not listed in its "
286-
"'sycl::device_has' attribute">, BackendInfo,
287-
InGroup<SyclAspectMismatch>;
286+
"%select{'device_has' property|'sycl::device_has' attribute}2">,
287+
BackendInfo, InGroup<SyclAspectMismatch>;
288288
def note_sycl_aspect_propagated_from_call
289289
: Note<"propagated from call to function '%0'">, BackendInfo;
290290

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,8 @@ void BackendConsumer::AspectMismatchDiagHandler(
867867
assert(LocCookie.isValid() &&
868868
"Invalid location for caller in aspect mismatch diagnostic");
869869
Diags.Report(LocCookie, diag::warn_sycl_device_has_aspect_mismatch)
870-
<< llvm::demangle(D.getFunctionName().str()) << D.getAspect();
870+
<< llvm::demangle(D.getFunctionName().str()) << D.getAspect()
871+
<< D.isFromDeviceHasAttribute();
871872
for (const std::pair<StringRef, unsigned> &CalleeInfo : D.getCallChain()) {
872873
LocCookie = SourceLocation::getFromRawEncoding(CalleeInfo.second);
873874
assert(LocCookie.isValid() &&

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7819,7 +7819,8 @@ void Sema::CheckSYCLAddIRAttributesFunctionAttrConflicts(Decl *D) {
78197819
for (const auto *Attr : std::vector<AttributeCommonInfo *>{
78207820
D->getAttr<ReqdWorkGroupSizeAttr>(),
78217821
D->getAttr<IntelReqdSubGroupSizeAttr>(),
7822-
D->getAttr<WorkGroupSizeHintAttr>()})
7822+
D->getAttr<WorkGroupSizeHintAttr>(),
7823+
D->getAttr<SYCLDeviceHasAttr>()})
78237824
if (Attr)
78247825
Diag(Attr->getLoc(), diag::warn_sycl_old_and_new_kernel_attributes)
78257826
<< Attr;

clang/test/SemaSYCL/attr-add-ir-attributes-function-conflict.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -fsyntax-only -verify %s
22

33
// Tests that add_ir_attributes_function causes a warning when appearing with
44
// potentially conflicting SYCL attributes.
55

6+
#include "sycl.hpp"
7+
68
constexpr const char AttrName1[] = "Attr1";
79
constexpr const char AttrVal1[] = "Val1";
810

@@ -20,10 +22,13 @@ int main() {
2022
EmptyWrapper.kernel_single_task<class EK1>([]() [[sycl::reqd_work_group_size(1)]] {});
2123
EmptyWrapper.kernel_single_task<class EK2>([]() [[sycl::reqd_work_group_size(1,2)]] {});
2224
EmptyWrapper.kernel_single_task<class EK3>([]() [[sycl::reqd_work_group_size(1,2,3)]] {});
23-
EmptyWrapper.kernel_single_task<class EK1>([]() [[sycl::work_group_size_hint(1)]] {});
24-
EmptyWrapper.kernel_single_task<class EK2>([]() [[sycl::work_group_size_hint(1,2)]] {});
25-
EmptyWrapper.kernel_single_task<class EK3>([]() [[sycl::work_group_size_hint(1,2,3)]] {});
25+
EmptyWrapper.kernel_single_task<class EK4>([]() [[sycl::work_group_size_hint(1)]] {});
26+
EmptyWrapper.kernel_single_task<class EK5>([]() [[sycl::work_group_size_hint(1,2)]] {});
27+
EmptyWrapper.kernel_single_task<class EK6>([]() [[sycl::work_group_size_hint(1,2,3)]] {});
2628
EmptyWrapper.kernel_single_task<class EK7>([]() [[sycl::reqd_sub_group_size(1)]] {});
29+
EmptyWrapper.kernel_single_task<class EK8>([]() [[sycl::device_has()]] {});
30+
EmptyWrapper.kernel_single_task<class EK9>([]() [[sycl::device_has(sycl::aspect::cpu)]] {});
31+
EmptyWrapper.kernel_single_task<class EK10>([]() [[sycl::device_has(sycl::aspect::cpu, sycl::aspect::gpu)]] {});
2732

2833
// expected-warning@+1 {{kernel has both attribute 'reqd_work_group_size' and kernel properties; conflicting properties are ignored}}
2934
NonemptyWrapper.kernel_single_task<class NEK1>([]() [[sycl::reqd_work_group_size(1)]] {});
@@ -32,11 +37,17 @@ int main() {
3237
// expected-warning@+1 {{kernel has both attribute 'reqd_work_group_size' and kernel properties; conflicting properties are ignored}}
3338
NonemptyWrapper.kernel_single_task<class NEK3>([]() [[sycl::reqd_work_group_size(1,2,3)]] {});
3439
// expected-warning@+1 {{kernel has both attribute 'work_group_size_hint' and kernel properties; conflicting properties are ignored}}
35-
NonemptyWrapper.kernel_single_task<class NEK1>([]() [[sycl::work_group_size_hint(1)]] {});
40+
NonemptyWrapper.kernel_single_task<class NEK4>([]() [[sycl::work_group_size_hint(1)]] {});
3641
// expected-warning@+1 {{kernel has both attribute 'work_group_size_hint' and kernel properties; conflicting properties are ignored}}
37-
NonemptyWrapper.kernel_single_task<class NEK2>([]() [[sycl::work_group_size_hint(1,2)]] {});
42+
NonemptyWrapper.kernel_single_task<class NEK5>([]() [[sycl::work_group_size_hint(1,2)]] {});
3843
// expected-warning@+1 {{kernel has both attribute 'work_group_size_hint' and kernel properties; conflicting properties are ignored}}
39-
NonemptyWrapper.kernel_single_task<class NEK3>([]() [[sycl::work_group_size_hint(1,2,3)]] {});
44+
NonemptyWrapper.kernel_single_task<class NEK6>([]() [[sycl::work_group_size_hint(1,2,3)]] {});
4045
// expected-warning@+1 {{kernel has both attribute 'reqd_sub_group_size' and kernel properties; conflicting properties are ignored}}
4146
NonemptyWrapper.kernel_single_task<class NEK7>([]() [[sycl::reqd_sub_group_size(1)]] {});
47+
// expected-warning@+1 {{kernel has both attribute 'device_has' and kernel properties; conflicting properties are ignored}}
48+
NonemptyWrapper.kernel_single_task<class NEK8>([]() [[sycl::device_has()]] {});
49+
// expected-warning@+1 {{kernel has both attribute 'device_has' and kernel properties; conflicting properties are ignored}}
50+
NonemptyWrapper.kernel_single_task<class NEK9>([]() [[sycl::device_has(sycl::aspect::cpu)]] {});
51+
// expected-warning@+1 {{kernel has both attribute 'device_has' and kernel properties; conflicting properties are ignored}}
52+
NonemptyWrapper.kernel_single_task<class NEK10>([]() [[sycl::device_has(sycl::aspect::cpu, sycl::aspect::gpu)]] {});
4253
}

llvm/include/llvm/IR/DiagnosticInfo.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,30 +1120,32 @@ class DiagnosticInfoDontCall : public DiagnosticInfo {
11201120

11211121
void diagnoseAspectsMismatch(const Function *F,
11221122
const SmallVector<Function *, 8> &CallChain,
1123-
StringRef Aspect);
1123+
StringRef Aspect, bool FromDeviceHasAttribute);
11241124

11251125
// Diagnostic information for SYCL aspects usage mismatch.
11261126
class DiagnosticInfoAspectsMismatch : public DiagnosticInfo {
11271127
StringRef FunctionName;
11281128
unsigned LocCookie;
11291129
llvm::SmallVector<std::pair<StringRef, unsigned>, 8> CallChain;
11301130
StringRef Aspect;
1131+
bool FromDeviceHasAttribute;
11311132

11321133
public:
11331134
DiagnosticInfoAspectsMismatch(
11341135
StringRef FunctionName, unsigned LocCookie,
11351136
const llvm::SmallVector<std::pair<StringRef, unsigned>, 8> &CallChain,
1136-
StringRef Aspect)
1137+
StringRef Aspect, bool FromDeviceHasAttribute)
11371138
: DiagnosticInfo(DK_AspectMismatch, DiagnosticSeverity::DS_Warning),
11381139
FunctionName(FunctionName), LocCookie(LocCookie), CallChain(CallChain),
1139-
Aspect(Aspect) {}
1140+
Aspect(Aspect), FromDeviceHasAttribute(FromDeviceHasAttribute) {}
11401141
StringRef getFunctionName() const { return FunctionName; }
11411142
unsigned getLocCookie() const { return LocCookie; }
11421143
const llvm::SmallVector<std::pair<StringRef, unsigned>, 8> &
11431144
getCallChain() const {
11441145
return CallChain;
11451146
}
11461147
StringRef getAspect() const { return Aspect; }
1148+
bool isFromDeviceHasAttribute() const { return FromDeviceHasAttribute; }
11471149
void print(DiagnosticPrinter &DP) const override;
11481150
static bool classof(const DiagnosticInfo *DI) {
11491151
return DI->getKind() == DK_AspectMismatch;

llvm/lib/IR/DiagnosticInfo.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const {
449449

450450
void llvm::diagnoseAspectsMismatch(const Function *F,
451451
const SmallVector<Function *, 8> &CallChain,
452-
StringRef Aspect) {
452+
StringRef Aspect,
453+
bool FromDeviceHasAttribute) {
453454
unsigned LocCookie = 0;
454455
if (MDNode *MD = F->getMetadata("srcloc"))
455456
LocCookie =
@@ -466,7 +467,7 @@ void llvm::diagnoseAspectsMismatch(const Function *F,
466467
}
467468

468469
DiagnosticInfoAspectsMismatch D(F->getName(), LocCookie, LoweredCallChain,
469-
Aspect);
470+
Aspect, FromDeviceHasAttribute);
470471
F->getContext().diagnose(D);
471472
}
472473

llvm/lib/SYCLLowerIR/SYCLPropagateAspectsUsage.cpp

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -246,29 +246,39 @@ AspectsSetTy getAspectsUsedByInstruction(const Instruction &I,
246246
using FunctionToAspectsMapTy = DenseMap<Function *, AspectsSetTy>;
247247
using CallGraphTy = DenseMap<Function *, SmallPtrSet<Function *, 8>>;
248248

249+
// Finds the first function in a list that uses a given aspect. Returns nullptr
250+
// if none of the functions satisfy the criteria.
251+
Function *findFirstAspectUsageCallee(
252+
const SmallPtrSetImpl<Function *> &Callees,
253+
const FunctionToAspectsMapTy &AspectsMap, int Aspect,
254+
SmallPtrSetImpl<const Function *> *Visited = nullptr) {
255+
for (Function *Callee : Callees) {
256+
if (Visited && !Visited->insert(Callee).second)
257+
continue;
258+
259+
auto AspectIt = AspectsMap.find(Callee);
260+
if (AspectIt != AspectsMap.end() && AspectIt->second.contains(Aspect))
261+
return Callee;
262+
}
263+
return nullptr;
264+
}
265+
249266
// Constructs an aspect usage chain for a given aspect from the function to the
250267
// last callee in the first found chain.
251268
void constructAspectUsageChain(const Function *F,
252269
const FunctionToAspectsMapTy &AspectsMap,
253270
const CallGraphTy &CG, int Aspect,
254-
SmallVector<Function *, 8> &CallChain,
255-
SmallPtrSet<const Function *, 16> &Visited) {
271+
SmallVectorImpl<Function *> &CallChain,
272+
SmallPtrSetImpl<const Function *> &Visited) {
256273
const auto EdgeIt = CG.find(F);
257274
if (EdgeIt == CG.end())
258275
return;
259276

260-
for (Function *Callee : EdgeIt->second) {
261-
if (!Visited.insert(Callee).second)
262-
continue;
263-
264-
auto AspectIt = AspectsMap.find(Callee);
265-
if (AspectIt == AspectsMap.end() || !AspectIt->second.contains(Aspect))
266-
continue;
267-
268-
CallChain.push_back(Callee);
269-
constructAspectUsageChain(Callee, AspectsMap, CG, Aspect, CallChain,
270-
Visited);
271-
break;
277+
if (Function *AspectUsingCallee = findFirstAspectUsageCallee(
278+
EdgeIt->second, AspectsMap, Aspect, &Visited)) {
279+
CallChain.push_back(AspectUsingCallee);
280+
constructAspectUsageChain(AspectUsingCallee, AspectsMap, CG, Aspect,
281+
CallChain, Visited);
272282
}
273283
}
274284

@@ -313,22 +323,33 @@ void validateUsedAspectsForFunctions(const FunctionToAspectsMapTy &Map,
313323
continue;
314324

315325
Function *F = It.first;
316-
317-
// Entry points will have their declared aspects from their kernel call.
318-
// To avoid double warnings, we skip them.
319-
if (std::find(EntryPoints.begin(), EntryPoints.end(), F) !=
320-
EntryPoints.end())
321-
continue;
322-
323-
const MDNode *DeviceHasMD = F->getMetadata("sycl_declared_aspects");
324-
if (!DeviceHasMD)
325-
continue;
326-
327326
AspectsSetTy DeviceHasAspectSet;
328-
for (size_t I = 0; I != DeviceHasMD->getNumOperands(); ++I) {
329-
const auto *CAM = cast<ConstantAsMetadata>(DeviceHasMD->getOperand(I));
330-
const Constant *C = CAM->getValue();
331-
DeviceHasAspectSet.insert(cast<ConstantInt>(C)->getSExtValue());
327+
bool OriginatedFromAttribute = true;
328+
if (const MDNode *DeviceHasMD = F->getMetadata("sycl_declared_aspects")) {
329+
// Entry points will have their declared aspects from their kernel call.
330+
// To avoid double warnings, we skip them.
331+
if (is_contained(EntryPoints, F))
332+
continue;
333+
for (const MDOperand &DeviceHasMDOp : DeviceHasMD->operands()) {
334+
const auto *CAM = cast<ConstantAsMetadata>(DeviceHasMDOp);
335+
const Constant *C = CAM->getValue();
336+
DeviceHasAspectSet.insert(cast<ConstantInt>(C)->getSExtValue());
337+
}
338+
OriginatedFromAttribute = true;
339+
} else if (F->hasFnAttribute("sycl-device-has")) {
340+
Attribute DeviceHasAttr = F->getFnAttribute("sycl-device-has");
341+
SmallVector<StringRef, 4> AspectValStrs;
342+
DeviceHasAttr.getValueAsString().split(
343+
AspectValStrs, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
344+
for (StringRef AspectValStr : AspectValStrs) {
345+
int AspectVal = -1;
346+
assert(!AspectValStr.getAsInteger(10, AspectVal) &&
347+
"Aspect value in sycl-device-has is not an integer.");
348+
DeviceHasAspectSet.insert(AspectVal);
349+
}
350+
OriginatedFromAttribute = false;
351+
} else {
352+
continue;
332353
}
333354

334355
for (int Aspect : Aspects) {
@@ -338,9 +359,19 @@ void validateUsedAspectsForFunctions(const FunctionToAspectsMapTy &Map,
338359
[=](auto AspectIt) { return Aspect == AspectIt.second; });
339360
assert(AspectNameIt != AspectValues.end() &&
340361
"Used aspect is not part of the existing aspects");
362+
// We may encounter an entry point when using the device_has property.
363+
// In this case we act like the usage came from the first callee to
364+
// avoid repeat warnings on the same line.
365+
Function *AdjustedOriginF =
366+
is_contained(EntryPoints, F)
367+
? findFirstAspectUsageCallee(CG.find(F)->second, Map, Aspect)
368+
: F;
369+
assert(AdjustedOriginF &&
370+
"Adjusted function pointer for aspect usage is null");
341371
SmallVector<Function *, 8> CallChain =
342-
getAspectUsageChain(F, Map, CG, Aspect);
343-
diagnoseAspectsMismatch(F, CallChain, AspectNameIt->first);
372+
getAspectUsageChain(AdjustedOriginF, Map, CG, Aspect);
373+
diagnoseAspectsMismatch(AdjustedOriginF, CallChain, AspectNameIt->first,
374+
OriginatedFromAttribute);
344375
}
345376
}
346377
}

0 commit comments

Comments
 (0)