and move argument registers to a couple of them.
This commit is contained in:
Takashi Kokubun 2022-12-31 14:14:53 -08:00
Родитель d7dba4c510
Коммит 2b8d1c93ea
4 изменённых файлов: 127 добавлений и 54 удалений

Просмотреть файл

@ -3,16 +3,29 @@ module RubyVM::MJIT
# https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf
# Mostly an x86_64 assembler, but this also has some stuff that is useful for any architecture.
class Assembler
# A thin Fiddle wrapper to write bytes to memory
ByteWriter = CType::Immediate.parse('char')
# Used for rel8 jumps
class Label < Data.define(:id, :name); end
# rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32
class Rel32 < Data.define(:addr); end
Rel32Pad = Object.new
ByteWriter = CType::Immediate.parse('char')
# A set of ModR/M values encoded on #insn
class ModRM < Data.define(:mod, :reg, :rm)
def initialize(mod:, reg: nil, rm: nil) = super
end
Mod00 = 0b00 # Mod 00: [reg]
Mod01 = 0b01 # Mod 01: [reg]+disp8
Mod10 = 0b10 # Mod 10: [reg]+disp16
Mod11 = 0b11 # Mod 11: reg
### prefix ###
# REX = 0100WR0B
REX_B = 0b01000001
REX_R = 0b01000100
REX_W = 0b01001000
def initialize
@ -47,24 +60,24 @@ module RubyVM::MJIT
def add(dst, src)
case [dst, src]
# ADD r/m64, imm8 (Mod 11)
# ADD r/m64, imm8 (Mod 11: reg)
in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm)
# REX.W + 83 /0 ib
# MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32
insn(
prefix: REX_W,
opcode: 0x83,
mod_rm: mod_rm(mod: 0b11, rm: reg_code(dst_reg)),
mod_rm: ModRM[mod: Mod11, rm: dst_reg],
imm: imm8(src_imm),
)
# ADD r/m64, imm8 (Mod 00)
# ADD r/m64, imm8 (Mod 00: [reg])
in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm)
# REX.W + 83 /0 ib
# MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32
insn(
prefix: REX_W,
opcode: 0x83,
mod_rm: mod_rm(mod: 0b00, rm: reg_code(dst_reg)), # Mod 00: [reg]
mod_rm: ModRM[mod: Mod00, rm: dst_reg],
imm: imm8(src_imm),
)
else
@ -98,42 +111,51 @@ module RubyVM::MJIT
case dst
in Symbol => dst_reg
case src
# MOV r64, r/m64 (Mod 00)
# MOV r64, r/m64 (Mod 00: [reg])
in [Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn(
prefix: REX_W,
opcode: 0x8b,
mod_rm: mod_rm(mod: 0b00, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 00: [reg]
mod_rm: ModRM[mod: Mod00, reg: dst_reg, rm: src_reg],
)
# MOV r32 r/m32 (Mod 01)
in [Symbol => src_reg, Integer => src_disp] if r32?(dst_reg) && imm8?(src_disp)
# 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn(
opcode: 0x8b,
mod_rm: mod_rm(mod: 0b01, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 01: [reg]+disp8
disp: src_disp,
)
# MOV r64, r/m64 (Mod 01)
# MOV r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => src_reg, Integer => src_disp] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn(
prefix: REX_W,
opcode: 0x8b,
mod_rm: mod_rm(mod: 0b01, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 01: [reg]+disp8
mod_rm: ModRM[mod: Mod01, reg: dst_reg, rm: src_reg],
disp: src_disp,
)
# MOV r/m64, imm32 (Mod 11)
# MOV r64, r/m64 (Mod 11: reg)
in Symbol => src_reg if r64?(dst_reg) && r64?(src_reg)
# REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn(
prefix: REX_W,
opcode: 0x8b,
mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg],
)
# MOV r32 r/m32 (Mod 01: [reg]+disp8)
in [Symbol => src_reg, Integer => src_disp] if r32?(dst_reg) && imm8?(src_disp)
# 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn(
opcode: 0x8b,
mod_rm: ModRM[mod: Mod01, reg: dst_reg, rm: src_reg],
disp: src_disp,
)
# MOV r/m64, imm32 (Mod 11: reg)
in Integer => src_imm if r64?(dst_reg) && imm32?(src_imm)
# REX.W + C7 /0 id
# MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64
insn(
prefix: REX_W,
opcode: 0xc7,
mod_rm: mod_rm(mod: 0b11, rm: reg_code(dst_reg)), # Mod 11: reg
mod_rm: ModRM[mod: Mod11, rm: dst_reg],
imm: imm32(src_imm),
)
# MOV r64, imm64
@ -142,7 +164,8 @@ module RubyVM::MJIT
# OI: Operand 1: opcode + rd (w), Operand 2: imm8/16/32/64
insn(
prefix: REX_W,
opcode: 0xb8 + reg_code(dst_reg),
opcode: 0xb8,
rd: dst_reg,
imm: imm64(src_imm),
)
else
@ -150,24 +173,24 @@ module RubyVM::MJIT
end
in [Symbol => dst_reg]
case src
# MOV r/m64, imm32 (Mod 00)
# MOV r/m64, imm32 (Mod 00: [reg])
in Integer => src_imm if r64?(dst_reg) && imm32?(src_imm)
# REX.W + C7 /0 id
# MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64
insn(
prefix: REX_W,
opcode: 0xc7,
mod_rm: mod_rm(mod: 0b00, rm: reg_code(dst_reg)), # Mod 00: [reg]
mod_rm: ModRM[mod: Mod00, rm: dst_reg],
imm: imm32(src_imm),
)
# MOV r/m64, r64 (Mod 00)
# MOV r/m64, r64 (Mod 00: [reg])
in Symbol => src_reg if r64?(dst_reg) && r64?(src_reg)
# REX.W + 89 /r
# MR: Operand 1: ModRM:r/m (w), Operand 2: ModRM:reg (r)
insn(
prefix: REX_W,
opcode: 0x89,
mod_rm: mod_rm(mod: 0b00, reg: reg_code(src_reg), rm: reg_code(dst_reg)), # Mod 00: [reg]
mod_rm: ModRM[mod: Mod00, reg: src_reg, rm: dst_reg],
)
else
raise NotImplementedError, "mov: not-implemented operands: #{dst.inspect}, #{src.inspect}"
@ -177,25 +200,25 @@ module RubyVM::MJIT
return mov([dst_reg], src) if dst_disp == 0
case src
# MOV r/m64, imm32 (Mod 01)
# MOV r/m64, imm32 (Mod 01: [reg]+disp8)
in Integer => src_imm if r64?(dst_reg) && imm8?(dst_disp) && imm32?(src_imm)
# REX.W + C7 /0 id
# MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64
insn(
prefix: REX_W,
opcode: 0xc7,
mod_rm: mod_rm(mod: 0b01, rm: reg_code(dst_reg)), # Mod 01: [reg]+disp8
mod_rm: ModRM[mod: Mod01, rm: dst_reg],
disp: dst_disp,
imm: imm32(src_imm),
)
# MOV r/m64, r64 (Mod 01)
# MOV r/m64, r64 (Mod 01: [reg]+disp8)
in Symbol => src_reg if r64?(dst_reg) && imm8?(dst_disp) && r64?(src_reg)
# REX.W + 89 /r
# MR: Operand 1: ModRM:r/m (w), Operand 2: ModRM:reg (r)
insn(
prefix: REX_W,
opcode: 0x89,
mod_rm: mod_rm(mod: 0b01, reg: reg_code(src_reg), rm: reg_code(dst_reg)), # Mod 01: [reg]+disp8
mod_rm: ModRM[mod: Mod01, reg: src_reg, rm: dst_reg],
disp: dst_disp,
)
else
@ -212,7 +235,7 @@ module RubyVM::MJIT
in Symbol => src_reg if r64?(src_reg)
# 50+rd
# O: Operand 1: opcode + rd (r)
insn(opcode: 0x50 + reg_code(src_reg))
insn(opcode: 0x50, rd: src_reg)
else
raise NotImplementedError, "push: not-implemented operands: #{src.inspect}"
end
@ -224,7 +247,7 @@ module RubyVM::MJIT
in Symbol => dst_reg if r64?(dst_reg)
# 58+ rd
# O: Operand 1: opcode + rd (r)
insn(opcode: 0x58 + reg_code(dst_reg))
insn(opcode: 0x58, rd: dst_reg)
else
raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}"
end
@ -238,13 +261,13 @@ module RubyVM::MJIT
def test(left, right)
case [left, right]
# TEST r/m32, r32 (Mod 11)
# TEST r/m32, r32 (Mod 11: reg)
in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg)
# 85 /r
# MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r)
insn(
opcode: 0x85,
mod_rm: mod_rm(mod: 0b11, reg: reg_code(right_reg), rm: reg_code(left_reg)), # Mod 11: reg
mod_rm: ModRM[mod: Mod11, reg: right_reg, rm: left_reg],
)
else
raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}"
@ -284,13 +307,29 @@ module RubyVM::MJIT
private
def insn(prefix: nil, opcode:, mod_rm: nil, disp: nil, imm: nil)
if prefix
def insn(prefix: 0, opcode:, rd: nil, mod_rm: nil, disp: nil, imm: nil)
# Determine prefix
if rd
prefix |= REX_B if extended_reg?(rd)
opcode += reg_code(rd)
end
if mod_rm
prefix |= REX_R if mod_rm.reg && extended_reg?(mod_rm.reg)
prefix |= REX_B if mod_rm.rm && extended_reg?(mod_rm.rm)
end
# Encode insn
if prefix > 0
@bytes.push(prefix)
end
@bytes.push(*Array(opcode))
if mod_rm
@bytes.push(mod_rm)
mod_rm_byte = encode_mod_rm(
mod: mod_rm.mod,
reg: mod_rm.reg ? reg_code(mod_rm.reg) : 0,
rm: mod_rm.rm ? reg_code(mod_rm.rm) : 0,
)
@bytes.push(mod_rm_byte)
end
if disp
unless imm8?(disp) # TODO: support displacement in 2 or 4 bytes as well
@ -304,15 +343,33 @@ module RubyVM::MJIT
end
def reg_code(reg)
reg_code_extended(reg).first
end
def extended_reg?(reg)
reg_code_extended(reg).last
end
def reg_code_extended(reg)
case reg
when :al, :ax, :eax, :rax then 0
when :cl, :cx, :ecx, :rcx then 1
when :dl, :dx, :edx, :rdx then 2
when :bl, :bx, :ebx, :rbx then 3
when :ah, :sp, :esp, :rsp then 4
when :ch, :bp, :ebp, :rbp then 5
when :dh, :si, :esi, :rsi then 6
when :bh, :di, :edi, :rdi then 7
# Not extended
when :al, :ax, :eax, :rax then [0, false]
when :cl, :cx, :ecx, :rcx then [1, false]
when :dl, :dx, :edx, :rdx then [2, false]
when :bl, :bx, :ebx, :rbx then [3, false]
when :ah, :sp, :esp, :rsp then [4, false]
when :ch, :bp, :ebp, :rbp then [5, false]
when :dh, :si, :esi, :rsi then [6, false]
when :bh, :di, :edi, :rdi then [7, false]
# Extended
when :r8b, :r8w, :r8d, :r8 then [0, true]
when :r9b, :r9w, :r9d, :r9 then [1, true]
when :r10b, :r10w, :r10d, :r10 then [2, true]
when :r11b, :r11w, :r11d, :r11 then [3, true]
when :r12b, :r12w, :r12d, :r12 then [4, true]
when :r13b, :r13w, :r13d, :r13 then [5, true]
when :r14b, :r14w, :r14d, :r14 then [6, true]
when :r15b, :r15w, :r15d, :r15 then [7, true]
else raise ArgumentError, "unexpected reg: #{reg.inspect}"
end
end
@ -330,7 +387,7 @@ module RubyVM::MJIT
#
# /0: R/M is 0 (not used)
# /r: R/M is a register
def mod_rm(mod:, reg: 0, rm: 0)
def encode_mod_rm(mod:, reg: 0, rm: 0)
if mod > 0b11
raise ArgumentError, "too large Mod: #{mod}"
end
@ -389,11 +446,19 @@ module RubyVM::MJIT
end
def r32?(reg)
reg.start_with?('e')
if extended_reg?(reg)
reg.end_with?('d')
else
reg.start_with?('e')
end
end
def r64?(reg)
reg.start_with?('r')
if extended_reg?(reg)
reg.match?(/\Ar\d+\z/)
else
reg.start_with?('r')
end
end
def rel32(addr)

Просмотреть файл

@ -16,9 +16,10 @@ module RubyVM::MJIT
Qnil = Fiddle::Qnil
Qundef = Fiddle::Qundef
# Fixed registers
EC = :rdi # TODO: change this
CFP = :rsi # TODO: change this
# Callee-saved registers
# TODO: support using r12/r13 here
EC = :r14
CFP = :r15
SP = :rbx
class Compiler
@ -64,9 +65,15 @@ module RubyVM::MJIT
asm.comment("MJIT entry")
# Save callee-saved registers used by JITed code
asm.push(CFP)
asm.push(EC)
asm.push(SP)
# Load sp to a register
# Move arguments EC and CFP to dedicated registers
asm.mov(EC, :rdi)
asm.mov(CFP, :rsi)
# Load sp to a dedicated register
asm.mov(SP, [CFP, C.rb_control_frame_t.offsetof(:sp)]) # rbx = cfp->sp
end

Просмотреть файл

@ -26,6 +26,8 @@ module RubyVM::MJIT
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(:rax, Qundef)
asm.ret

Просмотреть файл

@ -1,7 +1,4 @@
module RubyVM::MJIT
# ec: rdi
# cfp: rsi
# sp: rbx
# scratch regs: rax
#
# 4/101
@ -123,6 +120,8 @@ module RubyVM::MJIT
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.ret
EndBlock