Compiler-Inlining.st 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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 shouldBeInlined: 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: 'accessing'!
  61. internalVariables
  62. ^ Array with: self receiverInternalVariable
  63. !
  64. receiverInternalVariable
  65. ^ IRVariable new
  66. variable: (AliasVar new name: self receiverInternalVariableName);
  67. yourself.
  68. !
  69. receiverInternalVariableName
  70. ^ '$receiver'
  71. ! !
  72. !IRInlinedIfNilIfNotNil methodsFor: 'visiting'!
  73. acceptDagVisitor: aVisitor
  74. aVisitor visitIRInlinedIfNilIfNotNil: self
  75. ! !
  76. IRInlinedSend subclass: #IRInlinedIfTrue
  77. slots: {}
  78. package: 'Compiler-Inlining'!
  79. !IRInlinedIfTrue commentStamp!
  80. I represent an inlined `#ifTrue:` message send instruction.!
  81. !IRInlinedIfTrue methodsFor: 'visiting'!
  82. acceptDagVisitor: aVisitor
  83. aVisitor visitIRInlinedIfTrue: self
  84. ! !
  85. IRInlinedSend subclass: #IRInlinedIfTrueIfFalse
  86. slots: {}
  87. package: 'Compiler-Inlining'!
  88. !IRInlinedIfTrueIfFalse commentStamp!
  89. I represent an inlined `#ifTrue:ifFalse:` message send instruction.!
  90. !IRInlinedIfTrueIfFalse methodsFor: 'visiting'!
  91. acceptDagVisitor: aVisitor
  92. aVisitor visitIRInlinedIfTrueIfFalse: self
  93. ! !
  94. IRBlockSequence subclass: #IRInlinedSequence
  95. slots: {}
  96. package: 'Compiler-Inlining'!
  97. !IRInlinedSequence commentStamp!
  98. I represent a (block) sequence inside an inlined closure instruction (instance of `IRInlinedClosure`).!
  99. !IRInlinedSequence methodsFor: 'testing'!
  100. isInlined
  101. ^ true
  102. ! !
  103. !IRInlinedSequence methodsFor: 'visiting'!
  104. acceptDagVisitor: aVisitor
  105. aVisitor visitIRInlinedSequence: self
  106. ! !
  107. IRVisitor subclass: #IRInliner
  108. slots: {}
  109. package: 'Compiler-Inlining'!
  110. !IRInliner commentStamp!
  111. I visit an IR tree, inlining message sends and block closures.
  112. Message selectors that can be inlined are answered by `IRSendInliner >> #inlinedSelectors`!
  113. !IRInliner methodsFor: 'factory'!
  114. assignmentInliner
  115. ^ IRAssignmentInliner new
  116. translator: self;
  117. yourself
  118. !
  119. nonLocalReturnInliner
  120. ^ IRNonLocalReturnInliner new
  121. translator: self;
  122. yourself
  123. !
  124. returnInliner
  125. ^ IRReturnInliner new
  126. translator: self;
  127. yourself
  128. !
  129. sendInliner
  130. ^ IRSendInliner new
  131. translator: self;
  132. yourself
  133. ! !
  134. !IRInliner methodsFor: 'testing'!
  135. shouldInlineAssignment: anIRAssignment
  136. ^ anIRAssignment isInlined not and: [
  137. anIRAssignment right isSend and: [
  138. self shouldInlineSend: anIRAssignment right ]]
  139. !
  140. shouldInlineReturn: anIRReturn
  141. ^ anIRReturn isInlined not and: [
  142. anIRReturn expression isSend and: [
  143. self shouldInlineSend: anIRReturn expression ]]
  144. !
  145. shouldInlineSend: anIRSend
  146. ^ anIRSend isInlined not and: [
  147. IRSendInliner shouldInline: anIRSend ]
  148. ! !
  149. !IRInliner methodsFor: 'visiting'!
  150. visitIRAssignment: anIRAssignment
  151. ^ (self shouldInlineAssignment: anIRAssignment)
  152. ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]
  153. ifFalse: [ super visitIRAssignment: anIRAssignment ]
  154. !
  155. visitIRNonLocalReturn: anIRNonLocalReturn
  156. | localReturn |
  157. anIRNonLocalReturn scope canFlattenNonLocalReturns ifTrue: [
  158. anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.
  159. localReturn := IRReturn new
  160. scope: anIRNonLocalReturn scope;
  161. yourself.
  162. anIRNonLocalReturn dagChildren do: [ :each |
  163. localReturn add: each ].
  164. anIRNonLocalReturn replaceWith: localReturn.
  165. ^ self visitIRReturn: localReturn ].
  166. ^ (self shouldInlineReturn: anIRNonLocalReturn)
  167. ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
  168. ifFalse: [ super visitIRNonLocalReturn: anIRNonLocalReturn ]
  169. !
  170. visitIRReturn: anIRReturn
  171. ^ (self shouldInlineReturn: anIRReturn)
  172. ifTrue: [ self returnInliner inlineReturn: anIRReturn ]
  173. ifFalse: [ super visitIRReturn: anIRReturn ]
  174. !
  175. visitIRSend: anIRSend
  176. ^ (self shouldInlineSend: anIRSend)
  177. ifTrue: [ self sendInliner inlineSend: anIRSend ]
  178. ifFalse: [ super visitIRSend: anIRSend ]
  179. ! !
  180. IRJSTranslator subclass: #IRInliningJSTranslator
  181. slots: {}
  182. package: 'Compiler-Inlining'!
  183. !IRInliningJSTranslator commentStamp!
  184. I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!
  185. !IRInliningJSTranslator methodsFor: 'visiting'!
  186. visitIRInlinedClosure: anIRInlinedClosure
  187. self stream nextPutVars: (anIRInlinedClosure tempDeclarations collect: [ :each |
  188. each name asVariableName ]).
  189. self visitAllChildren: anIRInlinedClosure
  190. !
  191. visitIRInlinedIfFalse: anIRInlinedIfFalse
  192. self stream nextPutIf: [
  193. self stream nextPutAll: '!!$core.assert('.
  194. self visit: anIRInlinedIfFalse dagChildren first.
  195. self stream nextPutAll: ')' ]
  196. then: [ self visit: anIRInlinedIfFalse dagChildren last ]
  197. !
  198. visitIRInlinedIfNilIfNotNil: anIRInlinedIfNilIfNotNil
  199. self stream
  200. nextPutIf: [
  201. | recvVarName |
  202. recvVarName := anIRInlinedIfNilIfNotNil receiverInternalVariableName.
  203. self stream nextPutAll: '(', recvVarName, ' = '.
  204. self visit: anIRInlinedIfNilIfNotNil dagChildren first.
  205. self stream nextPutAll: ') == null || ', recvVarName, '.a$nil' ]
  206. then: [ self visit: anIRInlinedIfNilIfNotNil dagChildren second ]
  207. else: [ self visit: anIRInlinedIfNilIfNotNil dagChildren third ]
  208. !
  209. visitIRInlinedIfTrue: anIRInlinedIfTrue
  210. self stream nextPutIf: [
  211. self stream nextPutAll: '$core.assert('.
  212. self visit: anIRInlinedIfTrue dagChildren first.
  213. self stream nextPutAll: ')' ]
  214. then: [ self visit: anIRInlinedIfTrue dagChildren last ]
  215. !
  216. visitIRInlinedIfTrueIfFalse: anIRInlinedIfTrueIfFalse
  217. self stream
  218. nextPutIf: [
  219. self stream nextPutAll: '$core.assert('.
  220. self visit: anIRInlinedIfTrueIfFalse dagChildren first.
  221. self stream nextPutAll: ')' ]
  222. then: [ self visit: anIRInlinedIfTrueIfFalse dagChildren second ]
  223. else: [ self visit: anIRInlinedIfTrueIfFalse dagChildren third ]
  224. ! !
  225. Object subclass: #IRSendInliner
  226. slots: {#send. #translator}
  227. package: 'Compiler-Inlining'!
  228. !IRSendInliner commentStamp!
  229. I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!
  230. !IRSendInliner methodsFor: 'accessing'!
  231. send
  232. ^ send
  233. !
  234. send: anIRSend
  235. send := anIRSend
  236. !
  237. translator
  238. ^ translator
  239. !
  240. translator: anASTTranslator
  241. translator := anASTTranslator
  242. ! !
  243. !IRSendInliner methodsFor: 'error handling'!
  244. inliningError: aString
  245. InliningError signal: aString
  246. ! !
  247. !IRSendInliner methodsFor: 'factory'!
  248. inlinedClosure
  249. ^ IRInlinedClosure new
  250. !
  251. inlinedSequence
  252. ^ IRInlinedSequence new
  253. ! !
  254. !IRSendInliner methodsFor: 'inlining'!
  255. ifFalse: anIRInstruction
  256. ^ self inlinedSend: IRInlinedIfFalse new withBlock: anIRInstruction
  257. !
  258. ifFalse: anIRInstruction ifTrue: anotherIRInstruction
  259. ^ self perform: #ifTrue:ifFalse: withArguments: { anotherIRInstruction. anIRInstruction }
  260. !
  261. ifNil: anIRInstruction
  262. ^ self
  263. inlinedSend: IRInlinedIfNilIfNotNil new
  264. withBlock: anIRInstruction
  265. withBlock: (IRClosure new
  266. scope: anIRInstruction scope copy;
  267. add: (IRBlockSequence new
  268. add: self send receiver;
  269. yourself);
  270. yourself)
  271. !
  272. ifNil: anIRInstruction ifNotNil: anotherIRInstruction
  273. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anIRInstruction withBlock: anotherIRInstruction
  274. !
  275. ifNotNil: anIRInstruction
  276. ^ self
  277. inlinedSend: IRInlinedIfNilIfNotNil new
  278. withBlock: (IRClosure new
  279. scope: anIRInstruction scope copy;
  280. add: (IRBlockSequence new
  281. add: self send receiver;
  282. yourself);
  283. yourself)
  284. withBlock: anIRInstruction
  285. !
  286. ifNotNil: anIRInstruction ifNil: anotherIRInstruction
  287. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anotherIRInstruction withBlock: anIRInstruction
  288. !
  289. ifTrue: anIRInstruction
  290. ^ self inlinedSend: IRInlinedIfTrue new withBlock: anIRInstruction
  291. !
  292. ifTrue: anIRInstruction ifFalse: anotherIRInstruction
  293. ^ self inlinedSend: IRInlinedIfTrueIfFalse new withBlock: anIRInstruction withBlock: anotherIRInstruction
  294. !
  295. inlineClosure: anIRClosure
  296. | inlinedClosure sequence statements |
  297. inlinedClosure := self inlinedClosure.
  298. inlinedClosure
  299. scope: anIRClosure scope;
  300. parent: anIRClosure parent.
  301. "Add the possible temp declarations"
  302. anIRClosure tempDeclarations do: [ :each |
  303. inlinedClosure add: each ].
  304. "Add a block sequence"
  305. sequence := self inlinedSequence.
  306. "Map the closure arguments to the receiver of the message send"
  307. anIRClosure arguments do: [ :each |
  308. inlinedClosure add: (IRTempDeclaration new name: each; yourself).
  309. sequence add: (IRAssignment new
  310. add: (IRVariable new variable: (AliasVar new scope: inlinedClosure scope; name: each; yourself));
  311. add: (IRVariable new variable: (AliasVar new scope: inlinedClosure scope; name: '$receiver'; yourself));
  312. yourself) ].
  313. "To ensure the correct order of the closure instructions: first the temps then the sequence"
  314. inlinedClosure add: sequence.
  315. "Get all the statements"
  316. statements := anIRClosure sequence dagChildren.
  317. statements ifNotEmpty: [
  318. statements allButLast do: [ :each | sequence add: each ].
  319. "Inlined closures change local returns into result value itself"
  320. sequence add: statements last asInlinedBlockResult ].
  321. ^ inlinedClosure
  322. !
  323. inlineSend: anIRSend
  324. self send: anIRSend.
  325. ^ self
  326. perform: self send selector
  327. withArguments: self send arguments
  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 arguments allSatisfy: [ :each | each isClosure ]
  369. ! !
  370. IRSendInliner subclass: #IRAssignmentInliner
  371. slots: {#target}
  372. package: 'Compiler-Inlining'!
  373. !IRAssignmentInliner commentStamp!
  374. I inline message sends together with assignments by moving them around into the inline closure instructions.
  375. ##Example
  376. foo
  377. | a |
  378. a := true ifTrue: [ 1 ]
  379. Will produce:
  380. if($core.assert(true) {
  381. a = 1;
  382. };!
  383. !IRAssignmentInliner methodsFor: 'accessing'!
  384. target
  385. ^ target
  386. !
  387. target: anObject
  388. target := anObject
  389. ! !
  390. !IRAssignmentInliner methodsFor: 'inlining'!
  391. inlineAssignment: anIRAssignment
  392. self target: anIRAssignment left.
  393. ^ self inlineSend: anIRAssignment right andReplace: anIRAssignment
  394. !
  395. inlineClosure: anIRClosure
  396. | closure sequence statements |
  397. closure := super inlineClosure: anIRClosure.
  398. sequence := closure sequence.
  399. statements := sequence dagChildren.
  400. statements ifNotEmpty: [
  401. | final |
  402. final := statements last.
  403. final yieldsValue ifTrue: [
  404. sequence replace: final with: (IRAssignment new
  405. add: self target;
  406. add: final copy;
  407. yourself) ] ].
  408. ^ closure
  409. ! !
  410. IRSendInliner subclass: #IRNonLocalReturnInliner
  411. slots: {}
  412. package: 'Compiler-Inlining'!
  413. !IRNonLocalReturnInliner commentStamp!
  414. I inline message sends with inlined closure together with a return instruction.!
  415. !IRNonLocalReturnInliner methodsFor: 'inlining'!
  416. inlineClosure: anIRClosure
  417. | closure sequence statements |
  418. closure := super inlineClosure: anIRClosure.
  419. sequence := closure sequence.
  420. statements := sequence dagChildren.
  421. statements ifNotEmpty: [
  422. | final |
  423. final := statements last.
  424. final yieldsValue ifTrue: [
  425. sequence replace: final with: (IRNonLocalReturn new
  426. add: final copy;
  427. yourself) ] ].
  428. ^ closure
  429. !
  430. inlineReturn: anIRReturn
  431. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  432. ! !
  433. IRSendInliner subclass: #IRReturnInliner
  434. slots: {}
  435. package: 'Compiler-Inlining'!
  436. !IRReturnInliner commentStamp!
  437. I inline message sends with inlined closure together with a return instruction.!
  438. !IRReturnInliner methodsFor: 'inlining'!
  439. inlineClosure: anIRClosure
  440. | closure sequence statements |
  441. closure := super inlineClosure: anIRClosure.
  442. sequence := closure sequence.
  443. statements := sequence dagChildren.
  444. statements ifNotEmpty: [
  445. | final |
  446. final := statements last.
  447. final yieldsValue ifTrue: [
  448. sequence replace: final with: (IRReturn new
  449. add: final copy;
  450. yourself) ] ].
  451. ^ closure
  452. !
  453. inlineReturn: anIRReturn
  454. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  455. ! !
  456. CodeGenerator subclass: #InliningCodeGenerator
  457. slots: {}
  458. package: 'Compiler-Inlining'!
  459. !InliningCodeGenerator commentStamp!
  460. I am a specialized code generator that uses inlining to produce more optimized JavaScript output!
  461. !InliningCodeGenerator methodsFor: 'compiling'!
  462. inliner
  463. ^ IRInliner new
  464. !
  465. irTranslatorClass
  466. ^ IRInliningJSTranslator
  467. !
  468. preInliner
  469. ^ ASTPreInliner new
  470. !
  471. transformersDictionary
  472. ^ transformersDictionary ifNil: [ transformersDictionary := super transformersDictionary
  473. at: '3000-inlinerTagging' put: self preInliner;
  474. at: '6000-inliner' put: self inliner;
  475. at: '8000-irToJs' put: self irTranslator;
  476. yourself ]
  477. ! !
  478. SemanticError subclass: #InliningError
  479. slots: {}
  480. package: 'Compiler-Inlining'!
  481. !InliningError commentStamp!
  482. Instances of InliningError are signaled when using an `InliningCodeGenerator`in a `Compiler`.!
  483. !IRBlockReturn methodsFor: '*Compiler-Inlining'!
  484. asInlinedBlockResult
  485. ^ self expression
  486. ! !
  487. !IRInstruction methodsFor: '*Compiler-Inlining'!
  488. asInlinedBlockResult
  489. ^ self
  490. ! !