Skip to content

Commit

Permalink
amd64: removes embeddings of pointers of bit masks for FP arithmetic (#…
Browse files Browse the repository at this point in the history
…648)

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
  • Loading branch information
mathetake committed Jun 22, 2022
1 parent 36caf1a commit 4136a36
Show file tree
Hide file tree
Showing 11 changed files with 831 additions and 203 deletions.
8 changes: 6 additions & 2 deletions internal/asm/amd64/assembler.go
Expand Up @@ -88,7 +88,11 @@ type Assembler interface {
// the destination is the constant `value`.
CompileMemoryToConst(instruction asm.Instruction, srcBaseReg asm.Register, srcOffset int64, value int64) asm.Node

// CompileLoadStaticConstToRegister adds an instruction where the source operand is asm.StaticConst located in the
// CompileStaticConstToRegister adds an instruction where the source operand is asm.StaticConst located in the
// memory and the destination is the dstReg.
CompileLoadStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) error
CompileStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) error

// CompileRegisterToStaticConst adds an instruction where the destination operand is asm.StaticConst located in the
// memory and the source is the srcReg.
CompileRegisterToStaticConst(instruction asm.Instruction, srcReg asm.Register, c asm.StaticConst) error
}
2 changes: 1 addition & 1 deletion internal/asm/amd64/consts.go
Expand Up @@ -167,7 +167,7 @@ const (
MOVBQSX
// MOVBQZX is the MOVZX instruction for single-byte in 64-bit mode. https://www.felixcloutier.com/x86/movzx
MOVBQZX
// MOVL is the MOV instruction for a word.
// MOVL is the MOV instruction for a double word.
MOVL
// MOVLQSX is the MOVSXD instruction. https://www.felixcloutier.com/x86/movsx:movsxd
MOVLQSX
Expand Down
3 changes: 3 additions & 0 deletions internal/asm/amd64/impl.go
Expand Up @@ -204,6 +204,7 @@ var (
OperandTypesConstToRegister = OperandTypes{OperandTypeConst, OperandTypeRegister}
OperandTypesConstToMemory = OperandTypes{OperandTypeConst, OperandTypeMemory}
OperandTypesStaticConstToRegister = OperandTypes{OperandTypeStaticConst, OperandTypeRegister}
OperandTypesRegisterToStaticConst = OperandTypes{OperandTypeRegister, OperandTypeStaticConst}
)

// String implements fmt.Stringer
Expand Down Expand Up @@ -295,6 +296,8 @@ func (a *AssemblerImpl) EncodeNode(n *NodeImpl) (err error) {
err = a.EncodeMemoryToConst(n)
case OperandTypesStaticConstToRegister:
err = a.encodeStaticConstToRegister(n)
case OperandTypesRegisterToStaticConst:
err = a.encodeRegisterToStaticConst(n)
default:
err = fmt.Errorf("encoder undefined for [%s] operand type", n.Types)
}
Expand Down
129 changes: 102 additions & 27 deletions internal/asm/amd64/impl_staticconst.go
Expand Up @@ -74,15 +74,96 @@ func (a *AssemblerImpl) maybeFlushConstants(isEndOfFunction bool) {
}
}

type staticConstOpcode struct {
opcode []byte
mandatoryPrefix byte
rex RexPrefix
}

var registerToStaticConstOpcodes = map[asm.Instruction]staticConstOpcode{
// https://www.felixcloutier.com/x86/cmp
CMPL: {opcode: []byte{0x3b}},
CMPQ: {opcode: []byte{0x3b}, rex: RexPrefixW},
}

func (a *AssemblerImpl) encodeRegisterToStaticConst(n *NodeImpl) (err error) {
opc, ok := registerToStaticConstOpcodes[n.Instruction]
if !ok {
return errorEncodingUnsupported(n)
}
return a.encodeStaticConstImpl(n, opc.opcode, opc.rex, opc.mandatoryPrefix)
}

var staticConstToRegisterOpcodes = map[asm.Instruction]struct {
opcode []byte
mandatoryPrefix byte
rex RexPrefix
}{
// https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64
MOVDQU: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0x6f}},
// https://www.felixcloutier.com/x86/lea
LEAQ: {opcode: []byte{0x8d}, rex: RexPrefixW},
// https://www.felixcloutier.com/x86/movupd
MOVUPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x10}},
// https://www.felixcloutier.com/x86/mov
MOVL: {opcode: []byte{0x8b}},
MOVQ: {opcode: []byte{0x8b}, rex: RexPrefixW},
// https://www.felixcloutier.com/x86/ucomisd
UCOMISD: {opcode: []byte{0x0f, 0x2e}, mandatoryPrefix: 0x66},
// https://www.felixcloutier.com/x86/ucomiss
UCOMISS: {opcode: []byte{0x0f, 0x2e}},
// https://www.felixcloutier.com/x86/subss
SUBSS: {opcode: []byte{0x0f, 0x5c}, mandatoryPrefix: 0xf3},
// https://www.felixcloutier.com/x86/subsd
SUBSD: {opcode: []byte{0x0f, 0x5c}, mandatoryPrefix: 0xf2},
// https://www.felixcloutier.com/x86/cmp
CMPL: {opcode: []byte{0x39}},
CMPQ: {opcode: []byte{0x39}, rex: RexPrefixW},
// https://www.felixcloutier.com/x86/add
ADDL: {opcode: []byte{0x03}},
ADDQ: {opcode: []byte{0x03}, rex: RexPrefixW},
}

var staticConstToVectorRegisterOpcodes = map[asm.Instruction]staticConstOpcode{
// https://www.felixcloutier.com/x86/mov
MOVL: {opcode: []byte{0x0f, 0x6e}, mandatoryPrefix: 0x66},
MOVQ: {opcode: []byte{0x0f, 0x7e}, mandatoryPrefix: 0xf3},
}

func (a *AssemblerImpl) encodeStaticConstToRegister(n *NodeImpl) (err error) {
var opc staticConstOpcode
var ok bool
if IsVectorRegister(n.DstReg) && (n.Instruction == MOVL || n.Instruction == MOVQ) {
opc, ok = staticConstToVectorRegisterOpcodes[n.Instruction]
} else {
opc, ok = staticConstToRegisterOpcodes[n.Instruction]
}
if !ok {
return errorEncodingUnsupported(n)
}
return a.encodeStaticConstImpl(n, opc.opcode, opc.rex, opc.mandatoryPrefix)
}

// encodeStaticConstImpl encodes an instruction where mod:r/m points to the memory location of the static constant n.staticConst,
// and the other operand is the register given at n.SrcReg or n.DstReg.
func (a *AssemblerImpl) encodeStaticConstImpl(n *NodeImpl, opcode []byte, rex RexPrefix, mandatoryPrefix byte) (err error) {
a.pool.addConst(n.staticConst)

dstReg3Bits, rexPrefix, err := register3bits(n.DstReg, registerSpecifierPositionModRMFieldReg)
var reg asm.Register
if n.DstReg != asm.NilRegister {
reg = n.DstReg
} else {
reg = n.SrcReg
}

reg3Bits, rexPrefix, err := register3bits(reg, registerSpecifierPositionModRMFieldReg)
if err != nil {
return err
}

var inst []byte // mandatory prefix
rexPrefix |= rex

var inst []byte
key := asm.StaticConstKey(n.staticConst)
a.pool.offsetFinalizedCallbacks[key] = append(a.pool.offsetFinalizedCallbacks[key],
func(offsetOfConstInBinary int) {
Expand All @@ -96,37 +177,18 @@ func (a *AssemblerImpl) encodeStaticConstToRegister(n *NodeImpl) (err error) {
a.pool.firstUseOffsetInBinary = &nodeOffset

// https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing
modRM := 0b00_000_101 | // Indicate "MOVDQU [RIP + 32bit displacement], DstReg" encoding.
(dstReg3Bits << 3) // Place the DstReg on ModRM:reg.

var mandatoryPrefix byte
var opcodes []byte
switch n.Instruction {
case MOVDQU:
// https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64
mandatoryPrefix = 0xf3
opcodes = []byte{0x0f, 0x6f}
case LEAQ:
// https://www.felixcloutier.com/x86/lea
rexPrefix |= RexPrefixW
opcodes = []byte{0x8d}
case MOVUPD:
// https://www.felixcloutier.com/x86/movupd
mandatoryPrefix = 0x66
opcodes = []byte{0x0f, 0x10}
default:
err = errorEncodingUnsupported(n)
return
}
modRM := 0b00_000_101 | // Indicate "[RIP + 32bit displacement]" encoding.
(reg3Bits << 3) // Place the reg on ModRM:reg.

if mandatoryPrefix != 0 {
inst = append(inst, mandatoryPrefix)
}

if rexPrefix != RexPrefixNone {
inst = append(inst, rexPrefix)
}

inst = append(inst, opcodes...)
inst = append(inst, opcode...)
inst = append(inst, modRM,
0x0, 0x0, 0x0, 0x0, // Preserve 4 bytes for displacement.
)
Expand All @@ -135,8 +197,8 @@ func (a *AssemblerImpl) encodeStaticConstToRegister(n *NodeImpl) (err error) {
return
}

// CompileLoadStaticConstToRegister implements Assembler.CompileLoadStaticConstToRegister.
func (a *AssemblerImpl) CompileLoadStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) (err error) {
// CompileStaticConstToRegister implements Assembler.CompileStaticConstToRegister.
func (a *AssemblerImpl) CompileStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) (err error) {
if len(c)%2 != 0 {
err = fmt.Errorf("the length of a static constant must be even but was %d", len(c))
return
Expand All @@ -147,3 +209,16 @@ func (a *AssemblerImpl) CompileLoadStaticConstToRegister(instruction asm.Instruc
n.staticConst = c
return
}

// CompileRegisterToStaticConst implements Assembler.CompileRegisterToStaticConst.
func (a *AssemblerImpl) CompileRegisterToStaticConst(instruction asm.Instruction, srcReg asm.Register, c asm.StaticConst) (err error) {
if len(c)%2 != 0 {
err = fmt.Errorf("the length of a static constant must be even but was %d", len(c))
return
}

n := a.newNode(instruction, OperandTypesRegisterToStaticConst)
n.SrcReg = srcReg
n.staticConst = c
return
}

0 comments on commit 4136a36

Please sign in to comment.