Compiler-Inlining.st 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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. ^ { 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. flattenedReturn: anIRNonLocalReturn
  151. | localReturn |
  152. localReturn := IRReturn new
  153. scope: anIRNonLocalReturn scope;
  154. yourself.
  155. anIRNonLocalReturn dagChildren do: [ :each | localReturn add: each ].
  156. ^ localReturn
  157. !
  158. visitIRAssignment: anIRAssignment
  159. ^ (self shouldInlineAssignment: anIRAssignment)
  160. ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]
  161. ifFalse: [ super visitIRAssignment: anIRAssignment ]
  162. !
  163. visitIRNonLocalReturn: anIRNonLocalReturn
  164. anIRNonLocalReturn scope canFlattenNonLocalReturns ifTrue: [
  165. | localReturn |
  166. anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.
  167. localReturn := self flattenedReturn: anIRNonLocalReturn.
  168. anIRNonLocalReturn replaceWith: localReturn.
  169. ^ self visitIRReturn: localReturn ].
  170. ^ (self shouldInlineReturn: anIRNonLocalReturn)
  171. ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
  172. ifFalse: [ super visitIRNonLocalReturn: anIRNonLocalReturn ]
  173. !
  174. visitIRReturn: anIRReturn
  175. ^ (self shouldInlineReturn: anIRReturn)
  176. ifTrue: [ self returnInliner inlineReturn: anIRReturn ]
  177. ifFalse: [ super visitIRReturn: anIRReturn ]
  178. !
  179. visitIRSend: anIRSend
  180. ^ (self shouldInlineSend: anIRSend)
  181. ifTrue: [ self sendInliner inlineSend: anIRSend ]
  182. ifFalse: [ super visitIRSend: anIRSend ]
  183. ! !
  184. IRJSTranslator subclass: #IRInliningJSTranslator
  185. slots: {}
  186. package: 'Compiler-Inlining'!
  187. !IRInliningJSTranslator commentStamp!
  188. I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!
  189. !IRInliningJSTranslator methodsFor: 'visiting'!
  190. visitIRInlinedClosure: anIRInlinedClosure
  191. self stream nextPutVars: (anIRInlinedClosure tempDeclarations collect: [ :each |
  192. each name asVariableName ]).
  193. self visitAllChildren: anIRInlinedClosure
  194. !
  195. visitIRInlinedIfFalse: anIRInlinedIfFalse
  196. self stream nextPutIf: [
  197. self stream nextPutAll: '!!$core.assert('.
  198. self visit: anIRInlinedIfFalse dagChildren first.
  199. self stream nextPutAll: ')' ]
  200. then: [ self visit: anIRInlinedIfFalse dagChildren last ]
  201. !
  202. visitIRInlinedIfNilIfNotNil: anIRInlinedIfNilIfNotNil
  203. self stream
  204. nextPutIf: [
  205. | recvVarName |
  206. recvVarName := anIRInlinedIfNilIfNotNil receiverInternalVariableName.
  207. self stream nextPutAll: '(', recvVarName, ' = '.
  208. self visit: anIRInlinedIfNilIfNotNil dagChildren first.
  209. self stream nextPutAll: ') == null || ', recvVarName, '.a$nil' ]
  210. then: [ self visit: anIRInlinedIfNilIfNotNil dagChildren second ]
  211. else: [ self visit: anIRInlinedIfNilIfNotNil dagChildren third ]
  212. !
  213. visitIRInlinedIfTrue: anIRInlinedIfTrue
  214. self stream nextPutIf: [
  215. self stream nextPutAll: '$core.assert('.
  216. self visit: anIRInlinedIfTrue dagChildren first.
  217. self stream nextPutAll: ')' ]
  218. then: [ self visit: anIRInlinedIfTrue dagChildren last ]
  219. !
  220. visitIRInlinedIfTrueIfFalse: anIRInlinedIfTrueIfFalse
  221. self stream
  222. nextPutIf: [
  223. self stream nextPutAll: '$core.assert('.
  224. self visit: anIRInlinedIfTrueIfFalse dagChildren first.
  225. self stream nextPutAll: ')' ]
  226. then: [ self visit: anIRInlinedIfTrueIfFalse dagChildren second ]
  227. else: [ self visit: anIRInlinedIfTrueIfFalse dagChildren third ]
  228. ! !
  229. Object subclass: #IRSendInliner
  230. slots: {#send. #translator}
  231. package: 'Compiler-Inlining'!
  232. !IRSendInliner commentStamp!
  233. I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!
  234. !IRSendInliner methodsFor: 'accessing'!
  235. send
  236. ^ send
  237. !
  238. send: anIRSend
  239. send := anIRSend
  240. !
  241. translator
  242. ^ translator
  243. !
  244. translator: anASTTranslator
  245. translator := anASTTranslator
  246. ! !
  247. !IRSendInliner methodsFor: 'error handling'!
  248. inliningError: aString
  249. InliningError signal: aString
  250. ! !
  251. !IRSendInliner methodsFor: 'factory'!
  252. inlinedClosure
  253. ^ IRInlinedClosure new
  254. !
  255. inlinedSequence
  256. ^ IRInlinedSequence new
  257. ! !
  258. !IRSendInliner methodsFor: 'inlining'!
  259. ifFalse: anIRInstruction
  260. ^ self inlinedSend: IRInlinedIfFalse new withBlock: anIRInstruction
  261. !
  262. ifFalse: anIRInstruction ifTrue: anotherIRInstruction
  263. ^ self perform: #ifTrue:ifFalse: withArguments: { anotherIRInstruction. anIRInstruction }
  264. !
  265. ifNil: anIRInstruction
  266. ^ self
  267. inlinedSend: IRInlinedIfNilIfNotNil new
  268. withBlock: anIRInstruction
  269. withBlock: (IRClosure new
  270. scope: anIRInstruction scope copy;
  271. add: (IRBlockSequence new
  272. add: self send receiver;
  273. yourself);
  274. yourself)
  275. !
  276. ifNil: anIRInstruction ifNotNil: anotherIRInstruction
  277. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anIRInstruction withBlock: anotherIRInstruction
  278. !
  279. ifNotNil: anIRInstruction
  280. ^ self
  281. inlinedSend: IRInlinedIfNilIfNotNil new
  282. withBlock: (IRClosure new
  283. scope: anIRInstruction scope copy;
  284. add: (IRBlockSequence new
  285. add: self send receiver;
  286. yourself);
  287. yourself)
  288. withBlock: anIRInstruction
  289. !
  290. ifNotNil: anIRInstruction ifNil: anotherIRInstruction
  291. ^ self inlinedSend: IRInlinedIfNilIfNotNil new withBlock: anotherIRInstruction withBlock: anIRInstruction
  292. !
  293. ifTrue: anIRInstruction
  294. ^ self inlinedSend: IRInlinedIfTrue new withBlock: anIRInstruction
  295. !
  296. ifTrue: anIRInstruction ifFalse: anotherIRInstruction
  297. ^ self inlinedSend: IRInlinedIfTrueIfFalse new withBlock: anIRInstruction withBlock: anotherIRInstruction
  298. !
  299. inlineClosure: anIRClosure
  300. | inlinedClosure sequence statements |
  301. inlinedClosure := self inlinedClosure.
  302. inlinedClosure
  303. scope: anIRClosure scope;
  304. parent: anIRClosure parent.
  305. "Add the possible temp declarations"
  306. anIRClosure tempDeclarations do: [ :each |
  307. inlinedClosure add: each ].
  308. "Add a block sequence"
  309. sequence := self inlinedSequence.
  310. "Map the closure arguments to the receiver of the message send"
  311. anIRClosure arguments do: [ :each |
  312. inlinedClosure add: (IRTempDeclaration new name: each; yourself).
  313. sequence add: (IRAssignment new
  314. add: (IRVariable new variable: (AliasVar new scope: inlinedClosure scope; name: each; yourself));
  315. add: (IRVariable new variable: (AliasVar new scope: inlinedClosure scope; name: '$receiver'; yourself));
  316. yourself) ].
  317. "To ensure the correct order of the closure instructions: first the temps then the sequence"
  318. inlinedClosure add: sequence.
  319. "Get all the statements"
  320. statements := anIRClosure sequence dagChildren.
  321. statements ifNotEmpty: [
  322. statements allButLast do: [ :each | sequence add: each ].
  323. "Inlined closures change local returns into result value itself"
  324. sequence add: statements last asInlinedBlockResult ].
  325. ^ inlinedClosure
  326. !
  327. inlineSend: anIRSend
  328. self send: anIRSend.
  329. ^ self
  330. perform: self send selector
  331. withArguments: self send arguments
  332. !
  333. inlinedClosure: closure wrapFinalValueIn: aBlock
  334. | sequence statements final |
  335. sequence := closure sequence.
  336. statements := sequence dagChildren.
  337. sequence dagChildren ifEmpty: [ sequence add: (IRVariable new
  338. variable: (closure scope pseudoVars at: 'nil');
  339. yourself) ].
  340. final := statements last.
  341. final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ].
  342. ^ closure
  343. ! !
  344. !IRSendInliner methodsFor: 'private'!
  345. inlineSend: anIRSend andReplace: anIRInstruction
  346. anIRInstruction replaceWith: anIRSend.
  347. ^ self inlineSend: anIRSend
  348. !
  349. inlinedSend: inlinedSend withBlock: anIRInstruction
  350. | inlinedClosure |
  351. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  352. anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
  353. inlinedClosure := self translator visit: (self inlineClosure: anIRInstruction).
  354. inlinedSend
  355. add: self send receiver;
  356. add: inlinedClosure.
  357. self send replaceWith: inlinedSend.
  358. inlinedSend method internalVariables
  359. addAll: inlinedSend internalVariables.
  360. ^ inlinedSend
  361. !
  362. inlinedSend: inlinedSend withBlock: anIRInstruction withBlock: anotherIRInstruction
  363. | inlinedClosure1 inlinedClosure2 |
  364. anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  365. anotherIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
  366. inlinedClosure1 := self translator visit: (self inlineClosure: anIRInstruction).
  367. inlinedClosure2 := self translator visit: (self inlineClosure: anotherIRInstruction).
  368. inlinedSend
  369. add: self send receiver;
  370. add: inlinedClosure1;
  371. add: inlinedClosure2.
  372. self send replaceWith: inlinedSend.
  373. inlinedSend method internalVariables
  374. addAll: inlinedSend internalVariables.
  375. ^ inlinedSend
  376. ! !
  377. !IRSendInliner class methodsFor: 'accessing'!
  378. inlinedSelectors
  379. ^ #('ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:' 'ifNil:' 'ifNotNil:' 'ifNil:ifNotNil:' 'ifNotNil:ifNil:')
  380. !
  381. shouldInline: anIRSend
  382. (self inlinedSelectors includes: anIRSend selector) ifFalse: [ ^ false ].
  383. anIRSend receiver isSuper ifTrue: [ ^ false ].
  384. ^ anIRSend arguments allSatisfy: [ :each | each isClosure ]
  385. ! !
  386. IRSendInliner subclass: #IRAssignmentInliner
  387. slots: {#target}
  388. package: 'Compiler-Inlining'!
  389. !IRAssignmentInliner commentStamp!
  390. I inline message sends together with assignments by moving them around into the inline closure instructions.
  391. ##Example
  392. foo
  393. | a |
  394. a := true ifTrue: [ 1 ]
  395. Will produce:
  396. if($core.assert(true) {
  397. a = 1;
  398. };!
  399. !IRAssignmentInliner methodsFor: 'accessing'!
  400. target
  401. ^ target
  402. !
  403. target: anObject
  404. target := anObject
  405. ! !
  406. !IRAssignmentInliner methodsFor: 'inlining'!
  407. inlineAssignment: anIRAssignment
  408. self target: anIRAssignment left.
  409. ^ self inlineSend: anIRAssignment right andReplace: anIRAssignment
  410. !
  411. inlineClosure: anIRClosure
  412. ^ self
  413. inlinedClosure: (super inlineClosure: anIRClosure)
  414. wrapFinalValueIn: [ :final |
  415. IRAssignment new
  416. add: self target;
  417. add: final copy;
  418. yourself ]
  419. ! !
  420. IRSendInliner subclass: #IRNonLocalReturnInliner
  421. slots: {}
  422. package: 'Compiler-Inlining'!
  423. !IRNonLocalReturnInliner commentStamp!
  424. I inline message sends with inlined closure together with a return instruction.!
  425. !IRNonLocalReturnInliner methodsFor: 'inlining'!
  426. inlineClosure: anIRClosure
  427. ^ self
  428. inlinedClosure: (super inlineClosure: anIRClosure)
  429. wrapFinalValueIn: [ :final |
  430. IRNonLocalReturn new
  431. add: final copy;
  432. yourself ]
  433. !
  434. inlineReturn: anIRReturn
  435. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  436. ! !
  437. IRSendInliner subclass: #IRReturnInliner
  438. slots: {}
  439. package: 'Compiler-Inlining'!
  440. !IRReturnInliner commentStamp!
  441. I inline message sends with inlined closure together with a return instruction.!
  442. !IRReturnInliner methodsFor: 'inlining'!
  443. inlineClosure: anIRClosure
  444. ^ self
  445. inlinedClosure: (super inlineClosure: anIRClosure)
  446. wrapFinalValueIn: [ :final |
  447. IRReturn new
  448. add: final copy;
  449. yourself ]
  450. !
  451. inlineReturn: anIRReturn
  452. ^ self inlineSend: anIRReturn expression andReplace: anIRReturn
  453. ! !
  454. CodeGenerator subclass: #InliningCodeGenerator
  455. slots: {}
  456. package: 'Compiler-Inlining'!
  457. !InliningCodeGenerator commentStamp!
  458. I am a specialized code generator that uses inlining to produce more optimized JavaScript output!
  459. !InliningCodeGenerator methodsFor: 'compiling'!
  460. inliner
  461. ^ IRInliner new
  462. !
  463. irTranslatorClass
  464. ^ IRInliningJSTranslator
  465. !
  466. preInliner
  467. ^ ASTPreInliner new
  468. !
  469. transformersDictionary
  470. ^ transformersDictionary ifNil: [ transformersDictionary := super transformersDictionary
  471. at: '3000-inlinerTagging' put: self preInliner;
  472. at: '6000-inliner' put: self inliner;
  473. at: '8000-irToJs' put: self irTranslator;
  474. yourself ]
  475. ! !
  476. SemanticError subclass: #InliningError
  477. slots: {}
  478. package: 'Compiler-Inlining'!
  479. !InliningError commentStamp!
  480. Instances of InliningError are signaled when using an `InliningCodeGenerator`in a `Compiler`.!
  481. !IRBlockReturn methodsFor: '*Compiler-Inlining'!
  482. asInlinedBlockResult
  483. ^ self expression
  484. ! !
  485. !IRInstruction methodsFor: '*Compiler-Inlining'!
  486. asInlinedBlockResult
  487. ^ self
  488. ! !