Refactored VM registerlayout. The size and location of the registers in (#12775)
the instructions are now all derived from a single definition, allowing the register layout to be changed.
This commit is contained in:
parent
0d022622e3
commit
6aade97fe8
2 changed files with 54 additions and 29 deletions
|
|
@ -12,17 +12,42 @@
|
||||||
|
|
||||||
import ast, idents, options, modulegraphs, lineinfos
|
import ast, idents, options, modulegraphs, lineinfos
|
||||||
|
|
||||||
|
type TInstrType* = uint32
|
||||||
|
|
||||||
const
|
const
|
||||||
|
regOBits = 8 # Opcode
|
||||||
|
regABits = 8
|
||||||
|
regBBits = 8
|
||||||
|
regCBits = 8
|
||||||
|
regBxBits = 16
|
||||||
|
|
||||||
byteExcess* = 128 # we use excess-K for immediates
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
wordExcess* = 32768
|
|
||||||
|
|
||||||
MaxLoopIterations* = 10_000_000 # max iterations of all loops
|
MaxLoopIterations* = 10_000_000 # max iterations of all loops
|
||||||
|
|
||||||
|
# Calculate register shifts, masks and ranges
|
||||||
|
|
||||||
|
const
|
||||||
|
regOShift* = 0.TInstrType
|
||||||
|
regAShift* = (regOShift + regOBits)
|
||||||
|
regBShift* = (regAShift + regABits)
|
||||||
|
regCShift* = (regBShift + regBBits)
|
||||||
|
regBxShift* = (regAShift + regABits)
|
||||||
|
|
||||||
|
regOMask* = ((1.TInstrType shl regOBits) - 1)
|
||||||
|
regAMask* = ((1.TInstrType shl regABits) - 1)
|
||||||
|
regBMask* = ((1.TInstrType shl regBBits) - 1)
|
||||||
|
regCMask* = ((1.TInstrType shl regCBits) - 1)
|
||||||
|
regBxMask* = ((1.TInstrType shl regBxBits) - 1)
|
||||||
|
|
||||||
|
wordExcess* = 1 shl (regBxBits-1)
|
||||||
|
regBxMin* = -wordExcess+1
|
||||||
|
regBxMax* = wordExcess-1
|
||||||
|
|
||||||
type
|
type
|
||||||
TRegister* = range[0..255]
|
TRegister* = range[0..regAMask.int]
|
||||||
TDest* = range[-1..255]
|
TDest* = range[-1..regAMask.int]
|
||||||
TInstr* = distinct uint32
|
TInstr* = distinct TInstrType
|
||||||
|
|
||||||
TOpcode* = enum
|
TOpcode* = enum
|
||||||
opcEof, # end of code
|
opcEof, # end of code
|
||||||
|
|
@ -254,10 +279,10 @@ const
|
||||||
# flag is used to signal opcSeqLen if node is NimNode.
|
# flag is used to signal opcSeqLen if node is NimNode.
|
||||||
const nimNodeFlag* = 16
|
const nimNodeFlag* = 16
|
||||||
|
|
||||||
template opcode*(x: TInstr): TOpcode = TOpcode(x.uint32 and 0xff'u32)
|
template opcode*(x: TInstr): TOpcode = TOpcode(x.TInstrType shr regOShift and regOMask)
|
||||||
template regA*(x: TInstr): TRegister = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
|
template regA*(x: TInstr): TRegister = TRegister(x.TInstrType shr regAShift and regAMask)
|
||||||
template regB*(x: TInstr): TRegister = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
|
template regB*(x: TInstr): TRegister = TRegister(x.TInstrType shr regBShift and regBMask)
|
||||||
template regC*(x: TInstr): TRegister = TRegister(x.uint32 shr 24'u32)
|
template regC*(x: TInstr): TRegister = TRegister(x.TInstrType shr regCShift and regCMask)
|
||||||
template regBx*(x: TInstr): int = (x.uint32 shr 16'u32).int
|
template regBx*(x: TInstr): int = (x.TInstrType shr regBxShift and regBxMask).int
|
||||||
|
|
||||||
template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess
|
template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,9 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
|
||||||
## stores the result into register `a`
|
## stores the result into register `a`
|
||||||
## The node is needed for debug information
|
## The node is needed for debug information
|
||||||
assert opc.ord < 255
|
assert opc.ord < 255
|
||||||
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or
|
||||||
(b.uint32 shl 16'u32) or
|
(b.TInstrType shl regBShift) or
|
||||||
(c.uint32 shl 24'u32)).TInstr
|
(c.TInstrType shl regCShift)).TInstr
|
||||||
when false:
|
when false:
|
||||||
if ctx.code.len == 43:
|
if ctx.code.len == 43:
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
|
|
@ -131,9 +131,9 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
|
||||||
# and stores the output value into `a`.
|
# and stores the output value into `a`.
|
||||||
# `imm` is signed and must be within [-128, 127]
|
# `imm` is signed and must be within [-128, 127]
|
||||||
if imm >= -128 and imm <= 127:
|
if imm >= -128 and imm <= 127:
|
||||||
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or
|
||||||
(b.uint32 shl 16'u32) or
|
(b.TInstrType shl regBShift) or
|
||||||
(imm+byteExcess).uint32 shl 24'u32).TInstr
|
(imm+byteExcess).TInstrType shl regCShift).TInstr
|
||||||
c.code.add(ins)
|
c.code.add(ins)
|
||||||
c.debug.add(n.info)
|
c.debug.add(n.info)
|
||||||
else:
|
else:
|
||||||
|
|
@ -142,20 +142,20 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
|
||||||
|
|
||||||
proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
|
proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
|
||||||
# Applies `opc` to `bx` and stores it into register `a`
|
# Applies `opc` to `bx` and stores it into register `a`
|
||||||
# `bx` must be signed and in the range [-32768, 32767]
|
# `bx` must be signed and in the range [regBxMin, regBxMax]
|
||||||
when false:
|
when false:
|
||||||
if c.code.len == 43:
|
if c.code.len == 43:
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
echo "generating ", opc
|
echo "generating ", opc
|
||||||
|
|
||||||
if bx >= -32768 and bx <= 32767:
|
if bx >= regBxMin-1 and bx <= regBxMax:
|
||||||
let ins = (opc.uint32 or a.uint32 shl 8'u32 or
|
let ins = (opc.TInstrType or a.TInstrType shl regAShift or
|
||||||
(bx+wordExcess).uint32 shl 16'u32).TInstr
|
(bx+wordExcess).TInstrType shl regBxShift).TInstr
|
||||||
c.code.add(ins)
|
c.code.add(ins)
|
||||||
c.debug.add(n.info)
|
c.debug.add(n.info)
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info,
|
localError(c.config, n.info,
|
||||||
"VM: immediate value does not fit into an int16")
|
"VM: immediate value does not fit into regBx")
|
||||||
|
|
||||||
proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
|
proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
|
||||||
#assert opc in {opcJmp, opcFJmp, opcTJmp}
|
#assert opc in {opcJmp, opcFJmp, opcTJmp}
|
||||||
|
|
@ -168,7 +168,7 @@ proc genLabel(c: PCtx): TPosition =
|
||||||
|
|
||||||
proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
|
proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
|
||||||
let dist = p.int - c.code.len
|
let dist = p.int - c.code.len
|
||||||
internalAssert(c.config, -0x7fff < dist and dist < 0x7fff)
|
internalAssert(c.config, regBxMin < dist and dist < regBxMax)
|
||||||
gABx(c, n, opcJmpBack, 0, dist)
|
gABx(c, n, opcJmpBack, 0, dist)
|
||||||
|
|
||||||
proc patch(c: PCtx, p: TPosition) =
|
proc patch(c: PCtx, p: TPosition) =
|
||||||
|
|
@ -176,11 +176,11 @@ proc patch(c: PCtx, p: TPosition) =
|
||||||
let p = p.int
|
let p = p.int
|
||||||
let diff = c.code.len - p
|
let diff = c.code.len - p
|
||||||
#c.jumpTargets.incl(c.code.len)
|
#c.jumpTargets.incl(c.code.len)
|
||||||
internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
|
internalAssert(c.config, regBxMin < diff and diff < regBxMax)
|
||||||
let oldInstr = c.code[p]
|
let oldInstr = c.code[p]
|
||||||
# opcode and regA stay the same:
|
# opcode and regA stay the same:
|
||||||
c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
|
c.code[p] = ((oldInstr.TInstrType and regBxMask).TInstrType or
|
||||||
uint32(diff+wordExcess) shl 16'u32).TInstr
|
TInstrType(diff+wordExcess) shl regBxShift).TInstr
|
||||||
|
|
||||||
proc getSlotKind(t: PType): TSlotKind =
|
proc getSlotKind(t: PType): TSlotKind =
|
||||||
case t.skipTypes(abstractRange-{tyTypeDesc}).kind
|
case t.skipTypes(abstractRange-{tyTypeDesc}).kind
|
||||||
|
|
@ -440,7 +440,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int =
|
||||||
#assert(n.kind != nkCall)
|
#assert(n.kind != nkCall)
|
||||||
n.flags.incl nfAllConst
|
n.flags.incl nfAllConst
|
||||||
c.constants.add n.canonValue
|
c.constants.add n.canonValue
|
||||||
internalAssert c.config, result < 0x7fff
|
internalAssert c.config, result < regBxMax
|
||||||
|
|
||||||
proc sameConstant*(a, b: PNode): bool =
|
proc sameConstant*(a, b: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
@ -513,7 +513,7 @@ proc genType(c: PCtx; typ: PType): int =
|
||||||
if sameType(t, typ): return i
|
if sameType(t, typ): return i
|
||||||
result = c.types.len
|
result = c.types.len
|
||||||
c.types.add(typ)
|
c.types.add(typ)
|
||||||
internalAssert(c.config, result <= 0x7fff)
|
internalAssert(c.config, result <= regBxMax)
|
||||||
|
|
||||||
proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
||||||
|
|
@ -2179,11 +2179,11 @@ proc genParams(c: PCtx; params: PNode) =
|
||||||
c.prc.maxSlots = max(params.len, 1)
|
c.prc.maxSlots = max(params.len, 1)
|
||||||
|
|
||||||
proc finalJumpTarget(c: PCtx; pc, diff: int) =
|
proc finalJumpTarget(c: PCtx; pc, diff: int) =
|
||||||
internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
|
internalAssert(c.config, regBxMin < diff and diff < regBxMax)
|
||||||
let oldInstr = c.code[pc]
|
let oldInstr = c.code[pc]
|
||||||
# opcode and regA stay the same:
|
# opcode and regA stay the same:
|
||||||
c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
|
c.code[pc] = ((oldInstr.TInstrType and ((regOMask shl regOShift) or (regAMask shl regAShift))).TInstrType or
|
||||||
uint32(diff+wordExcess) shl 16'u32).TInstr
|
TInstrType(diff+wordExcess) shl regBxShift).TInstr
|
||||||
|
|
||||||
proc genGenericParams(c: PCtx; gp: PNode) =
|
proc genGenericParams(c: PCtx; gp: PNode) =
|
||||||
var base = c.prc.maxSlots
|
var base = c.prc.maxSlots
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue