Skip to content

Commit b7f77c2

Browse files
author
ovdiiuv
committed
add comments and minor changes
1 parent b24250c commit b7f77c2

3 files changed

Lines changed: 198 additions & 183 deletions

File tree

include/clad/Differentiator/CladUtils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,12 @@ namespace clad {
408408
/// create modifiable adjoints.
409409
clang::QualType replaceStdInitListWithCladArray(clang::Sema& S,
410410
clang::QualType origTy);
411+
/// Helper fucntion for isInjective.
412+
bool isInjectiveE(const clang::Expr* E);
413+
/// Currently is only used for CUDA in the reverse mode. Determines whether
414+
/// an expression, most likely an index, is injective, meaning no two
415+
/// threads have the same value.
416+
bool isInjective(const clang::Expr* E, clang::ASTContext& ctx);
411417
} // namespace utils
412418
} // namespace clad
413419

lib/Differentiator/CladUtils.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,5 +1144,195 @@ namespace clad {
11441144
return S.getASTContext().getLValueReferenceType(T);
11451145
return T;
11461146
}
1147+
1148+
bool isInjectiveE(const clang::Expr* E) {
1149+
class InjectiveCheckerExpr
1150+
: public clang::RecursiveASTVisitor<InjectiveCheckerExpr> {
1151+
struct IdxNode {
1152+
struct ExprOrBinOp {
1153+
const clang::DeclRefExpr* m_E = nullptr;
1154+
clang::BinaryOperatorKind m_Opcode;
1155+
enum class Kind { Expr, Opcode } m_Kind;
1156+
1157+
ExprOrBinOp(const clang::DeclRefExpr* E)
1158+
: m_E(E), m_Kind(Kind::Expr) {}
1159+
ExprOrBinOp(clang::BinaryOperatorKind op)
1160+
: m_Opcode(op), m_Kind(Kind::Opcode) {}
1161+
1162+
[[nodiscard]] bool isExpr() const { return m_Kind == Kind::Expr; }
1163+
[[nodiscard]] bool isOpcode() const {
1164+
return m_Kind == Kind::Opcode;
1165+
}
1166+
};
1167+
1168+
ExprOrBinOp Node;
1169+
std::unique_ptr<IdxNode> left;
1170+
std::unique_ptr<IdxNode> right;
1171+
1172+
IdxNode(ExprOrBinOp N) : Node(N) {}
1173+
};
1174+
std::unique_ptr<IdxNode> m_Root;
1175+
IdxNode* m_ParentNode = nullptr;
1176+
1177+
enum class side { left, right } m_Side;
1178+
1179+
public:
1180+
InjectiveCheckerExpr() = default;
1181+
/// This function recursively checks whether a given pattern matches the
1182+
/// previously computed graph. It uses a fairly standard graph
1183+
/// comparison algorithm.
1184+
bool comparePatternToTree(const IdxNode* current,
1185+
const std::vector<std::string>& patternIdx,
1186+
size_t i = 1) {
1187+
// If current is not initialized and child is empty or does not exist,
1188+
// we have a match.
1189+
if (!current && (i >= patternIdx.size() || patternIdx[i].empty()))
1190+
return true;
1191+
1192+
if (!current || i >= patternIdx.size() || patternIdx[i].empty())
1193+
return false;
1194+
1195+
const std::string& expected = patternIdx[i];
1196+
1197+
if (current->Node.isOpcode()) {
1198+
std::string actualOp =
1199+
clang::BinaryOperator::getOpcodeStr(current->Node.m_Opcode)
1200+
.str();
1201+
if (actualOp != expected)
1202+
return false;
1203+
} else if (current->Node.isExpr()) {
1204+
std::string actualName =
1205+
current->Node.m_E->getNameInfo().getAsString();
1206+
if (actualName != expected)
1207+
return false;
1208+
}
1209+
1210+
bool sameOrder =
1211+
comparePatternToTree(current->left.get(), patternIdx, 2 * i) &&
1212+
comparePatternToTree(current->right.get(), patternIdx, 2 * i + 1);
1213+
1214+
if (sameOrder)
1215+
return true;
1216+
// Here we account for a sub-tree rotation wrt to the current node. If
1217+
// there is no match at this point, we compare a pattern to a graph
1218+
// with the rotation.
1219+
return comparePatternToTree(current->left.get(), patternIdx,
1220+
2 * i + 1) &&
1221+
comparePatternToTree(current->right.get(), patternIdx, 2 * i);
1222+
}
1223+
1224+
bool isInjectiveIdx(const clang::Expr* E) {
1225+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
1226+
TraverseStmt(const_cast<clang::Expr*>(E));
1227+
std::vector<std::string> pattern = {"", "+", "threadIdx", "*",
1228+
"", "", "blockIdx", "blockDim"};
1229+
return comparePatternToTree(m_Root.get(), pattern);
1230+
}
1231+
1232+
bool TraverseBinaryOperator(clang::BinaryOperator* BinOp) {
1233+
const auto opCode = BinOp->getOpcode();
1234+
if (opCode == BO_Add || opCode == BO_Mul) {
1235+
std::unique_ptr<IdxNode> curr = std::make_unique<IdxNode>(opCode);
1236+
IdxNode* currPtr = nullptr;
1237+
1238+
if (!m_Root) {
1239+
m_Root = std::move(curr);
1240+
currPtr = m_Root.get();
1241+
} else {
1242+
currPtr = curr.get();
1243+
1244+
if (m_Side == side::left)
1245+
m_ParentNode->left = std::move(curr);
1246+
1247+
if (m_Side == side::right)
1248+
m_ParentNode->right = std::move(curr);
1249+
}
1250+
1251+
Expr* L = BinOp->getLHS();
1252+
Expr* R = BinOp->getRHS();
1253+
1254+
m_ParentNode = currPtr;
1255+
m_Side = side::left;
1256+
TraverseStmt(L);
1257+
m_ParentNode = currPtr;
1258+
1259+
m_Side = side::right;
1260+
TraverseStmt(R);
1261+
}
1262+
return true;
1263+
}
1264+
1265+
bool TraverseDeclRefExpr(clang::DeclRefExpr* DRE) {
1266+
std::unique_ptr<IdxNode> curr = std::make_unique<IdxNode>(DRE);
1267+
1268+
if (m_ParentNode) {
1269+
if (m_Side == side::left)
1270+
m_ParentNode->left = std::move(curr);
1271+
1272+
if (m_Side == side::right)
1273+
m_ParentNode->right = std::move(curr);
1274+
}
1275+
return true;
1276+
}
1277+
1278+
} checker;
1279+
return checker.isInjectiveIdx(E);
1280+
}
1281+
1282+
bool isInjective(const clang::Expr* E, clang::ASTContext& ctx) {
1283+
class InjectiveChecker
1284+
: public clang::RecursiveASTVisitor<InjectiveChecker> {
1285+
clang::ASTContext& m_Context;
1286+
1287+
public:
1288+
InjectiveChecker(clang::ASTContext& Context) : m_Context(Context) {};
1289+
1290+
bool isInjectiveIdx(const clang::Expr* E) {
1291+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
1292+
return TraverseStmt(const_cast<clang::Expr*>(E));
1293+
}
1294+
1295+
bool TraverseBinaryOperator(clang::BinaryOperator* BinOp) {
1296+
const auto opCode = BinOp->getOpcode();
1297+
Expr* L = BinOp->getLHS();
1298+
Expr* R = BinOp->getRHS();
1299+
1300+
if (opCode == BO_Add || opCode == BO_Mul) {
1301+
Expr::EvalResult dummy;
1302+
1303+
bool isConstL =
1304+
clad_compat::Expr_EvaluateAsConstantExpr(L, dummy, m_Context);
1305+
bool isConstR =
1306+
clad_compat::Expr_EvaluateAsConstantExpr(R, dummy, m_Context);
1307+
1308+
if (isConstL && isConstR)
1309+
return false;
1310+
1311+
if (!isConstL && isConstR)
1312+
return TraverseStmt(L);
1313+
1314+
if (!isConstL && !isConstR)
1315+
return isInjectiveE(BinOp);
1316+
1317+
if (!isConstR)
1318+
return TraverseStmt(R);
1319+
}
1320+
return false;
1321+
}
1322+
1323+
bool TraverseDeclRefExpr(clang::DeclRefExpr* DRE) {
1324+
if (auto* VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1325+
if (auto* init = VD->getInit())
1326+
return isInjectiveE(init->IgnoreImpCasts());
1327+
}
1328+
return false;
1329+
}
1330+
1331+
bool TraverseIntegerLiteral(clang::IntegerLiteral* IL) { return false; }
1332+
1333+
} checker(ctx);
1334+
1335+
return checker.isInjectiveIdx(E);
1336+
}
11471337
} // namespace utils
11481338
} // namespace clad

0 commit comments

Comments
 (0)