Compiler-Inlining.st 15 KB

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