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

Improve register allocation (part 1) #40

Merged
merged 28 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1426248
RISC-V: avoid storing / reloading link register for leaf functions
janvrany Oct 20, 2023
8311627
RISC-V: prefer volatile registers for leaf methods
janvrany Oct 20, 2023
73c8081
Consider `TRLeave`s return value register as "read" virtual register
janvrany Oct 27, 2023
e0e102e
Rename `#virtualRegistersModifiedBy...` to `#virtualRegistersAssigned…
janvrany Oct 30, 2023
f4e62df
Introduce new default register allocator - reverse linear scan allocator
janvrany Oct 30, 2023
9b7c248
Remove support for (unused) per-virtual-register constraints
janvrany Oct 30, 2023
ca3a88b
Rename `TRLinearScanRegisterAllocator >> #expireOldRanges:` to `#expi…
janvrany Nov 1, 2023
60e547d
Introduce `TRRegisterDependencies` to express register constraints...
janvrany Nov 1, 2023
f5c8f2a
Add (virtual) register to `TRRegisterLiveRange`
janvrany Nov 2, 2023
8061a46
Delegate `?return` evaluation to linkage
janvrany Nov 6, 2023
2796102
Add `TRCodeGenerator >> #cursor:` (and `#cursor`)
janvrany Nov 6, 2023
2e3a542
Add `TRCodeGenerator >> #registerCopyFrom:to:`
janvrany Nov 6, 2023
81f89e4
RISC-V: Implement `TRCodeGenerator >> #registerCopyFrom:to:`
janvrany Nov 6, 2023
ae54ae1
RISC-V: use register dependencies to express register constraints
janvrany Nov 6, 2023
5de102e
POWER: Implement `TRCodeGenerator >> #registerCopyFrom:to:`
janvrany Nov 6, 2023
7c918d0
POWER: use register dependencies to express register constraints
janvrany Nov 6, 2023
d43edf9
Treat pre-dependent virtual registers of an instruction as "read"
janvrany Nov 7, 2023
d5d185d
Treat post-dependent virtual registers of an instruction as "assigned"
janvrany Nov 7, 2023
1de66be
Let the register allocator to handle (virtual) register dependencies
janvrany Nov 7, 2023
1e78238
Simplify linear scan allocators
janvrany Nov 7, 2023
35c00a4
Refactor reverse linear scan allocator (part i)
janvrany Nov 10, 2023
62335c2
Refactor reverse linear scan allocator (part ii)
janvrany Nov 9, 2023
73e3c0b
Keep reference to code generator in `TRVirtualRegister`
janvrany Nov 9, 2023
4481da3
Add new compilation option: `stressRA`
janvrany Nov 10, 2023
1d75e60
RISC-V: always prefer volatile registers when stress-testing the regi…
janvrany Nov 10, 2023
a8ebd65
When fixing up registers, build fixup map manually
janvrany Nov 17, 2023
192d92d
RLSRA: spill / reload trashed live registers
janvrany Nov 20, 2023
9825aad
Remove (old) linear scan and (even older) naive constraint solving re…
janvrany Nov 27, 2023
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: 19 additions & 24 deletions src/Tinyrossa-POWER/TRPPC64CodeGenerator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -50,35 +50,30 @@ TRPPC64CodeGenerator >> loadConstant32: value into: reg [
].
]

{ #category : #registers }
TRPPC64CodeGenerator >> virtualRegistersModifiedBy: instruction do: block [
"Evaluate block for each virtual register modified by
given instruction."

instruction isPseudoInstruction "such as label" ifTrue: [
^ self
].

instruction externalBindings keysAndValuesDo: [ :name :value |
name = 'rt' ifTrue: [
(value isBitVector and: [ value isSymbolic and: [ value isConstant ] ]) ifTrue: [
| vReg |
{ #category : #utilities }
TRPPC64CodeGenerator >> registerCopyFrom: srcReg to: dstReg [
generate mr: dstReg, srcReg
]

vReg := virtualRegisters at: value sym ifAbsent: nil.
block value: vReg.
{ #category : #'registers-private' }
TRPPC64CodeGenerator >> virtualRegistersAssignedByProcessorInstruction: instruction do: block [
self assert: instruction isProcessorInstruction.
instruction externalBindings
keysAndValuesDo: [:name :value |
name = 'rt' ifTrue: [
(value isBitVector and: [ value isSymbolic and: [ value isConstant ] ]) ifTrue: [
| vReg |

vReg := virtualRegisters at: value sym ifAbsent: nil.
block value: vReg.
].
].
].
].
]

{ #category : #registers }
TRPPC64CodeGenerator >> virtualRegistersReadBy: instruction do: block [
"Evaluate block for each virtual register read by
given instruction."

instruction isPseudoInstruction "such as label" ifTrue: [
^ self
].
{ #category : #'registers-private' }
TRPPC64CodeGenerator >> virtualRegistersReadByProcessorInstruction: instruction do: block [
self assert: instruction isProcessorInstruction.

instruction externalBindings keysAndValuesDo: [ :name :value |
(#('ra' 'rb' 'rc' 'rs') includes: name) ifTrue: [
Expand Down
21 changes: 17 additions & 4 deletions src/Tinyrossa-POWER/TRPPC64PSABILinkage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ TRPPC64PSABILinkage >> generateCall: node [

{ #category : #'code generation' }
TRPPC64PSABILinkage >> generateEpilogue: valReg [
"Move value to ABI return register..."
generate
mr: gr4, valReg.

"Restore preserved registers"
self preservedRegisters do: [:reg |
(self allocatedRegisters includes: reg) ifTrue: [
Expand Down Expand Up @@ -120,6 +116,23 @@ TRPPC64PSABILinkage >> generatePrologue [
].
]

{ #category : #'code generation' }
TRPPC64PSABILinkage >> generateReturn: node [
| srcReg leave deps |

self assert: codegen compilation functionType == node child1 type.

srcReg := codegen evaluator evaluate: node child1.

deps := TRRegisterDependencies new.
deps pre addDependency: srcReg on: gr4.

leave := generate leave: srcReg.
leave dependencies: deps.

^ nil
]

{ #category : #mapping }
TRPPC64PSABILinkage >> mapParameters: parameterTypes [
"Map parameters to argument registers.
Expand Down
66 changes: 44 additions & 22 deletions src/Tinyrossa-RISCV/TRRV64GCodeGenerator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Class {
#name : #TRRV64GCodeGenerator,
#superclass : #TRCodeGenerator,
#pools : [
'TRDataTypes',
'TRIntLimits',
'TRRV64GISALimits',
'TRRV64GRegisters'
Expand Down Expand Up @@ -114,35 +115,56 @@ TRRV64GCodeGenerator >> loadConstant64: value into: reg [
].
]

{ #category : #registers }
TRRV64GCodeGenerator >> virtualRegistersModifiedBy: instruction do: block [
"Evaluate block for each virtual register modified by
given instruction."
{ #category : #utilities }
TRRV64GCodeGenerator >> registerCopyFrom: srcReg to: dstReg [
"Copy value of source register into destination register"

instruction isPseudoInstruction "such as label" ifTrue: [
^ self
].
generate addi: dstReg, srcReg, 0
]

instruction externalBindings keysAndValuesDo: [ :name :value |
name = 'rd' ifTrue: [
(value isBitVector and: [ value isSymbolic and: [ value isConstant ] ]) ifTrue: [
| vReg |
{ #category : #utilities }
TRRV64GCodeGenerator >> registerLoad: reg from: sym [
| offset |

vReg := virtualRegisters at: value sym ifAbsent: nil.
block value: vReg.
self assert: reg isTRRegister.
self assert: sym isTRAutomaticSymbol.
self assert: sym type == Address.

offset := AcDSLSymbol value: sym name.
generate ld: reg, (sp + offset).
]

{ #category : #utilities }
TRRV64GCodeGenerator >> registerStore: reg to: sym [
| offset |

self assert: reg isTRRegister.
self assert: sym isTRAutomaticSymbol.
self assert: sym type == Address.

offset := AcDSLSymbol value: sym name.
generate sd: reg, (sp + offset).
]

{ #category : #'registers-private' }
TRRV64GCodeGenerator >> virtualRegistersAssignedByProcessorInstruction: instruction do: block [
self assert: instruction isProcessorInstruction.
instruction externalBindings
keysAndValuesDo: [:name :value |
name = 'rd' ifTrue: [
(value isBitVector and: [ value isSymbolic and: [ value isConstant ] ]) ifTrue: [
| vReg |

vReg := virtualRegisters at: value sym ifAbsent: nil.
block value: vReg.
].
].
].
].
]

{ #category : #registers }
TRRV64GCodeGenerator >> virtualRegistersReadBy: instruction do: block [
"Evaluate block for each virtual register read by
given instruction."

instruction isPseudoInstruction "such as label" ifTrue: [
^ self
].
{ #category : #'registers-private' }
TRRV64GCodeGenerator >> virtualRegistersReadByProcessorInstruction: instruction do: block [
self assert: instruction isProcessorInstruction.

instruction externalBindings keysAndValuesDo: [ :name :value |
(#('rs1' 'rs2' 'rs3') includes: name) ifTrue: [
Expand Down
100 changes: 81 additions & 19 deletions src/Tinyrossa-RISCV/TRRV64GPSABILinkage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,19 @@ Class {

{ #category : #accessing }
TRRV64GPSABILinkage >> allocatableRegisters [
"^ { t0 . t1 . t2 . t3 . t4 . t5 . t6 }"
^ self preservedRegisters
(codegen linkRegisterKilled not or:[codegen compilation config stressRA]) ifTrue:[
"For leaf methods (or when we want to stress RA), we prefer volatile registers
over preserved registers. This might save us a need to spill / reload (preserved)
registers in prologue / epilogue for small methods."

^ self volatileRegisters , self preservedRegisters
] ifFalse:[
"For non-leaf methods we prefer preserved registers over volatile registers.
This might save us a need to spill / reload (volatile) registers at call
instructions for small functions."

^ self preservedRegisters , self volatileRegisters
]
]

{ #category : #accessing }
Expand All @@ -35,14 +46,18 @@ TRRV64GPSABILinkage >> allocatedPreservedRegisters [

{ #category : #'code generation' }
TRRV64GPSABILinkage >> generateCall: node [
"Also handles indirect calls"
| indirect firstParameterIndex parameterVregs parameterTypes parameterRregs retVreg|
"Also handles indirect calls."
| indirect deps firstParameterIndex parameterVregs parameterTypes parameterRregs call retVreg |

indirect := node opcode isIndirect.
deps := TRRegisterDependencies new.

"Step 1: collect parameters and map parameters to parameter registers
(and stack slots, thought that's not supported yet)"

"If this is an indirect call, the first child is the address of the callee."
firstParameterIndex := indirect ifTrue: [ 2 ] ifFalse: [ 1 ].


parameterVregs := Array new: node children size - firstParameterIndex + 1.
parameterTypes := Array new: node children size - firstParameterIndex + 1.

Expand All @@ -55,30 +70,47 @@ TRRV64GPSABILinkage >> generateCall: node [
parameterTypes at: i - firstParameterIndex + 1
put: parameter type.
].

parameterRregs := self mapParameters: parameterTypes.
parameterVregs with: parameterRregs do: [:valReg :paramReg |
generate addi: paramReg , valReg, 0

"Step 2: map parameters into parameter registers and
thrash all other unused parameter registers and volatile
registers."
parameterVregs with: parameterRregs do: [:vReg :rReg |
deps pre addDependency: vReg on: rReg.
rReg ~~ a0 ifTrue: [
deps post addDependency: vReg on: rReg.
]
].
self parameterRegisters do: [:rReg |
(parameterRregs includes: rReg) ifFalse: [
deps pre addTrashed: rReg.
deps post addTrashed: rReg.
].
].
self volatileRegisters do: [:rReg |
deps pre addTrashed: rReg.
deps post addTrashed: rReg.
].

"Step 3: generate all instruction"
indirect ifTrue: [
| addrReg |

addrReg := codegen evaluator evaluate: node child1.
generate jalr: ra, addrReg, 0.
call := generate jalr: ra, addrReg, 0.
] ifFalse: [
"If the call a recursive call..."
node symbol == codegen compilation functionSymbol ifTrue: [
"...then generate 'jal ra, <function>'..."
generate jal: ra, node symbol.
call := generate jal: ra, node symbol.
] ifFalse: [
"...otherwise..."
codegen compilation isAOT ifTrue: [
"
In AOT mode we generate call and let the (runtime) linker
to properly relocate it.
"
generate call: node symbol
call := generate call: node symbol
] ifFalse: [
"
In JIT mode we load address directly into 'ra'
Expand All @@ -93,17 +125,24 @@ TRRV64GPSABILinkage >> generateCall: node [
self assert: node symbol address notNil description: 'No address set for function symbol'.

codegen loadConstant64: node symbol address into: ra.
generate jalr: ra, ra, 0.
call := generate jalr: ra, ra, 0.
].
].
].
call dependencies: deps.


"Note that link register has been overwritten"
codegen linkRegisterKilled: true.

"Step 4: map return value into return register (if any) and finish"
node symbol type == Void ifTrue:[
retVreg := nil.
] ifFalse:[
retVreg := codegen allocateRegister.
generate addi: retVreg , a0, 0.
deps post addDependency: retVreg on: a0.
].

^ retVreg
]

Expand All @@ -116,11 +155,10 @@ TRRV64GPSABILinkage >> generateCallIndirect: node [
TRRV64GPSABILinkage >> generateEpilogue: valReg [
| preserved offset |

"Move value to ABI return register...
...and reload link register."
generate
addi: a0, valReg, 0;
ld: ra, (sp + 0).
"Reload link register if needed"
codegen linkRegisterKilled ifTrue:[
generate ld: ra, (sp + 0).
].

"Restore preserved registers"
offset := framePreservedOffset.
Expand All @@ -145,7 +183,9 @@ TRRV64GPSABILinkage >> generatePrologue [
generate addi: sp, sp, frameSize negated.

"Save link register"
generate sd: ra, (sp + 0).
codegen linkRegisterKilled ifTrue:[
generate sd: ra, (sp + 0).
].

"Save parameters"
parameters := codegen compilation symbolManager lookupSymbolsByType: TRParameterSymbol.
Expand All @@ -164,6 +204,23 @@ TRRV64GPSABILinkage >> generatePrologue [
].
]

{ #category : #'code generation' }
TRRV64GPSABILinkage >> generateReturn: node [
| srcReg leave deps |

self assert: codegen compilation functionType == node child1 type.

srcReg := codegen evaluator evaluate: node child1.

deps := TRRegisterDependencies new.
deps pre addDependency: srcReg on: a0.

leave := generate leave: srcReg.
leave dependencies: deps.

^ nil
]

{ #category : #initialization }
TRRV64GPSABILinkage >> initializeWithCodeGenerator: aTRCodeGenerator [
super initializeWithCodeGenerator: aTRCodeGenerator.
Expand Down Expand Up @@ -260,3 +317,8 @@ TRRV64GPSABILinkage >> parameterRegisters [
TRRV64GPSABILinkage >> preservedRegisters [
^ { s0 . s1 . s2 . s3 . s4 . s5 . s6 . s7 . s8 . s9 . s10 . s11 }
]

{ #category : #accessing }
TRRV64GPSABILinkage >> volatileRegisters [
^ { t0 . t1 . t2 . t3 . t4 . t5 . t6 }
]
10 changes: 10 additions & 0 deletions src/Tinyrossa/AcInstruction.extension.st
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
Extension { #name : #AcInstruction }

{ #category : #'*Tinyrossa' }
AcInstruction >> dependencies [
^ self annotationAt: TRRegisterDependencies
]

{ #category : #'*Tinyrossa' }
AcInstruction >> dependencies: aTRRegisterDependencies [
self annotationAddOrReplace: aTRRegisterDependencies
]

{ #category : #'*Tinyrossa' }
AcInstruction >> isLeaveInstruction [
^ false
Expand Down
8 changes: 1 addition & 7 deletions src/Tinyrossa/TRCodeEvaluator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,7 @@ TRCodeEvaluator >> evaluate_lreturn: node [

{ #category : #evaluation }
TRCodeEvaluator >> evaluate_return: node [
| retReg |

self assert: codegen compilation functionType == node child1 type.

retReg := self evaluate: node child1.
generate leave: retReg.
^ nil
^ codegen linkage generateReturn: node.
]

{ #category : #evaluation }
Expand Down
Loading
Loading