1 | //===--- SingleWorkItemBarrierCheck.cpp - clang-tidy-----------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "SingleWorkItemBarrierCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::altera { |
16 | |
17 | void SingleWorkItemBarrierCheck::registerMatchers(MatchFinder *Finder) { |
18 | // Find any function that calls barrier but does not call an ID function. |
19 | // hasAttr(attr::Kind::OpenCLKernel) restricts it to only kernel functions. |
20 | // FIXME: Have it accept all functions but check for a parameter that gets an |
21 | // ID from one of the four ID functions. |
22 | Finder->addMatcher( |
23 | // Find function declarations... |
24 | functionDecl( |
25 | // That are OpenCL kernels... |
26 | hasAttr(attr::Kind::OpenCLKernel), |
27 | // And call a barrier function (either 1.x or 2.x version)... |
28 | forEachDescendant(callExpr(callee(functionDecl(hasAnyName( |
29 | "barrier" , "work_group_barrier" )))) |
30 | .bind("barrier" )), |
31 | // But do not call an ID function. |
32 | unless(hasDescendant(callExpr(callee(functionDecl( |
33 | hasAnyName("get_global_id" , "get_local_id" , "get_group_id" , |
34 | "get_local_linear_id" ))))))) |
35 | .bind("function" ), |
36 | this); |
37 | } |
38 | |
39 | void SingleWorkItemBarrierCheck::check(const MatchFinder::MatchResult &Result) { |
40 | const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>(ID: "function" ); |
41 | const auto *MatchedBarrier = Result.Nodes.getNodeAs<CallExpr>(ID: "barrier" ); |
42 | if (AOCVersion < 1701) { |
43 | // get_group_id and get_local_linear_id were added at/after v17.01 |
44 | diag(MatchedDecl->getLocation(), |
45 | "kernel function %0 does not call 'get_global_id' or 'get_local_id' " |
46 | "and will be treated as a single work-item" ) |
47 | << MatchedDecl; |
48 | diag(Loc: MatchedBarrier->getBeginLoc(), |
49 | Description: "barrier call is in a single work-item and may error out" , |
50 | Level: DiagnosticIDs::Note); |
51 | } else { |
52 | // If reqd_work_group_size is anything other than (1,1,1), it will be |
53 | // interpreted as an NDRange in AOC version >= 17.1. |
54 | bool IsNDRange = false; |
55 | if (MatchedDecl->hasAttr<ReqdWorkGroupSizeAttr>()) { |
56 | const auto *Attribute = MatchedDecl->getAttr<ReqdWorkGroupSizeAttr>(); |
57 | if (Attribute->getXDim() > 1 || Attribute->getYDim() > 1 || |
58 | Attribute->getZDim() > 1) |
59 | IsNDRange = true; |
60 | } |
61 | if (IsNDRange) // No warning if kernel is treated as an NDRange. |
62 | return; |
63 | diag(MatchedDecl->getLocation(), |
64 | "kernel function %0 does not call an ID function and may be a viable " |
65 | "single work-item, but will be forced to execute as an NDRange" ) |
66 | << MatchedDecl; |
67 | diag(Loc: MatchedBarrier->getBeginLoc(), |
68 | Description: "barrier call will force NDRange execution; if single work-item " |
69 | "semantics are desired a mem_fence may be more efficient" , |
70 | Level: DiagnosticIDs::Note); |
71 | } |
72 | } |
73 | |
74 | void SingleWorkItemBarrierCheck::storeOptions( |
75 | ClangTidyOptions::OptionMap &Opts) { |
76 | Options.store(Options&: Opts, LocalName: "AOCVersion" , Value: AOCVersion); |
77 | } |
78 | |
79 | } // namespace clang::tidy::altera |
80 | |