Compiler-Inlining.st 16 KB

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