Skip to content

Commit

Permalink
Merge pull request #150 from guillep/stackcoallescing
Browse files Browse the repository at this point in the history
Add stack coallescing
  • Loading branch information
guillep authored Jul 5, 2024
2 parents 5589445 + 7bacda5 commit 742b6c9
Show file tree
Hide file tree
Showing 90 changed files with 3,174 additions and 1,610 deletions.
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

0 comments on commit 742b6c9

Please sign in to comment.