hqlcse.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "jlib.hpp"
  15. #include "jmisc.hpp"
  16. #include "jstream.ipp"
  17. #include "hql.hpp"
  18. #include "hqlcse.ipp"
  19. #include "hqlutil.hpp"
  20. #include "hqlcpputil.hpp"
  21. #include "hqlthql.hpp"
  22. #include "hqlcatom.hpp"
  23. #include "hqlfold.hpp"
  24. #include "hqlpmap.hpp"
  25. #include "hqlopt.hpp"
  26. #include "hqlcerrors.hpp"
  27. #include "hqlttcpp.ipp"
  28. #ifdef _DEBUG
  29. //#define TRACE_CSE
  30. //#define TRACE_CSE_SCOPE
  31. #endif
  32. //The following allows x != y and x == y to be commoned up. It works, but currently disabled
  33. //because cse doesn't preserve short circuit of AND and OR, and some examples mean it will do more
  34. //work because the alias will always be evaluated. (e.g., salt1.xhql)
  35. //Really aliases need to be functional and executed on demand or something similar.
  36. //#define OPTIMIZE_INVERSE
  37. //---------------------------------------------------------------------------
  38. inline bool canWrapWithCSE(IHqlExpression * expr)
  39. {
  40. switch (expr->getOperator())
  41. {
  42. case no_mapto:
  43. return false;
  44. }
  45. return true;
  46. }
  47. bool canCreateTemporary(IHqlExpression * expr)
  48. {
  49. switch (expr->getOperator())
  50. {
  51. case no_range:
  52. case no_rangefrom:
  53. case no_rangeto:
  54. case no_rangecommon:
  55. case no_constant:
  56. case no_all:
  57. case no_mapto:
  58. case no_record:
  59. case no_attr:
  60. case no_attr_expr:
  61. case no_attr_link:
  62. case no_joined:
  63. case no_sizeof:
  64. case no_offsetof:
  65. case no_newtransform:
  66. case no_transform:
  67. case no_assign:
  68. case no_assignall:
  69. case no_left:
  70. case no_right:
  71. case no_self:
  72. case no_top:
  73. case no_activetable:
  74. case no_alias:
  75. case no_skip:
  76. case no_assert:
  77. case no_counter:
  78. case no_sortlist:
  79. case no_matched:
  80. case no_matchtext:
  81. case no_matchunicode:
  82. case no_matchposition:
  83. case no_matchlength:
  84. case no_matchattr:
  85. case no_matchrow:
  86. case no_matchutf8:
  87. case no_recordlist:
  88. case no_transformlist:
  89. case no_rowvalue:
  90. case no_pipe:
  91. case no_colon:
  92. case no_globalscope:
  93. case no_subgraph:
  94. case no_forcelocal:
  95. case no_forcenolocal:
  96. case no_allnodes:
  97. case no_thisnode:
  98. case no_libraryscopeinstance:
  99. case no_loopbody:
  100. case no_external:
  101. return false;
  102. }
  103. ITypeInfo * type = expr->queryType();
  104. if (!type)
  105. return false;
  106. switch (type->getTypeCode())
  107. {
  108. case type_transform:
  109. case type_null:
  110. case type_void:
  111. case type_rule:
  112. case type_pattern:
  113. case type_token:
  114. case type_event:
  115. return false;
  116. default:
  117. return true;
  118. }
  119. }
  120. //---------------------------------------------------------------------------
  121. /*
  122. Cse spotting...
  123. * Don't remove named symbols from items that aren't transformed.
  124. * Common items up regardless of the named symbol used to reference it.
  125. */
  126. CseSpotterInfo::CseSpotterInfo(IHqlExpression * expr) : NewTransformInfo(expr)
  127. {
  128. numRefs = 0;
  129. numAssociatedRefs = 0;
  130. alreadyAliased = false;
  131. canAlias = false;
  132. dontTransform = false;
  133. dontTransformSelector = false;
  134. treatAsAliased = false;
  135. inverse = NULL;
  136. annotatedExpr = NULL;
  137. }
  138. //worth aliasing if referenced more than once, and used more than once in the expressions that are going to be evaluated now
  139. bool CseSpotterInfo::worthAliasingOnOwn()
  140. {
  141. return numRefs > 1 && (numRefs != numAssociatedRefs);
  142. }
  143. bool CseSpotterInfo::worthAliasing()
  144. {
  145. if (!inverse)
  146. return worthAliasingOnOwn();
  147. //no_not will always traverse the inverse (at least once), so don't sum the two counts - just use the non inverted count
  148. if (original->getOperator() == no_not)
  149. return worthAliasingOnOwn() || inverse->worthAliasingOnOwn();
  150. if (inverse->original->getOperator() == no_not)
  151. return worthAliasingOnOwn();
  152. unsigned totalRefs = numRefs + inverse->numRefs;
  153. unsigned totalAssociatedRefs = numAssociatedRefs + inverse->numAssociatedRefs;
  154. if ((totalRefs > 1) && (totalRefs != totalAssociatedRefs))
  155. return true;
  156. return false;
  157. }
  158. //Do we create an alias for this node, or the other one?
  159. bool CseSpotterInfo::useInverseForAlias()
  160. {
  161. if (!inverse)
  162. return false;
  163. if (numRefs == numAssociatedRefs)
  164. return true;
  165. node_operator op = original->getOperator();
  166. switch (op)
  167. {
  168. case no_not:
  169. case no_ne:
  170. case no_notin:
  171. case no_notbetween:
  172. return inverse->worthAliasingOnOwn();
  173. }
  174. node_operator invOp = inverse->original->getOperator();
  175. switch (invOp)
  176. {
  177. case no_not: return false; //No otherwise we'll expand recursively!
  178. case no_ne:
  179. case no_notin:
  180. case no_notbetween:
  181. return !worthAliasingOnOwn();
  182. }
  183. return op > invOp;
  184. }
  185. static HqlTransformerInfo cseSpotterInfo("CseSpotter");
  186. CseSpotter::CseSpotter(bool _spotCseInIfDatasetConditions)
  187. : NewHqlTransformer(cseSpotterInfo), spotCseInIfDatasetConditions(_spotCseInIfDatasetConditions)
  188. {
  189. canAlias = true;
  190. isAssociated = false;
  191. spottedCandidate = false;
  192. invariantSelector = NULL;
  193. createLocalAliases = false;
  194. createdAlias = false;
  195. }
  196. void CseSpotter::analyseAssociated(IHqlExpression * expr, unsigned pass)
  197. {
  198. isAssociated = true;
  199. analyse(expr, pass);
  200. isAssociated = false;
  201. }
  202. void CseSpotter::analyseExpr(IHqlExpression * expr)
  203. {
  204. CseSpotterInfo * extra = queryBodyExtra(expr);
  205. if (!extra->annotatedExpr && expr->isAnnotation())
  206. extra->annotatedExpr = expr;
  207. if (isAssociated)
  208. extra->numAssociatedRefs++;
  209. node_operator op = expr->getOperator();
  210. #ifdef OPTIMIZE_INVERSE
  211. if (getInverseOp(op) != no_none)
  212. {
  213. OwnedHqlExpr inverse = getInverse(expr);
  214. CseSpotterInfo * inverseExtra = queryBodyExtra(inverse);
  215. extra->inverse = inverseExtra;
  216. inverseExtra->inverse = extra;
  217. }
  218. #endif
  219. if (op == no_alias)
  220. {
  221. queryBodyExtra(expr->queryChild(0))->alreadyAliased = true;
  222. extra->alreadyAliased = true;
  223. }
  224. switch (op)
  225. {
  226. case no_assign:
  227. case no_transform:
  228. case no_newtransform:
  229. case no_range:
  230. case no_rangefrom:
  231. if (expr->isConstant())
  232. return;
  233. break;
  234. case no_constant:
  235. return;
  236. }
  237. if (extra->numRefs++ != 0)
  238. {
  239. if (op == no_alias)
  240. return;
  241. if (!spottedCandidate && extra->worthAliasing())
  242. spottedCandidate = true;
  243. if (canCreateTemporary(expr))
  244. return;
  245. //Ugly! This is here as a temporary hack to stop branches of maps being commoned up and always
  246. //evaluated. The alias spotting and generation really needs to take conditionality into account....
  247. if (op == no_mapto)
  248. return;
  249. }
  250. if (!containsPotentialCSE(expr))
  251. return;
  252. if (canAlias && !expr->isDataset())
  253. extra->canAlias = true;
  254. bool savedCanAlias = canAlias;
  255. if (expr->isDataset() && (op != no_select) && (!spotCseInIfDatasetConditions || (op != no_if)))
  256. {
  257. //There is little point looking for CSEs within dataset expressions, because only a very small
  258. //minority which would correctly cse, and it can cause lots of problems - e.g., join conditions.
  259. unsigned first = getFirstActivityArgument(expr);
  260. unsigned num = getNumActivityArguments(expr);
  261. HqlExprArray children;
  262. bool defaultCanAlias = canAlias;
  263. ForEachChild(i, expr)
  264. {
  265. IHqlExpression * cur = expr->queryChild(i);
  266. if (i >= first && i < first+num)
  267. canAlias = defaultCanAlias;
  268. else
  269. canAlias = false;
  270. analyseExpr(cur);
  271. }
  272. }
  273. else
  274. PARENT::analyseExpr(expr);
  275. canAlias = savedCanAlias;
  276. }
  277. IHqlExpression * CseSpotter::createAliasOwn(IHqlExpression * expr, CseSpotterInfo * extra)
  278. {
  279. #ifdef TRACE_CSE
  280. StringBuffer s;
  281. DBGLOG("Create alias for %s (%d refs)", getExprIdentifier(s, expr).str(), extra->numRefs);
  282. #endif
  283. extra->alreadyAliased = true;
  284. if (createLocalAliases)
  285. return ::createAliasOwn(expr, createLocalAttribute());
  286. return ::createAliasOwn(expr, NULL);
  287. }
  288. IHqlExpression * CseSpotter::createTransformed(IHqlExpression * expr)
  289. {
  290. node_operator op = expr->getOperator();
  291. switch (op)
  292. {
  293. case no_matched:
  294. case no_matchtext:
  295. case no_matchunicode:
  296. case no_matchposition:
  297. case no_matchlength:
  298. case no_matchrow:
  299. case no_matchutf8:
  300. //These actually go wrong if we remove the named symbols, so traverse under no circumstances.
  301. //others can be traversed to patch up references to datasets that have changed.
  302. case no_translated:
  303. return LINK(expr);
  304. }
  305. OwnedHqlExpr transformed = PARENT::createTransformed(expr);
  306. CseSpotterInfo * splitter = queryBodyExtra(expr);
  307. //MORE: Possibly add a unique number to the alias when this starts worrying about child scopes.
  308. if (splitter->canAlias && splitter->worthAliasing() && checkPotentialCSE(expr, splitter))
  309. {
  310. if (splitter->useInverseForAlias())
  311. {
  312. OwnedHqlExpr inverse = getInverse(expr);
  313. OwnedHqlExpr transformedInverse = transform(inverse);
  314. return getInverse(transformedInverse);
  315. }
  316. createdAlias = true;
  317. //Use the transformed body to ensure that any cses only create a single instance,
  318. //But annotate with first annotation spotted, try and retain the symbols to aid debugging.
  319. LinkedHqlExpr aliasValue = transformed->queryBody();
  320. // if (splitter->annotatedExpr)
  321. // aliasValue.setown(splitter->annotatedExpr->cloneAllAnnotations(aliasValue));
  322. OwnedHqlExpr alias = createAliasOwn(aliasValue.getClear(), splitter);
  323. return alias.getClear();
  324. //return expr->cloneAllAnnotations(alias);
  325. }
  326. return transformed.getClear();
  327. }
  328. ANewTransformInfo * CseSpotter::createTransformInfo(IHqlExpression * expr)
  329. {
  330. return CREATE_NEWTRANSFORMINFO(CseSpotterInfo, expr);
  331. }
  332. bool CseSpotter::containsPotentialCSE(IHqlExpression * expr)
  333. {
  334. switch (expr->getOperator())
  335. {
  336. case no_record:
  337. case no_attr:
  338. case no_attr_expr:
  339. case no_attr_link:
  340. case no_joined:
  341. case no_sizeof:
  342. case no_offsetof:
  343. case no_field:
  344. case no_evaluate: // MORE: This is an example of introducing a new scope...
  345. case no_translated: // Causes recursion otherwise....
  346. case no_left:
  347. case no_right:
  348. case no_top:
  349. case no_self:
  350. case no_selfref:
  351. case no_activetable:
  352. case no_filepos:
  353. case no_file_logicalname:
  354. case no_matched:
  355. case no_matchtext:
  356. case no_matchunicode:
  357. case no_matchposition:
  358. case no_matchrow:
  359. case no_matchlength:
  360. case no_matchutf8:
  361. case no_catch:
  362. case no_projectrow:
  363. // case no_evalonce:
  364. return false;
  365. case no_select:
  366. return false; //isNewSelector(expr);
  367. case NO_AGGREGATE:
  368. //There may possibly be cses, but we would need to do lots of scoping analysis to work out whether they were
  369. //really common.
  370. return false;
  371. case no_assign:
  372. case no_assignall:
  373. case no_transform:
  374. case no_newtransform:
  375. case no_range:
  376. case no_rangefrom:
  377. case no_rangeto:
  378. case no_rangecommon:
  379. case no_skip:
  380. return true;
  381. case no_compound_diskread:
  382. case no_compound_indexread:
  383. case no_compound_disknormalize:
  384. case no_compound_diskaggregate:
  385. case no_compound_diskcount:
  386. case no_compound_diskgroupaggregate:
  387. case no_compound_indexnormalize:
  388. case no_compound_indexaggregate:
  389. case no_compound_indexcount:
  390. case no_compound_indexgroupaggregate:
  391. case no_compound_childread:
  392. case no_compound_childnormalize:
  393. case no_compound_childaggregate:
  394. case no_compound_childcount:
  395. case no_compound_childgroupaggregate:
  396. case no_compound_selectnew:
  397. case no_compound_inline:
  398. return false;
  399. #if 0
  400. //Strictly speaking, we shouldn't common up conditional expressions, but it generally provides such a reduction in code
  401. //that it will stay enabled until I come up with a better scheme.
  402. case no_if:
  403. case no_rejected:
  404. case no_which:
  405. case no_case:
  406. case no_map:
  407. return false;
  408. #endif
  409. }
  410. ITypeInfo * type = expr->queryType();
  411. if (type && type->getTypeCode() == type_void)
  412. return false;
  413. return !expr->isConstant();// || expr->isDataset() || expr->isDatarow();
  414. }
  415. bool CseSpotter::checkPotentialCSE(IHqlExpression * expr, CseSpotterInfo * extra)
  416. {
  417. if (extra->alreadyAliased)
  418. return false;
  419. if (!expr->isPure())
  420. return false;
  421. if (!canCreateTemporary(expr))
  422. return false;
  423. if (invariantSelector && exprReferencesDataset(expr, invariantSelector))
  424. return false;
  425. switch (expr->getOperator())
  426. {
  427. case no_eq:
  428. case no_ne:
  429. case no_gt:
  430. case no_ge:
  431. case no_lt:
  432. case no_le:
  433. {
  434. //Don't combine integer comparisons into a CSE - not worth it...
  435. ITypeInfo * type = expr->queryChild(0)->queryType();
  436. switch (type->getTypeCode())
  437. {
  438. case type_boolean:
  439. case type_int:
  440. return false;
  441. }
  442. return true;
  443. }
  444. case no_not:
  445. {
  446. IHqlExpression * child = expr->queryChild(0);
  447. if (queryBodyExtra(child)->isAliased())
  448. return false;
  449. break;
  450. }
  451. case no_charlen:
  452. {
  453. IHqlExpression * child = expr->queryChild(0);
  454. if (queryBodyExtra(child)->isAliased() || child->getOperator() == no_select)
  455. {
  456. type_t tc = child->queryType()->getTypeCode();
  457. switch (tc)
  458. {
  459. case type_varstring:
  460. case type_varunicode:
  461. return true;
  462. }
  463. //prevent (trivial-cast)length(x) from being serialized etc.
  464. extra->treatAsAliased = true;
  465. return false;
  466. }
  467. break;
  468. }
  469. case no_field:
  470. throwUnexpected();
  471. case no_select:
  472. //MORE: ds[n].x would probably be worth cseing.
  473. return false;
  474. case no_list:
  475. case no_datasetlist:
  476. case no_getresult: // these are commoned up in the code generator, so don't do it twice.
  477. case no_getgraphresult:
  478. case no_getgraphloopresult:
  479. case no_translated: // Causes recursion otherwise....
  480. case no_random:
  481. return false;
  482. case no_call:
  483. case no_externalcall:
  484. case no_libraryinput:
  485. case no_counter:
  486. return true;
  487. case no_substring:
  488. {
  489. SubStringHelper helper(expr);
  490. return !helper.canGenerateInline();
  491. }
  492. case no_cast:
  493. case no_implicitcast:
  494. {
  495. ITypeInfo * exprType = expr->queryType();
  496. if (exprType->getTypeCode() == type_set)
  497. return false;
  498. IHqlExpression * uncast = expr->queryChild(0);
  499. if (uncast->queryValue())
  500. return false;
  501. //Ignore integral casts of items that have already been aliased
  502. if (queryBodyExtra(uncast)->isAliased())
  503. {
  504. if (exprType->isInteger() && uncast->queryType()->isInteger())
  505. {
  506. if (extra->numRefs < 5)
  507. return false;
  508. }
  509. }
  510. break;
  511. }
  512. //Following are all source datasets - no point in commoning them up
  513. //although probably exceptions e.g., table(,pipe)
  514. case no_none:
  515. case no_null:
  516. case no_anon:
  517. case no_pseudods:
  518. case no_all:
  519. // case no_table: - normally work commoning up
  520. case no_temptable:
  521. case no_inlinetable:
  522. case no_xmlproject:
  523. case no_datasetfromrow:
  524. case no_datasetfromdictionary:
  525. case no_preservemeta:
  526. case no_dataset_alias:
  527. case no_workunit_dataset:
  528. case no_left:
  529. case no_right:
  530. case no_top:
  531. case no_self:
  532. case no_selfref:
  533. case no_keyindex:
  534. case no_newkeyindex:
  535. case no_fail:
  536. case no_activetable:
  537. case no_soapcall:
  538. case no_newsoapcall:
  539. case no_id2blob:
  540. case no_embedbody:
  541. case no_rows:
  542. return false;
  543. }
  544. if (!expr->queryType())
  545. return false;
  546. return (expr->numChildren() > 0);
  547. }
  548. IHqlExpression * CseSpotter::transform(IHqlExpression * expr)
  549. {
  550. return PARENT::transform(expr);
  551. }
  552. IHqlExpression * CseSpotter::queryAlreadyTransformed(IHqlExpression * expr)
  553. {
  554. CseSpotterInfo * extra = queryBodyExtra(expr);
  555. if (extra->dontTransform)
  556. return expr;
  557. IHqlExpression * ret = PARENT::queryAlreadyTransformed(expr);
  558. if (!ret)
  559. {
  560. IHqlExpression * body = expr->queryBody();
  561. if (body != expr)
  562. {
  563. ret = PARENT::queryAlreadyTransformed(body);
  564. if (ret == body)
  565. return NULL;
  566. }
  567. }
  568. return ret;
  569. }
  570. IHqlExpression * CseSpotter::queryAlreadyTransformedSelector(IHqlExpression * expr)
  571. {
  572. CseSpotterInfo * extra = queryBodyExtra(expr);
  573. if (extra->dontTransformSelector)
  574. return expr;
  575. return PARENT::queryAlreadyTransformedSelector(expr);
  576. }
  577. void CseSpotter::stopTransformation(IHqlExpression * expr)
  578. {
  579. IHqlExpression * normalized = expr->queryNormalizedSelector();
  580. queryBodyExtra(expr)->dontTransform = true;
  581. queryBodyExtra(normalized)->dontTransformSelector = true;
  582. }
  583. //---------------------------------------------------------------------------
  584. static HqlTransformerInfo conjunctionTransformerInfo("ConjunctionTransformer");
  585. ConjunctionTransformer::ConjunctionTransformer() : NewHqlTransformer(conjunctionTransformerInfo)
  586. {
  587. }
  588. IHqlExpression * ConjunctionTransformer::createTransformed(IHqlExpression * expr)
  589. {
  590. node_operator op = expr->getOperator();
  591. OwnedHqlExpr transformed;
  592. switch (op)
  593. {
  594. case no_matched:
  595. case no_matchtext:
  596. case no_matchunicode:
  597. case no_matchlength:
  598. case no_matchposition:
  599. case no_matchrow:
  600. case no_matchutf8:
  601. return LINK(expr);
  602. //not so sure why the following causes problems - because the tables get changed I think.
  603. case no_filepos:
  604. case no_file_logicalname:
  605. case no_sizeof:
  606. case no_offsetof:
  607. return LINK(expr);
  608. case no_and:
  609. case no_or:
  610. {
  611. IHqlExpression * left = expr->queryChild(0);
  612. if (left->getOperator() == op)
  613. {
  614. HqlExprArray args, transformedArgs;
  615. left->unwindList(args, op);
  616. ForEachItemIn(i, args)
  617. transformedArgs.append(*transform(&args.item(i)));
  618. transformedArgs.append(*transform(expr->queryChild(1)));
  619. transformed.setown(createLeftBinaryList(op, transformedArgs));
  620. // return expr->cloneAllAnnotations(transformed);
  621. }
  622. break;
  623. }
  624. }
  625. if (!transformed)
  626. transformed.setown(NewHqlTransformer::createTransformed(expr));
  627. return transformed.getClear();
  628. }
  629. //---------------------------------------------------------------------------
  630. inline bool canInsertCodeAlias(IHqlExpression * expr)
  631. {
  632. switch (expr->getOperator())
  633. {
  634. case no_range:
  635. case no_rangefrom:
  636. case no_rangeto:
  637. case no_rangecommon:
  638. case no_mapto:
  639. case no_recordlist:
  640. case no_transformlist:
  641. case no_rowvalue:
  642. case no_sortlist:
  643. return false;
  644. default:
  645. return true;
  646. }
  647. }
  648. //Which expression node contains all references to left and right?
  649. CseScopeInfo * findCommonPath(CseScopeInfo * left, CseScopeInfo * right)
  650. {
  651. for (;;)
  652. {
  653. if (!left || !right)
  654. return NULL;
  655. if (left == right)
  656. return left;
  657. //By using the sequence number we originally traversed the tree, if we always selected the higher sequence we can guarantee that we
  658. //will converge on a common node if there is one (since seq of a parent is always less than all its children)
  659. if (left->seq > right->seq)
  660. left = left->queryCommonParent();
  661. else
  662. right = right->queryCommonParent();
  663. }
  664. }
  665. void CseScopeInfo::calcCommonLocation()
  666. {
  667. assertex(!calcedCommonLocation);
  668. CseScopeInfo * common = this;
  669. CseScopeInfo * thisLocation = this;
  670. CseScopeInfo * parent = nullptr;
  671. bool singleParent = true; // Does this specific node only have a single parent node?
  672. moved = false;
  673. if (firstParent)
  674. {
  675. CseScopeInfo *firstParentExtra = firstParent;
  676. parent = firstParentExtra;
  677. CseScopeInfo * parentPath = parent ? parent->queryValidPath() : nullptr;
  678. moved = firstParent->hasMoved();
  679. ForEachItemIn(i, parents)
  680. {
  681. CseScopeInfo * parentExtra = parents.item(i);
  682. //REVISIT: Think about IF() conditions.... - otherwise duplicates could be removed.
  683. if (parentExtra != firstParentExtra)
  684. {
  685. singleParent = false;
  686. CseScopeInfo * newParent = findCommonPath(parent, parentExtra);
  687. if (isAlias)
  688. {
  689. //If the common parent is the same as the firstParent then don't treat it as being moved
  690. if (newParent != parent)
  691. {
  692. // If parent nodes are no_mapto or similar, they wouldn't be treated as different
  693. if (parent->queryValidPath() != newParent->queryValidPath())
  694. {
  695. //The common parent is definitely different..
  696. moved = true;
  697. }
  698. }
  699. }
  700. else
  701. moved = true;
  702. parent = newParent;
  703. if (!parent)
  704. break;
  705. }
  706. }
  707. if (!singleParent)
  708. common = parent;
  709. if (parent)
  710. {
  711. if (isAlias)
  712. {
  713. //If there are multiple paths to this node, but the common nodes do not create aliases (e.g., no_mapto)
  714. //then this needs to be evaluated on that nodes commonParent's location.
  715. CseScopeInfo * parentLocation = parent->queryEvalLocation();
  716. if (moved)
  717. thisLocation = parentLocation;
  718. //If the path to the parent has changed, then the path to this alias has changed.
  719. if (!parentLocation || parent->hasMoved())
  720. {
  721. thisLocation = parentLocation;
  722. moved = true;
  723. }
  724. //If the path to the calculated parent doesn't match the path to the first parent, then this alias will
  725. //need to be evaluated in the parent's location
  726. if ((parent != firstParentExtra) &&
  727. (parent->queryValidPath() != firstParentExtra->queryValidPath()))
  728. {
  729. thisLocation = parentLocation;;
  730. moved = true;
  731. }
  732. //If any parent alias has moved (so it will be evaluated earlier), then the insertion point for this alias
  733. //will be the restricted to the insertion point for the parent alias - to ensure it is evaluated first.
  734. ForEachItemIn(i, parentAliases)
  735. {
  736. CseScopeInfo & alias = parentAliases.item(i);
  737. if (alias.hasMoved())
  738. {
  739. CseScopeInfo * aliasLocation = alias.queryEvalLocation();
  740. CseScopeInfo * newLocation;
  741. //Need to find the common path between the parent and the aliases
  742. if (thisLocation == this)
  743. newLocation = findCommonPath(parent, aliasLocation);
  744. else
  745. newLocation = findCommonPath(thisLocation, aliasLocation);
  746. //If the shared location is different from the parent, or if the parent is higher up the graph
  747. //than the alias (e.g. if it was already the root)
  748. if (newLocation != parent)
  749. //REVISIT: The following also improved the generated code
  750. //if ((newLocation != parent) || parent->minDepth < alias.minDepth)
  751. //REVISIT: This improved code for some queries, but isn't really correct!
  752. // if (newLocation != parent || parent->seq < alias.seq)
  753. {
  754. moved = true;
  755. if (newLocation)
  756. thisLocation=newLocation->queryValidPath();
  757. else
  758. thisLocation=newLocation;
  759. }
  760. }
  761. }
  762. }
  763. else
  764. {
  765. if (parent->hasMoved())
  766. {
  767. common = parent->queryCommonNode();
  768. moved = true;
  769. }
  770. if (singleParent && !parent->hasMoved())
  771. {
  772. thisLocation = this;
  773. }
  774. else
  775. {
  776. thisLocation = parent->queryEvalLocation();
  777. }
  778. }
  779. }
  780. else
  781. {
  782. //Multiple paths with no common parent
  783. common = nullptr;
  784. thisLocation = nullptr;
  785. moved = true;
  786. }
  787. }
  788. calcedCommonLocation = true;
  789. commonNode = common;
  790. evalLocation = thisLocation;
  791. #ifdef TRACE_CSE_SCOPE
  792. StringBuffer parentText, aliasText;
  793. if (firstParent)
  794. {
  795. parentText.append(firstParent->seq);
  796. ForEachItemIn(i1, parents)
  797. parentText.append(",").append(parents.item(i1)->seq);
  798. }
  799. ForEachItemIn(i2, parentAliases)
  800. {
  801. if (i2)
  802. aliasText.append(",");
  803. aliasText.append(parentAliases.item(i2).seq);
  804. }
  805. CseScopeInfo * commonParent = queryCommonParent();
  806. CseScopeInfo * parentAlias = commonParent ? commonParent->queryAlias() : nullptr;
  807. CseScopeInfo * myLocation = queryScopeLocation();
  808. printf("%u: %s [%s:%s] parent(%u:%u) common(%u) location(%u%s) child(%u)\n", seq, getOpString(original->getOperator()),
  809. parentText.str(), aliasText.str(),
  810. parent ? parent->seq : 0,
  811. parentAlias ? parentAlias->seq : 0,
  812. commonNode ? commonNode->seq : 0,
  813. myLocation ? myLocation->seq : 0, moved ? ",moved" : "",
  814. evalLocation ? evalLocation->seq : 0
  815. );
  816. #endif
  817. }
  818. void CseScopeInfo::cloneAliases(ICopyArrayOf<CseScopeInfo> & target) const
  819. {
  820. unsigned max = childAliases.ordinality();
  821. target.ensure(target.ordinality() + max);
  822. for (unsigned i=0; i < max; ++i)
  823. target.append(childAliases.item(i));
  824. }
  825. void CseScopeInfo::connectChildAliases(CseScopeInfo * parent)
  826. {
  827. ForEachItemIn(i, childAliases)
  828. childAliases.item(i).addParentAlias(parent);
  829. if (inheritedAliases)
  830. inheritedAliases->connectChildAliases(parent);
  831. }
  832. void CseScopeInfo::expandInheritedAliases()
  833. {
  834. if (inheritedAliases)
  835. {
  836. inheritedAliases->cloneAliases(childAliases);
  837. inheritedAliases = nullptr;
  838. }
  839. }
  840. CseScopeInfo * CseScopeInfo::queryScopeLocation()
  841. {
  842. CseScopeInfo * location = queryEvalLocation();
  843. if (!location)
  844. return nullptr;
  845. if (location != this)
  846. return location->queryValidPath();
  847. //REVISIT: could probably return nullptr if it isn't moving to avoid creating a no_aliasscope
  848. CseScopeInfo * parent = queryCommonParent();
  849. if (!parent)
  850. return nullptr;
  851. CseScopeInfo * best = parent->queryEvalLocation();
  852. return best->queryValidPath();
  853. }
  854. CseScopeInfo * CseScopeInfo::queryValidPath()
  855. {
  856. //REVISIT: Is it worth caching this expression?
  857. if (canInsertCodeAlias(original))
  858. return this;
  859. CseScopeInfo * parent = queryCommonParent();
  860. if (!parent)
  861. return nullptr;
  862. return parent->queryValidPath();
  863. }
  864. static HqlTransformerInfo cseScopeTransformerInfo("CseScopeTransformer");
  865. CseScopeTransformer::CseScopeTransformer() : NewHqlTransformer(cseScopeTransformerInfo)
  866. {
  867. }
  868. void CseScopeTransformer::analyseExpr(IHqlExpression * expr)
  869. {
  870. expr = expr->queryBody();
  871. if (!containsNonGlobalAlias(expr))
  872. return;
  873. CseScopeInfo * extra = queryBodyExtra(expr);
  874. if (extra->seq)
  875. {
  876. //REVISIT: Flawed since this will not update the depth for any child expressions.
  877. if (depth < extra->minDepth)
  878. extra->minDepth = depth;
  879. extra->addParent(activeParent);
  880. }
  881. else
  882. {
  883. extra->firstParent = activeParent;
  884. extra->seq = ++seq;
  885. extra->isUnconditional = (conditionDepth == 0);
  886. extra->minDepth = depth;
  887. node_operator op = expr->getOperator();
  888. if (op == no_alias)
  889. extra->isAlias = true;
  890. depth++;
  891. {
  892. CseScopeInfo * savedParent = activeParent;
  893. activeParent = extra;
  894. switch (op)
  895. {
  896. case no_if:
  897. case no_or:
  898. case no_and:
  899. case no_case:
  900. {
  901. //REVISIT: isUnconditional is not currently used - could delete this code if there is no benefit
  902. // or could use a base class that calculates it for us
  903. analyseExpr(expr->queryChild(0));
  904. conditionDepth++;
  905. ForEachChildFrom(i, expr, 1)
  906. analyseExpr(expr->queryChild(i));
  907. conditionDepth--;
  908. break;
  909. }
  910. //REVISIT: Do something clever with activeParent for assignments within transforms to improve insert location.
  911. default:
  912. NewHqlTransformer::analyseExpr(expr);
  913. break;
  914. }
  915. activeParent = savedParent;
  916. }
  917. depth--;
  918. //Add here so the cse are in the correct order to cope with dependencies...
  919. if (op == no_alias)
  920. {
  921. extra->connectChildAliases();
  922. assertex(!expr->hasAttribute(globalAtom));
  923. allCSEs.append(*LINK(extra));
  924. //REVISIT: Can now kill the child alias array since no longer required.
  925. }
  926. }
  927. if (activeParent)
  928. {
  929. if (extra->isAlias)
  930. activeParent->addChildAlias(extra);
  931. else if (!extra->childAliases.empty())
  932. activeParent->inheritAliases(extra);
  933. else
  934. activeParent->inheritAliases(extra->inheritedAliases);
  935. }
  936. }
  937. bool CseScopeTransformer::attachCSEs(IHqlExpression * root)
  938. {
  939. bool changed = false;
  940. ForEachItemIn(idx, allCSEs)
  941. {
  942. CseScopeInfo& cur = allCSEs.item(idx);
  943. CseScopeInfo * aliasLocation = cur.queryScopeLocation();
  944. //REVISIT:
  945. //if (!aliasLocation && cur.isUnconditional)
  946. // aliasLocation = root;
  947. //REVISIT:
  948. //if (aliasLocation && aliasLocation != cur.original)
  949. if (aliasLocation)
  950. {
  951. #ifdef TRACE_CSE_SCOPE
  952. printf("Attach %u at %u\n", cur.seq, aliasLocation->seq);
  953. #endif
  954. aliasLocation->aliasesToDefine.append(*LINK(cur.original));
  955. changed = true;
  956. }
  957. else
  958. {
  959. #ifdef TRACE_CSE_SCOPE
  960. printf("Nowhere to attach %u\n", cur.seq);
  961. #endif
  962. }
  963. }
  964. return changed;
  965. }
  966. IHqlExpression * CseScopeTransformer::createTransformed(IHqlExpression * expr)
  967. {
  968. //NB: Can not short-circuit transformation if (!containsAlias(expr)) because references to transformed datasets will not get patched up
  969. IHqlExpression * body = expr->queryBody(true);
  970. if (body != expr)
  971. {
  972. OwnedHqlExpr ret = transform(body);
  973. return expr->cloneAnnotation(ret);
  974. }
  975. IHqlExpression * transformed = NewHqlTransformer::createTransformed(expr);
  976. CseScopeInfo * splitter = queryBodyExtra(expr);
  977. if (splitter->aliasesToDefine.ordinality())
  978. {
  979. HqlExprArray args;
  980. args.append(*transformed);
  981. ForEachItemIn(idx, splitter->aliasesToDefine)
  982. {
  983. IHqlExpression * value = &splitter->aliasesToDefine.item(idx);
  984. args.append(*transform(value));
  985. }
  986. if (expr->isDataset())
  987. transformed = createDataset(no_alias_scope, args);
  988. else if (expr->isDatarow())
  989. transformed = createRow(no_alias_scope, args);
  990. else
  991. transformed = createValue(no_alias_scope, transformed->getType(), args);
  992. }
  993. return transformed;
  994. }
  995. ANewTransformInfo * CseScopeTransformer::createTransformInfo(IHqlExpression * expr)
  996. {
  997. return CREATE_NEWTRANSFORMINFO(CseScopeInfo, expr);
  998. }
  999. IHqlExpression * spotScalarCSE(IHqlExpression * expr, IHqlExpression * limit, bool spotCseInIfDatasetConditions)
  1000. {
  1001. if (expr->isConstant())
  1002. return LINK(expr);
  1003. switch (expr->getOperator())
  1004. {
  1005. case no_select:
  1006. if (!isNewSelector(expr))
  1007. return LINK(expr);
  1008. break;
  1009. }
  1010. OwnedHqlExpr transformed = LINK(expr); //removeNamedSymbols(expr);
  1011. bool addedAliases = false;
  1012. //First spot the aliases - so that restructuring the ands doesn't lose any existing aliases.
  1013. {
  1014. CseSpotter spotter(spotCseInIfDatasetConditions);
  1015. spotter.analyse(transformed, 0);
  1016. if (spotter.foundCandidates())
  1017. {
  1018. if (limit)
  1019. spotter.stopTransformation(limit);
  1020. transformed.setown(spotter.transformRoot(transformed));
  1021. addedAliases = spotter.createdNewAliases();
  1022. }
  1023. }
  1024. if (!containsAlias(transformed))
  1025. return transformed.getClear();
  1026. //Transform conjunctions so they are (a AND (b AND (c AND d))) not (((a AND b) AND c) AND d)
  1027. //so that alias scope can be introduced in a better place.
  1028. {
  1029. ConjunctionTransformer tr;
  1030. transformed.setown(tr.transformRoot(transformed));
  1031. }
  1032. if (!addedAliases)
  1033. return transformed.getClear();
  1034. //Now work out where in the tree the aliases should be evaluated.
  1035. {
  1036. CseScopeTransformer scoper;
  1037. scoper.analyse(transformed, 0);
  1038. if (scoper.attachCSEs(transformed))
  1039. transformed.setown(scoper.transformRoot(transformed));
  1040. }
  1041. return transformed.getClear();
  1042. }
  1043. void spotScalarCSE(SharedHqlExpr & expr, SharedHqlExpr & associated, IHqlExpression * limit, IHqlExpression * invariantSelector, bool spotCseInIfDatasetConditions)
  1044. {
  1045. CseSpotter spotter(spotCseInIfDatasetConditions);
  1046. spotter.analyse(expr, 0);
  1047. if (associated)
  1048. spotter.analyseAssociated(associated, 0);
  1049. if (!spotter.foundCandidates())
  1050. return;
  1051. if (limit)
  1052. spotter.stopTransformation(limit);
  1053. if (invariantSelector)
  1054. spotter.setInvariantSelector(invariantSelector);
  1055. expr.setown(spotter.transformRoot(expr));
  1056. associated.setown(spotter.transformRoot(associated));
  1057. }
  1058. void spotScalarCSE(HqlExprArray & exprs, HqlExprArray & associated, IHqlExpression * limit, IHqlExpression * invariantSelector, bool spotCseInIfDatasetConditions)
  1059. {
  1060. CseSpotter spotter(spotCseInIfDatasetConditions);
  1061. spotter.analyseArray(exprs, 0);
  1062. ForEachItemIn(ia, associated)
  1063. spotter.analyseAssociated(&associated.item(ia), 0);
  1064. if (!spotter.foundCandidates())
  1065. return;
  1066. if (limit)
  1067. spotter.stopTransformation(limit);
  1068. if (invariantSelector)
  1069. spotter.setInvariantSelector(invariantSelector);
  1070. HqlExprArray newExprs;
  1071. HqlExprArray newAssociated;
  1072. spotter.transformRoot(exprs, newExprs);
  1073. spotter.transformRoot(associated, newAssociated);
  1074. replaceArray(exprs, newExprs);
  1075. replaceArray(associated, newAssociated);
  1076. }
  1077. //---------------------------------------------------------------------------
  1078. //The TableInvariantTransformer is important for ensuring that getResultXXX code is executed in the code context, amongst other things
  1079. //It must ensure that any global aliases couldn't contain some other global aliases inside a child query, otherwise when the child query is
  1080. //evaluated the result won't be in the correct place.
  1081. //
  1082. //MORE: This could be improved to work out whether it is worth creating an alias (which will then be serialized...)
  1083. //e.g., don't alias i) <alias<n>> +- offset or ii) extension of an alias's size., iii) substring of a fixed size string. iv) length(string
  1084. //however it is pretty good as it stands.
  1085. //ideally it would need information about how many times the expression is likely to be evaluated (e.g., 1/many)
  1086. //so that could be taken into account (e.g, filenames which are 'string' + conditional)
  1087. static bool canHoistInvariant(IHqlExpression * expr)
  1088. {
  1089. if (!canCreateTemporary(expr))
  1090. {
  1091. if ((expr->getOperator() != no_alias) || expr->hasAttribute(globalAtom))
  1092. return false;
  1093. }
  1094. if (!expr->isPure())
  1095. return false;
  1096. if (expr->isFunction())
  1097. return false;
  1098. switch (expr->getOperator())
  1099. {
  1100. case no_list:
  1101. case no_datasetlist:
  1102. case no_createdictionary:
  1103. return false; // probably don't want to hoist these
  1104. }
  1105. return true;
  1106. }
  1107. static HqlTransformerInfo tableInvariantTransformerInfo("TableInvariantTransformer");
  1108. TableInvariantTransformer::TableInvariantTransformer() : NewHqlTransformer(tableInvariantTransformerInfo)
  1109. {
  1110. canAlias = true;
  1111. }
  1112. bool TableInvariantTransformer::isInvariant(IHqlExpression * expr)
  1113. {
  1114. TableInvariantInfo * extra = queryBodyExtra(expr);
  1115. if (extra->cachedInvariant)
  1116. return extra->isInvariant;
  1117. bool invariant = false;
  1118. node_operator op = expr->getOperator();
  1119. switch (op)
  1120. {
  1121. case no_record:
  1122. case no_null:
  1123. case no_activetable:
  1124. case no_activerow:
  1125. case no_left:
  1126. case no_right:
  1127. case no_self:
  1128. case no_top:
  1129. case no_selfref:
  1130. case no_filepos:
  1131. case no_file_logicalname:
  1132. case no_joined:
  1133. case no_offsetof:
  1134. case no_sizeof:
  1135. case NO_AGGREGATE:
  1136. break;
  1137. case no_preservemeta:
  1138. invariant = isInvariant(expr->queryChild(0));
  1139. break;
  1140. case no_workunit_dataset:
  1141. case no_getresult:
  1142. if (expr->hasAttribute(wuidAtom))
  1143. invariant = isInvariant(expr->queryAttribute(wuidAtom));
  1144. else
  1145. invariant = true;
  1146. break;
  1147. case no_constant:
  1148. case no_getgraphresult:
  1149. invariant = true;
  1150. break;
  1151. case no_select:
  1152. {
  1153. if (!expr->isDataset())
  1154. {
  1155. IHqlExpression * ds = expr->queryChild(0);
  1156. if (expr->hasAttribute(newAtom) || ds->isDatarow())
  1157. invariant = isInvariant(ds);
  1158. }
  1159. break;
  1160. }
  1161. case no_newaggregate:
  1162. {
  1163. //Allow these on a very strict subset of the datasets - to ensure that no potential globals can be included in the dataset
  1164. if (!isInvariant(expr->queryChild(0)))
  1165. break;
  1166. switch (querySimpleAggregate(expr, false, true))
  1167. {
  1168. case no_existsgroup:
  1169. case no_countgroup:
  1170. invariant = true;
  1171. break;
  1172. }
  1173. break;
  1174. }
  1175. case no_selectnth:
  1176. switch (expr->queryChild(1)->getOperator())
  1177. {
  1178. case no_constant:
  1179. case no_counter:
  1180. invariant = isInvariant(expr->queryChild(0));
  1181. break;
  1182. }
  1183. break;
  1184. default:
  1185. if (!isContextDependent(expr))
  1186. //MORE: The following line is needed if the xml/parse flags are removed from the context, but it causes problems
  1187. //preventing counts from being hoisted as aliases. That is really correct - but it makes code worse for some examples.
  1188. //if (!isContextDependent(expr) && expr->isIndependentOfScope())
  1189. {
  1190. if (!expr->isAction())// && !expr->isDataset() && !expr->isDatarow())
  1191. {
  1192. invariant = true;
  1193. ForEachChild(i, expr)
  1194. {
  1195. IHqlExpression * cur = expr->queryChild(i);
  1196. if (!isInvariant(cur))
  1197. {
  1198. invariant = false;
  1199. break;
  1200. }
  1201. }
  1202. }
  1203. }
  1204. break;
  1205. }
  1206. extra->cachedInvariant = true;
  1207. extra->isInvariant = invariant;
  1208. return invariant;
  1209. }
  1210. #if 0
  1211. void TableInvariantTransformer::analyseExpr(IHqlExpression * expr)
  1212. {
  1213. expr = expr->queryBody();
  1214. if (alreadyVisited(expr))
  1215. return;
  1216. node_operator op = expr->getOperator();
  1217. switch (op)
  1218. {
  1219. case no_record:
  1220. case no_constant:
  1221. return;
  1222. }
  1223. if (isInvariant(expr) && !expr->isAttribute() && !expr->isConstant() && canHoistInvariant(expr))
  1224. {
  1225. TableInvariantInfo * extra = queryBodyExtra(expr);
  1226. if (op == no_alias)
  1227. {
  1228. if (!expr->hasAttribute(globalAtom))
  1229. extra->createAlias = true;
  1230. }
  1231. else
  1232. extra->createAlias = true;
  1233. return;
  1234. }
  1235. if (op == no_attr_expr)
  1236. analyseChildren(expr);
  1237. else
  1238. NewHqlTransformer::analyseExpr(expr);
  1239. }
  1240. #else
  1241. void TableInvariantTransformer::analyseExpr(IHqlExpression * expr)
  1242. {
  1243. expr = expr->queryBody();
  1244. TableInvariantInfo * extra = queryBodyExtra(expr);
  1245. if (alreadyVisited(expr))
  1246. return;
  1247. //More - these need to be handled properly...
  1248. node_operator op = expr->getOperator();
  1249. switch (op)
  1250. {
  1251. case no_record:
  1252. case no_constant:
  1253. return;
  1254. }
  1255. //We are trying to ensure that any expressions that don't access fields that are dependent on the activeDatasets/context are only
  1256. //evaluated once => check for active dataset rather than any dataset
  1257. bool candidate = false;
  1258. if (!isContextDependent(expr) && !expr->isAttribute())
  1259. {
  1260. if (isInlineTrivialDataset(expr) && !expr->isConstant())
  1261. {
  1262. candidate = (op != no_null);
  1263. }
  1264. else
  1265. {
  1266. if (!containsActiveDataset(expr))
  1267. {
  1268. //MORE: We should be able to hoist constant datasets (e.g., temptables), but it causes problems
  1269. //e.g., stops items it contains from being aliased. So
  1270. if (!expr->isAction() && !expr->isDataset() && !expr->isDatarow())
  1271. {
  1272. switch (op)
  1273. {
  1274. case no_alias:
  1275. if (!expr->hasAttribute(globalAtom))
  1276. extra->createAlias = true;
  1277. return;
  1278. default:
  1279. //MORE: We should be able to hoist constant datasets (e.g., temptables), but it causes problems
  1280. //e.g., stops items it contains from being aliased.
  1281. candidate = !expr->isConstant();
  1282. break;
  1283. }
  1284. }
  1285. }
  1286. }
  1287. if (candidate && canHoistInvariant(expr))
  1288. {
  1289. extra->createAlias = true;
  1290. return;
  1291. }
  1292. }
  1293. if (op == no_attr_expr)
  1294. analyseChildren(expr);
  1295. else
  1296. NewHqlTransformer::analyseExpr(expr);
  1297. }
  1298. #endif
  1299. bool TableInvariantTransformer::isAlwaysAlias(IHqlExpression * expr)
  1300. {
  1301. if (queryBodyExtra(expr)->createAlias)
  1302. return true;
  1303. switch (expr->getOperator())
  1304. {
  1305. case no_alias:
  1306. case no_getresult: // these are commoned up in the code generator, so don't do it twice.
  1307. case no_getgraphresult:
  1308. case no_getgraphloopresult:
  1309. return true;
  1310. }
  1311. return false;
  1312. }
  1313. bool TableInvariantTransformer::isTrivialAlias(IHqlExpression * expr)
  1314. {
  1315. switch (expr->getOperator())
  1316. {
  1317. case no_cast:
  1318. case no_implicitcast:
  1319. //Don't create aliases for items that are simply integral casts of other aliases.
  1320. {
  1321. ITypeInfo * type = expr->queryType();
  1322. if (type->isInteger())
  1323. {
  1324. IHqlExpression * cast = expr->queryChild(0);
  1325. ITypeInfo * castType = cast->queryType();
  1326. if (castType->isInteger() && isAlwaysAlias(cast))
  1327. {
  1328. switch (type->getSize())
  1329. {
  1330. case 1: case 2: case 4: case 8:
  1331. return true;
  1332. }
  1333. }
  1334. }
  1335. break;
  1336. }
  1337. case no_not:
  1338. {
  1339. IHqlExpression * child = expr->queryChild(0);
  1340. if (isAlwaysAlias(child))
  1341. return true;
  1342. break;
  1343. }
  1344. }
  1345. return false;
  1346. }
  1347. IHqlExpression * TableInvariantTransformer::createTransformed(IHqlExpression * expr)
  1348. {
  1349. if (expr->getOperator() == no_alias)
  1350. {
  1351. OwnedHqlExpr newChild = transform(expr->queryChild(0));
  1352. if (newChild->getOperator() == no_alias)
  1353. return newChild.getClear();
  1354. }
  1355. OwnedHqlExpr transformed = NewHqlTransformer::createTransformed(expr);
  1356. updateOrphanedSelectors(transformed, expr);
  1357. if (queryBodyExtra(expr)->createAlias)
  1358. {
  1359. if (!isTrivialAlias(expr))
  1360. {
  1361. OwnedHqlExpr attr = createAttribute(globalAtom);
  1362. if (transformed->getOperator() == no_alias)
  1363. transformed.set(transformed->queryChild(0));
  1364. return createAlias(transformed->queryBody(), attr);
  1365. }
  1366. }
  1367. return transformed.getClear();
  1368. }
  1369. //---------------------------------------------------------------------------
  1370. IHqlExpression * spotTableInvariant(IHqlExpression * expr)
  1371. {
  1372. TableInvariantTransformer transformer;
  1373. transformer.analyse(expr, 0);
  1374. return transformer.transformRoot(expr);
  1375. }
  1376. IHqlExpression * spotTableInvariantChildren(IHqlExpression * expr)
  1377. {
  1378. TableInvariantTransformer transformer;
  1379. ForEachChild(i1, expr)
  1380. transformer.analyse(expr->queryChild(i1), 0);
  1381. return transformer.transformRoot(expr);
  1382. }
  1383. //---------------------------------------------------------------------------
  1384. static HqlTransformerInfo globalAliasTransformerInfo("GlobalAliasTransformer");
  1385. GlobalAliasTransformer::GlobalAliasTransformer() : NewHqlTransformer(globalAliasTransformerInfo)
  1386. {
  1387. insideGlobal = false;
  1388. }
  1389. void GlobalAliasTransformer::analyseExpr(IHqlExpression * expr)
  1390. {
  1391. if (!containsAlias(expr))
  1392. return;
  1393. bool wasInsideGlobal = insideGlobal;
  1394. GlobalAliasInfo * extra = queryBodyExtra(expr);
  1395. extra->numUses++;
  1396. if (expr->getOperator() == no_alias)
  1397. {
  1398. if (expr->hasAttribute(globalAtom))
  1399. {
  1400. // assertex(!containsActiveDataset(expr) || isInlineTrivialDataset(expr));
  1401. if (!insideGlobal)
  1402. extra->isOuter = true;
  1403. }
  1404. if (extra->numUses > 1)
  1405. return;
  1406. if (extra->isOuter)
  1407. insideGlobal = true;
  1408. }
  1409. else
  1410. {
  1411. //ugly, but we need to walk children more than once even if we've already been here.
  1412. //What is important is if visited >1 or occur globally, so can short circuit based on that condition.
  1413. //This currently links too many times because subsequent cse generation may common up multiple uses of the same item
  1414. //but it's not too bad.
  1415. //We could rerun this again if that was a major issue.
  1416. if (insideGlobal)
  1417. {
  1418. if (extra->numUses > 2)
  1419. return; // may need to visit children more than once so that alias is linked twice.
  1420. }
  1421. else
  1422. {
  1423. if (extra->isOuter && (extra->numUses > 2))
  1424. return;
  1425. extra->isOuter = true;
  1426. }
  1427. }
  1428. if (expr->getOperator() == no_attr_expr)
  1429. analyseChildren(expr);
  1430. else
  1431. NewHqlTransformer::analyseExpr(expr);
  1432. insideGlobal = wasInsideGlobal;
  1433. }
  1434. IHqlExpression * GlobalAliasTransformer::createTransformed(IHqlExpression * expr)
  1435. {
  1436. OwnedHqlExpr transformed = NewHqlTransformer::createTransformed(expr);
  1437. if ((expr->getOperator() == no_alias))
  1438. {
  1439. GlobalAliasInfo * extra = queryBodyExtra(expr);
  1440. if (expr->hasAttribute(globalAtom))
  1441. {
  1442. if (!extra->isOuter)
  1443. {
  1444. if (extra->numUses == 1)
  1445. return LINK(transformed->queryChild(0));
  1446. if (!expr->hasAttribute(localAtom))
  1447. return appendLocalAttribute(transformed);
  1448. }
  1449. else if (expr->hasAttribute(localAtom))
  1450. {
  1451. //Should never occur - but just about conceivable that some kind of constant folding
  1452. //might cause a surrounding global alias to be removed.
  1453. return removeLocalAttribute(transformed);
  1454. }
  1455. }
  1456. else
  1457. {
  1458. if ((extra->numUses == 1) && !expr->hasAttribute(internalAtom))
  1459. return LINK(transformed->queryChild(0));
  1460. }
  1461. }
  1462. return transformed.getClear();
  1463. }
  1464. //---------------------------------------------------------------------------
  1465. IHqlExpression * optimizeActivityAliasReferences(IHqlExpression * expr)
  1466. {
  1467. if (!containsAlias(expr))
  1468. return LINK(expr);
  1469. unsigned first = getFirstActivityArgument(expr);
  1470. unsigned last = first + getNumActivityArguments(expr);
  1471. bool foundAlias = false;
  1472. ForEachChild(i1, expr)
  1473. {
  1474. IHqlExpression * cur = expr->queryChild(i1);
  1475. if (((i1 < first) || (i1 >= last)) && containsAlias(cur))
  1476. {
  1477. foundAlias = true;
  1478. break;
  1479. }
  1480. }
  1481. if (!foundAlias)
  1482. return LINK(expr);
  1483. GlobalAliasTransformer transformer;
  1484. ForEachChild(i2, expr)
  1485. {
  1486. IHqlExpression * cur = expr->queryChild(i2);
  1487. if (((i2 < first) || (i2 >= last)) && containsAlias(cur))
  1488. transformer.analyse(cur, 0);
  1489. }
  1490. HqlExprArray args;
  1491. ForEachChild(i3, expr)
  1492. {
  1493. IHqlExpression * cur = expr->queryChild(i3);
  1494. if ((i3 < first) || (i3 >= last))
  1495. args.append(*transformer.transformRoot(cur));
  1496. else
  1497. args.append(*LINK(cur));
  1498. }
  1499. return cloneOrLink(expr, args);
  1500. }