diff options
Diffstat (limited to 'vendor/github.com/cloudwego/iasm/x86_64/operands.go')
-rw-r--r-- | vendor/github.com/cloudwego/iasm/x86_64/operands.go | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/vendor/github.com/cloudwego/iasm/x86_64/operands.go b/vendor/github.com/cloudwego/iasm/x86_64/operands.go new file mode 100644 index 000000000..61adbf83b --- /dev/null +++ b/vendor/github.com/cloudwego/iasm/x86_64/operands.go @@ -0,0 +1,665 @@ +// +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package x86_64 + +import ( + "errors" + "fmt" + "math" + "reflect" + "strconv" + "strings" + "sync/atomic" +) + +// RelativeOffset represents an RIP-relative offset. +type RelativeOffset int32 + +// String implements the fmt.Stringer interface. +func (self RelativeOffset) String() string { + if self == 0 { + return "(%rip)" + } else { + return fmt.Sprintf("%d(%%rip)", self) + } +} + +// RoundingControl represents a floating-point rounding option. +type RoundingControl uint8 + +const ( + // RN_SAE represents "Round Nearest", which is the default rounding option. + RN_SAE RoundingControl = iota + + // RD_SAE represents "Round Down". + RD_SAE + + // RU_SAE represents "Round Up". + RU_SAE + + // RZ_SAE represents "Round towards Zero". + RZ_SAE +) + +var _RC_NAMES = map[RoundingControl]string{ + RN_SAE: "rn-sae", + RD_SAE: "rd-sae", + RU_SAE: "ru-sae", + RZ_SAE: "rz-sae", +} + +func (self RoundingControl) String() string { + if v, ok := _RC_NAMES[self]; ok { + return v + } else { + panic("invalid RoundingControl value") + } +} + +// ExceptionControl represents the "Suppress All Exceptions" flag. +type ExceptionControl uint8 + +const ( + // SAE represents the flag "Suppress All Exceptions" for floating point operations. + SAE ExceptionControl = iota +) + +func (ExceptionControl) String() string { + return "sae" +} + +// AddressType indicates which kind of value that an Addressable object contains. +type AddressType uint + +const ( + // None indicates the Addressable does not contain any addressable value. + None AddressType = iota + + // Memory indicates the Addressable contains a memory address. + Memory + + // Offset indicates the Addressable contains an RIP-relative offset. + Offset + + // Reference indicates the Addressable contains a label reference. + Reference +) + +// Disposable is a type of object that can be Free'd manually. +type Disposable interface { + Free() +} + +// Label represents a location within the program. +type Label struct { + refs int64 + Name string + Dest *Instruction +} + +func (self *Label) offset(p uintptr, n int) RelativeOffset { + if self.Dest == nil { + panic("unresolved label: " + self.Name) + } else { + return RelativeOffset(self.Dest.pc - p - uintptr(n)) + } +} + +// Free decreases the reference count of a Label, if the +// refcount drops to 0, the Label will be recycled. +func (self *Label) Free() { + if atomic.AddInt64(&self.refs, -1) == 0 { + //freeLabel(self) + } +} + +// String implements the fmt.Stringer interface. +func (self *Label) String() string { + if self.Dest == nil { + return fmt.Sprintf("%s(%%rip)", self.Name) + } else { + return fmt.Sprintf("%s(%%rip)@%#x", self.Name, self.Dest.pc) + } +} + +// Retain increases the reference count of a Label. +func (self *Label) Retain() *Label { + atomic.AddInt64(&self.refs, 1) + return self +} + +// Evaluate implements the interface expr.Term. +func (self *Label) Evaluate() (int64, error) { + if self.Dest != nil { + return int64(self.Dest.pc), nil + } else { + return 0, errors.New("unresolved label: " + self.Name) + } +} + +// Addressable is a union to represent an addressable operand. +type Addressable struct { + Type AddressType + Memory MemoryAddress + Offset RelativeOffset + Reference *Label +} + +// String implements the fmt.Stringer interface. +func (self *Addressable) String() string { + switch self.Type { + case None: + return "(not addressable)" + case Memory: + return self.Memory.String() + case Offset: + return self.Offset.String() + case Reference: + return self.Reference.String() + default: + return "(invalid addressable)" + } +} + +// MemoryOperand represents a memory operand for an instruction. +type MemoryOperand struct { + refs int64 + Size int + Addr Addressable + Mask RegisterMask + Masked bool + Broadcast uint8 +} + +const ( + _Sizes = 0b10000000100010111 // bit-mask for valid sizes (0, 1, 2, 4, 8, 16) +) + +func (self *MemoryOperand) isVMX(evex bool) bool { + return self.Addr.Type == Memory && self.Addr.Memory.isVMX(evex) +} + +func (self *MemoryOperand) isVMY(evex bool) bool { + return self.Addr.Type == Memory && self.Addr.Memory.isVMY(evex) +} + +func (self *MemoryOperand) isVMZ() bool { + return self.Addr.Type == Memory && self.Addr.Memory.isVMZ() +} + +func (self *MemoryOperand) isMem() bool { + if (_Sizes & (1 << self.Broadcast)) == 0 { + return false + } else if self.Addr.Type == Memory { + return self.Addr.Memory.isMem() + } else if self.Addr.Type == Offset { + return true + } else if self.Addr.Type == Reference { + return true + } else { + return false + } +} + +func (self *MemoryOperand) isSize(n int) bool { + return self.Size == 0 || self.Size == n +} + +func (self *MemoryOperand) isBroadcast(n int, b uint8) bool { + return self.Size == n && self.Broadcast == b +} + +func (self *MemoryOperand) formatMask() string { + if !self.Masked { + return "" + } else { + return self.Mask.String() + } +} + +func (self *MemoryOperand) formatBroadcast() string { + if self.Broadcast == 0 { + return "" + } else { + return fmt.Sprintf("{1to%d}", self.Broadcast) + } +} + +func (self *MemoryOperand) ensureAddrValid() { + switch self.Addr.Type { + case None: + break + case Memory: + self.Addr.Memory.EnsureValid() + case Offset: + break + case Reference: + break + default: + panic("invalid address type") + } +} + +func (self *MemoryOperand) ensureSizeValid() { + if (_Sizes & (1 << self.Size)) == 0 { + panic("invalid memory operand size") + } +} + +func (self *MemoryOperand) ensureBroadcastValid() { + if (_Sizes & (1 << self.Broadcast)) == 0 { + panic("invalid memory operand broadcast") + } +} + +// Free decreases the reference count of a MemoryOperand, if the +// refcount drops to 0, the Label will be recycled. +func (self *MemoryOperand) Free() { + if atomic.AddInt64(&self.refs, -1) == 0 { + //freeMemoryOperand(self) + } +} + +// String implements the fmt.Stringer interface. +func (self *MemoryOperand) String() string { + return self.Addr.String() + self.formatMask() + self.formatBroadcast() +} + +// Retain increases the reference count of a MemoryOperand. +func (self *MemoryOperand) Retain() *MemoryOperand { + atomic.AddInt64(&self.refs, 1) + return self +} + +// EnsureValid checks if the memory operand is valid, if not, it panics. +func (self *MemoryOperand) EnsureValid() { + self.ensureAddrValid() + self.ensureSizeValid() + self.ensureBroadcastValid() +} + +// MemoryAddress represents a memory address. +type MemoryAddress struct { + Base Register + Index Register + Scale uint8 + Displacement int32 +} + +const ( + _Scales = 0b100010111 // bit-mask for valid scales (0, 1, 2, 4, 8) +) + +func (self *MemoryAddress) isVMX(evex bool) bool { + return self.isMemBase() && (self.Index == nil || isXMM(self.Index) || (evex && isEVEXXMM(self.Index))) +} + +func (self *MemoryAddress) isVMY(evex bool) bool { + return self.isMemBase() && (self.Index == nil || isYMM(self.Index) || (evex && isEVEXYMM(self.Index))) +} + +func (self *MemoryAddress) isVMZ() bool { + return self.isMemBase() && (self.Index == nil || isZMM(self.Index)) +} + +func (self *MemoryAddress) isMem() bool { + return self.isMemBase() && (self.Index == nil || isReg64(self.Index)) +} + +func (self *MemoryAddress) isMemBase() bool { + return (self.Base == nil || isReg64(self.Base)) && // `Base` must be 64-bit if present + (self.Scale == 0) == (self.Index == nil) && // `Scale` and `Index` depends on each other + (_Scales&(1<<self.Scale)) != 0 // `Scale` can only be 0, 1, 2, 4 or 8 +} + +// String implements the fmt.Stringer interface. +func (self *MemoryAddress) String() string { + var dp int + var sb strings.Builder + + /* the displacement part */ + if dp = int(self.Displacement); dp != 0 { + sb.WriteString(strconv.Itoa(dp)) + } + + /* the base register */ + if sb.WriteByte('('); self.Base != nil { + sb.WriteByte('%') + sb.WriteString(self.Base.String()) + } + + /* index is optional */ + if self.Index != nil { + sb.WriteString(",%") + sb.WriteString(self.Index.String()) + + /* scale is also optional */ + if self.Scale >= 2 { + sb.WriteByte(',') + sb.WriteString(strconv.Itoa(int(self.Scale))) + } + } + + /* close the bracket */ + sb.WriteByte(')') + return sb.String() +} + +// EnsureValid checks if the memory address is valid, if not, it panics. +func (self *MemoryAddress) EnsureValid() { + if !self.isMemBase() || (self.Index != nil && !isIndexable(self.Index)) { + panic("not a valid memory address") + } +} + +// Ref constructs a memory reference to a label. +func Ref(ref *Label) (v *MemoryOperand) { + v = CreateMemoryOperand() + v.Addr.Type = Reference + v.Addr.Reference = ref + return +} + +// Abs construct a simple memory address that represents absolute addressing. +func Abs(disp int32) *MemoryOperand { + return Sib(nil, nil, 0, disp) +} + +// Ptr constructs a simple memory operand with base and displacement. +func Ptr(base Register, disp int32) *MemoryOperand { + return Sib(base, nil, 0, disp) +} + +// Sib constructs a simple memory operand that represents a complete memory address. +func Sib(base Register, index Register, scale uint8, disp int32) (v *MemoryOperand) { + v = CreateMemoryOperand() + v.Addr.Type = Memory + v.Addr.Memory.Base = base + v.Addr.Memory.Index = index + v.Addr.Memory.Scale = scale + v.Addr.Memory.Displacement = disp + v.EnsureValid() + return +} + +/** Operand Matching Helpers **/ + +const _IntMask = (1 << reflect.Int) | + (1 << reflect.Int8) | + (1 << reflect.Int16) | + (1 << reflect.Int32) | + (1 << reflect.Int64) | + (1 << reflect.Uint) | + (1 << reflect.Uint8) | + (1 << reflect.Uint16) | + (1 << reflect.Uint32) | + (1 << reflect.Uint64) | + (1 << reflect.Uintptr) + +func isInt(k reflect.Kind) bool { + return (_IntMask & (1 << k)) != 0 +} + +func asInt64(v interface{}) (int64, bool) { + if isSpecial(v) { + return 0, false + } else if x := efaceOf(v); isInt(x.kind()) { + return x.toInt64(), true + } else { + return 0, false + } +} + +func inRange(v interface{}, low int64, high int64) bool { + x, ok := asInt64(v) + return ok && x >= low && x <= high +} + +func isSpecial(v interface{}) bool { + switch v.(type) { + case Register8: + return true + case Register16: + return true + case Register32: + return true + case Register64: + return true + case KRegister: + return true + case MMRegister: + return true + case XMMRegister: + return true + case YMMRegister: + return true + case ZMMRegister: + return true + case RelativeOffset: + return true + case RoundingControl: + return true + case ExceptionControl: + return true + default: + return false + } +} + +func isIndexable(v interface{}) bool { + return isZMM(v) || isReg64(v) || isEVEXXMM(v) || isEVEXYMM(v) +} + +func isImm4(v interface{}) bool { return inRange(v, 0, 15) } +func isImm8(v interface{}) bool { return inRange(v, math.MinInt8, math.MaxUint8) } +func isImm16(v interface{}) bool { return inRange(v, math.MinInt16, math.MaxUint16) } +func isImm32(v interface{}) bool { return inRange(v, math.MinInt32, math.MaxUint32) } +func isImm64(v interface{}) bool { _, r := asInt64(v); return r } +func isConst1(v interface{}) bool { x, r := asInt64(v); return r && x == 1 } +func isConst3(v interface{}) bool { x, r := asInt64(v); return r && x == 3 } +func isRel8(v interface{}) bool { + x, r := v.(RelativeOffset) + return r && x >= math.MinInt8 && x <= math.MaxInt8 +} +func isRel32(v interface{}) bool { _, r := v.(RelativeOffset); return r } +func isLabel(v interface{}) bool { _, r := v.(*Label); return r } +func isReg8(v interface{}) bool { _, r := v.(Register8); return r } +func isReg8REX(v interface{}) bool { + x, r := v.(Register8) + return r && (x&0x80) == 0 && x >= SPL +} +func isReg16(v interface{}) bool { _, r := v.(Register16); return r } +func isReg32(v interface{}) bool { _, r := v.(Register32); return r } +func isReg64(v interface{}) bool { _, r := v.(Register64); return r } +func isMM(v interface{}) bool { _, r := v.(MMRegister); return r } +func isXMM(v interface{}) bool { x, r := v.(XMMRegister); return r && x <= XMM15 } +func isEVEXXMM(v interface{}) bool { _, r := v.(XMMRegister); return r } +func isXMMk(v interface{}) bool { + x, r := v.(MaskedRegister) + return isXMM(v) || (r && isXMM(x.Reg) && !x.Mask.Z) +} +func isXMMkz(v interface{}) bool { + x, r := v.(MaskedRegister) + return isXMM(v) || (r && isXMM(x.Reg)) +} +func isYMM(v interface{}) bool { x, r := v.(YMMRegister); return r && x <= YMM15 } +func isEVEXYMM(v interface{}) bool { _, r := v.(YMMRegister); return r } +func isYMMk(v interface{}) bool { + x, r := v.(MaskedRegister) + return isYMM(v) || (r && isYMM(x.Reg) && !x.Mask.Z) +} +func isYMMkz(v interface{}) bool { + x, r := v.(MaskedRegister) + return isYMM(v) || (r && isYMM(x.Reg)) +} +func isZMM(v interface{}) bool { _, r := v.(ZMMRegister); return r } +func isZMMk(v interface{}) bool { + x, r := v.(MaskedRegister) + return isZMM(v) || (r && isZMM(x.Reg) && !x.Mask.Z) +} +func isZMMkz(v interface{}) bool { + x, r := v.(MaskedRegister) + return isZMM(v) || (r && isZMM(x.Reg)) +} +func isK(v interface{}) bool { _, r := v.(KRegister); return r } +func isKk(v interface{}) bool { + x, r := v.(MaskedRegister) + return isK(v) || (r && isK(x.Reg) && !x.Mask.Z) +} +func isM(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isMem() && x.Broadcast == 0 && !x.Masked +} +func isMk(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isMem() && x.Broadcast == 0 && !(x.Masked && x.Mask.Z) +} +func isMkz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isMem() && x.Broadcast == 0 +} +func isM8(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(1) +} +func isM16(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(2) +} +func isM16kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(2) +} +func isM32(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(4) +} +func isM32k(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMk(v) && x.isSize(4) +} +func isM32kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(4) +} +func isM64(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(8) +} +func isM64k(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMk(v) && x.isSize(8) +} +func isM64kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(8) +} +func isM128(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(16) +} +func isM128kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(16) +} +func isM256(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(32) +} +func isM256kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(32) +} +func isM512(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isM(v) && x.isSize(64) +} +func isM512kz(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && isMkz(v) && x.isSize(64) +} +func isM64M32bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM64(v) || (r && x.isBroadcast(4, 2)) +} +func isM128M32bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM128(v) || (r && x.isBroadcast(4, 4)) +} +func isM256M32bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM256(v) || (r && x.isBroadcast(4, 8)) +} +func isM512M32bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM512(v) || (r && x.isBroadcast(4, 16)) +} +func isM128M64bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM128(v) || (r && x.isBroadcast(8, 2)) +} +func isM256M64bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM256(v) || (r && x.isBroadcast(8, 4)) +} +func isM512M64bcst(v interface{}) bool { + x, r := v.(*MemoryOperand) + return isM512(v) || (r && x.isBroadcast(8, 8)) +} +func isVMX(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isVMX(false) && !x.Masked +} +func isEVEXVMX(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isVMX(true) && !x.Masked +} +func isVMXk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMX(true) } +func isVMY(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isVMY(false) && !x.Masked +} +func isEVEXVMY(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isVMY(true) && !x.Masked +} +func isVMYk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMY(true) } +func isVMZ(v interface{}) bool { + x, r := v.(*MemoryOperand) + return r && x.isVMZ() && !x.Masked +} +func isVMZk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMZ() } +func isSAE(v interface{}) bool { _, r := v.(ExceptionControl); return r } +func isER(v interface{}) bool { _, r := v.(RoundingControl); return r } + +func isImmExt(v interface{}, ext int, min int64, max int64) bool { + if x, ok := asInt64(v); !ok { + return false + } else if m := int64(1) << (8 * ext); x < m && x >= m+min { + return true + } else { + return x <= max && x >= min + } +} + +func isImm8Ext(v interface{}, ext int) bool { + return isImmExt(v, ext, math.MinInt8, math.MaxInt8) +} + +func isImm32Ext(v interface{}, ext int) bool { + return isImmExt(v, ext, math.MinInt32, math.MaxInt32) +} |