operands.go raw

   1  //
   2  // Copyright 2024 CloudWeGo Authors
   3  //
   4  // Licensed under the Apache License, Version 2.0 (the "License");
   5  // you may not use this file except in compliance with the License.
   6  // You may obtain a copy of the License at
   7  //
   8  //     http://www.apache.org/licenses/LICENSE-2.0
   9  //
  10  // Unless required by applicable law or agreed to in writing, software
  11  // distributed under the License is distributed on an "AS IS" BASIS,
  12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  // See the License for the specific language governing permissions and
  14  // limitations under the License.
  15  //
  16  
  17  package x86_64
  18  
  19  import (
  20  	"errors"
  21  	"fmt"
  22  	"math"
  23  	"reflect"
  24  	"strconv"
  25  	"strings"
  26  	"sync/atomic"
  27  )
  28  
  29  // RelativeOffset represents an RIP-relative offset.
  30  type RelativeOffset int32
  31  
  32  // String implements the fmt.Stringer interface.
  33  func (self RelativeOffset) String() string {
  34  	if self == 0 {
  35  		return "(%rip)"
  36  	} else {
  37  		return fmt.Sprintf("%d(%%rip)", self)
  38  	}
  39  }
  40  
  41  // RoundingControl represents a floating-point rounding option.
  42  type RoundingControl uint8
  43  
  44  const (
  45  	// RN_SAE represents "Round Nearest", which is the default rounding option.
  46  	RN_SAE RoundingControl = iota
  47  
  48  	// RD_SAE represents "Round Down".
  49  	RD_SAE
  50  
  51  	// RU_SAE represents "Round Up".
  52  	RU_SAE
  53  
  54  	// RZ_SAE represents "Round towards Zero".
  55  	RZ_SAE
  56  )
  57  
  58  var _RC_NAMES = map[RoundingControl]string{
  59  	RN_SAE: "rn-sae",
  60  	RD_SAE: "rd-sae",
  61  	RU_SAE: "ru-sae",
  62  	RZ_SAE: "rz-sae",
  63  }
  64  
  65  func (self RoundingControl) String() string {
  66  	if v, ok := _RC_NAMES[self]; ok {
  67  		return v
  68  	} else {
  69  		panic("invalid RoundingControl value")
  70  	}
  71  }
  72  
  73  // ExceptionControl represents the "Suppress All Exceptions" flag.
  74  type ExceptionControl uint8
  75  
  76  const (
  77  	// SAE represents the flag "Suppress All Exceptions" for floating point operations.
  78  	SAE ExceptionControl = iota
  79  )
  80  
  81  func (ExceptionControl) String() string {
  82  	return "sae"
  83  }
  84  
  85  // AddressType indicates which kind of value that an Addressable object contains.
  86  type AddressType uint
  87  
  88  const (
  89  	// None indicates the Addressable does not contain any addressable value.
  90  	None AddressType = iota
  91  
  92  	// Memory indicates the Addressable contains a memory address.
  93  	Memory
  94  
  95  	// Offset indicates the Addressable contains an RIP-relative offset.
  96  	Offset
  97  
  98  	// Reference indicates the Addressable contains a label reference.
  99  	Reference
 100  )
 101  
 102  // Disposable is a type of object that can be Free'd manually.
 103  type Disposable interface {
 104  	Free()
 105  }
 106  
 107  // Label represents a location within the program.
 108  type Label struct {
 109  	refs int64
 110  	Name string
 111  	Dest *Instruction
 112  }
 113  
 114  func (self *Label) offset(p uintptr, n int) RelativeOffset {
 115  	if self.Dest == nil {
 116  		panic("unresolved label: " + self.Name)
 117  	} else {
 118  		return RelativeOffset(self.Dest.pc - p - uintptr(n))
 119  	}
 120  }
 121  
 122  // Free decreases the reference count of a Label, if the
 123  // refcount drops to 0, the Label will be recycled.
 124  func (self *Label) Free() {
 125  	if atomic.AddInt64(&self.refs, -1) == 0 {
 126  		//freeLabel(self)
 127  	}
 128  }
 129  
 130  // String implements the fmt.Stringer interface.
 131  func (self *Label) String() string {
 132  	if self.Dest == nil {
 133  		return fmt.Sprintf("%s(%%rip)", self.Name)
 134  	} else {
 135  		return fmt.Sprintf("%s(%%rip)@%#x", self.Name, self.Dest.pc)
 136  	}
 137  }
 138  
 139  // Retain increases the reference count of a Label.
 140  func (self *Label) Retain() *Label {
 141  	atomic.AddInt64(&self.refs, 1)
 142  	return self
 143  }
 144  
 145  // Evaluate implements the interface expr.Term.
 146  func (self *Label) Evaluate() (int64, error) {
 147  	if self.Dest != nil {
 148  		return int64(self.Dest.pc), nil
 149  	} else {
 150  		return 0, errors.New("unresolved label: " + self.Name)
 151  	}
 152  }
 153  
 154  // Addressable is a union to represent an addressable operand.
 155  type Addressable struct {
 156  	Type      AddressType
 157  	Memory    MemoryAddress
 158  	Offset    RelativeOffset
 159  	Reference *Label
 160  }
 161  
 162  // String implements the fmt.Stringer interface.
 163  func (self *Addressable) String() string {
 164  	switch self.Type {
 165  	case None:
 166  		return "(not addressable)"
 167  	case Memory:
 168  		return self.Memory.String()
 169  	case Offset:
 170  		return self.Offset.String()
 171  	case Reference:
 172  		return self.Reference.String()
 173  	default:
 174  		return "(invalid addressable)"
 175  	}
 176  }
 177  
 178  // MemoryOperand represents a memory operand for an instruction.
 179  type MemoryOperand struct {
 180  	refs      int64
 181  	Size      int
 182  	Addr      Addressable
 183  	Mask      RegisterMask
 184  	Masked    bool
 185  	Broadcast uint8
 186  }
 187  
 188  const (
 189  	_Sizes = 0b10000000100010111 // bit-mask for valid sizes (0, 1, 2, 4, 8, 16)
 190  )
 191  
 192  func (self *MemoryOperand) isVMX(evex bool) bool {
 193  	return self.Addr.Type == Memory && self.Addr.Memory.isVMX(evex)
 194  }
 195  
 196  func (self *MemoryOperand) isVMY(evex bool) bool {
 197  	return self.Addr.Type == Memory && self.Addr.Memory.isVMY(evex)
 198  }
 199  
 200  func (self *MemoryOperand) isVMZ() bool {
 201  	return self.Addr.Type == Memory && self.Addr.Memory.isVMZ()
 202  }
 203  
 204  func (self *MemoryOperand) isMem() bool {
 205  	if (_Sizes & (1 << self.Broadcast)) == 0 {
 206  		return false
 207  	} else if self.Addr.Type == Memory {
 208  		return self.Addr.Memory.isMem()
 209  	} else if self.Addr.Type == Offset {
 210  		return true
 211  	} else if self.Addr.Type == Reference {
 212  		return true
 213  	} else {
 214  		return false
 215  	}
 216  }
 217  
 218  func (self *MemoryOperand) isSize(n int) bool {
 219  	return self.Size == 0 || self.Size == n
 220  }
 221  
 222  func (self *MemoryOperand) isBroadcast(n int, b uint8) bool {
 223  	return self.Size == n && self.Broadcast == b
 224  }
 225  
 226  func (self *MemoryOperand) formatMask() string {
 227  	if !self.Masked {
 228  		return ""
 229  	} else {
 230  		return self.Mask.String()
 231  	}
 232  }
 233  
 234  func (self *MemoryOperand) formatBroadcast() string {
 235  	if self.Broadcast == 0 {
 236  		return ""
 237  	} else {
 238  		return fmt.Sprintf("{1to%d}", self.Broadcast)
 239  	}
 240  }
 241  
 242  func (self *MemoryOperand) ensureAddrValid() {
 243  	switch self.Addr.Type {
 244  	case None:
 245  		break
 246  	case Memory:
 247  		self.Addr.Memory.EnsureValid()
 248  	case Offset:
 249  		break
 250  	case Reference:
 251  		break
 252  	default:
 253  		panic("invalid address type")
 254  	}
 255  }
 256  
 257  func (self *MemoryOperand) ensureSizeValid() {
 258  	if (_Sizes & (1 << self.Size)) == 0 {
 259  		panic("invalid memory operand size")
 260  	}
 261  }
 262  
 263  func (self *MemoryOperand) ensureBroadcastValid() {
 264  	if (_Sizes & (1 << self.Broadcast)) == 0 {
 265  		panic("invalid memory operand broadcast")
 266  	}
 267  }
 268  
 269  // Free decreases the reference count of a MemoryOperand, if the
 270  // refcount drops to 0, the Label will be recycled.
 271  func (self *MemoryOperand) Free() {
 272  	if atomic.AddInt64(&self.refs, -1) == 0 {
 273  		//freeMemoryOperand(self)
 274  	}
 275  }
 276  
 277  // String implements the fmt.Stringer interface.
 278  func (self *MemoryOperand) String() string {
 279  	return self.Addr.String() + self.formatMask() + self.formatBroadcast()
 280  }
 281  
 282  // Retain increases the reference count of a MemoryOperand.
 283  func (self *MemoryOperand) Retain() *MemoryOperand {
 284  	atomic.AddInt64(&self.refs, 1)
 285  	return self
 286  }
 287  
 288  // EnsureValid checks if the memory operand is valid, if not, it panics.
 289  func (self *MemoryOperand) EnsureValid() {
 290  	self.ensureAddrValid()
 291  	self.ensureSizeValid()
 292  	self.ensureBroadcastValid()
 293  }
 294  
 295  // MemoryAddress represents a memory address.
 296  type MemoryAddress struct {
 297  	Base         Register
 298  	Index        Register
 299  	Scale        uint8
 300  	Displacement int32
 301  }
 302  
 303  const (
 304  	_Scales = 0b100010111 // bit-mask for valid scales (0, 1, 2, 4, 8)
 305  )
 306  
 307  func (self *MemoryAddress) isVMX(evex bool) bool {
 308  	return self.isMemBase() && (self.Index == nil || isXMM(self.Index) || (evex && isEVEXXMM(self.Index)))
 309  }
 310  
 311  func (self *MemoryAddress) isVMY(evex bool) bool {
 312  	return self.isMemBase() && (self.Index == nil || isYMM(self.Index) || (evex && isEVEXYMM(self.Index)))
 313  }
 314  
 315  func (self *MemoryAddress) isVMZ() bool {
 316  	return self.isMemBase() && (self.Index == nil || isZMM(self.Index))
 317  }
 318  
 319  func (self *MemoryAddress) isMem() bool {
 320  	return self.isMemBase() && (self.Index == nil || isReg64(self.Index))
 321  }
 322  
 323  func (self *MemoryAddress) isMemBase() bool {
 324  	return (self.Base == nil || isReg64(self.Base)) && // `Base` must be 64-bit if present
 325  		(self.Scale == 0) == (self.Index == nil) && // `Scale` and `Index` depends on each other
 326  		(_Scales&(1<<self.Scale)) != 0 // `Scale` can only be 0, 1, 2, 4 or 8
 327  }
 328  
 329  // String implements the fmt.Stringer interface.
 330  func (self *MemoryAddress) String() string {
 331  	var dp int
 332  	var sb strings.Builder
 333  
 334  	/* the displacement part */
 335  	if dp = int(self.Displacement); dp != 0 {
 336  		sb.WriteString(strconv.Itoa(dp))
 337  	}
 338  
 339  	/* the base register */
 340  	if sb.WriteByte('('); self.Base != nil {
 341  		sb.WriteByte('%')
 342  		sb.WriteString(self.Base.String())
 343  	}
 344  
 345  	/* index is optional */
 346  	if self.Index != nil {
 347  		sb.WriteString(",%")
 348  		sb.WriteString(self.Index.String())
 349  
 350  		/* scale is also optional */
 351  		if self.Scale >= 2 {
 352  			sb.WriteByte(',')
 353  			sb.WriteString(strconv.Itoa(int(self.Scale)))
 354  		}
 355  	}
 356  
 357  	/* close the bracket */
 358  	sb.WriteByte(')')
 359  	return sb.String()
 360  }
 361  
 362  // EnsureValid checks if the memory address is valid, if not, it panics.
 363  func (self *MemoryAddress) EnsureValid() {
 364  	if !self.isMemBase() || (self.Index != nil && !isIndexable(self.Index)) {
 365  		panic("not a valid memory address")
 366  	}
 367  }
 368  
 369  // Ref constructs a memory reference to a label.
 370  func Ref(ref *Label) (v *MemoryOperand) {
 371  	v = CreateMemoryOperand()
 372  	v.Addr.Type = Reference
 373  	v.Addr.Reference = ref
 374  	return
 375  }
 376  
 377  // Abs construct a simple memory address that represents absolute addressing.
 378  func Abs(disp int32) *MemoryOperand {
 379  	return Sib(nil, nil, 0, disp)
 380  }
 381  
 382  // Ptr constructs a simple memory operand with base and displacement.
 383  func Ptr(base Register, disp int32) *MemoryOperand {
 384  	return Sib(base, nil, 0, disp)
 385  }
 386  
 387  // Sib constructs a simple memory operand that represents a complete memory address.
 388  func Sib(base Register, index Register, scale uint8, disp int32) (v *MemoryOperand) {
 389  	v = CreateMemoryOperand()
 390  	v.Addr.Type = Memory
 391  	v.Addr.Memory.Base = base
 392  	v.Addr.Memory.Index = index
 393  	v.Addr.Memory.Scale = scale
 394  	v.Addr.Memory.Displacement = disp
 395  	v.EnsureValid()
 396  	return
 397  }
 398  
 399  /** Operand Matching Helpers **/
 400  
 401  const _IntMask = (1 << reflect.Int) |
 402  	(1 << reflect.Int8) |
 403  	(1 << reflect.Int16) |
 404  	(1 << reflect.Int32) |
 405  	(1 << reflect.Int64) |
 406  	(1 << reflect.Uint) |
 407  	(1 << reflect.Uint8) |
 408  	(1 << reflect.Uint16) |
 409  	(1 << reflect.Uint32) |
 410  	(1 << reflect.Uint64) |
 411  	(1 << reflect.Uintptr)
 412  
 413  func isInt(k reflect.Kind) bool {
 414  	return (_IntMask & (1 << k)) != 0
 415  }
 416  
 417  func asInt64(v interface{}) (int64, bool) {
 418  	if isSpecial(v) {
 419  		return 0, false
 420  	} else if x := efaceOf(v); isInt(x.kind()) {
 421  		return x.toInt64(), true
 422  	} else {
 423  		return 0, false
 424  	}
 425  }
 426  
 427  func inRange(v interface{}, low int64, high int64) bool {
 428  	x, ok := asInt64(v)
 429  	return ok && x >= low && x <= high
 430  }
 431  
 432  func isSpecial(v interface{}) bool {
 433  	switch v.(type) {
 434  	case Register8:
 435  		return true
 436  	case Register16:
 437  		return true
 438  	case Register32:
 439  		return true
 440  	case Register64:
 441  		return true
 442  	case KRegister:
 443  		return true
 444  	case MMRegister:
 445  		return true
 446  	case XMMRegister:
 447  		return true
 448  	case YMMRegister:
 449  		return true
 450  	case ZMMRegister:
 451  		return true
 452  	case RelativeOffset:
 453  		return true
 454  	case RoundingControl:
 455  		return true
 456  	case ExceptionControl:
 457  		return true
 458  	default:
 459  		return false
 460  	}
 461  }
 462  
 463  func isIndexable(v interface{}) bool {
 464  	return isZMM(v) || isReg64(v) || isEVEXXMM(v) || isEVEXYMM(v)
 465  }
 466  
 467  func isImm4(v interface{}) bool   { return inRange(v, 0, 15) }
 468  func isImm8(v interface{}) bool   { return inRange(v, math.MinInt8, math.MaxUint8) }
 469  func isImm16(v interface{}) bool  { return inRange(v, math.MinInt16, math.MaxUint16) }
 470  func isImm32(v interface{}) bool  { return inRange(v, math.MinInt32, math.MaxUint32) }
 471  func isImm64(v interface{}) bool  { _, r := asInt64(v); return r }
 472  func isConst1(v interface{}) bool { x, r := asInt64(v); return r && x == 1 }
 473  func isConst3(v interface{}) bool { x, r := asInt64(v); return r && x == 3 }
 474  func isRel8(v interface{}) bool {
 475  	x, r := v.(RelativeOffset)
 476  	return r && x >= math.MinInt8 && x <= math.MaxInt8
 477  }
 478  func isRel32(v interface{}) bool { _, r := v.(RelativeOffset); return r }
 479  func isLabel(v interface{}) bool { _, r := v.(*Label); return r }
 480  func isReg8(v interface{}) bool  { _, r := v.(Register8); return r }
 481  func isReg8REX(v interface{}) bool {
 482  	x, r := v.(Register8)
 483  	return r && (x&0x80) == 0 && x >= SPL
 484  }
 485  func isReg16(v interface{}) bool   { _, r := v.(Register16); return r }
 486  func isReg32(v interface{}) bool   { _, r := v.(Register32); return r }
 487  func isReg64(v interface{}) bool   { _, r := v.(Register64); return r }
 488  func isMM(v interface{}) bool      { _, r := v.(MMRegister); return r }
 489  func isXMM(v interface{}) bool     { x, r := v.(XMMRegister); return r && x <= XMM15 }
 490  func isEVEXXMM(v interface{}) bool { _, r := v.(XMMRegister); return r }
 491  func isXMMk(v interface{}) bool {
 492  	x, r := v.(MaskedRegister)
 493  	return isXMM(v) || (r && isXMM(x.Reg) && !x.Mask.Z)
 494  }
 495  func isXMMkz(v interface{}) bool {
 496  	x, r := v.(MaskedRegister)
 497  	return isXMM(v) || (r && isXMM(x.Reg))
 498  }
 499  func isYMM(v interface{}) bool     { x, r := v.(YMMRegister); return r && x <= YMM15 }
 500  func isEVEXYMM(v interface{}) bool { _, r := v.(YMMRegister); return r }
 501  func isYMMk(v interface{}) bool {
 502  	x, r := v.(MaskedRegister)
 503  	return isYMM(v) || (r && isYMM(x.Reg) && !x.Mask.Z)
 504  }
 505  func isYMMkz(v interface{}) bool {
 506  	x, r := v.(MaskedRegister)
 507  	return isYMM(v) || (r && isYMM(x.Reg))
 508  }
 509  func isZMM(v interface{}) bool { _, r := v.(ZMMRegister); return r }
 510  func isZMMk(v interface{}) bool {
 511  	x, r := v.(MaskedRegister)
 512  	return isZMM(v) || (r && isZMM(x.Reg) && !x.Mask.Z)
 513  }
 514  func isZMMkz(v interface{}) bool {
 515  	x, r := v.(MaskedRegister)
 516  	return isZMM(v) || (r && isZMM(x.Reg))
 517  }
 518  func isK(v interface{}) bool { _, r := v.(KRegister); return r }
 519  func isKk(v interface{}) bool {
 520  	x, r := v.(MaskedRegister)
 521  	return isK(v) || (r && isK(x.Reg) && !x.Mask.Z)
 522  }
 523  func isM(v interface{}) bool {
 524  	x, r := v.(*MemoryOperand)
 525  	return r && x.isMem() && x.Broadcast == 0 && !x.Masked
 526  }
 527  func isMk(v interface{}) bool {
 528  	x, r := v.(*MemoryOperand)
 529  	return r && x.isMem() && x.Broadcast == 0 && !(x.Masked && x.Mask.Z)
 530  }
 531  func isMkz(v interface{}) bool {
 532  	x, r := v.(*MemoryOperand)
 533  	return r && x.isMem() && x.Broadcast == 0
 534  }
 535  func isM8(v interface{}) bool {
 536  	x, r := v.(*MemoryOperand)
 537  	return r && isM(v) && x.isSize(1)
 538  }
 539  func isM16(v interface{}) bool {
 540  	x, r := v.(*MemoryOperand)
 541  	return r && isM(v) && x.isSize(2)
 542  }
 543  func isM16kz(v interface{}) bool {
 544  	x, r := v.(*MemoryOperand)
 545  	return r && isMkz(v) && x.isSize(2)
 546  }
 547  func isM32(v interface{}) bool {
 548  	x, r := v.(*MemoryOperand)
 549  	return r && isM(v) && x.isSize(4)
 550  }
 551  func isM32k(v interface{}) bool {
 552  	x, r := v.(*MemoryOperand)
 553  	return r && isMk(v) && x.isSize(4)
 554  }
 555  func isM32kz(v interface{}) bool {
 556  	x, r := v.(*MemoryOperand)
 557  	return r && isMkz(v) && x.isSize(4)
 558  }
 559  func isM64(v interface{}) bool {
 560  	x, r := v.(*MemoryOperand)
 561  	return r && isM(v) && x.isSize(8)
 562  }
 563  func isM64k(v interface{}) bool {
 564  	x, r := v.(*MemoryOperand)
 565  	return r && isMk(v) && x.isSize(8)
 566  }
 567  func isM64kz(v interface{}) bool {
 568  	x, r := v.(*MemoryOperand)
 569  	return r && isMkz(v) && x.isSize(8)
 570  }
 571  func isM128(v interface{}) bool {
 572  	x, r := v.(*MemoryOperand)
 573  	return r && isM(v) && x.isSize(16)
 574  }
 575  func isM128kz(v interface{}) bool {
 576  	x, r := v.(*MemoryOperand)
 577  	return r && isMkz(v) && x.isSize(16)
 578  }
 579  func isM256(v interface{}) bool {
 580  	x, r := v.(*MemoryOperand)
 581  	return r && isM(v) && x.isSize(32)
 582  }
 583  func isM256kz(v interface{}) bool {
 584  	x, r := v.(*MemoryOperand)
 585  	return r && isMkz(v) && x.isSize(32)
 586  }
 587  func isM512(v interface{}) bool {
 588  	x, r := v.(*MemoryOperand)
 589  	return r && isM(v) && x.isSize(64)
 590  }
 591  func isM512kz(v interface{}) bool {
 592  	x, r := v.(*MemoryOperand)
 593  	return r && isMkz(v) && x.isSize(64)
 594  }
 595  func isM64M32bcst(v interface{}) bool {
 596  	x, r := v.(*MemoryOperand)
 597  	return isM64(v) || (r && x.isBroadcast(4, 2))
 598  }
 599  func isM128M32bcst(v interface{}) bool {
 600  	x, r := v.(*MemoryOperand)
 601  	return isM128(v) || (r && x.isBroadcast(4, 4))
 602  }
 603  func isM256M32bcst(v interface{}) bool {
 604  	x, r := v.(*MemoryOperand)
 605  	return isM256(v) || (r && x.isBroadcast(4, 8))
 606  }
 607  func isM512M32bcst(v interface{}) bool {
 608  	x, r := v.(*MemoryOperand)
 609  	return isM512(v) || (r && x.isBroadcast(4, 16))
 610  }
 611  func isM128M64bcst(v interface{}) bool {
 612  	x, r := v.(*MemoryOperand)
 613  	return isM128(v) || (r && x.isBroadcast(8, 2))
 614  }
 615  func isM256M64bcst(v interface{}) bool {
 616  	x, r := v.(*MemoryOperand)
 617  	return isM256(v) || (r && x.isBroadcast(8, 4))
 618  }
 619  func isM512M64bcst(v interface{}) bool {
 620  	x, r := v.(*MemoryOperand)
 621  	return isM512(v) || (r && x.isBroadcast(8, 8))
 622  }
 623  func isVMX(v interface{}) bool {
 624  	x, r := v.(*MemoryOperand)
 625  	return r && x.isVMX(false) && !x.Masked
 626  }
 627  func isEVEXVMX(v interface{}) bool {
 628  	x, r := v.(*MemoryOperand)
 629  	return r && x.isVMX(true) && !x.Masked
 630  }
 631  func isVMXk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMX(true) }
 632  func isVMY(v interface{}) bool {
 633  	x, r := v.(*MemoryOperand)
 634  	return r && x.isVMY(false) && !x.Masked
 635  }
 636  func isEVEXVMY(v interface{}) bool {
 637  	x, r := v.(*MemoryOperand)
 638  	return r && x.isVMY(true) && !x.Masked
 639  }
 640  func isVMYk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMY(true) }
 641  func isVMZ(v interface{}) bool {
 642  	x, r := v.(*MemoryOperand)
 643  	return r && x.isVMZ() && !x.Masked
 644  }
 645  func isVMZk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMZ() }
 646  func isSAE(v interface{}) bool  { _, r := v.(ExceptionControl); return r }
 647  func isER(v interface{}) bool   { _, r := v.(RoundingControl); return r }
 648  
 649  func isImmExt(v interface{}, ext int, min int64, max int64) bool {
 650  	if x, ok := asInt64(v); !ok {
 651  		return false
 652  	} else if m := int64(1) << (8 * ext); x < m && x >= m+min {
 653  		return true
 654  	} else {
 655  		return x <= max && x >= min
 656  	}
 657  }
 658  
 659  func isImm8Ext(v interface{}, ext int) bool {
 660  	return isImmExt(v, ext, math.MinInt8, math.MaxInt8)
 661  }
 662  
 663  func isImm32Ext(v interface{}, ext int) bool {
 664  	return isImmExt(v, ext, math.MinInt32, math.MaxInt32)
 665  }
 666