optloadmodifystore.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. {
  2. Copyright (c) 2017 by Nikolay Nikolov
  3. Optimizations for making use of load-modify-store operations in CISC-like
  4. instruction set architectures (such as x86)
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit optloadmodifystore;
  19. {$i fpcdefs.inc}
  20. {$if defined(i386) or defined(x86_64)}
  21. {$define enable_shl_shr_assign_x_y}
  22. {$endif}
  23. interface
  24. uses
  25. node;
  26. procedure do_optloadmodifystore(var rootnode : tnode);
  27. implementation
  28. uses
  29. globtype,verbose,nutils,compinnr,
  30. defutil,defcmp,htypechk,pass_1,
  31. nadd,ncal,ncnv,ninl,nld,nmat;
  32. function try_opt_assignmentnode(assignmentnode: tassignmentnode): tnode;
  33. var
  34. newinlinenodetype: tinlinenumber;
  35. begin
  36. result:=nil;
  37. with assignmentnode do
  38. begin
  39. { replace i:=succ/pred(i) by inc/dec(i)? }
  40. if (right.nodetype=inlinen) and
  41. ((tinlinenode(right).inlinenumber=in_succ_x) or (tinlinenode(right).inlinenumber=in_pred_x)) and
  42. (tinlinenode(right).left.isequal(left)) and
  43. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  44. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  45. valid_for_var(tinlinenode(right).left,false) and
  46. not(might_have_sideeffects(tinlinenode(right).left)) then
  47. begin
  48. if tinlinenode(right).inlinenumber=in_succ_x then
  49. newinlinenodetype:=in_inc_x
  50. else
  51. newinlinenodetype:=in_dec_x;
  52. result:=cinlinenode.createintern(
  53. newinlinenodetype,false,ccallparanode.create(
  54. tinlinenode(right).left,nil));
  55. result.localswitches:=localswitches;
  56. tinlinenode(right).left:=nil;
  57. exit;
  58. end;
  59. { replace i:=i+k by inc(i,k)
  60. i:=i-k by dec(i,k)
  61. i:=i and/or/xor k by in_[and/or/xor]_assign_x_y(i,k)
  62. this handles the case, where there are no implicit type conversions }
  63. if (right.nodetype in [addn,subn,andn,orn,xorn]) and
  64. (taddnode(right).left.isequal(left)) and
  65. is_integer(taddnode(right).left.resultdef) and
  66. is_integer(taddnode(right).right.resultdef) and
  67. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  68. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  69. valid_for_var(taddnode(right).left,false) and
  70. not(might_have_sideeffects(taddnode(right).left)) then
  71. begin
  72. case right.nodetype of
  73. addn:
  74. newinlinenodetype:=in_inc_x;
  75. subn:
  76. newinlinenodetype:=in_dec_x;
  77. andn:
  78. newinlinenodetype:=in_and_assign_x_y;
  79. orn:
  80. newinlinenodetype:=in_or_assign_x_y;
  81. xorn:
  82. newinlinenodetype:=in_xor_assign_x_y;
  83. else
  84. internalerror(2017032901);
  85. end;
  86. if right.nodetype in [addn,subn] then
  87. result:=cinlinenode.createintern(
  88. newinlinenodetype,false,ccallparanode.create(
  89. taddnode(right).left,ccallparanode.create(taddnode(right).right,nil)))
  90. else
  91. result:=cinlinenode.createintern(
  92. newinlinenodetype,false,ccallparanode.create(
  93. taddnode(right).right,ccallparanode.create(taddnode(right).left,nil)));
  94. result.localswitches:=localswitches;
  95. taddnode(right).left:=nil;
  96. taddnode(right).right:=nil;
  97. exit;
  98. end;
  99. { replace i:=i+k by inc(i,k)
  100. i:=i-k by dec(i,k)
  101. i:=i and/or/xor k by in_[and/or/xor]_assign_x_y(i,k)
  102. this handles the case with two conversions (outer and inner):
  103. outer typeconv: right
  104. add/sub/and/or/xor: ttypeconvnode(right).left
  105. inner typeconv: taddnode(ttypeconvnode(right).left).left
  106. right side 'i': ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left
  107. right side 'k': taddnode(ttypeconvnode(right).left).right }
  108. if (right.nodetype=typeconvn) and
  109. (ttypeconvnode(right).convtype=tc_int_2_int) and
  110. (ttypeconvnode(right).left.nodetype in [addn,subn,andn,orn,xorn]) and
  111. is_integer(ttypeconvnode(right).left.resultdef) and
  112. (right.resultdef.size<=ttypeconvnode(right).left.resultdef.size) and
  113. (taddnode(ttypeconvnode(right).left).left.nodetype=typeconvn) and
  114. (ttypeconvnode(taddnode(ttypeconvnode(right).left).left).convtype=tc_int_2_int) and
  115. are_equal_ints(right.resultdef,ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left.resultdef) and
  116. ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left.isequal(left) and
  117. is_integer(taddnode(ttypeconvnode(right).left).left.resultdef) and
  118. is_integer(taddnode(ttypeconvnode(right).left).right.resultdef) and
  119. is_integer(ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left.resultdef) and
  120. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  121. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  122. valid_for_var(ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left,false) and
  123. not(might_have_sideeffects(ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left)) then
  124. begin
  125. case ttypeconvnode(right).left.nodetype of
  126. addn:
  127. newinlinenodetype:=in_inc_x;
  128. subn:
  129. newinlinenodetype:=in_dec_x;
  130. andn:
  131. newinlinenodetype:=in_and_assign_x_y;
  132. orn:
  133. newinlinenodetype:=in_or_assign_x_y;
  134. xorn:
  135. newinlinenodetype:=in_xor_assign_x_y;
  136. else
  137. internalerror(2017032901);
  138. end;
  139. inserttypeconv_internal(taddnode(ttypeconvnode(right).left).right,left.resultdef);
  140. if ttypeconvnode(right).left.nodetype in [addn,subn] then
  141. result:=cinlinenode.createintern(
  142. newinlinenodetype,false,ccallparanode.create(
  143. ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left,ccallparanode.create(taddnode(ttypeconvnode(right).left).right,nil)))
  144. else
  145. result:=cinlinenode.createintern(
  146. newinlinenodetype,false,ccallparanode.create(
  147. taddnode(ttypeconvnode(right).left).right,ccallparanode.create(ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left,nil)));
  148. result.localswitches:=localswitches;
  149. ttypeconvnode(taddnode(ttypeconvnode(right).left).left).left:=nil;
  150. taddnode(ttypeconvnode(right).left).right:=nil;
  151. exit;
  152. end;
  153. { replace i:=k+i by inc(i,k)
  154. i:=k and/or/xor i by in_[and/or/xor]_assign_x_y(i,k)
  155. this handles the case, where there are no implicit type conversions }
  156. if (right.nodetype in [addn,andn,orn,xorn]) and
  157. (taddnode(right).right.isequal(left)) and
  158. is_integer(taddnode(right).left.resultdef) and
  159. is_integer(taddnode(right).right.resultdef) and
  160. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  161. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  162. valid_for_var(taddnode(right).right,false) and
  163. not(might_have_sideeffects(taddnode(right).right)) then
  164. begin
  165. case right.nodetype of
  166. addn:
  167. newinlinenodetype:=in_inc_x;
  168. andn:
  169. newinlinenodetype:=in_and_assign_x_y;
  170. orn:
  171. newinlinenodetype:=in_or_assign_x_y;
  172. xorn:
  173. newinlinenodetype:=in_xor_assign_x_y;
  174. else
  175. internalerror(2017032902);
  176. end;
  177. if right.nodetype=addn then
  178. result:=cinlinenode.createintern(
  179. newinlinenodetype,false,ccallparanode.create(
  180. taddnode(right).right,ccallparanode.create(taddnode(right).left,nil)))
  181. else
  182. result:=cinlinenode.createintern(
  183. newinlinenodetype,false,ccallparanode.create(
  184. taddnode(right).left,ccallparanode.create(taddnode(right).right,nil)));
  185. result.localswitches:=localswitches;
  186. taddnode(right).right:=nil;
  187. taddnode(right).left:=nil;
  188. exit;
  189. end;
  190. { replace i:=k+i by inc(i,k)
  191. i:=k and/or/xor i by in_[and/or/xor]_assign_x_y(i,k)
  192. this handles the case with two conversions (outer and inner):
  193. outer typeconv: right
  194. add/and/or/xor: ttypeconvnode(right).left
  195. inner typeconv: taddnode(ttypeconvnode(right).left).right
  196. right side 'i': ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left
  197. right side 'k': taddnode(ttypeconvnode(right).left).left }
  198. if (right.nodetype=typeconvn) and
  199. (ttypeconvnode(right).convtype=tc_int_2_int) and
  200. (ttypeconvnode(right).left.nodetype in [addn,andn,orn,xorn]) and
  201. is_integer(ttypeconvnode(right).left.resultdef) and
  202. (right.resultdef.size<=ttypeconvnode(right).left.resultdef.size) and
  203. (taddnode(ttypeconvnode(right).left).right.nodetype=typeconvn) and
  204. (ttypeconvnode(taddnode(ttypeconvnode(right).left).right).convtype=tc_int_2_int) and
  205. are_equal_ints(right.resultdef,ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left.resultdef) and
  206. ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left.isequal(left) and
  207. is_integer(taddnode(ttypeconvnode(right).left).left.resultdef) and
  208. is_integer(taddnode(ttypeconvnode(right).left).right.resultdef) and
  209. is_integer(ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left.resultdef) and
  210. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  211. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  212. valid_for_var(ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left,false) and
  213. not(might_have_sideeffects(ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left)) then
  214. begin
  215. case ttypeconvnode(right).left.nodetype of
  216. addn:
  217. newinlinenodetype:=in_inc_x;
  218. andn:
  219. newinlinenodetype:=in_and_assign_x_y;
  220. orn:
  221. newinlinenodetype:=in_or_assign_x_y;
  222. xorn:
  223. newinlinenodetype:=in_xor_assign_x_y;
  224. else
  225. internalerror(2017051101);
  226. end;
  227. inserttypeconv_internal(taddnode(ttypeconvnode(right).left).left,left.resultdef);
  228. if ttypeconvnode(right).left.nodetype=addn then
  229. result:=cinlinenode.createintern(
  230. newinlinenodetype,false,ccallparanode.create(
  231. ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left,ccallparanode.create(taddnode(ttypeconvnode(right).left).left,nil)))
  232. else
  233. result:=cinlinenode.createintern(
  234. newinlinenodetype,false,ccallparanode.create(
  235. taddnode(ttypeconvnode(right).left).left,ccallparanode.create(ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left,nil)));
  236. result.localswitches:=localswitches;
  237. ttypeconvnode(taddnode(ttypeconvnode(right).left).right).left:=nil;
  238. taddnode(ttypeconvnode(right).left).left:=nil;
  239. exit;
  240. end;
  241. {$ifdef enable_shl_shr_assign_x_y}
  242. { replace i:=i shl k by in_shl_assign_x_y(i,k)
  243. i:=i shr k by in_shr_assign_x_y(i,k)
  244. this handles the case, where there are no implicit type conversions }
  245. if (right.nodetype in [shln,shrn]) and
  246. (tshlshrnode(right).left.isequal(left)) and
  247. is_integer(tshlshrnode(right).left.resultdef) and
  248. is_integer(tshlshrnode(right).right.resultdef) and
  249. {$if not defined(cpu64bitalu) and not defined(cpucg64shiftsupport)}
  250. not(is_64bitint(tshlshrnode(right).left.resultdef)) and
  251. {$endif}
  252. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  253. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  254. valid_for_var(tshlshrnode(right).left,false) and
  255. not(might_have_sideeffects(tshlshrnode(right).left)) then
  256. begin
  257. case right.nodetype of
  258. shln:
  259. newinlinenodetype:=in_shl_assign_x_y;
  260. shrn:
  261. newinlinenodetype:=in_shr_assign_x_y;
  262. else
  263. internalerror(2017051201);
  264. end;
  265. result:=cinlinenode.createintern(
  266. newinlinenodetype,false,ccallparanode.create(
  267. tshlshrnode(right).right,ccallparanode.create(tshlshrnode(right).left,nil)));
  268. result.localswitches:=localswitches;
  269. tshlshrnode(right).left:=nil;
  270. tshlshrnode(right).right:=nil;
  271. exit;
  272. end;
  273. { replace i:=i shl k by in_shl_assign_x_y(i,k)
  274. i:=i shr k by in_shr_assign_x_y(i,k)
  275. this handles the case with two conversions (outer and inner):
  276. outer typeconv: right
  277. shl/shr: ttypeconvnode(right).left
  278. inner typeconv: tshlshrnode(ttypeconvnode(right).left).left
  279. right side 'i': ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left
  280. right side 'k': tshlshrnode(ttypeconvnode(right).left).right }
  281. if (right.nodetype=typeconvn) and
  282. (ttypeconvnode(right).convtype=tc_int_2_int) and
  283. (ttypeconvnode(right).left.nodetype in [shln,shrn]) and
  284. is_integer(ttypeconvnode(right).left.resultdef) and
  285. {$if not defined(cpu64bitalu) and not defined(cpucg64shiftsupport)}
  286. not(is_64bitint(ttypeconvnode(right).left.resultdef)) and
  287. {$endif}
  288. (right.resultdef.size<=ttypeconvnode(right).left.resultdef.size) and
  289. (tshlshrnode(ttypeconvnode(right).left).left.nodetype=typeconvn) and
  290. (ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).convtype=tc_int_2_int) and
  291. are_equal_ints(right.resultdef,ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left.resultdef) and
  292. ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left.isequal(left) and
  293. is_integer(tshlshrnode(ttypeconvnode(right).left).left.resultdef) and
  294. is_integer(tshlshrnode(ttypeconvnode(right).left).right.resultdef) and
  295. is_integer(ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left.resultdef) and
  296. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  297. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  298. valid_for_var(ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left,false) and
  299. not(might_have_sideeffects(ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left)) then
  300. begin
  301. case ttypeconvnode(right).left.nodetype of
  302. shln:
  303. newinlinenodetype:=in_shl_assign_x_y;
  304. shrn:
  305. newinlinenodetype:=in_shr_assign_x_y;
  306. else
  307. internalerror(2017051201);
  308. end;
  309. inserttypeconv_internal(tshlshrnode(ttypeconvnode(right).left).right,left.resultdef);
  310. result:=cinlinenode.createintern(
  311. newinlinenodetype,false,ccallparanode.create(
  312. tshlshrnode(ttypeconvnode(right).left).right,ccallparanode.create(ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left,nil)));
  313. result.localswitches:=localswitches;
  314. ttypeconvnode(tshlshrnode(ttypeconvnode(right).left).left).left:=nil;
  315. tshlshrnode(ttypeconvnode(right).left).right:=nil;
  316. exit;
  317. end;
  318. {$endif enable_shl_shr_assign_x_y}
  319. { replace i:=not i by in_not_assign_x(i)
  320. i:=-i by in_neg_assign_x(i)
  321. this handles the case, where there are no implicit type conversions }
  322. if (right.nodetype in [notn,unaryminusn]) and
  323. (tunarynode(right).left.isequal(left)) and
  324. is_integer(tunarynode(right).left.resultdef) and
  325. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  326. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  327. valid_for_var(tunarynode(right).left,false) and
  328. not(might_have_sideeffects(tunarynode(right).left)) then
  329. begin
  330. if right.nodetype=notn then
  331. newinlinenodetype:=in_not_assign_x
  332. else
  333. newinlinenodetype:=in_neg_assign_x;
  334. result:=cinlinenode.createintern(
  335. newinlinenodetype,false,tunarynode(right).left);
  336. result.localswitches:=localswitches;
  337. tunarynode(right).left:=nil;
  338. exit;
  339. end;
  340. { replace i:=not i by in_not_assign_x(i)
  341. i:=-i by in_neg_assign_x(i)
  342. this handles the case with type conversions:
  343. outer typeconv: right
  344. neg/not: ttypeconvnode(right).left
  345. inner typeconv: tunarynode(ttypeconvnode(right).left).left
  346. right side 'i': ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left }
  347. if (right.nodetype=typeconvn) and
  348. (ttypeconvnode(right).convtype=tc_int_2_int) and
  349. (ttypeconvnode(right).left.nodetype in [notn,unaryminusn]) and
  350. is_integer(ttypeconvnode(right).left.resultdef) and
  351. (right.resultdef.size<=ttypeconvnode(right).left.resultdef.size) and
  352. (tunarynode(ttypeconvnode(right).left).left.nodetype=typeconvn) and
  353. (ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).convtype=tc_int_2_int) and
  354. are_equal_ints(right.resultdef,ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left.resultdef) and
  355. ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left.isequal(left) and
  356. is_integer(ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left.resultdef) and
  357. ((localswitches*[cs_check_overflow,cs_check_range])=[]) and
  358. ((right.localswitches*[cs_check_overflow,cs_check_range])=[]) and
  359. valid_for_var(ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left,false) and
  360. not(might_have_sideeffects(ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left)) then
  361. begin
  362. if ttypeconvnode(right).left.nodetype=notn then
  363. newinlinenodetype:=in_not_assign_x
  364. else
  365. newinlinenodetype:=in_neg_assign_x;
  366. result:=cinlinenode.createintern(
  367. newinlinenodetype,false,ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left);
  368. result.localswitches:=localswitches;
  369. ttypeconvnode(tunarynode(ttypeconvnode(right).left).left).left:=nil;
  370. exit;
  371. end;
  372. end;
  373. end;
  374. function try_opt_node(var n: tnode; arg: pointer): foreachnoderesult;
  375. var
  376. hn : tnode;
  377. begin
  378. result:=fen_false;
  379. if n.nodetype=assignn then
  380. begin
  381. hn:=try_opt_assignmentnode(tassignmentnode(n));
  382. if assigned(hn) then
  383. begin
  384. n.free;
  385. n:=hn;
  386. typecheckpass(n);
  387. do_firstpass(n);
  388. end;
  389. end;
  390. end;
  391. procedure do_optloadmodifystore(var rootnode : tnode);
  392. begin
  393. foreachnodestatic(pm_postprocess,rootnode,@try_opt_node,nil);
  394. end;
  395. end.