@@ -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