Compiler-Inlining.st 16 KB

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