Compiler-Inlining.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. Smalltalk current createPackage: 'Compiler-Inlining' properties: #{}!
  2. IRAssignment subclass: #IRInlinedAssignment
  3. instanceVariableNames: ''
  4. package: 'Compiler-Inlining'!
  5. !IRInlinedAssignment commentStamp!
  6. I represent an inlined assignment instruction.!
  7. !IRInlinedAssignment methodsFor: 'testing'!
  8. isInlined
  9. ^ true
  10. ! !
  11. !IRInlinedAssignment methodsFor: 'visiting'!
  12. accept: aVisitor
  13. ^ aVisitor visitIRInlinedAssignment: self
  14. ! !
  15. IRClosure subclass: #IRInlinedClosure
  16. instanceVariableNames: ''
  17. package: 'Compiler-Inlining'!
  18. !IRInlinedClosure commentStamp!
  19. I represent an inlined closure instruction.!
  20. !IRInlinedClosure methodsFor: 'testing'!
  21. isInlined
  22. ^ true
  23. ! !
  24. !IRInlinedClosure methodsFor: 'visiting'!
  25. accept: aVisitor
  26. aVisitor visitIRInlinedClosure: self
  27. ! !
  28. IRReturn subclass: #IRInlinedReturn
  29. instanceVariableNames: ''
  30. package: 'Compiler-Inlining'!
  31. !IRInlinedReturn commentStamp!
  32. I represent an inlined local return instruction.!
  33. !IRInlinedReturn methodsFor: 'testing'!
  34. isInlined
  35. ^ true
  36. ! !
  37. !IRInlinedReturn methodsFor: 'visiting'!
  38. accept: aVisitor
  39. ^ aVisitor visitIRInlinedReturn: self
  40. ! !
  41. IRInlinedReturn subclass: #IRInlinedNonLocalReturn
  42. instanceVariableNames: ''
  43. package: 'Compiler-Inlining'!
  44. !IRInlinedNonLocalReturn commentStamp!
  45. I represent an inlined non local return instruction.!
  46. !IRInlinedNonLocalReturn methodsFor: 'testing'!
  47. isInlined
  48. ^ true
  49. ! !
  50. !IRInlinedNonLocalReturn methodsFor: 'visiting'!
  51. accept: aVisitor
  52. ^ aVisitor visitIRInlinedNonLocalReturn: self
  53. ! !
  54. IRSend subclass: #IRInlinedSend
  55. instanceVariableNames: ''
  56. package: 'Compiler-Inlining'!
  57. !IRInlinedSend commentStamp!
  58. I am the abstract super class of inlined message send instructions.!
  59. !IRInlinedSend methodsFor: 'testing'!
  60. isInlined
  61. ^ true
  62. ! !
  63. !IRInlinedSend methodsFor: 'visiting'!
  64. accept: aVisitor
  65. aVisitor visitInlinedSend: self
  66. ! !
  67. IRInlinedSend subclass: #IRInlinedIfFalse
  68. instanceVariableNames: ''
  69. package: 'Compiler-Inlining'!
  70. !IRInlinedIfFalse methodsFor: 'visiting'!
  71. accept: aVisitor
  72. aVisitor visitIRInlinedIfFalse: self
  73. ! !
  74. IRInlinedSend subclass: #IRInlinedIfTrue
  75. instanceVariableNames: ''
  76. package: 'Compiler-Inlining'!
  77. !IRInlinedIfTrue methodsFor: 'visiting'!
  78. accept: aVisitor
  79. aVisitor visitIRInlinedIfTrue: self
  80. ! !
  81. IRBlockSequence subclass: #IRInlinedSequence
  82. instanceVariableNames: ''
  83. package: 'Compiler-Inlining'!
  84. !IRInlinedSequence commentStamp!
  85. I represent a (block) sequence inside an inlined closure instruction (instance of `IRInlinedClosure`).!
  86. !IRInlinedSequence methodsFor: 'testing'!
  87. isInlined
  88. ^ true
  89. ! !
  90. !IRInlinedSequence methodsFor: 'visiting'!
  91. accept: aVisitor
  92. aVisitor visitIRInlinedSequence: self
  93. ! !
  94. IRInlinedSequence subclass: #IRAssigningInlinedSequence
  95. instanceVariableNames: 'assignTo'
  96. package: 'Compiler-Inlining'!
  97. !IRAssigningInlinedSequence methodsFor: 'accessing'!
  98. accept: aVisitor
  99. ^ aVisitor visitIRAssigningInlinedSequence: self
  100. !
  101. assignTo
  102. ^ assignTo
  103. !
  104. assignTo: anIRInstruction
  105. assignTo := anIRInstruction
  106. ! !
  107. IRInlinedSequence subclass: #IRReturningInlinedSequence
  108. instanceVariableNames: ''
  109. package: 'Compiler-Inlining'!
  110. !IRReturningInlinedSequence methodsFor: 'visiting'!
  111. accept: aVisitor
  112. ^ aVisitor visitIRReturningInlinedSequence: self
  113. ! !
  114. IRReturningInlinedSequence subclass: #IRNonLocalReturningInlinedSequence
  115. instanceVariableNames: ''
  116. package: 'Compiler-Inlining'!
  117. !IRNonLocalReturningInlinedSequence methodsFor: 'visiting'!
  118. accept: aVisitor
  119. ^ aVisitor visitIRNonLocalReturningInlinedSequence: self
  120. ! !
  121. IRVisitor subclass: #IRInliner
  122. instanceVariableNames: ''
  123. package: 'Compiler-Inlining'!
  124. !IRInliner commentStamp!
  125. I visit an IR tree, inlining message sends and block closures.
  126. Message selectors that can be inlined are answered by `IRSendInliner >> #inlinedSelectors`!
  127. !IRInliner methodsFor: 'factory'!
  128. assignmentInliner
  129. ^ IRAssignmentInliner new
  130. translator: self;
  131. yourself
  132. !
  133. nonLocalReturnInliner
  134. ^ IRNonLocalReturnInliner new
  135. translator: self;
  136. yourself
  137. !
  138. returnInliner
  139. ^ IRReturnInliner new
  140. translator: self;
  141. yourself
  142. !
  143. sendInliner
  144. ^ IRSendInliner new
  145. translator: self;
  146. yourself
  147. ! !
  148. !IRInliner methodsFor: 'testing'!
  149. shouldInlineAssignment: anIRAssignment
  150. ^ anIRAssignment isInlined not and: [
  151. anIRAssignment instructions last isSend and: [
  152. self shouldInlineSend: (anIRAssignment instructions last) ]]
  153. !
  154. shouldInlineReturn: anIRReturn
  155. ^ anIRReturn isInlined not and: [
  156. anIRReturn instructions first isSend and: [
  157. self shouldInlineSend: (anIRReturn instructions first) ]]
  158. !
  159. shouldInlineSend: anIRSend
  160. ^ anIRSend isInlined not and: [
  161. IRSendInliner shouldInline: anIRSend ]
  162. ! !
  163. !IRInliner methodsFor: 'visiting'!
  164. transformNonLocalReturn: anIRNonLocalReturn
  165. "Replace a non local return into a local return"
  166. | localReturn |
  167. anIRNonLocalReturn scope canInlineNonLocalReturns ifTrue: [
  168. anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.
  169. localReturn := IRReturn new
  170. scope: anIRNonLocalReturn scope;
  171. yourself.
  172. anIRNonLocalReturn instructions do: [ :each |
  173. localReturn add: each ].
  174. anIRNonLocalReturn replaceWith: localReturn.
  175. ^ localReturn ].
  176. ^ super visitIRNonLocalReturn: anIRNonLocalReturn
  177. !
  178. visitIRAssignment: anIRAssignment
  179. ^ (self shouldInlineAssignment: anIRAssignment)
  180. ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]
  181. ifFalse: [ super visitIRAssignment: anIRAssignment ]
  182. !
  183. visitIRNonLocalReturn: anIRNonLocalReturn
  184. ^ (self shouldInlineReturn: anIRNonLocalReturn)
  185. ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
  186. ifFalse: [ self transformNonLocalReturn: anIRNonLocalReturn ]
  187. !
  188. visitIRReturn: anIRReturn
  189. ^ (self shouldInlineReturn: anIRReturn)
  190. ifTrue: [ self returnInliner inlineReturn: anIRReturn ]
  191. ifFalse: [ super visitIRReturn: anIRReturn ]
  192. !
  193. visitIRSend: anIRSend
  194. ^ (self shouldInlineSend: anIRSend)
  195. ifTrue: [ self sendInliner inlineSend: anIRSend ]
  196. ifFalse: [ super visitIRSend: anIRSend ]
  197. ! !
  198. IRJSTranslator subclass: #IRInliningJSTranslator
  199. instanceVariableNames: ''
  200. package: 'Compiler-Inlining'!
  201. !IRInliningJSTranslator commentStamp!
  202. I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!
  203. !IRInliningJSTranslator methodsFor: 'visiting'!
  204. visitIRAssigningInlinedSequence: anIRInlinedSequence
  205. anIRInlinedSequence instructions allButLast do: [ :each |
  206. self stream nextPutStatementWith: [ self visit: each ]].
  207. self stream nextPutStatementWith: [
  208. anIRInlinedSequence instructions last canBeAssigned
  209. ifTrue: [
  210. self stream
  211. nextPutAll: anIRInlinedSequence assignTo variable alias;
  212. nextPutAssignment.
  213. self visit: anIRInlinedSequence instructions last ]
  214. ifFalse: [ self visit: anIRInlinedSequence instructions last ]]
  215. !
  216. visitIRInlinedAssignment: anIRInlinedAssignment
  217. self visit: anIRInlinedAssignment instructions last
  218. !
  219. visitIRInlinedClosure: anIRInlinedClosure
  220. anIRInlinedClosure instructions do: [ :each |
  221. self visit: each ]
  222. !
  223. visitIRInlinedIfFalse: anIRInlinedIfFalse
  224. self stream nextPutIf: [
  225. self stream nextPutAll: '!! smalltalk.assert('.
  226. self visit: anIRInlinedIfFalse instructions first.
  227. self stream nextPutAll: ')' ]
  228. with: [ self visit: anIRInlinedIfFalse instructions last ]
  229. !
  230. visitIRInlinedIfTrue: anIRInlinedIfTrue
  231. self stream nextPutIf: [
  232. self stream nextPutAll: 'smalltalk.assert('.
  233. self visit: anIRInlinedIfTrue instructions first.
  234. self stream nextPutAll: ')' ]
  235. with: [ self visit: anIRInlinedIfTrue instructions last ]
  236. !
  237. visitIRInlinedNonLocalReturn: anIRInlinedReturn
  238. self stream nextPutStatementWith: [
  239. self visit: anIRInlinedReturn instructions last ].
  240. self stream nextPutNonLocalReturnWith: [ ]
  241. !
  242. visitIRInlinedReturn: anIRInlinedReturn
  243. self visit: anIRInlinedReturn instructions last
  244. !
  245. visitIRInlinedSequence: anIRInlinedSequence
  246. anIRInlinedSequence instructions do: [ :each |
  247. self stream nextPutStatementWith: [ self visit: each ]]
  248. !
  249. visitIRNonLocalReturningInlinedSequence: anIRInlinedSequence
  250. anIRInlinedSequence instructions allButLast do: [ :each |
  251. self stream nextPutStatementWith: [ self visit: each ]].
  252. self stream nextPutStatementWith: [
  253. anIRInlinedSequence instructions last canBeAssigned
  254. ifTrue: [
  255. self stream nextPutNonLocalReturnWith: [
  256. self visit: anIRInlinedSequence instructions last ]]
  257. ifFalse: [ self visit: anIRInlinedSequence instructions last ]]
  258. !
  259. visitIRReturningInlinedSequence: anIRInlinedSequence
  260. anIRInlinedSequence instructions allButLast do: [ :each |
  261. self stream nextPutStatementWith: [ self visit: each ]].
  262. self stream nextPutStatementWith: [
  263. anIRInlinedSequence instructions last canBeAssigned
  264. ifTrue: [
  265. self stream nextPutReturn.
  266. self visit: anIRInlinedSequence instructions last ]
  267. ifFalse: [ self visit: anIRInlinedSequence instructions last ]]
  268. ! !
  269. Object subclass: #IRSendInliner
  270. instanceVariableNames: 'send translator'
  271. package: 'Compiler-Inlining'!
  272. !IRSendInliner commentStamp!
  273. I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!
  274. !IRSendInliner methodsFor: 'accessing'!
  275. send
  276. ^ send
  277. !
  278. send: anIRSend
  279. send := anIRSend
  280. !
  281. translator
  282. ^ translator
  283. !
  284. translator: anASTTranslator
  285. translator := anASTTranslator
  286. ! !
  287. !IRSendInliner methodsFor: 'error handling'!
  288. inliningError: aString
  289. InliningError signal: aString
  290. ! !
  291. !IRSendInliner methodsFor: 'factory'!
  292. inlinedClosure
  293. ^ IRInlinedClosure new
  294. !
  295. inlinedSequence
  296. ^ IRInlinedSequence new
  297. ! !
  298. !IRSendInliner methodsFor: 'inlining'!
  299. ifFalse: anIRInstruction
  300. | inlinedSend inlinedClosure |
  301. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  302. anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
  303. inlinedClosure := self inlineClosure: anIRInstruction.
  304. inlinedSend := IRInlinedIfFalse new.
  305. inlinedSend
  306. add: self send instructions first;
  307. add: inlinedClosure.
  308. self send replaceWith: inlinedSend.
  309. ^ inlinedSend
  310. !
  311. ifTrue: anIRInstruction
  312. | inlinedSend inlinedClosure |
  313. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  314. anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
  315. inlinedClosure := self inlineClosure: anIRInstruction.
  316. inlinedSend := IRInlinedIfTrue new.
  317. inlinedSend
  318. add: self send instructions first;
  319. add: inlinedClosure.
  320. self send replaceWith: inlinedSend.
  321. ^ inlinedSend
  322. !
  323. inlineClosure: anIRClosure
  324. | inlinedClosure sequence statements |
  325. inlinedClosure := self inlinedClosure.
  326. inlinedClosure scope: anIRClosure scope.
  327. "Add the possible temp declarations"
  328. anIRClosure instructions do: [ :each |
  329. each isSequence ifFalse: [
  330. inlinedClosure add: each ]].
  331. "Add a block sequence"
  332. sequence := self inlinedSequence.
  333. inlinedClosure add: sequence.
  334. "Get all the statements"
  335. statements := anIRClosure instructions last instructions.
  336. statements ifNotEmpty: [
  337. statements allButLast do: [ :each | sequence add: (self translator visit: each) ].
  338. "Inlined closures don't have implicit local returns"
  339. statements last isLocalReturn
  340. ifTrue: [ sequence add: (self translator visit: statements last instructions first) ]
  341. ifFalse: [ sequence add: (self translator visit: statements last) ]].
  342. ^ inlinedClosure
  343. !
  344. inlineSend: anIRSend
  345. self send: anIRSend.
  346. ^ self perform: self send selector withArguments: self send instructions allButFirst
  347. ! !
  348. !IRSendInliner class methodsFor: 'accessing'!
  349. inlinedSelectors
  350. ^ #('ifTrue:' 'ifFalse:')
  351. !
  352. shouldInline: anIRInstruction
  353. (self inlinedSelectors includes: anIRInstruction selector) ifFalse: [ ^ false ].
  354. anIRInstruction instructions allButFirst do: [ :each |
  355. each isClosure ifFalse: [ ^ false ]].
  356. ^ true
  357. ! !
  358. IRSendInliner subclass: #IRAssignmentInliner
  359. instanceVariableNames: 'assignment'
  360. package: 'Compiler-Inlining'!
  361. !IRAssignmentInliner commentStamp!
  362. I inline message sends together with assignments by moving them around into the inline closure instructions.
  363. ##Example
  364. foo
  365. | a |
  366. a := true ifTrue: [ 1 ]
  367. Will produce:
  368. if(smalltalk.assert(true) {
  369. a = 1;
  370. };!
  371. !IRAssignmentInliner methodsFor: 'accessing'!
  372. assignment
  373. ^ assignment
  374. !
  375. assignment: aNode
  376. assignment := aNode
  377. ! !
  378. !IRAssignmentInliner methodsFor: 'factory'!
  379. inlinedSequence
  380. ^ IRAssigningInlinedSequence new
  381. assignTo: self assignment instructions first;
  382. yourself
  383. ! !
  384. !IRAssignmentInliner methodsFor: 'inlining'!
  385. inlineAssignment: anIRAssignment
  386. | inlinedAssignment |
  387. self assignment: anIRAssignment.
  388. inlinedAssignment := IRInlinedAssignment new.
  389. anIRAssignment instructions do: [ :each |
  390. inlinedAssignment add: each ].
  391. anIRAssignment replaceWith: inlinedAssignment.
  392. self inlineSend: inlinedAssignment instructions last.
  393. ^ inlinedAssignment
  394. ! !
  395. IRSendInliner subclass: #IRReturnInliner
  396. instanceVariableNames: ''
  397. package: 'Compiler-Inlining'!
  398. !IRReturnInliner commentStamp!
  399. I inline message sends with inlined closure together with a return instruction.!
  400. !IRReturnInliner methodsFor: 'factory'!
  401. inlinedReturn
  402. ^ IRInlinedReturn new
  403. !
  404. inlinedSequence
  405. ^ IRReturningInlinedSequence new
  406. ! !
  407. !IRReturnInliner methodsFor: 'inlining'!
  408. inlineReturn: anIRReturn
  409. | return |
  410. return := self inlinedReturn.
  411. anIRReturn instructions do: [ :each |
  412. return add: each ].
  413. anIRReturn replaceWith: return.
  414. self inlineSend: return instructions last.
  415. ^ return
  416. ! !
  417. IRReturnInliner subclass: #IRNonLocalReturnInliner
  418. instanceVariableNames: ''
  419. package: 'Compiler-Inlining'!
  420. !IRNonLocalReturnInliner methodsFor: 'factory'!
  421. inlinedReturn
  422. ^ IRInlinedNonLocalReturn new
  423. !
  424. inlinedSequence
  425. ^ IRNonLocalReturningInlinedSequence new
  426. ! !
  427. CodeGenerator subclass: #InliningCodeGenerator
  428. instanceVariableNames: ''
  429. package: 'Compiler-Inlining'!
  430. !InliningCodeGenerator commentStamp!
  431. I am a specialized code generator that uses inlining to produce more optimized JavaScript output!
  432. !InliningCodeGenerator methodsFor: 'compiling'!
  433. compileNode: aNode
  434. | ir stream |
  435. self semanticAnalyzer visit: aNode.
  436. ir := self translator visit: aNode.
  437. self inliner visit: ir.
  438. ^ self irTranslator
  439. visit: ir;
  440. contents
  441. !
  442. inliner
  443. ^ IRInliner new
  444. !
  445. irTranslator
  446. ^ IRInliningJSTranslator new
  447. ! !