|
56 | 56 | #include "clad/Differentiator/CladUtils.h" |
57 | 57 | #include "clad/Differentiator/Compatibility.h" |
58 | 58 |
|
59 | | -using namespace clang; |
| 59 | +#include "clang/ASTMatchers/ASTMatchFinder.h" |
| 60 | +#include "clang/ASTMatchers/ASTMatchers.h" |
| 61 | + |
| 62 | +using namespace clang::ast_matchers; |
60 | 63 |
|
61 | 64 | namespace clad { |
62 | 65 |
|
@@ -133,30 +136,141 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { |
133 | 136 | return CladTapeResult{*this, PushExpr, PopExpr, TapeRef}; |
134 | 137 | } |
135 | 138 |
|
| 139 | + static bool isInjectiveE(const clang::Expr* E, clang::ASTContext& ctx) { |
| 140 | + StatementMatcher matcher = |
| 141 | + binaryOperator( |
| 142 | + hasOperatorName("+"), |
| 143 | + hasLHS(expr( |
| 144 | + has(callExpr(hasDescendant(memberExpr( |
| 145 | + hasObjectExpression(opaqueValueExpr(hasSourceExpression( |
| 146 | + declRefExpr(to(varDecl(hasName("threadIdx"))))))))))), |
| 147 | + unless(binaryOperator()))), |
| 148 | + hasRHS(binaryOperator( |
| 149 | + hasOperatorName("*"), |
| 150 | + has(expr( |
| 151 | + has(callExpr(hasDescendant(memberExpr(hasObjectExpression( |
| 152 | + opaqueValueExpr(hasSourceExpression(declRefExpr( |
| 153 | + to(varDecl(hasName("blockIdx"))))))))))), |
| 154 | + unless(binaryOperator()))), |
| 155 | + has(expr( |
| 156 | + has(callExpr(hasDescendant(memberExpr(hasObjectExpression( |
| 157 | + opaqueValueExpr(hasSourceExpression(declRefExpr( |
| 158 | + to(varDecl(hasName("blockDim"))))))))))), |
| 159 | + unless(binaryOperator())))))) |
| 160 | + .bind("targetVar"); |
| 161 | + |
| 162 | + class InjectivePatternMatchCallback : public MatchFinder::MatchCallback { |
| 163 | + const clang::Expr* targetExpr; |
| 164 | + |
| 165 | + public: |
| 166 | + InjectivePatternMatchCallback(const clang::Expr* E) : targetExpr(E) {} |
| 167 | + bool matched = false; |
| 168 | + virtual void run(const MatchFinder::MatchResult& Result) override { |
| 169 | + if (const auto* expr = |
| 170 | + Result.Nodes.getNodeAs<clang::BinaryOperator>("targetVar")) { |
| 171 | + if (targetExpr == expr) |
| 172 | + matched = true; |
| 173 | + } |
| 174 | + } |
| 175 | + bool hasMatched() const { return matched; } |
| 176 | + }; |
| 177 | + |
| 178 | + MatchFinder Finder; |
| 179 | + InjectivePatternMatchCallback Callback(E); |
| 180 | + Finder.addMatcher(matcher, &Callback); |
| 181 | + Finder.matchAST(ctx); |
| 182 | + |
| 183 | + return Callback.hasMatched(); |
| 184 | + } |
| 185 | + |
| 186 | + static bool isInjective(const clang::Expr* E, clang::ASTContext& ctx) { |
| 187 | + class InjectiveChecker |
| 188 | + : public clang::RecursiveASTVisitor<InjectiveChecker> { |
| 189 | + clang::ASTContext& m_Context; |
| 190 | + |
| 191 | + public: |
| 192 | + InjectiveChecker(clang::ASTContext& Context) : m_Context(Context){}; |
| 193 | + |
| 194 | + bool isInjectiveIdx(const clang::Expr* E) { |
| 195 | + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) |
| 196 | + return TraverseStmt(const_cast<clang::Expr*>(E)); |
| 197 | + } |
| 198 | + |
| 199 | + bool TraverseBinaryOperator(clang::BinaryOperator* BinOp) { |
| 200 | + const auto opCode = BinOp->getOpcode(); |
| 201 | + Expr* L = BinOp->getLHS(); |
| 202 | + Expr* R = BinOp->getRHS(); |
| 203 | + |
| 204 | + if (opCode == BO_Add || opCode == BO_Mul) { |
| 205 | + Expr::EvalResult dummy; |
| 206 | + |
| 207 | + bool isConstL = |
| 208 | + clad_compat::Expr_EvaluateAsConstantExpr(L, dummy, m_Context); |
| 209 | + bool isConstR = |
| 210 | + clad_compat::Expr_EvaluateAsConstantExpr(R, dummy, m_Context); |
| 211 | + |
| 212 | + if (isConstL && isConstR) |
| 213 | + return false; |
| 214 | + |
| 215 | + if (!isConstL && isConstR) |
| 216 | + return TraverseStmt(L); |
| 217 | + |
| 218 | + if (isConstL && !isConstR) |
| 219 | + return TraverseStmt(R); |
| 220 | + |
| 221 | + return isInjectiveE(BinOp, m_Context); |
| 222 | + } |
| 223 | + return false; |
| 224 | + } |
| 225 | + |
| 226 | + bool TraverseDeclRefExpr(clang::DeclRefExpr* DRE) { |
| 227 | + if (auto* VD = dyn_cast<VarDecl>(DRE->getDecl())) { |
| 228 | + if (auto* init = VD->getInit()) |
| 229 | + return isInjectiveE(init->IgnoreImpCasts(), m_Context); |
| 230 | + } |
| 231 | + return false; |
| 232 | + } |
| 233 | + |
| 234 | + bool TraverseIntegerLiteral(clang::IntegerLiteral* IL) { return false; } |
| 235 | + |
| 236 | + } checker(ctx); |
| 237 | + |
| 238 | + return checker.isInjectiveIdx(E); |
| 239 | + } |
| 240 | + |
136 | 241 | bool ReverseModeVisitor::shouldUseCudaAtomicOps(const Expr* E) { |
137 | 242 | if (!m_Context.getLangOpts().CUDA) |
138 | 243 | return false; |
139 | | - |
140 | | - if (!isa<DeclRefExpr>(E)) |
141 | | - return false; |
142 | | - |
143 | | - const auto* DRE = cast<DeclRefExpr>(E); |
144 | | - |
145 | | - if (const auto* PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) { |
146 | | - if (m_DiffReq->hasAttr<clang::CUDAGlobalAttr>()) |
147 | | - // Check whether this param is in the global memory of the GPU |
148 | | - return m_DiffReq.HasIndependentParameter(PVD); |
149 | | - if (m_DiffReq->hasAttr<clang::CUDADeviceAttr>()) { |
150 | | - for (auto index : m_DiffReq.CUDAGlobalArgsIndexes) { |
151 | | - const auto* PVDOrig = m_DiffReq->getParamDecl(index); |
152 | | - if ("_d_" + PVDOrig->getNameAsString() == PVD->getNameAsString() && |
153 | | - (utils::isArrayOrPointerType(PVDOrig->getType()) || |
154 | | - PVDOrig->getType()->isReferenceType())) |
155 | | - return true; |
| 244 | + if (const auto* DRE = dyn_cast<DeclRefExpr>(E)) { |
| 245 | + if (const auto* PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) { |
| 246 | + if (m_DiffReq->hasAttr<clang::CUDAGlobalAttr>()) |
| 247 | + // Check whether this param is in the global memory of the GPU |
| 248 | + return m_DiffReq.HasIndependentParameter(PVD); |
| 249 | + if (m_DiffReq->hasAttr<clang::CUDADeviceAttr>()) { |
| 250 | + for (auto index : m_DiffReq.CUDAGlobalArgsIndexes) { |
| 251 | + const auto* PVDOrig = m_DiffReq->getParamDecl(index); |
| 252 | + if ("_d_" + PVDOrig->getNameAsString() == PVD->getNameAsString() && |
| 253 | + (utils::isArrayOrPointerType(PVDOrig->getType()) || |
| 254 | + PVDOrig->getType()->isReferenceType())) |
| 255 | + return true; |
| 256 | + } |
156 | 257 | } |
157 | 258 | } |
| 259 | + } else if (const auto* ASE = dyn_cast<ArraySubscriptExpr>(E)) { |
| 260 | + if (m_DiffReq->hasAttr<clang::CUDAGlobalAttr>()) { |
| 261 | + auto* idx = ASE->getIdx(); |
| 262 | + auto* base = dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreImpCasts()); |
| 263 | + if (const auto* PVD = dyn_cast<ParmVarDecl>(base->getDecl())) { |
| 264 | + if (m_DiffReq->hasAttr<clang::CUDAGlobalAttr>()) { |
| 265 | + // Check whether this param is in the global memory of the GPU and |
| 266 | + // if index is injective. |
| 267 | + return m_DiffReq.HasIndependentParameter(PVD) && |
| 268 | + !isInjective(idx, m_Context); |
| 269 | + } |
| 270 | + } |
| 271 | + return true; |
| 272 | + } |
158 | 273 | } |
159 | | - |
160 | 274 | return false; |
161 | 275 | } |
162 | 276 |
|
@@ -1341,7 +1455,7 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { |
1341 | 1455 | // Create the (target += dfdx) statement. |
1342 | 1456 | if (dfdx()) { |
1343 | 1457 | Expr* add_assign = nullptr; |
1344 | | - if (shouldUseCudaAtomicOps(target)) |
| 1458 | + if (shouldUseCudaAtomicOps(ASE)) |
1345 | 1459 | add_assign = BuildCallToCudaAtomicAdd(result, dfdx()); |
1346 | 1460 | else |
1347 | 1461 | add_assign = BuildOp(BO_AddAssign, result, dfdx()); |
|
0 commit comments