| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 | Smalltalk current createPackage: 'Compiler-Inlining'!IRAssignment subclass: #IRInlinedAssignment	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedAssignment commentStamp!I represent an inlined assignment instruction.!!IRInlinedAssignment methodsFor: 'testing'!isInlined	^ true! !!IRInlinedAssignment methodsFor: 'visiting'!accept: aVisitor	^ aVisitor visitIRInlinedAssignment: self! !IRClosure subclass: #IRInlinedClosure	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedClosure commentStamp!I represent an inlined closure instruction.!!IRInlinedClosure methodsFor: 'testing'!isInlined	^ true! !!IRInlinedClosure methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedClosure: self! !IRReturn subclass: #IRInlinedReturn	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedReturn commentStamp!I represent an inlined local return instruction.!!IRInlinedReturn methodsFor: 'testing'!isInlined	^ true! !!IRInlinedReturn methodsFor: 'visiting'!accept: aVisitor	^ aVisitor visitIRInlinedReturn: self! !IRInlinedReturn subclass: #IRInlinedNonLocalReturn	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedNonLocalReturn commentStamp!I represent an inlined non local return instruction.!!IRInlinedNonLocalReturn methodsFor: 'testing'!isInlined	^ true! !!IRInlinedNonLocalReturn methodsFor: 'visiting'!accept: aVisitor	^ aVisitor visitIRInlinedNonLocalReturn: self! !IRSend subclass: #IRInlinedSend	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedSend commentStamp!I am the abstract super class of inlined message send instructions.!!IRInlinedSend methodsFor: 'testing'!isInlined	^ true! !!IRInlinedSend methodsFor: 'visiting'!accept: aVisitor	aVisitor visitInlinedSend: self! !IRInlinedSend subclass: #IRInlinedIfFalse	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedIfFalse methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedIfFalse: self! !IRInlinedSend subclass: #IRInlinedIfNilIfNotNil	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedIfNilIfNotNil methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedIfNilIfNotNil: self! !IRInlinedSend subclass: #IRInlinedIfTrue	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedIfTrue methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedIfTrue: self! !IRInlinedSend subclass: #IRInlinedIfTrueIfFalse	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedIfTrueIfFalse methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedIfTrueIfFalse: self! !IRBlockSequence subclass: #IRInlinedSequence	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInlinedSequence commentStamp!I represent a (block) sequence inside an inlined closure instruction (instance of `IRInlinedClosure`).!!IRInlinedSequence methodsFor: 'testing'!isInlined	^ true! !!IRInlinedSequence methodsFor: 'visiting'!accept: aVisitor	aVisitor visitIRInlinedSequence: self! !IRVisitor subclass: #IRInliner	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInliner commentStamp!I visit an IR tree, inlining message sends and block closures.Message selectors that can be inlined are answered by `IRSendInliner >> #inlinedSelectors`!!IRInliner methodsFor: 'factory'!assignmentInliner	^ IRAssignmentInliner new		translator: self;		yourself!nonLocalReturnInliner	^ IRNonLocalReturnInliner new		translator: self;		yourself!returnInliner	^ IRReturnInliner new		translator: self;		yourself!sendInliner	^ IRSendInliner new		translator: self;		yourself! !!IRInliner methodsFor: 'testing'!shouldInlineAssignment: anIRAssignment	^ anIRAssignment isInlined not and: [		anIRAssignment instructions last isSend and: [			self shouldInlineSend: (anIRAssignment instructions last) ]]!shouldInlineReturn: anIRReturn	^ anIRReturn isInlined not and: [		anIRReturn instructions first isSend and: [			self shouldInlineSend: (anIRReturn instructions first) ]]!shouldInlineSend: anIRSend	^ anIRSend isInlined not and: [		IRSendInliner shouldInline: anIRSend ]! !!IRInliner methodsFor: 'visiting'!transformNonLocalReturn: anIRNonLocalReturn	"Replace a non local return into a local return"	| localReturn |	anIRNonLocalReturn scope canInlineNonLocalReturns ifTrue: [		anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.		localReturn := IRReturn new			scope: anIRNonLocalReturn scope;			yourself.		anIRNonLocalReturn instructions do: [ :each |			localReturn add: each ].		anIRNonLocalReturn replaceWith: localReturn.		^ localReturn ].	^ super visitIRNonLocalReturn: anIRNonLocalReturn!visitIRAssignment: anIRAssignment	^ (self shouldInlineAssignment: anIRAssignment)		ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]		ifFalse: [ super visitIRAssignment: anIRAssignment ]!visitIRNonLocalReturn: anIRNonLocalReturn	^ (self shouldInlineReturn: anIRNonLocalReturn)		ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]		ifFalse: [ self transformNonLocalReturn: anIRNonLocalReturn ]!visitIRReturn: anIRReturn	^ (self shouldInlineReturn: anIRReturn)		ifTrue: [ self returnInliner inlineReturn: anIRReturn ]		ifFalse: [ super visitIRReturn: anIRReturn ]!visitIRSend: anIRSend	^ (self shouldInlineSend: anIRSend)		ifTrue: [ self sendInliner inlineSend: anIRSend ]		ifFalse: [ super visitIRSend: anIRSend ]! !IRJSTranslator subclass: #IRInliningJSTranslator	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRInliningJSTranslator commentStamp!I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!!IRInliningJSTranslator methodsFor: 'visiting'!visitIRInlinedAssignment: anIRInlinedAssignment	self visit: anIRInlinedAssignment instructions last!visitIRInlinedClosure: anIRInlinedClosure	anIRInlinedClosure instructions do: [ :each |		self visit: each ]!visitIRInlinedIfFalse: anIRInlinedIfFalse	self stream nextPutIf: [		self stream nextPutAll: '!! smalltalk.assert('.		self visit: anIRInlinedIfFalse instructions first.		self stream nextPutAll: ')' ]		with: [ self visit: anIRInlinedIfFalse instructions last ]!visitIRInlinedIfNil: anIRInlinedIfNil	self stream nextPutIf: [		self stream nextPutAll: '($receiver = '.		self visit: anIRInlinedIfNil instructions first.		self stream nextPutAll: ') == nil || $receiver == undefined' ]		with: [ self visit: anIRInlinedIfNil instructions last ]!visitIRInlinedIfNilIfNotNil: anIRInlinedIfNilIfNotNil	self stream		nextPutIfElse: [			self stream nextPutAll: '($receiver = '.			self visit: anIRInlinedIfNilIfNotNil instructions first.			self stream nextPutAll: ') == nil || $receiver == undefined' ]		with: [ self visit: anIRInlinedIfNilIfNotNil instructions second ]		with: [ self visit: anIRInlinedIfNilIfNotNil instructions third ]!visitIRInlinedIfTrue: anIRInlinedIfTrue	self stream nextPutIf: [		self stream nextPutAll: 'smalltalk.assert('.		self visit: anIRInlinedIfTrue instructions first.		self stream nextPutAll: ')' ]		with: [ self visit: anIRInlinedIfTrue instructions last ]!visitIRInlinedIfTrueIfFalse: anIRInlinedIfTrueIfFalse	self stream		nextPutIfElse: [			self stream nextPutAll: 'smalltalk.assert('.			self visit: anIRInlinedIfTrueIfFalse instructions first.			self stream nextPutAll: ')' ]		with: [ self visit: anIRInlinedIfTrueIfFalse instructions second ]		with: [ self visit: anIRInlinedIfTrueIfFalse instructions third ]!visitIRInlinedNonLocalReturn: anIRInlinedReturn	self stream nextPutStatementWith: [		self visit: anIRInlinedReturn instructions last ].	self stream nextPutNonLocalReturnWith: [ ]!visitIRInlinedReturn: anIRInlinedReturn	self visit: anIRInlinedReturn instructions last!visitIRInlinedSequence: anIRInlinedSequence	anIRInlinedSequence instructions do: [ :each |		self stream nextPutStatementWith: [ self visit: each ]]! !Object subclass: #IRSendInliner	instanceVariableNames: 'send translator'	package: 'Compiler-Inlining'!!IRSendInliner commentStamp!I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!!IRSendInliner methodsFor: 'accessing'!send	^ send!send: anIRSend	send := anIRSend!translator	^ translator!translator: anASTTranslator	translator := anASTTranslator! !!IRSendInliner methodsFor: 'error handling'!inliningError: aString	InliningError signal: aString! !!IRSendInliner methodsFor: 'factory'!inlinedClosure	^ IRInlinedClosure new!inlinedSequence	^ IRInlinedSequence new! !!IRSendInliner methodsFor: 'inlining'!ifFalse: anIRInstruction	^ self inlinedSend: IRInlinedIfFalse new with: anIRInstruction!ifFalse: anIRInstruction ifTrue: anotherIRInstruction	^ self perform: #ifTrue:ifFalse: withArguments: { anotherIRInstruction. anIRInstruction }!ifNil: anIRInstruction	^ self		inlinedSend: IRInlinedIfNilIfNotNil new		with: anIRInstruction		with: (IRClosure new			scope: anIRInstruction scope copy;			add: (IRBlockSequence new				add: self send instructions first;				yourself);			yourself)!ifNil: anIRInstruction ifNotNil: anotherIRInstruction	^ self inlinedSend: IRInlinedIfNilIfNotNil new with: anIRInstruction with: anotherIRInstruction!ifNotNil: anIRInstruction	^ self		inlinedSend: IRInlinedIfNilIfNotNil new		with: (IRClosure new			scope: anIRInstruction scope copy;			add: (IRBlockSequence new				add: self send instructions first;				yourself);			yourself)		with: anIRInstruction!ifNotNil: anIRInstruction ifNil: anotherIRInstruction	^ self inlinedSend: IRInlinedIfNilIfNotNil new with: anotherIRInstruction with: anIRInstruction!ifTrue: anIRInstruction	^ self inlinedSend: IRInlinedIfTrue new with: anIRInstruction!ifTrue: anIRInstruction ifFalse: anotherIRInstruction	^ self inlinedSend: IRInlinedIfTrueIfFalse new with: anIRInstruction with: anotherIRInstruction!inlineClosure: anIRClosure	| inlinedClosure sequence statements |	inlinedClosure := self inlinedClosure.	inlinedClosure scope: anIRClosure scope.	"Add the possible temp declarations"	anIRClosure instructions do: [ :each |		each isSequence ifFalse: [			inlinedClosure add: each ]].	"Add a block sequence"	sequence := self inlinedSequence.	inlinedClosure add: sequence.	"Get all the statements"	statements := anIRClosure instructions last instructions.		statements ifNotEmpty: [		statements allButLast do: [ :each | sequence add: each ].		"Inlined closures don't have implicit local returns"		(statements last isReturn and: [ statements last isBlockReturn ])			ifTrue: [ sequence add: statements last instructions first ]			ifFalse: [ sequence add: statements last ] ].	^ inlinedClosure!inlineSend: anIRSend	self send: anIRSend.	^ self		perform: self send selector		withArguments: self send instructions allButFirst!inlinedSend: inlinedSend with: anIRInstruction	| inlinedClosure |	anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].	anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].	inlinedClosure := self translator visit: (self inlineClosure: anIRInstruction).	inlinedSend		add: self send instructions first;		add: inlinedClosure.	self send replaceWith: inlinedSend.	^ inlinedSend!inlinedSend: inlinedSend with: anIRInstruction with: anotherIRInstruction	| inlinedClosure1 inlinedClosure2 |	anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].	anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].	anotherIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].	anotherIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].	inlinedClosure1 := self translator visit: (self inlineClosure: anIRInstruction).	inlinedClosure2 := self translator visit: (self inlineClosure: anotherIRInstruction).	inlinedSend		add: self send instructions first;		add: inlinedClosure1;		add: inlinedClosure2.	self send replaceWith: inlinedSend.	^ inlinedSend! !!IRSendInliner class methodsFor: 'accessing'!inlinedSelectors	^ #('ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:' 'ifNil:' 'ifNotNil:' 'ifNil:ifNotNil:' 'ifNotNil:ifNil')!shouldInline: anIRInstruction	(self inlinedSelectors includes: anIRInstruction selector) ifFalse: [ ^ false ].	anIRInstruction instructions allButFirst do: [ :each |		each isClosure ifFalse: [ ^ false ]].	^ true! !IRSendInliner subclass: #IRAssignmentInliner	instanceVariableNames: 'assignment'	package: 'Compiler-Inlining'!!IRAssignmentInliner commentStamp!I inline message sends together with assignments by moving them around into the inline closure instructions.##Example	foo		| a |		a := true ifTrue: [ 1 ]Will produce:	if(smalltalk.assert(true) {		a = 1;	};!!IRAssignmentInliner methodsFor: 'accessing'!assignment	^ assignment!assignment: aNode	assignment := aNode! !!IRAssignmentInliner methodsFor: 'inlining'!inlineAssignment: anIRAssignment	| inlinedAssignment |	self assignment: anIRAssignment.	inlinedAssignment := IRInlinedAssignment new.	anIRAssignment instructions do: [ :each |		inlinedAssignment add: each ].	anIRAssignment replaceWith: inlinedAssignment.	self inlineSend: inlinedAssignment instructions last.	^ inlinedAssignment!inlineClosure: anIRClosure	| inlinedClosure statements |	inlinedClosure := super inlineClosure: anIRClosure.	statements := inlinedClosure instructions last instructions.		statements ifNotEmpty: [		statements last canBeAssigned ifTrue: [			statements last replaceWith: (IRAssignment new				add: self assignment instructions first;				add: statements last copy;				yourself) ] ].	^ inlinedClosure! !IRSendInliner subclass: #IRNonLocalReturnInliner	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRNonLocalReturnInliner methodsFor: 'factory'!inlinedReturn	^ IRInlinedNonLocalReturn new! !!IRNonLocalReturnInliner methodsFor: 'inlining'!inlineClosure: anIRClosure	"| inlinedClosure statements |	inlinedClosure := super inlineClosure: anIRClosure.	statements := inlinedClosure instructions last instructions.		statements ifNotEmpty: [		statements last replaceWith: (IRNonLocalReturn new			add: statements last copy;			yourself) ].	^ inlinedClosure"	^ super inlineCLosure: anIRClosure! !IRSendInliner subclass: #IRReturnInliner	instanceVariableNames: ''	package: 'Compiler-Inlining'!!IRReturnInliner commentStamp!I inline message sends with inlined closure together with a return instruction.!!IRReturnInliner methodsFor: 'factory'!inlinedReturn	^ IRInlinedReturn new! !!IRReturnInliner methodsFor: 'inlining'!inlineClosure: anIRClosure	| closure statements |	closure := super inlineClosure: anIRClosure.	statements := closure instructions last instructions.		statements ifNotEmpty: [		statements last isReturn			ifFalse: [ statements last replaceWith: (IRReturn new				add: statements last copy;				yourself)] ].	^ closure!inlineReturn: anIRReturn	| return |	return := self inlinedReturn.	anIRReturn instructions do: [ :each |		return add: each ].	anIRReturn replaceWith: return.	self inlineSend: return instructions last.	^ return! !CodeGenerator subclass: #InliningCodeGenerator	instanceVariableNames: ''	package: 'Compiler-Inlining'!!InliningCodeGenerator commentStamp!I am a specialized code generator that uses inlining to produce more optimized JavaScript output!!InliningCodeGenerator methodsFor: 'compiling'!compileNode: aNode	| ir stream |	self semanticAnalyzer visit: aNode.	ir := self translator visit: aNode.	self inliner visit: ir.	^ self irTranslator		visit: ir;		contents!inliner	^ IRInliner new!irTranslator	^ IRInliningJSTranslator new! !
 |