Compiler-Inlining.st 15 KB

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