Compiler-Inlining.st 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. Smalltalk createPackage: 'Compiler-Inlining'!
  2. NodeVisitor subclass: #ASTPreInliner
  3. slots: {}
  4. package: 'Compiler-Inlining'!
  5. !ASTPreInliner methodsFor: 'visiting'!
  6. visitSendNode: aNode
  7. aNode superSend ifFalse: [
  8. (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [
  9. aNode receiver ifNotNil: [ :receiver |
  10. receiver shouldBeAliased: true ] ] ].
  11. ^ super visitSendNode: aNode
  12. ! !
  13. IRClosure subclass: #IRInlinedClosure
  14. slots: {}
  15. package: 'Compiler-Inlining'!
  16. !IRInlinedClosure commentStamp!
  17. I represent an inlined closure instruction.!
  18. !IRInlinedClosure methodsFor: 'testing'!
  19. isInlined
  20. ^ true
  21. ! !
  22. !IRInlinedClosure methodsFor: 'visiting'!
  23. acceptDagVisitor: aVisitor
  24. aVisitor visitIRInlinedClosure: self
  25. ! !
  26. IRSend subclass: #IRInlinedSend
  27. slots: {}
  28. package: 'Compiler-Inlining'!
  29. !IRInlinedSend commentStamp!
  30. I am the abstract super class of inlined message send instructions.!
  31. !IRInlinedSend methodsFor: 'accessing'!
  32. internalVariables
  33. "Answer a collection of internal variables required
  34. to perform the inlining"
  35. ^ #()
  36. ! !
  37. !IRInlinedSend methodsFor: 'testing'!
  38. isInlined
  39. ^ true
  40. ! !
  41. !IRInlinedSend methodsFor: 'visiting'!
  42. acceptDagVisitor: aVisitor
  43. aVisitor visitInlinedSend: self
  44. ! !
  45. IRInlinedSend subclass: #IRInlinedIfFalse
  46. slots: {}
  47. package: 'Compiler-Inlining'!
  48. !IRInlinedIfFalse commentStamp!
  49. I represent an inlined `#ifFalse:` message send instruction.!
  50. !IRInlinedIfFalse methodsFor: 'visiting'!
  51. acceptDagVisitor: aVisitor
  52. aVisitor visitIRInlinedIfFalse: self
  53. ! !
  54. IRInlinedSend subclass: #IRInlinedIfNilIfNotNil
  55. slots: {}
  56. package: 'Compiler-Inlining'!
  57. !IRInlinedIfNilIfNotNil commentStamp!
  58. I represent an inlined `#ifNil:ifNotNil:` message send instruction.!
  59. !IRInlinedIfNilIfNotNil methodsFor: 'visiting'!
  60. acceptDagVisitor: aVisitor
  61. aVisitor visitIRInlinedIfNilIfNotNil: self
  62. ! !
  63. IRInlinedSend subclass: #IRInlinedIfTrue
  64. slots: {}
  65. package: 'Compiler-Inlining'!
  66. !IRInlinedIfTrue commentStamp!
  67. I represent an inlined `#ifTrue:` message send instruction.!
  68. !IRInlinedIfTrue methodsFor: 'visiting'!
  69. acceptDagVisitor: aVisitor
  70. aVisitor visitIRInlinedIfTrue: self
  71. ! !
  72. IRInlinedSend subclass: #IRInlinedIfTrueIfFalse
  73. slots: {}
  74. package: 'Compiler-Inlining'!
  75. !IRInlinedIfTrueIfFalse commentStamp!
  76. I represent an inlined `#ifTrue:ifFalse:` message send instruction.!
  77. !IRInlinedIfTrueIfFalse methodsFor: 'visiting'!
  78. acceptDagVisitor: aVisitor
  79. aVisitor visitIRInlinedIfTrueIfFalse: self
  80. ! !
  81. IRBlockSequence subclass: #IRInlinedSequence
  82. slots: {}
  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. acceptDagVisitor: aVisitor
  92. aVisitor visitIRInlinedSequence: self
  93. ! !
  94. IRVisitor subclass: #IRInliner
  95. slots: {}
  96. package: 'Compiler-Inlining'!
  97. !IRInliner commentStamp!
  98. I visit an IR tree, inlining message sends and block closures.
  99. Message selectors that can be inlined are answered by `IRSendInliner >> #inlinedSelectors`!
  100. !IRInliner methodsFor: 'factory'!
  101. assignmentInliner
  102. ^ IRAssignmentInliner new
  103. translator: self;
  104. yourself
  105. !
  106. nonLocalReturnInliner
  107. ^ IRNonLocalReturnInliner new
  108. translator: self;
  109. yourself
  110. !
  111. returnInliner
  112. ^ IRReturnInliner new
  113. translator: self;
  114. yourself
  115. !
  116. sendInliner
  117. ^ IRSendInliner new
  118. translator: self;
  119. yourself
  120. ! !
  121. !IRInliner methodsFor: 'testing'!
  122. shouldInlineAssignment: anIRAssignment
  123. ^ anIRAssignment isInlined not and: [
  124. anIRAssignment right isSend and: [
  125. self shouldInlineSend: anIRAssignment right ]]
  126. !
  127. shouldInlineReturn: anIRReturn
  128. ^ anIRReturn isInlined not and: [
  129. anIRReturn expression isSend and: [
  130. self shouldInlineSend: anIRReturn expression ]]
  131. !
  132. shouldInlineSend: anIRSend
  133. ^ anIRSend isInlined not and: [
  134. IRSendInliner shouldInline: anIRSend ]
  135. ! !
  136. !IRInliner methodsFor: 'visiting'!
  137. flattenedReturn: anIRNonLocalReturn
  138. | localReturn |
  139. localReturn := IRReturn new
  140. scope: anIRNonLocalReturn scope;
  141. yourself.
  142. anIRNonLocalReturn dagChildren do: [ :each | localReturn add: each ].
  143. ^ localReturn
  144. !
  145. visitIRAssignment: anIRAssignment
  146. ^ (self shouldInlineAssignment: anIRAssignment)
  147. ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]
  148. ifFalse: [ super visitIRAssignment: anIRAssignment ]
  149. !
  150. visitIRNonLocalReturn: anIRNonLocalReturn
  151. anIRNonLocalReturn scope canFlattenNonLocalReturns ifTrue: [
  152. | localReturn |
  153. anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.
  154. localReturn := self flattenedReturn: anIRNonLocalReturn.
  155. anIRNonLocalReturn replaceWith: localReturn.
  156. ^ self visitIRReturn: localReturn ].
  157. ^ (self shouldInlineReturn: anIRNonLocalReturn)
  158. ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
  159. ifFalse: [ super visitIRNonLocalReturn: anIRNonLocalReturn ]
  160. !
  161. visitIRReturn: anIRReturn
  162. ^ (self shouldInlineReturn: anIRReturn)
  163. ifTrue: [ self returnInliner inlineReturn: anIRReturn ]
  164. ifFalse: [ super visitIRReturn: anIRReturn ]
  165. !
  166. visitIRSend: anIRSend
  167. ^ (self shouldInlineSend: anIRSend)
  168. ifTrue: [ self sendInliner inlineSend: anIRSend ]
  169. ifFalse: [ super visitIRSend: anIRSend ]
  170. ! !
  171. IRJSTranslator subclass: #IRInliningJSTranslator
  172. slots: {}
  173. package: 'Compiler-Inlining'!
  174. !IRInliningJSTranslator commentStamp!
  175. I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!
  176. !IRInliningJSTranslator methodsFor: 'visiting'!
  177. visitIRInlinedClosure: anIRInlinedClosure
  178. self stream nextPutVars: (anIRInlinedClosure tempDeclarations collect: [ :each |
  179. each name asVariableName ]).
  180. self visitAllChildren: anIRInlinedClosure
  181. !
  182. visitIRInlinedIfFalse: anIRInlinedIfFalse
  183. self stream nextPutIf: [
  184. self stream nextPutAll: '!!$core.assert('.
  185. self visit: anIRInlinedIfFalse dagChildren first.
  186. self stream nextPutAll: ')' ]
  187. then: [ self visit: anIRInlinedIfFalse dagChildren last ]
  188. !
  189. visitIRInlinedIfNilIfNotNil: anIRInlinedIfNilIfNotNil
  190. self stream
  191. nextPutIf: [
  192. self visit: anIRInlinedIfNilIfNotNil dagChildren first.
  193. self stream nextPutAll: ' == null || '.
  194. self visit: anIRInlinedIfNilIfNotNil dagChildren first.
  195. self stream nextPutAll: '.a$nil' ]
  196. then: [ self visit: anIRInlinedIfNilIfNotNil dagChildren second ]
  197. else: [ self visit: anIRInlinedIfNilIfNotNil dagChildren third ]
  198. !
  199. visitIRInlinedIfTrue: anIRInlinedIfTrue
  200. self stream nextPutIf: [
  201. self stream nextPutAll: '$core.assert('.
  202. self visit: anIRInlinedIfTrue dagChildren first.
  203. self stream nextPutAll: ')' ]
  204. then: [ self visit: anIRInlinedIfTrue dagChildren last ]
  205. !
  206. visitIRInlinedIfTrueIfFalse: anIRInlinedIfTrueIfFalse
  207. self stream
  208. nextPutIf: [
  209. self stream nextPutAll: '$core.assert('.
  210. self visit: anIRInlinedIfTrueIfFalse dagChildren first.
  211. self stream nextPutAll: ')' ]
  212. then: [ self visit: anIRInlinedIfTrueIfFalse dagChildren second ]
  213. else: [ self visit: anIRInlinedIfTrueIfFalse dagChildren third ]
  214. ! !
  215. Object subclass: #IRSendInliner
  216. slots: {#send. #translator}
  217. package: 'Compiler-Inlining'!
  218. !IRSendInliner commentStamp!
  219. I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!
  220. !IRSendInliner methodsFor: 'accessing'!
  221. send
  222. ^ send
  223. !
  224. send: anIRSend
  225. send := anIRSend
  226. !
  227. translator
  228. ^ translator
  229. !
  230. translator: anASTTranslator
  231. translator := anASTTranslator
  232. ! !
  233. !IRSendInliner methodsFor: 'error handling'!
  234. inliningError: aString
  235. InliningError signal: aString
  236. ! !
  237. !IRSendInliner methodsFor: 'factory'!
  238. inlinedClosure
  239. ^ IRInlinedClosure new
  240. !
  241. inlinedSequence
  242. ^ IRInlinedSequence new
  243. ! !
  244. !IRSendInliner methodsFor: 'inlining'!
  245. ifFalse: anIRInstruction
  246. ^ self inlinedSend: IRInlinedIfFalse new withBlock: anIRInstruction
  247. !
  248. ifFalse: anIRInstruction ifTrue: anotherIRInstruction
  249. ^ self perform: #ifTrue:ifFalse: withArguments: { anotherIRInstruction. anIRInstruction }
  250. !
  251. ifNil: anIRInstruction
  252. ^ self
  253. inlinedSend: IRInlinedIfNilIfNotNil new
  254. withBlock: anIRInstruction
  255. withBlock: (IRClosure new
  256. scope: anIRInstruction scope copy;
  257. add: (IRBlockSequence new
  258. add: self send receiver;
  259. yourself);
  260. yourself)
  261. !
  262. ifNil: anIRInstruction ifNotNil: anotherIRInstruction
  263. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anIRInstruction withBlock: anotherIRInstruction
  264. !
  265. ifNotNil: anIRInstruction
  266. ^ self
  267. inlinedSend: IRInlinedIfNilIfNotNil new
  268. withBlock: (IRClosure new
  269. scope: anIRInstruction scope copy;
  270. add: (IRBlockSequence new
  271. add: self send receiver;
  272. yourself);
  273. yourself)
  274. withBlock: anIRInstruction
  275. !
  276. ifNotNil: anIRInstruction ifNil: anotherIRInstruction
  277. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anotherIRInstruction withBlock: anIRInstruction
  278. !
  279. ifTrue: anIRInstruction
  280. ^ self inlinedSend: IRInlinedIfTrue new withBlock: anIRInstruction
  281. !
  282. ifTrue: anIRInstruction ifFalse: anotherIRInstruction
  283. ^ self inlinedSend: IRInlinedIfTrueIfFalse new withBlock: anIRInstruction withBlock: anotherIRInstruction
  284. !
  285. inlineClosure: anIRClosure
  286. | inlinedClosure sequence statements |
  287. inlinedClosure := self inlinedClosure.
  288. inlinedClosure
  289. scope: anIRClosure scope;
  290. parent: anIRClosure parent.
  291. "Add the possible temp declarations"
  292. anIRClosure tempDeclarations do: [ :each |
  293. inlinedClosure add: each ].
  294. "Add a block sequence"
  295. sequence := self inlinedSequence.
  296. "Map the closure arguments to the receiver of the message send"
  297. anIRClosure arguments do: [ :each |
  298. inlinedClosure add: (IRTempDeclaration new name: each; yourself).
  299. sequence add: (IRAssignment new
  300. add: (IRVariable new variable: (ArgVar new scope: inlinedClosure scope; name: each; yourself));
  301. add: self send receiver;
  302. yourself) ].
  303. "To ensure the correct order of the closure instructions: first the temps then the sequence"
  304. inlinedClosure add: sequence.
  305. "Get all the statements"
  306. statements := anIRClosure sequence dagChildren.
  307. statements ifNotEmpty: [
  308. statements allButLast do: [ :each | sequence add: each ].
  309. "Inlined closures change local returns into result value itself"
  310. sequence add: statements last asInlinedBlockResult ].
  311. ^ inlinedClosure
  312. !
  313. inlineSend: anIRSend
  314. self send: anIRSend.
  315. ^ self
  316. perform: self send selector
  317. withArguments: self send arguments
  318. !
  319. inlinedClosure: closure wrapFinalValueIn: aBlock
  320. | sequence final |
  321. sequence := closure sequence.
  322. sequence dagChildren ifEmpty: [ sequence add: (IRVariable new
  323. variable: (closure scope pseudoVars at: 'nil');
  324. yourself) ].
  325. final := sequence dagChildren last.
  326. final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ].
  327. ^ closure
  328. ! !
  329. !IRSendInliner methodsFor: 'private'!
  330. inlineSend: anIRSend andReplace: anIRInstruction
  331. anIRInstruction replaceWith: anIRSend.
  332. ^ self inlineSend: anIRSend
  333. !
  334. inlinedSend: inlinedSend withBlock: anIRInstruction
  335. | inlinedClosure |
  336. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  337. anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
  338. inlinedClosure := self translator visit: (self inlineClosure: anIRInstruction).
  339. inlinedSend
  340. add: self send receiver;
  341. add: inlinedClosure.
  342. self send replaceWith: inlinedSend.
  343. inlinedSend method internalVariables
  344. addAll: inlinedSend internalVariables.
  345. ^ inlinedSend
  346. !
  347. inlinedSend: inlinedSend withBlock: anIRInstruction withBlock: anotherIRInstruction
  348. | inlinedClosure1 inlinedClosure2 |
  349. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  350. anotherIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  351. inlinedClosure1 := self translator visit: (self inlineClosure: anIRInstruction).
  352. inlinedClosure2 := self translator visit: (self inlineClosure: anotherIRInstruction).
  353. inlinedSend
  354. add: self send receiver;
  355. add: inlinedClosure1;
  356. add: inlinedClosure2.
  357. self send replaceWith: inlinedSend.
  358. inlinedSend method internalVariables
  359. addAll: inlinedSend internalVariables.
  360. ^ inlinedSend
  361. ! !
  362. !IRSendInliner class methodsFor: 'accessing'!
  363. inlinedSelectors
  364. ^ #('ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:' 'ifNil:' 'ifNotNil:' 'ifNil:ifNotNil:' 'ifNotNil:ifNil:')
  365. !
  366. shouldInline: anIRSend
  367. (self inlinedSelectors includes: anIRSend selector) ifFalse: [ ^ false ].
  368. anIRSend receiver isSuper ifTrue: [ ^ false ].
  369. ^ anIRSend arguments allSatisfy: [ :each | each isClosure ]
  370. ! !
  371. IRSendInliner subclass: #IRAssignmentInliner
  372. slots: {#target}
  373. package: 'Compiler-Inlining'!
  374. !IRAssignmentInliner commentStamp!
  375. I inline message sends together with assignments by moving them around into the inline closure instructions.
  376. ##Example
  377. foo
  378. | a |
  379. a := true ifTrue: [ 1 ]
  380. Will produce:
  381. if($core.assert(true) {
  382. a = 1;
  383. };!
  384. !IRAssignmentInliner methodsFor: 'accessing'!
  385. target
  386. ^ target
  387. !
  388. target: anObject
  389. target := anObject
  390. ! !
  391. !IRAssignmentInliner methodsFor: 'inlining'!
  392. inlineAssignment: anIRAssignment
  393. self target: anIRAssignment left.
  394. ^ self inlineSend: anIRAssignment right andReplace: anIRAssignment
  395. !
  396. inlineClosure: anIRClosure
  397. ^ self
  398. inlinedClosure: (super inlineClosure: anIRClosure)
  399. wrapFinalValueIn: [ :final |
  400. IRAssignment new
  401. add: self target;
  402. add: final copy;
  403. yourself ]
  404. ! !
  405. IRSendInliner subclass: #IRNonLocalReturnInliner
  406. slots: {}
  407. package: 'Compiler-Inlining'!
  408. !IRNonLocalReturnInliner commentStamp!
  409. I inline message sends with inlined closure together with a return instruction.!
  410. !IRNonLocalReturnInliner methodsFor: 'inlining'!
  411. inlineClosure: anIRClosure
  412. ^ self
  413. inlinedClosure: (super inlineClosure: anIRClosure)
  414. wrapFinalValueIn: [ :final |
  415. IRNonLocalReturn new
  416. add: final copy;
  417. yourself ]
  418. !
  419. inlineReturn: anIRReturn
  420. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  421. ! !
  422. IRSendInliner subclass: #IRReturnInliner
  423. slots: {}
  424. package: 'Compiler-Inlining'!
  425. !IRReturnInliner commentStamp!
  426. I inline message sends with inlined closure together with a return instruction.!
  427. !IRReturnInliner methodsFor: 'inlining'!
  428. inlineClosure: anIRClosure
  429. ^ self
  430. inlinedClosure: (super inlineClosure: anIRClosure)
  431. wrapFinalValueIn: [ :final |
  432. IRReturn new
  433. add: final copy;
  434. yourself ]
  435. !
  436. inlineReturn: anIRReturn
  437. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  438. ! !
  439. CodeGenerator subclass: #InliningCodeGenerator
  440. slots: {}
  441. package: 'Compiler-Inlining'!
  442. !InliningCodeGenerator commentStamp!
  443. I am a specialized code generator that uses inlining to produce more optimized JavaScript output!
  444. !InliningCodeGenerator methodsFor: 'compiling'!
  445. inliner
  446. ^ IRInliner new
  447. !
  448. irTranslatorClass
  449. ^ IRInliningJSTranslator
  450. !
  451. preInliner
  452. ^ ASTPreInliner new
  453. !
  454. transformersDictionary
  455. ^ transformersDictionary ifNil: [ transformersDictionary := super transformersDictionary
  456. at: '3000-inlinerTagging' put: self preInliner;
  457. at: '6000-inliner' put: self inliner;
  458. at: '8000-irToJs' put: self irTranslator;
  459. yourself ]
  460. ! !
  461. SemanticError subclass: #InliningError
  462. slots: {}
  463. package: 'Compiler-Inlining'!
  464. !InliningError commentStamp!
  465. Instances of InliningError are signaled when using an `InliningCodeGenerator`in a `Compiler`.!
  466. Trait named: #TIRInlinedVisitor
  467. package: 'Compiler-Inlining'!
  468. !TIRInlinedVisitor methodsFor: 'visiting'!
  469. visitIRInlinedClosure: anIRInlinedClosure
  470. ^ self visitIRClosure: anIRInlinedClosure
  471. !
  472. visitIRInlinedSequence: anIRInlinedSequence
  473. ^ self visitIRSequence: anIRInlinedSequence
  474. ! !
  475. IRInliner setTraitComposition: {TIRInlinedVisitor} asTraitComposition!
  476. IRInliningJSTranslator setTraitComposition: {TIRInlinedVisitor} asTraitComposition!
  477. ! !
  478. !IRBlockReturn methodsFor: '*Compiler-Inlining'!
  479. asInlinedBlockResult
  480. ^ self expression
  481. ! !
  482. !IRInstruction methodsFor: '*Compiler-Inlining'!
  483. asInlinedBlockResult
  484. ^ self
  485. ! !