Skip to content

Commit

Permalink
Merge pull request #21 from janvrany/pr/riscv-implement-load-constant…
Browse files Browse the repository at this point in the history
…-and-external-function-calls

RISC-V: properly implement `Xconst` and calls to external functions
  • Loading branch information
janvrany authored Jun 17, 2023
2 parents 1782398 + be354b5 commit 56cb78f
Show file tree
Hide file tree
Showing 11 changed files with 498 additions and 17 deletions.
98 changes: 95 additions & 3 deletions src/Tinyrossa-RISCV/TRRV64GCodeEvaluator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,50 @@ TRRV64GCodeEvaluator >> evaluate_Xcmplt: node [
^ trgReg
]

{ #category : #'evaluation-helpers' }
TRRV64GCodeEvaluator >> evaluate_Xloadi: node [
"Handles Address, Int64 Int32, Int16 & Int8"

| dstReg baseReg offset type |

"FIXME: we can do better by checking for loads in form
Xloadi
aXadd
...
Xconst offset
and check if offset fits into displacement immediate and if
so, generate
ld dstReg, offset ( ... )
But for now, this will do.
"
baseReg := self evaluate: node child1.
offset := 0.


dstReg := codegen allocateRegister.

type := node type.
(type == Address or:[type == Int64]) ifTrue:[
generate
ld: dstReg, (baseReg + offset).
] ifFalse:[ type == Int32 ifTrue:[
generate
lw: dstReg, (baseReg + offset).
] ifFalse:[ type == Int16 ifTrue:[
generate
lh: dstReg, (baseReg + offset).
] ifFalse:[ type == Int8 ifTrue:[
generate
lb: dstReg, (baseReg + offset).
]]]].

^dstReg.
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_acmpgt: node [
^ self evaluate_Xcmpgt: node
Expand All @@ -46,16 +90,37 @@ TRRV64GCodeEvaluator >> evaluate_acmplt: node [
^ self evaluate_Xcmplt: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_aconst: node [
^self evaluate_lconst: node

]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_aload: node [
^ self evaluate_lload: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_aloadi: node [
^ self evaluate_Xloadi: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_astore: node [
^ self evaluate_lstore: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_bconst: node [
^ self evaluate_iconst: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_bloadi: node [
^ self evaluate_Xloadi: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_goto: node [
generate
Expand Down Expand Up @@ -100,8 +165,7 @@ TRRV64GCodeEvaluator >> evaluate_iconst: node [

dstReg := self codegen allocateRegister.

generate
addi: dstReg, zero, node constant.
codegen loadConstant32: node constant into: dstReg.

^ dstReg
]
Expand Down Expand Up @@ -284,6 +348,11 @@ TRRV64GCodeEvaluator >> evaluate_iload: node [
^ dstReg
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_iloadi: node [
^ self evaluate_Xloadi: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_imul: node [
| src1Reg src2Reg dstReg |
Expand Down Expand Up @@ -336,6 +405,19 @@ TRRV64GCodeEvaluator >> evaluate_lcmplt: node [
^ self evaluate_Xcmplt: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_lconst: node [
| dstReg |

node constant == 0 ifTrue: [ ^ zero ].

dstReg := self codegen allocateRegister.

codegen loadConstant64: node constant into: dstReg.

^ dstReg
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_lload: node [
| symbol dstReg |
Expand All @@ -354,4 +436,14 @@ TRRV64GCodeEvaluator >> evaluate_lstore: node [
srcReg := self evaluate: node child1.
generate sd: srcReg, (sp + (AcDSLSymbol value: symbol name)).
^ nil
]
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_sconst: node [
^ self evaluate_iconst: node
]

{ #category : #evaluation }
TRRV64GCodeEvaluator >> evaluate_sloadi: node [
^ self evaluate_Xloadi: node
]
106 changes: 94 additions & 12 deletions src/Tinyrossa-RISCV/TRRV64GCodeGenerator.class.st
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
Class {
#name : #TRRV64GCodeGenerator,
#superclass : #TRCodeGenerator,
#classVars : [
'MaxItypeImm',
'MinItypeImm'
],
#pools : [
'TRRV64GRegisters'
'TRRV64GRegisters',
'TRIntLimits',
'TRRV64GISALimits'
],
#category : #'Tinyrossa-RISCV-Codegen'
}

{ #category : #initialization }
TRRV64GCodeGenerator class >> initialize [
MaxItypeImm := 16r7FF.
MinItypeImm := -16r800.
]

{ #category : #'accessing - config' }
TRRV64GCodeGenerator >> assemblerClass [
^ AcDSLRV64GAssembler
Expand All @@ -29,7 +21,97 @@ TRRV64GCodeGenerator >> evaluatorClass [

{ #category : #queries }
TRRV64GCodeGenerator >> fitsInItypeImm: anInteger [
^ anInteger between: MinItypeImm and: MaxItypeImm
^ anInteger between: RISCV_IMM_MIN and: RISCV_IMM_MAX
]

{ #category : #utilities }
TRRV64GCodeGenerator >> loadConstant32: value into: reg [
self assert: (value between: INT32_MIN and: INT32_MAX).

"Code below is almost literal translation of code in OMR, see
compiler/riscv/codegen/OMRTreeEvaluator.cpp, loadConstant32()."

(value between: RISCV_IMM_MIN and: RISCV_IMM_MAX) ifTrue: [
generate
addi: reg, zero, value.
] ifFalse: [
| lo hi |

lo := value & (16rFFFFFFFF << RISCV_IMM_BITS) bitInvert32.
hi := value & (16rFFFFFFFF << RISCV_IMM_BITS).

(lo & (1 << (RISCV_IMM_BITS - 1))) ~~ 0 ifTrue: [
hi := hi + (1 << RISCV_IMM_BITS).
].

"This is PITA! Here we have to convert unsigned
hi and lo to signed values!"
lo := lo > RISCV_IMM_MAX ifTrue: [ lo - (1 << RISCV_IMM_BITS) ] ifFalse: [ lo ].
hi := hi > INT32_MAX ifTrue: [ hi - (1 << 32) ] ifFalse: [ hi ].

generate
lui: reg, (hi >> RISCV_IMM_BITS);
addiw: reg, reg, lo
].
]

{ #category : #utilities }
TRRV64GCodeGenerator >> loadConstant64: value into: reg [
self assert: (value between: INT64_MIN and: INT64_MAX).

"Code below is almost literal translation of code in OMR, see
compiler/riscv/codegen/OMRTreeEvaluator.cpp, loadConstant64()."

(value between: INT32_MIN and: INT32_MAX) ifTrue: [
self loadConstant32: value into: reg.
] ifFalse: [
| unsigned hi32 nbits toShift bits |

"Convert value to 'unsigned' (positive) integer - we need to do this
because extractBitsFrom:to: expect positive value."
unsigned := value < 0 ifTrue: [ value + "(1 << 64)hex"16r10000000000000000 ] ifFalse: [ value ].

hi32 := unsigned >> 32.

nbits := RISCV_IMM_BITS - 1.
toShift := 0.
bits := 0.

"Sigh, and convert hi32 back to signed since loadConstant32 takes
signed value!"
hi32 := hi32 > INT32_MAX ifTrue: [ hi32 - "(1 << 32)hex"16r100000000 ] ifFalse: [ hi32 ].
self loadConstant32: hi32 into: reg.


bits := unsigned extractBitsFrom: 31 - (0*nbits) to: 31 - (1*nbits) + 1.
toShift := toShift + (hi32 == 0 ifTrue:[ 0 ] ifFalse: [ nbits ]).
(bits ~~ 0) ifTrue: [
toShift ~~ 0 ifTrue: [
generate slli: reg, reg, toShift.
].
generate addi: reg, reg, bits.
toShift := 0.
].

bits := unsigned extractBitsFrom: 31 - (1*nbits) to: 31 - (2*nbits) + 1.
toShift := toShift + nbits.
(bits ~~ 0) ifTrue: [
toShift ~~ 0 ifTrue: [
generate slli: reg, reg, toShift.
].
generate addi: reg, reg, bits.
toShift := 0.
].

bits := unsigned extractBitsFrom: 31 - (2*nbits) to: 0.
toShift := toShift + (31 - (2*nbits) + 1).
toShift ~~ 0 ifTrue: [
generate slli: reg, reg, toShift.
].
(bits ~~ 0) ifTrue: [
generate addi: reg, reg, bits.
].
].
]

{ #category : #registers }
Expand Down
18 changes: 18 additions & 0 deletions src/Tinyrossa-RISCV/TRRV64GISALimits.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Class {
#name : #TRRV64GISALimits,
#superclass : #TRSharedPool,
#classVars : [
'RISCV_IMM_BITS',
'RISCV_IMM_MIN',
'RISCV_IMM_MAX'
],
#category : #'Tinyrossa-RISCV-Codegen'
}

{ #category : #initialization }
TRRV64GISALimits class >> initialize [
RISCV_IMM_BITS := 12.
RISCV_IMM_MIN := -16r800.
RISCV_IMM_MAX := 16r7FF.

]
28 changes: 26 additions & 2 deletions src/Tinyrossa-RISCV/TRRV64GPSABILinkage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Class {
'framePreservedOffset'
],
#pools : [
'TRRV64GRegisters'
'TRRV64GRegisters',
'TRIntLimits'
],
#category : #'Tinyrossa-RISCV-Codegen'
}
Expand Down Expand Up @@ -43,7 +44,30 @@ TRRV64GPSABILinkage >> generateCall: node [
generate addi: paramReg , valReg, 0
].

generate jal: ra, node symbol.
"If the call a recursive call..."
node symbol == codegen compilation functionSymbol ifTrue: [
"...then generate 'jal ra, <function>'..."
generate jal: ra, node symbol.
] ifFalse: [
"...otherwise, load callee's address into register 'ra'
and generate 'jalr ra, ra, 0'.
Note, that here we load address directly into 'ra' (as opposite
to allocating new v-register) because will be clobbered anyways
by jalr storing return address there. This lowers the pressure on
RA.
Also note that rather than this, we should generate a trampoline
and call calle through it. Or relative jal if it's close enough.
But that's left as future work."

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.
].



retVreg := codegen allocateRegister.
generate addi: retVreg , a0, 0.
Expand Down
Loading

0 comments on commit 56cb78f

Please sign in to comment.