Closed apt1002 closed 3 years ago
We left x86_64
unchanged (it uses MOV
instructions).
We implemented constant pools for aarch64
in an incremental way. We allocate code memory in 1MB chunks. In each chunk we assemble code forwards and collect constants backwards. When there is too little free space between the two, we allocate the next chunk and assemble a branch to it. This incremental scheme avoided any changes to Lower::action()
.
Currently Mijit supposes that any word-sized value can be placed in an immediate constant. This is true only because we're on x86_64 (#18) and only because we're writing every constant to a register before we use it (#15). In future, we need to do something different.
64-bit ARM
64-bit ARM seems to be the most awkward target, and it will therefore drive the design. The strategy required for ARM is something like this (included for illustration only):
ADD
withSUB
, and negate the constant.AND
withBIC
, and invert the constant.OR
withORN
, and invert the constant.EOR
withEON
, and invert the constant.LSL
,LSR
,ASR
, andROR
.ADD
SUB
,ADDS
andSUBS
.AND
,OR
,EOR
andANDS
.LDR
,STR
,LDRB
,STRB
,LDRSB
,LDRH
,STRH
,LDRSH
, andLDRSW
.LDR
,STR
,LDRB
,STRB
,LDRSB
,LDRH
,STRH
,LDRSH
, andLDRSW
.MOVZ
instruction. If it is the inverse of such a value, useMOVN
.OR
, or it with the zero register.PC
-relativeLDR
. The offset must be a signed 21-bit number.x86_64
Most instructions can use a signed 32-bit immediate constant. Larger constants must be written to a register first, either using a wide
MOV
instruction, or using anIP
-relative load.Position-independent load
Regardless of the platform, the final case (a position-independent load) is the most general. Let us say that a constant is "awkward" if it requires a position-independent load instruction. Such constants need to be held in a constant pool: a read-only area of memory that is contiguous with the executable code but that is never executed.
Because of the limitations of the position-independent load instruction, the constant pool must be within +-1MB of the instruction (on ARM). We must allow Mijit to compile more than 1MB of code in total. Therefore, more than one constant pool will be needed. It is less likely that it will compile a basic block that is more than 1MB in size, so it probably suffices to have one constant pool per basic block. If a basic block does grow too big, we can split it in two using a jump instruction.
Currently, basic blocks are represented by a
Vec
ofAction
s. I suggest we supplement this with aVec
of constants. The lowerer will need to receive the constants first, followed by theAction
s. TheAction
s can specify constants by value. The pool for a basic block is only required to contain the awkward constants that are mentioned by theAction
s in the basic block. The target abstraction layer (#18) will need to provide a way of testing whether a constant is awkward.