hqlcse.cpp 48 KB

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