package amd64
import (
"fmt"
"unsafe"
"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
)
type operand struct {
kind operandKind
data uint64
}
type operandKind byte
const (
operandKindReg operandKind = iota + 1
operandKindMem
operandKindImm32
operandKindLabel
)
func (o operandKind ) String () string {
switch o {
case operandKindReg :
return "reg"
case operandKindMem :
return "mem"
case operandKindImm32 :
return "imm32"
case operandKindLabel :
return "label"
default :
panic ("BUG: invalid operand kind" )
}
}
func (o *operand ) format (_64 bool ) string {
switch o .kind {
case operandKindReg :
return formatVRegSized (o .reg (), _64 )
case operandKindMem :
return o .addressMode ().String ()
case operandKindImm32 :
return fmt .Sprintf ("$%d" , int32 (o .imm32 ()))
case operandKindLabel :
return label (o .imm32 ()).String ()
default :
panic (fmt .Sprintf ("BUG: invalid operand: %s" , o .kind ))
}
}
func (o *operand ) reg () regalloc .VReg {
return regalloc .VReg (o .data )
}
func (o *operand ) setReg (r regalloc .VReg ) {
o .data = uint64 (r )
}
func (o *operand ) addressMode () *amode {
return wazevoapi .PtrFromUintptr [amode ](uintptr (o .data ))
}
func (o *operand ) imm32 () uint32 {
return uint32 (o .data )
}
func (o *operand ) label () label {
switch o .kind {
case operandKindLabel :
return label (o .data )
case operandKindMem :
mem := o .addressMode ()
if mem .kind () != amodeRipRel {
panic ("BUG: invalid label" )
}
return label (mem .imm32 )
default :
panic ("BUG: invalid operand kind" )
}
}
func newOperandLabel(label label ) operand {
return operand {kind : operandKindLabel , data : uint64 (label )}
}
func newOperandReg(r regalloc .VReg ) operand {
return operand {kind : operandKindReg , data : uint64 (r )}
}
func newOperandImm32(imm32 uint32 ) operand {
return operand {kind : operandKindImm32 , data : uint64 (imm32 )}
}
func newOperandMem(amode *amode ) operand {
return operand {kind : operandKindMem , data : uint64 (uintptr (unsafe .Pointer (amode )))}
}
type amode struct {
kindWithShift uint32
imm32 uint32
base regalloc .VReg
index regalloc .VReg
}
type amodeKind byte
const (
amodeImmReg amodeKind = iota + 1
amodeImmRBP
amodeRegRegShift
amodeRipRel
)
func (a *amode ) kind () amodeKind {
return amodeKind (a .kindWithShift & 0xff )
}
func (a *amode ) shift () byte {
return byte (a .kindWithShift >> 8 )
}
func (a *amode ) uses (rs *[]regalloc .VReg ) {
switch a .kind () {
case amodeImmReg :
*rs = append (*rs , a .base )
case amodeRegRegShift :
*rs = append (*rs , a .base , a .index )
case amodeImmRBP , amodeRipRel :
default :
panic ("BUG: invalid amode kind" )
}
}
func (a *amode ) nregs () int {
switch a .kind () {
case amodeImmReg :
return 1
case amodeRegRegShift :
return 2
case amodeImmRBP , amodeRipRel :
return 0
default :
panic ("BUG: invalid amode kind" )
}
}
func (a *amode ) assignUses (i int , reg regalloc .VReg ) {
switch a .kind () {
case amodeImmReg :
if i == 0 {
a .base = reg
} else {
panic ("BUG: invalid amode assignment" )
}
case amodeRegRegShift :
if i == 0 {
a .base = reg
} else if i == 1 {
a .index = reg
} else {
panic ("BUG: invalid amode assignment" )
}
default :
panic ("BUG: invalid amode assignment" )
}
}
func (m *machine ) newAmodeImmReg (imm32 uint32 , base regalloc .VReg ) *amode {
ret := m .amodePool .Allocate ()
*ret = amode {kindWithShift : uint32 (amodeImmReg ), imm32 : imm32 , base : base }
return ret
}
func (m *machine ) newAmodeImmRBPReg (imm32 uint32 ) *amode {
ret := m .amodePool .Allocate ()
*ret = amode {kindWithShift : uint32 (amodeImmRBP ), imm32 : imm32 , base : rbpVReg }
return ret
}
func (m *machine ) newAmodeRegRegShift (imm32 uint32 , base , index regalloc .VReg , shift byte ) *amode {
if shift > 3 {
panic (fmt .Sprintf ("BUG: invalid shift (must be 3>=): %d" , shift ))
}
ret := m .amodePool .Allocate ()
*ret = amode {kindWithShift : uint32 (amodeRegRegShift ) | uint32 (shift )<<8 , imm32 : imm32 , base : base , index : index }
return ret
}
func (m *machine ) newAmodeRipRel (label label ) *amode {
ret := m .amodePool .Allocate ()
*ret = amode {kindWithShift : uint32 (amodeRipRel ), imm32 : uint32 (label )}
return ret
}
func (a *amode ) String () string {
switch a .kind () {
case amodeImmReg , amodeImmRBP :
if a .imm32 == 0 {
return fmt .Sprintf ("(%s)" , formatVRegSized (a .base , true ))
}
return fmt .Sprintf ("%d(%s)" , int32 (a .imm32 ), formatVRegSized (a .base , true ))
case amodeRegRegShift :
shift := 1 << a .shift ()
if a .imm32 == 0 {
return fmt .Sprintf (
"(%s,%s,%d)" ,
formatVRegSized (a .base , true ), formatVRegSized (a .index , true ), shift )
}
return fmt .Sprintf (
"%d(%s,%s,%d)" ,
int32 (a .imm32 ), formatVRegSized (a .base , true ), formatVRegSized (a .index , true ), shift )
case amodeRipRel :
return fmt .Sprintf ("%s(%%rip)" , label (a .imm32 ))
default :
panic ("BUG: invalid amode kind" )
}
}
func (m *machine ) getOperand_Mem_Reg (def backend .SSAValueDefinition ) (op operand ) {
if !def .IsFromInstr () {
return newOperandReg (m .c .VRegOf (def .V ))
}
if def .V .Type () == ssa .TypeV128 {
return m .getOperand_Reg (def )
}
if m .c .MatchInstr (def , ssa .OpcodeLoad ) {
instr := def .Instr
ptr , offset , _ := instr .LoadData ()
op = newOperandMem (m .lowerToAddressMode (ptr , offset ))
instr .MarkLowered ()
return op
}
return m .getOperand_Reg (def )
}
func (m *machine ) getOperand_Mem_Imm32_Reg (def backend .SSAValueDefinition ) (op operand ) {
if !def .IsFromInstr () {
return newOperandReg (m .c .VRegOf (def .V ))
}
if m .c .MatchInstr (def , ssa .OpcodeLoad ) {
instr := def .Instr
ptr , offset , _ := instr .LoadData ()
op = newOperandMem (m .lowerToAddressMode (ptr , offset ))
instr .MarkLowered ()
return op
}
return m .getOperand_Imm32_Reg (def )
}
func (m *machine ) getOperand_Imm32_Reg (def backend .SSAValueDefinition ) (op operand ) {
if !def .IsFromInstr () {
return newOperandReg (m .c .VRegOf (def .V ))
}
instr := def .Instr
if instr .Constant () {
if op , ok := asImm32Operand (instr .ConstantVal (), instr .Return ().Type () == ssa .TypeI32 ); ok {
instr .MarkLowered ()
return op
}
}
return m .getOperand_Reg (def )
}
func asImm32Operand(val uint64 , allowSignExt bool ) (operand , bool ) {
if imm32 , ok := asImm32 (val , allowSignExt ); ok {
return newOperandImm32 (imm32 ), true
}
return operand {}, false
}
func asImm32(val uint64 , allowSignExt bool ) (uint32 , bool ) {
u32val := uint32 (val )
if uint64 (u32val ) != val {
return 0 , false
}
if !allowSignExt && u32val &0x80000000 != 0 {
return 0 , false
}
return u32val , true
}
func (m *machine ) getOperand_Reg (def backend .SSAValueDefinition ) (op operand ) {
var v regalloc .VReg
if instr := def .Instr ; instr != nil && instr .Constant () {
v = m .lowerConstant (instr )
instr .MarkLowered ()
} else {
v = m .c .VRegOf (def .V )
}
return newOperandReg (v )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .