1
0

Compiler-Inlining.st 16 KB

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