Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stack coallescing #150

Merged
merged 35 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1c99572
Draft of stack effect coallescing
guillep Apr 18, 2024
ed35e70
Merge fda1378f9cf5fa96c89a2468b1924aa3f87b598a
guillep Apr 23, 2024
9d22dcb
Fix primitive tests
guillep Apr 23, 2024
f812b73
Rename and add tests
guillep Apr 23, 2024
41e0961
Add tests on stack dependencies
guillep Apr 23, 2024
932b1b9
Merge DRPop and DRPopMany
guillep Apr 23, 2024
647cdbc
Fix bytecode tests
guillep Apr 23, 2024
9e2d78b
Division operands should be mutable
guillep Apr 24, 2024
76e40c8
Fix dead block elimination tests
guillep Apr 24, 2024
8895f87
Fix linearization tests merge state tests
guillep Apr 24, 2024
53d65af
Removal of exitBasicBlock step 1
guillep Apr 24, 2024
a8e94de
Fix a dead path test depending on block ids
guillep Apr 24, 2024
e97efa7
Fix tail duplication tests
guillep Apr 24, 2024
1e842a5
Fix dead path elimination tests
guillep Apr 24, 2024
817ce42
fix loop unrolling tests
guillep Apr 24, 2024
074a9a6
Fix loop invariant code motion tests
guillep Apr 24, 2024
f651391
Remove reliance on single exit block
guillep Apr 25, 2024
a1d7450
Fix tail duplication in presence of staged registers
guillep Apr 25, 2024
19a7422
Do not tail duplicate stack effect instructions...
guillep Apr 26, 2024
793ec52
Fix stack coalescing test
guillep Apr 26, 2024
efae63e
Rename stack dependency accessor
guillep Apr 26, 2024
381feb1
Properly sorting coalesced stack instructions using a topo sort betwe…
guillep May 6, 2024
4f6a606
Extend register allocation to take into account staged stack.
guillep May 6, 2024
4726372
Extract push and pop instructions from marshalling methods
guillep May 6, 2024
c7da575
Recover register allocation of division
guillep May 6, 2024
96379dc
Add support for unspilling slots
guillep May 6, 2024
0787ff6
Rework data and stack dependencies for stack operation coallescing
guillep Jun 20, 2024
45c36d2
Merge 5589445728e7d8b40b218cfa9c61320f263ad4f7
guillep Jun 20, 2024
f8de5bd
Finish merge with Nahuel's change
guillep Jun 24, 2024
35bf164
Add control on stack flush by moving flush insertion to instruction i…
guillep Jun 27, 2024
edc72b6
Remove halt
guillep Jun 27, 2024
ee005dd
Fix and simplify tests, add comments
guillep Jul 4, 2024
8276df4
Fix testBytecodeExtendedSuperSend
guillep Jul 4, 2024
eb19318
Fix tests
guillep Jul 4, 2024
7bacda5
Fix testCoalesceOneUnspilledAndOneSpilledPopInsertsUnspill
guillep Jul 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions Druid-Tests/DRBytecodeCompilationTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,44 @@ DRBytecodeCompilationTest >> compileBytecode: bytecode1 selector: selector1 andB
]

{ #category : #tests }
DRBytecodeCompilationTest >> compileBytecode: bytecode selector: aSelector thenDo: aBlock [
DRBytecodeCompilationTest >> compileBytecode: bytecode selector: aSelector options: options thenDo: aBlock [

| generatorSelector compiler |
generatorSelector := (#gen, '_' , aSelector) asSymbol.

"First generate druid code"
self interpreter currentBytecode: bytecode.
compiler := DRBytecodeCompilerCompiler new
bytecodes: { bytecode -> aSelector };
interpreter: self interpreter;
configureForCompilerClass: DruidTestRTLCompiler.
bytecodes: { (bytecode -> aSelector) };
interpreter: self interpreter;
configureForCompilerClass: DruidTestRTLCompiler;
addCompilerOptions: options.

compiler compile.
"Then generate the machine code for that method"
cogInitialAddress := self compile: [
cogit needsFrame: true.
cogit byte0: bytecode.
cogit methodOrBlockNumArgs: 3. "Hack"
cogit methodOrBlockNumTemps: 2. "Hack"
"Initialize the simulated stack"
cogit initSimStackForFramefulMethod: 2.
cogit zeroOpcodeIndexForNewOpcodes.

aBlock value: [ cogit perform: generatorSelector ].
] bytecodes: 100
cogInitialAddress := self
compile: [
cogit needsFrame: true.
cogit byte0: bytecode.
cogit methodOrBlockNumArgs: 3. "Hack"
cogit methodOrBlockNumTemps: 2. "Hack"
"Initialize the simulated stack"
cogit initSimStackForFramefulMethod: 2.
cogit zeroOpcodeIndexForNewOpcodes.

aBlock value: [
cogit perform: generatorSelector ] ]
bytecodes: 100
]

{ #category : #tests }
DRBytecodeCompilationTest >> compileBytecode: bytecode selector: aSelector thenDo: aBlock [

^ self
compileBytecode: bytecode
selector: aSelector
options: #( )
thenDo: aBlock
]

{ #category : #tests }
Expand Down
287 changes: 285 additions & 2 deletions Druid-Tests/DRBytecodeScenarioCompilationTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ DRBytecodeScenarioCompilationTest >> testBytecodePrimIdenticalAndJumpFalse [

"Force failing test if tries to compile the trampoline call:
Should be removed due to super-instruction."
cogit ceSendMustBeBooleanTrampoline: nil.
cogit ceSendMustBeBooleanTrampoline: fakeTrampoline.

self
compileBytecode: 118
Expand Down Expand Up @@ -60,6 +60,188 @@ DRBytecodeScenarioCompilationTest >> testBytecodePrimIdenticalAndJumpFalse [
self assert: machineSimulator receiverRegisterValue equals: 0
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceOneSpilledAndOneUnspilledPopInsertsUnspill [

| cfg basicBlock1 basicBlock2 mergeBlock popToUnspill popStackDepth |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
popToUnspill := block pop
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

popStackDepth := popToUnspill stackDepth.

DRCogitStackCoalescing applyTo: cfg.

self assert: basicBlock2 firstInstruction isUnspill.
self assert: basicBlock2 firstInstruction operand1 value equals: popStackDepth negated - 1
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceOneSpilledAndOneUnspilledPopsIntoSingleUnpilledPop [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
block pop
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

self assert: mergeBlock firstInstruction isPop.
self assert: mergeBlock firstInstruction isUnspilled
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceOneUnspilledAndOneSpilledPopInsertsUnspill [

| cfg basicBlock1 basicBlock2 mergeBlock popToUnspill popStackDepth |
cfg := DRControlFlowGraph new.
basicBlock2 := cfg newBasicBlockWith: [ :block |
popToUnspill := block pop
].
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

popStackDepth := popToUnspill stackDepth.

DRCogitStackCoalescing applyTo: cfg.

self assert: basicBlock2 firstInstruction isUnspill.
self assert: basicBlock2 firstInstruction operand1 value equals: popStackDepth negated - 1
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceOneUnspilledAndOnespilledPopsIntoSingleUnpilledPop [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock2 := cfg newBasicBlockWith: [ :block |
block pop
].
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

self assert: mergeBlock firstInstruction isPop.
self assert: mergeBlock firstInstruction isUnspilled
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceTwoSpilledPopsDoesNotReplaceMovedInstructions [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block pop
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
block pop
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

"Blocks only contain the jumps"
self assert: basicBlock1 instructions size equals: 1.
self assert: basicBlock2 instructions size equals: 1.
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceTwoSpilledPopsIntoSingleSpilledPop [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block pop
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
block pop
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

self assert: mergeBlock firstInstruction isPop.
self deny: mergeBlock firstInstruction isUnspilled
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceTwoUnspilledPopsDoesNotReplaceMovedInstructions [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

"Blocks only contain the jumps"
self assert: basicBlock1 instructions size equals: 1.
self assert: basicBlock2 instructions size equals: 1.
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testCoalesceTwoUnspilledPopsIntoSingleUnspilledPop [

| cfg basicBlock1 basicBlock2 mergeBlock |
cfg := DRControlFlowGraph new.
basicBlock1 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
basicBlock2 := cfg newBasicBlockWith: [ :block |
block popUnspilled
].
cfg initialBasicBlock jumpIf: true to: basicBlock1 ifFalseTo: basicBlock2.
mergeBlock := cfg newBasicBlock.
basicBlock1 jumpTo: mergeBlock.
basicBlock2 jumpTo: mergeBlock.

DRCogitStackCoalescing applyTo: cfg.

self assert: mergeBlock firstInstruction isPop.
self assert: mergeBlock firstInstruction isUnspilled
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testFlushStackOnBranch1 [

Expand Down Expand Up @@ -129,6 +311,57 @@ DRBytecodeScenarioCompilationTest >> testPopAfterFlushStack [
self assert: cogit ssTop constant equals: 237
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testPopsAreCoallesced [

self
compileBytecode: 0
selector: #bytecodePopOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: 17.

self assert: machineSimulator receiverRegisterValue equals: 17
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testPushAreCoallescedBranchOne [

self
compileBytecode: 0
selector: #bytecodePushOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: 17.

self assert: machineSimulator receiverRegisterValue equals: 1
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testPushAreCoallescedBranchTwo [

self
compileBytecode: 0
selector: #bytecodePushOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: -17.

self assert: machineSimulator receiverRegisterValue equals: 2
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testPushTrueAndJumpFalse [

Expand Down Expand Up @@ -255,9 +488,59 @@ DRBytecodeScenarioCompilationTest >> testSuperInstructionCompilation [

returns := compiledMethod ast allChildren select: [ :n | n isReturn ].
self assert: returns size equals: 2.
self assert: returns first value selector equals: #gen_pushConstantTrueBytecode_returnTopFromMethod
self assert: returns first value selector equals: #gen_pushConstantTrueBytecode_returnTopFromMethod_1
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testTwoPopsAreCoallesced [

self
compileBytecode: 0
selector: #bytecodeTwoPopOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
cogit ssPushRegister: Arg0Reg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: 17 withArguments: { 42 }.

self assert: machineSimulator receiverRegisterValue equals: 59
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testTwoPushesAreCoallescedBranchOne [

self
compileBytecode: 0
selector: #bytecodeTwoPushOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: 17.

self assert: machineSimulator receiverRegisterValue equals: 3
]

{ #category : #tests }
DRBytecodeScenarioCompilationTest >> testTwoPushesAreCoallescedBranchTwo [

self
compileBytecode: 0
selector: #bytecodeTwoPushOnTwoBranches
thenDo: [ :generator | "Push the receiver"
cogit ssPushRegister: ReceiverResultReg.
"Then execute the druid's compiled code"
generator value.
cogit genReturnTopFromMethod ].

self executePrimitiveWithReceiver: -17.

self assert: machineSimulator receiverRegisterValue equals: 7
]

{ #category : #tests }
Expand Down
5 changes: 5 additions & 0 deletions Druid-Tests/DRCogitStackCoalescingTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Class {
#name : #DRCogitStackCoalescingTest,
#superclass : #DROptimisationTest,
#category : #'Druid-Tests'
}
2 changes: 1 addition & 1 deletion Druid-Tests/DRCogitStackToRegisterGeneratorTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ DRCogitStackToRegisterGeneratorTest >> testGenerateWhile [
DRStager new applyTo: cfg.
generator generateCodeForCFG: cfg.

self assert: self statements sixth selector equals: #whileTrue:
self assert: (self statements at: 10) selector equals: #whileTrue:
]
Loading
Loading