blob: 65f789833290f8b3a4c22cc1104ea1e4f7a870a9 [file] [log] [blame]
// cmd/7l/asm.c, cmd/7l/asmout.c, cmd/7l/optab.c, cmd/7l/span.c, cmd/ld/sub.c, cmd/ld/mod.c, from Vita Nuova.
// https://code.google.com/p/ken-cc/source/browse/
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package arm64
import (
"cmd/internal/obj"
"cmd/internal/objabi"
"fmt"
"log"
"math"
"sort"
)
// ctxt7 holds state while assembling a single function.
// Each function gets a fresh ctxt7.
// This allows for multiple functions to be safely concurrently assembled.
type ctxt7 struct {
ctxt *obj.Link
newprog obj.ProgAlloc
cursym *obj.LSym
blitrl *obj.Prog
elitrl *obj.Prog
autosize int32
extrasize int32
instoffset int64
pc int64
pool struct {
start uint32
size uint32
}
}
const (
funcAlign = 16
)
const (
REGFROM = 1
)
type Optab struct {
as obj.As
a1 uint8
a2 uint8
a3 uint8
a4 uint8
type_ int8
size int8
param int16
flag int8
scond uint16
}
func IsAtomicInstruction(as obj.As) bool {
_, ok := atomicInstructions[as]
return ok
}
// known field values of an instruction.
var atomicInstructions = map[obj.As]uint32{
ALDADDAD: 3<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDAW: 2<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDAH: 1<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDAB: 0<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDALD: 3<<30 | 0x1c7<<21 | 0x00<<10,
ALDADDALW: 2<<30 | 0x1c7<<21 | 0x00<<10,
ALDADDALH: 1<<30 | 0x1c7<<21 | 0x00<<10,
ALDADDALB: 0<<30 | 0x1c7<<21 | 0x00<<10,
ALDADDD: 3<<30 | 0x1c1<<21 | 0x00<<10,
ALDADDW: 2<<30 | 0x1c1<<21 | 0x00<<10,
ALDADDH: 1<<30 | 0x1c1<<21 | 0x00<<10,
ALDADDB: 0<<30 | 0x1c1<<21 | 0x00<<10,
ALDADDLD: 3<<30 | 0x1c3<<21 | 0x00<<10,
ALDADDLW: 2<<30 | 0x1c3<<21 | 0x00<<10,
ALDADDLH: 1<<30 | 0x1c3<<21 | 0x00<<10,
ALDADDLB: 0<<30 | 0x1c3<<21 | 0x00<<10,
ALDANDAD: 3<<30 | 0x1c5<<21 | 0x04<<10,
ALDANDAW: 2<<30 | 0x1c5<<21 | 0x04<<10,
ALDANDAH: 1<<30 | 0x1c5<<21 | 0x04<<10,
ALDANDAB: 0<<30 | 0x1c5<<21 | 0x04<<10,
ALDANDALD: 3<<30 | 0x1c7<<21 | 0x04<<10,
ALDANDALW: 2<<30 | 0x1c7<<21 | 0x04<<10,
ALDANDALH: 1<<30 | 0x1c7<<21 | 0x04<<10,
ALDANDALB: 0<<30 | 0x1c7<<21 | 0x04<<10,
ALDANDD: 3<<30 | 0x1c1<<21 | 0x04<<10,
ALDANDW: 2<<30 | 0x1c1<<21 | 0x04<<10,
ALDANDH: 1<<30 | 0x1c1<<21 | 0x04<<10,
ALDANDB: 0<<30 | 0x1c1<<21 | 0x04<<10,
ALDANDLD: 3<<30 | 0x1c3<<21 | 0x04<<10,
ALDANDLW: 2<<30 | 0x1c3<<21 | 0x04<<10,
ALDANDLH: 1<<30 | 0x1c3<<21 | 0x04<<10,
ALDANDLB: 0<<30 | 0x1c3<<21 | 0x04<<10,
ALDEORAD: 3<<30 | 0x1c5<<21 | 0x08<<10,
ALDEORAW: 2<<30 | 0x1c5<<21 | 0x08<<10,
ALDEORAH: 1<<30 | 0x1c5<<21 | 0x08<<10,
ALDEORAB: 0<<30 | 0x1c5<<21 | 0x08<<10,
ALDEORALD: 3<<30 | 0x1c7<<21 | 0x08<<10,
ALDEORALW: 2<<30 | 0x1c7<<21 | 0x08<<10,
ALDEORALH: 1<<30 | 0x1c7<<21 | 0x08<<10,
ALDEORALB: 0<<30 | 0x1c7<<21 | 0x08<<10,
ALDEORD: 3<<30 | 0x1c1<<21 | 0x08<<10,
ALDEORW: 2<<30 | 0x1c1<<21 | 0x08<<10,
ALDEORH: 1<<30 | 0x1c1<<21 | 0x08<<10,
ALDEORB: 0<<30 | 0x1c1<<21 | 0x08<<10,
ALDEORLD: 3<<30 | 0x1c3<<21 | 0x08<<10,
ALDEORLW: 2<<30 | 0x1c3<<21 | 0x08<<10,
ALDEORLH: 1<<30 | 0x1c3<<21 | 0x08<<10,
ALDEORLB: 0<<30 | 0x1c3<<21 | 0x08<<10,
ALDORAD: 3<<30 | 0x1c5<<21 | 0x0c<<10,
ALDORAW: 2<<30 | 0x1c5<<21 | 0x0c<<10,
ALDORAH: 1<<30 | 0x1c5<<21 | 0x0c<<10,
ALDORAB: 0<<30 | 0x1c5<<21 | 0x0c<<10,
ALDORALD: 3<<30 | 0x1c7<<21 | 0x0c<<10,
ALDORALW: 2<<30 | 0x1c7<<21 | 0x0c<<10,
ALDORALH: 1<<30 | 0x1c7<<21 | 0x0c<<10,
ALDORALB: 0<<30 | 0x1c7<<21 | 0x0c<<10,
ALDORD: 3<<30 | 0x1c1<<21 | 0x0c<<10,
ALDORW: 2<<30 | 0x1c1<<21 | 0x0c<<10,
ALDORH: 1<<30 | 0x1c1<<21 | 0x0c<<10,
ALDORB: 0<<30 | 0x1c1<<21 | 0x0c<<10,
ALDORLD: 3<<30 | 0x1c3<<21 | 0x0c<<10,
ALDORLW: 2<<30 | 0x1c3<<21 | 0x0c<<10,
ALDORLH: 1<<30 | 0x1c3<<21 | 0x0c<<10,
ALDORLB: 0<<30 | 0x1c3<<21 | 0x0c<<10,
ASWPAD: 3<<30 | 0x1c5<<21 | 0x20<<10,
ASWPAW: 2<<30 | 0x1c5<<21 | 0x20<<10,
ASWPAH: 1<<30 | 0x1c5<<21 | 0x20<<10,
ASWPAB: 0<<30 | 0x1c5<<21 | 0x20<<10,
ASWPALD: 3<<30 | 0x1c7<<21 | 0x20<<10,
ASWPALW: 2<<30 | 0x1c7<<21 | 0x20<<10,
ASWPALH: 1<<30 | 0x1c7<<21 | 0x20<<10,
ASWPALB: 0<<30 | 0x1c7<<21 | 0x20<<10,
ASWPD: 3<<30 | 0x1c1<<21 | 0x20<<10,
ASWPW: 2<<30 | 0x1c1<<21 | 0x20<<10,
ASWPH: 1<<30 | 0x1c1<<21 | 0x20<<10,
ASWPB: 0<<30 | 0x1c1<<21 | 0x20<<10,
ASWPLD: 3<<30 | 0x1c3<<21 | 0x20<<10,
ASWPLW: 2<<30 | 0x1c3<<21 | 0x20<<10,
ASWPLH: 1<<30 | 0x1c3<<21 | 0x20<<10,
ASWPLB: 0<<30 | 0x1c3<<21 | 0x20<<10,
}
var oprange [ALAST & obj.AMask][]Optab
var xcmp [C_NCLASS][C_NCLASS]bool
const (
S32 = 0 << 31
S64 = 1 << 31
Sbit = 1 << 29
LSL0_32 = 2 << 13
LSL0_64 = 3 << 13
)
func OPDP2(x uint32) uint32 {
return 0<<30 | 0<<29 | 0xd6<<21 | x<<10
}
func OPDP3(sf uint32, op54 uint32, op31 uint32, o0 uint32) uint32 {
return sf<<31 | op54<<29 | 0x1B<<24 | op31<<21 | o0<<15
}
func OPBcc(x uint32) uint32 {
return 0x2A<<25 | 0<<24 | 0<<4 | x&15
}
func OPBLR(x uint32) uint32 {
/* x=0, JMP; 1, CALL; 2, RET */
return 0x6B<<25 | 0<<23 | x<<21 | 0x1F<<16 | 0<<10
}
func SYSOP(l uint32, op0 uint32, op1 uint32, crn uint32, crm uint32, op2 uint32, rt uint32) uint32 {
return 0x354<<22 | l<<21 | op0<<19 | op1<<16 | crn&15<<12 | crm&15<<8 | op2<<5 | rt
}
func SYSHINT(x uint32) uint32 {
return SYSOP(0, 0, 3, 2, 0, x, 0x1F)
}
func LDSTR12U(sz uint32, v uint32, opc uint32) uint32 {
return sz<<30 | 7<<27 | v<<26 | 1<<24 | opc<<22
}
func LDSTR9S(sz uint32, v uint32, opc uint32) uint32 {
return sz<<30 | 7<<27 | v<<26 | 0<<24 | opc<<22
}
func LD2STR(o uint32) uint32 {
return o &^ (3 << 22)
}
func LDSTX(sz uint32, o2 uint32, l uint32, o1 uint32, o0 uint32) uint32 {
return sz<<30 | 0x8<<24 | o2<<23 | l<<22 | o1<<21 | o0<<15
}
func FPCMP(m uint32, s uint32, type_ uint32, op uint32, op2 uint32) uint32 {
return m<<31 | s<<29 | 0x1E<<24 | type_<<22 | 1<<21 | op<<14 | 8<<10 | op2
}
func FPCCMP(m uint32, s uint32, type_ uint32, op uint32) uint32 {
return m<<31 | s<<29 | 0x1E<<24 | type_<<22 | 1<<21 | 1<<10 | op<<4
}
func FPOP1S(m uint32, s uint32, type_ uint32, op uint32) uint32 {
return m<<31 | s<<29 | 0x1E<<24 | type_<<22 | 1<<21 | op<<15 | 0x10<<10
}
func FPOP2S(m uint32, s uint32, type_ uint32, op uint32) uint32 {
return m<<31 | s<<29 | 0x1E<<24 | type_<<22 | 1<<21 | op<<12 | 2<<10
}
func FPOP3S(m uint32, s uint32, type_ uint32, op uint32, op2 uint32) uint32 {
return m<<31 | s<<29 | 0x1F<<24 | type_<<22 | op<<21 | op2<<15
}
func FPCVTI(sf uint32, s uint32, type_ uint32, rmode uint32, op uint32) uint32 {
return sf<<31 | s<<29 | 0x1E<<24 | type_<<22 | 1<<21 | rmode<<19 | op<<16 | 0<<10
}
func ADR(p uint32, o uint32, rt uint32) uint32 {
return p<<31 | (o&3)<<29 | 0x10<<24 | ((o>>2)&0x7FFFF)<<5 | rt&31
}
func OPBIT(x uint32) uint32 {
return 1<<30 | 0<<29 | 0xD6<<21 | 0<<16 | x<<10
}
func MOVCONST(d int64, s int, rt int) uint32 {
return uint32(((d>>uint(s*16))&0xFFFF)<<5) | uint32(s)&3<<21 | uint32(rt&31)
}
const (
// Optab.flag
LFROM = 1 << 0 // p.From uses constant pool
LTO = 1 << 1 // p.To uses constant pool
NOTUSETMP = 1 << 2 // p expands to multiple instructions, but does NOT use REGTMP
)
var optab = []Optab{
/* struct Optab:
OPCODE, from, prog->reg, from3, to, type,size,param,flag,scond */
{obj.ATEXT, C_ADDR, C_NONE, C_NONE, C_TEXTSIZE, 0, 0, 0, 0, 0},
/* arithmetic operations */
{AADD, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AADD, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AADC, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AADC, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{ANEG, C_REG, C_NONE, C_NONE, C_REG, 25, 4, 0, 0, 0},
{ANEG, C_NONE, C_NONE, C_NONE, C_REG, 25, 4, 0, 0, 0},
{ANGC, C_REG, C_NONE, C_NONE, C_REG, 17, 4, 0, 0, 0},
{ACMP, C_REG, C_REG, C_NONE, C_NONE, 1, 4, 0, 0, 0},
{AADD, C_ADDCON, C_RSP, C_NONE, C_RSP, 2, 4, 0, 0, 0},
{AADD, C_ADDCON, C_NONE, C_NONE, C_RSP, 2, 4, 0, 0, 0},
{ACMP, C_ADDCON, C_RSP, C_NONE, C_NONE, 2, 4, 0, 0, 0},
{AADD, C_MOVCON, C_RSP, C_NONE, C_RSP, 62, 8, 0, 0, 0},
{AADD, C_MOVCON, C_NONE, C_NONE, C_RSP, 62, 8, 0, 0, 0},
{ACMP, C_MOVCON, C_RSP, C_NONE, C_NONE, 62, 8, 0, 0, 0},
{AADD, C_BITCON, C_RSP, C_NONE, C_RSP, 62, 8, 0, 0, 0},
{AADD, C_BITCON, C_NONE, C_NONE, C_RSP, 62, 8, 0, 0, 0},
{ACMP, C_BITCON, C_RSP, C_NONE, C_NONE, 62, 8, 0, 0, 0},
{AADD, C_ADDCON2, C_RSP, C_NONE, C_RSP, 48, 8, 0, NOTUSETMP, 0},
{AADD, C_ADDCON2, C_NONE, C_NONE, C_RSP, 48, 8, 0, NOTUSETMP, 0},
{AADD, C_MOVCON2, C_RSP, C_NONE, C_RSP, 13, 12, 0, 0, 0},
{AADD, C_MOVCON2, C_NONE, C_NONE, C_RSP, 13, 12, 0, 0, 0},
{AADD, C_MOVCON3, C_RSP, C_NONE, C_RSP, 13, 16, 0, 0, 0},
{AADD, C_MOVCON3, C_NONE, C_NONE, C_RSP, 13, 16, 0, 0, 0},
{AADD, C_VCON, C_RSP, C_NONE, C_RSP, 13, 20, 0, 0, 0},
{AADD, C_VCON, C_NONE, C_NONE, C_RSP, 13, 20, 0, 0, 0},
{ACMP, C_MOVCON2, C_REG, C_NONE, C_NONE, 13, 12, 0, 0, 0},
{ACMP, C_MOVCON3, C_REG, C_NONE, C_NONE, 13, 16, 0, 0, 0},
{ACMP, C_VCON, C_REG, C_NONE, C_NONE, 13, 20, 0, 0, 0},
{AADD, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0},
{AADD, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0},
{AMVN, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0},
{ACMP, C_SHIFT, C_REG, C_NONE, C_NONE, 3, 4, 0, 0, 0},
{ANEG, C_SHIFT, C_NONE, C_NONE, C_REG, 26, 4, 0, 0, 0},
{AADD, C_REG, C_RSP, C_NONE, C_RSP, 27, 4, 0, 0, 0},
{AADD, C_REG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0},
{ACMP, C_REG, C_RSP, C_NONE, C_NONE, 27, 4, 0, 0, 0},
{AADD, C_EXTREG, C_RSP, C_NONE, C_RSP, 27, 4, 0, 0, 0},
{AADD, C_EXTREG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0},
{AMVN, C_EXTREG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0},
{ACMP, C_EXTREG, C_RSP, C_NONE, C_NONE, 27, 4, 0, 0, 0},
{AADD, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AADD, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AMUL, C_REG, C_REG, C_NONE, C_REG, 15, 4, 0, 0, 0},
{AMUL, C_REG, C_NONE, C_NONE, C_REG, 15, 4, 0, 0, 0},
{AMADD, C_REG, C_REG, C_REG, C_REG, 15, 4, 0, 0, 0},
{AREM, C_REG, C_REG, C_NONE, C_REG, 16, 8, 0, 0, 0},
{AREM, C_REG, C_NONE, C_NONE, C_REG, 16, 8, 0, 0, 0},
{ASDIV, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{ASDIV, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AFADDS, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0},
{AFADDS, C_FREG, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0},
{AFMSUBD, C_FREG, C_FREG, C_FREG, C_FREG, 15, 4, 0, 0, 0},
{AFCMPS, C_FREG, C_FREG, C_NONE, C_NONE, 56, 4, 0, 0, 0},
{AFCMPS, C_FCON, C_FREG, C_NONE, C_NONE, 56, 4, 0, 0, 0},
{AVADDP, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
{AVADD, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
{AVADD, C_VREG, C_VREG, C_NONE, C_VREG, 89, 4, 0, 0, 0},
{AVADD, C_VREG, C_NONE, C_NONE, C_VREG, 89, 4, 0, 0, 0},
{AVADDV, C_ARNG, C_NONE, C_NONE, C_VREG, 85, 4, 0, 0, 0},
/* logical operations */
{AAND, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AAND, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AANDS, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
{AANDS, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0},
{ATST, C_REG, C_REG, C_NONE, C_NONE, 1, 4, 0, 0, 0},
{AAND, C_MBCON, C_REG, C_NONE, C_RSP, 53, 4, 0, 0, 0},
{AAND, C_MBCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0},
{AANDS, C_MBCON, C_REG, C_NONE, C_REG, 53, 4, 0, 0, 0},
{AANDS, C_MBCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0},
{ATST, C_MBCON, C_REG, C_NONE, C_NONE, 53, 4, 0, 0, 0},
{AAND, C_BITCON, C_REG, C_NONE, C_RSP, 53, 4, 0, 0, 0},
{AAND, C_BITCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0},
{AANDS, C_BITCON, C_REG, C_NONE, C_REG, 53, 4, 0, 0, 0},
{AANDS, C_BITCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0},
{ATST, C_BITCON, C_REG, C_NONE, C_NONE, 53, 4, 0, 0, 0},
{AAND, C_MOVCON, C_REG, C_NONE, C_REG, 62, 8, 0, 0, 0},
{AAND, C_MOVCON, C_NONE, C_NONE, C_REG, 62, 8, 0, 0, 0},
{AANDS, C_MOVCON, C_REG, C_NONE, C_REG, 62, 8, 0, 0, 0},
{AANDS, C_MOVCON, C_NONE, C_NONE, C_REG, 62, 8, 0, 0, 0},
{ATST, C_MOVCON, C_REG, C_NONE, C_NONE, 62, 8, 0, 0, 0},
{AAND, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0},
{AAND, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0},
{AAND, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0},
{AAND, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0},
{AAND, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0},
{AAND, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0},
{AANDS, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0},
{AANDS, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0},
{AANDS, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0},
{AANDS, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0},
{AANDS, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0},
{AANDS, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0},
{ATST, C_MOVCON2, C_REG, C_NONE, C_NONE, 28, 12, 0, 0, 0},
{ATST, C_MOVCON3, C_REG, C_NONE, C_NONE, 28, 16, 0, 0, 0},
{ATST, C_VCON, C_REG, C_NONE, C_NONE, 28, 20, 0, 0, 0},
{AAND, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0},
{AAND, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0},
{AANDS, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0},
{AANDS, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0},
{ATST, C_SHIFT, C_REG, C_NONE, C_NONE, 3, 4, 0, 0, 0},
{AMOVD, C_RSP, C_NONE, C_NONE, C_RSP, 24, 4, 0, 0, 0},
{AMVN, C_REG, C_NONE, C_NONE, C_REG, 24, 4, 0, 0, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, /* also MOVHU */
{AMOVW, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, /* also MOVWU */
/* TODO: MVN C_SHIFT */
/* MOVs that become MOVK/MOVN/MOVZ/ADD/SUB/OR */
{AMOVW, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
{AMOVD, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
{AMOVW, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
{AMOVD, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
{AMOVW, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0},
{AMOVD, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0},
{AMOVD, C_MOVCON3, C_NONE, C_NONE, C_REG, 12, 12, 0, NOTUSETMP, 0},
{AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, NOTUSETMP, 0},
{AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0},
{AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0},
/* jump operations */
{AB, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0},
{ABL, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0},
{AB, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0},
{ABL, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0},
{ABL, C_REG, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0},
{ABL, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0},
{obj.ARET, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0},
{obj.ARET, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0},
{ABEQ, C_NONE, C_NONE, C_NONE, C_SBRA, 7, 4, 0, 0, 0},
{AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0},
{AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0},
{ACBZ, C_REG, C_NONE, C_NONE, C_SBRA, 39, 4, 0, 0, 0},
{ATBZ, C_VCON, C_REG, C_NONE, C_SBRA, 40, 4, 0, 0, 0},
{AERET, C_NONE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0},
{ACLREX, C_NONE, C_NONE, C_NONE, C_VCON, 38, 4, 0, 0, 0},
{ACLREX, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0, 0},
{ABFM, C_VCON, C_REG, C_VCON, C_REG, 42, 4, 0, 0, 0},
{ABFI, C_VCON, C_REG, C_VCON, C_REG, 43, 4, 0, 0, 0},
{AEXTR, C_VCON, C_REG, C_REG, C_REG, 44, 4, 0, 0, 0},
{ASXTB, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0},
{ACLS, C_REG, C_NONE, C_NONE, C_REG, 46, 4, 0, 0, 0},
{ALSL, C_VCON, C_REG, C_NONE, C_REG, 8, 4, 0, 0, 0},
{ALSL, C_VCON, C_NONE, C_NONE, C_REG, 8, 4, 0, 0, 0},
{ALSL, C_REG, C_NONE, C_NONE, C_REG, 9, 4, 0, 0, 0},
{ALSL, C_REG, C_REG, C_NONE, C_REG, 9, 4, 0, 0, 0},
{ASVC, C_VCON, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0},
{ASVC, C_NONE, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0},
{ADWORD, C_NONE, C_NONE, C_NONE, C_VCON, 11, 8, 0, NOTUSETMP, 0},
{ADWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 11, 8, 0, NOTUSETMP, 0},
{ADWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 11, 8, 0, NOTUSETMP, 0},
{ADWORD, C_NONE, C_NONE, C_NONE, C_LACON, 11, 8, 0, NOTUSETMP, 0},
{AWORD, C_NONE, C_NONE, C_NONE, C_LCON, 14, 4, 0, 0, 0},
{AWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 14, 4, 0, 0, 0},
{AWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 14, 4, 0, 0, 0},
{AMOVW, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0},
{AMOVD, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AMOVB, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0},
{AMOVBU, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0},
{AMOVH, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0},
{AMOVW, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0},
{AMOVD, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0},
{AMOVD, C_GOTADDR, C_NONE, C_NONE, C_REG, 71, 8, 0, 0, 0},
{AMOVD, C_TLS_LE, C_NONE, C_NONE, C_REG, 69, 4, 0, 0, 0},
{AMOVD, C_TLS_IE, C_NONE, C_NONE, C_REG, 70, 8, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AFMOVS, C_ADDR, C_NONE, C_NONE, C_FREG, 65, 12, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
{AFMOVD, C_ADDR, C_NONE, C_NONE, C_FREG, 65, 12, 0, 0, 0},
{AFMOVS, C_FCON, C_NONE, C_NONE, C_FREG, 55, 4, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0},
{AFMOVD, C_FCON, C_NONE, C_NONE, C_FREG, 55, 4, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0},
{AFMOVS, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0},
{AFMOVD, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0},
{AFCVTZSD, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0},
{ASCVTFD, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0},
{AFCVTSD, C_FREG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0},
{AVMOV, C_ELEM, C_NONE, C_NONE, C_REG, 73, 4, 0, 0, 0},
{AVMOV, C_ELEM, C_NONE, C_NONE, C_ELEM, 92, 4, 0, 0, 0},
{AVMOV, C_ELEM, C_NONE, C_NONE, C_VREG, 80, 4, 0, 0, 0},
{AVMOV, C_REG, C_NONE, C_NONE, C_ARNG, 82, 4, 0, 0, 0},
{AVMOV, C_REG, C_NONE, C_NONE, C_ELEM, 78, 4, 0, 0, 0},
{AVMOV, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0},
{AVDUP, C_ELEM, C_NONE, C_NONE, C_ARNG, 79, 4, 0, 0, 0},
{AVMOVI, C_ADDCON, C_NONE, C_NONE, C_ARNG, 86, 4, 0, 0, 0},
{AVFMLA, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
{AVEXT, C_VCON, C_ARNG, C_ARNG, C_ARNG, 94, 4, 0, 0, 0},
{AVTBL, C_ARNG, C_NONE, C_LIST, C_ARNG, 100, 4, 0, 0, 0},
{AVUSHR, C_VCON, C_ARNG, C_NONE, C_ARNG, 95, 4, 0, 0, 0},
{AVZIP1, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
/* conditional operations */
{ACSEL, C_COND, C_REG, C_REG, C_REG, 18, 4, 0, 0, 0},
{ACINC, C_COND, C_REG, C_NONE, C_REG, 18, 4, 0, 0, 0},
{ACSET, C_COND, C_NONE, C_NONE, C_REG, 18, 4, 0, 0, 0},
{AFCSELD, C_COND, C_FREG, C_FREG, C_FREG, 18, 4, 0, 0, 0},
{ACCMN, C_COND, C_REG, C_REG, C_VCON, 19, 4, 0, 0, 0},
{ACCMN, C_COND, C_REG, C_VCON, C_VCON, 19, 4, 0, 0, 0},
{AFCCMPS, C_COND, C_FREG, C_FREG, C_VCON, 57, 4, 0, 0, 0},
/* scaled 12-bit unsigned displacement store */
{AMOVB, C_REG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_UAUTO8K, 20, 4, REGSP, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_UOREG8K, 20, 4, 0, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_UAUTO16K, 20, 4, REGSP, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_UAUTO32K, 20, 4, REGSP, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_UOREG32K, 20, 4, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_UAUTO16K, 20, 4, REGSP, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_UAUTO32K, 20, 4, REGSP, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_UOREG32K, 20, 4, 0, 0, 0},
/* unscaled 9-bit signed displacement store */
{AMOVB, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0},
/* scaled 12-bit unsigned displacement load */
{AMOVB, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVB, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVBU, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVBU, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVH, C_UAUTO8K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVH, C_UOREG8K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVW, C_UAUTO16K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVW, C_UOREG16K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVD, C_UAUTO32K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVD, C_UOREG32K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AFMOVS, C_UAUTO16K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0},
{AFMOVS, C_UOREG16K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0},
{AFMOVD, C_UAUTO32K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0},
{AFMOVD, C_UOREG32K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0},
/* unscaled 9-bit signed displacement load */
{AMOVB, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVB, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVBU, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVBU, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVH, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVH, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVW, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVW, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AMOVD, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0},
{AMOVD, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0},
{AFMOVS, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0},
{AFMOVS, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0},
{AFMOVD, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0},
{AFMOVD, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0},
/* long displacement store */
{AMOVB, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0},
/* long displacement load */
{AMOVB, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0},
{AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0},
{AMOVBU, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0},
{AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0},
{AMOVH, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0},
{AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0},
{AMOVW, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0},
{AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0},
{AMOVD, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0},
{AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0},
{AFMOVS, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0},
{AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0},
{AFMOVD, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0},
{AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0},
/* load long effective stack address (load int32 offset and add) */
{AMOVD, C_LACON, C_NONE, C_NONE, C_REG, 34, 8, REGSP, LFROM, 0},
/* pre/post-indexed load (unscaled, signed 9-bit offset) */
{AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST},
{AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST},
{AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST},
{AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST},
{AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST},
{AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST},
{AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST},
{AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE},
{AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE},
{AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE},
{AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE},
{AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE},
{AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE},
{AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE},
/* pre/post-indexed store (unscaled, signed 9-bit offset) */
{AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST},
{AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
/* load with shifted or extended register offset */
{AMOVD, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AMOVW, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AMOVH, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AMOVB, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AMOVBU, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AFMOVS, C_ROFF, C_NONE, C_NONE, C_FREG, 98, 4, 0, 0, 0},
{AFMOVD, C_ROFF, C_NONE, C_NONE, C_FREG, 98, 4, 0, 0, 0},
/* store with extended register offset */
{AMOVD, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
{AMOVW, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
{AMOVH, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
{AMOVB, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
{AFMOVS, C_FREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
{AFMOVD, C_FREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0},
/* pre/post-indexed/signed-offset load/store register pair
(unscaled, signed 10-bit quad-aligned and long offset) */
{ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0},
{ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE},
{ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST},
{ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0},
{ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE},
{ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST},
{ALDP, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0},
{ALDP, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPRE},
{ALDP, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPOST},
{ALDP, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0},
{ALDP, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPRE},
{ALDP, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPOST},
{ALDP, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, 0},
{ALDP, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, C_XPRE},
{ALDP, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, C_XPOST},
{ALDP, C_NPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, 0},
{ALDP, C_NPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPRE},
{ALDP, C_NPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPOST},
{ALDP, C_PPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, 0},
{ALDP, C_PPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPRE},
{ALDP, C_PPOREG, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPOST},
{ALDP, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, 0},
{ALDP, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPRE},
{ALDP, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPOST},
{ALDP, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, 0},
{ALDP, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPRE},
{ALDP, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPOST},
{ALDP, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, 0},
{ALDP, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, C_XPRE},
{ALDP, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, C_XPOST},
{ALDP, C_ADDR, C_NONE, C_NONE, C_PAIR, 88, 12, 0, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPOREG, 67, 4, 0, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPOREG, 67, 4, 0, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NPOREG, 67, 4, 0, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPOREG, 67, 4, 0, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPOREG, 67, 4, 0, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_PPOREG, 67, 4, 0, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, 0},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPRE},
{ASTP, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPOST},
{ASTP, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0},
// differ from LDP/STP for C_NSAUTO_4/C_PSAUTO_4/C_NSOREG_4/C_PSOREG_4
{ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0},
{ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE},
{ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST},
{ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0},
{ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE},
{ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST},
{ALDPW, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0},
{ALDPW, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPRE},
{ALDPW, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPOST},
{ALDPW, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0},
{ALDPW, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPRE},
{ALDPW, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, C_XPOST},
{ALDPW, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, 0},
{ALDPW, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, C_XPRE},
{ALDPW, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, C_XPOST},
{ALDPW, C_NSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, 0},
{ALDPW, C_NSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPRE},
{ALDPW, C_NSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPOST},
{ALDPW, C_PSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, 0},
{ALDPW, C_PSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPRE},
{ALDPW, C_PSOREG_4, C_NONE, C_NONE, C_PAIR, 66, 4, 0, 0, C_XPOST},
{ALDPW, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, 0},
{ALDPW, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPRE},
{ALDPW, C_UOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPOST},
{ALDPW, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, 0},
{ALDPW, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPRE},
{ALDPW, C_NOREG4K, C_NONE, C_NONE, C_PAIR, 74, 8, 0, 0, C_XPOST},
{ALDPW, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, 0},
{ALDPW, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, C_XPRE},
{ALDPW, C_LOREG, C_NONE, C_NONE, C_PAIR, 75, 12, 0, LFROM, C_XPOST},
{ALDPW, C_ADDR, C_NONE, C_NONE, C_PAIR, 88, 12, 0, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 12, REGSP, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSOREG_4, 67, 4, 0, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSOREG_4, 67, 4, 0, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NSOREG_4, 67, 4, 0, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSOREG_4, 67, 4, 0, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSOREG_4, 67, 4, 0, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_PSOREG_4, 67, 4, 0, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_UOREG4K, 76, 8, 0, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_NOREG4K, 76, 8, 0, 0, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, 0},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPRE},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0},
{ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG
{ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG
{ALDAR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
{ALDXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
{ALDAXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
{ALDXP, C_ZOREG, C_NONE, C_NONE, C_PAIR, 58, 4, 0, 0, 0},
{ASTLR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_NONE
{ASTXR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG
{ASTLXR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG
{ASTXP, C_PAIR, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0},
/* VLD[1-4]/VST[1-4] */
{AVLD1, C_ZOREG, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, 0},
{AVLD1, C_LOREG, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, C_XPOST},
{AVLD1, C_ROFF, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, C_XPOST},
{AVLD1R, C_ZOREG, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, 0},
{AVLD1R, C_LOREG, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, C_XPOST},
{AVLD1R, C_ROFF, C_NONE, C_NONE, C_LIST, 81, 4, 0, 0, C_XPOST},
{AVLD1, C_LOREG, C_NONE, C_NONE, C_ELEM, 97, 4, 0, 0, C_XPOST},
{AVLD1, C_ROFF, C_NONE, C_NONE, C_ELEM, 97, 4, 0, 0, C_XPOST},
{AVLD1, C_LOREG, C_NONE, C_NONE, C_ELEM, 97, 4, 0, 0, 0},
{AVST1, C_LIST, C_NONE, C_NONE, C_ZOREG, 84, 4, 0, 0, 0},
{AVST1, C_LIST, C_NONE, C_NONE, C_LOREG, 84, 4, 0, 0, C_XPOST},
{AVST1, C_LIST, C_NONE, C_NONE, C_ROFF, 84, 4, 0, 0, C_XPOST},
{AVST2, C_LIST, C_NONE, C_NONE, C_ZOREG, 84, 4, 0, 0, 0},
{AVST2, C_LIST, C_NONE, C_NONE, C_LOREG, 84, 4, 0, 0, C_XPOST},
{AVST2, C_LIST, C_NONE, C_NONE, C_ROFF, 84, 4, 0, 0, C_XPOST},
{AVST3, C_LIST, C_NONE, C_NONE, C_ZOREG, 84, 4, 0, 0, 0},
{AVST3, C_LIST, C_NONE, C_NONE, C_LOREG, 84, 4, 0, 0, C_XPOST},
{AVST3, C_LIST, C_NONE, C_NONE, C_ROFF, 84, 4, 0, 0, C_XPOST},
{AVST4, C_LIST, C_NONE, C_NONE, C_ZOREG, 84, 4, 0, 0, 0},
{AVST4, C_LIST, C_NONE, C_NONE, C_LOREG, 84, 4, 0, 0, C_XPOST},
{AVST4, C_LIST, C_NONE, C_NONE, C_ROFF, 84, 4, 0, 0, C_XPOST},
{AVST1, C_ELEM, C_NONE, C_NONE, C_LOREG, 96, 4, 0, 0, C_XPOST},
{AVST1, C_ELEM, C_NONE, C_NONE, C_ROFF, 96, 4, 0, 0, C_XPOST},
{AVST1, C_ELEM, C_NONE, C_NONE, C_LOREG, 96, 4, 0, 0, 0},
/* special */
{AMOVD, C_SPR, C_NONE, C_NONE, C_REG, 35, 4, 0, 0, 0},
{AMRS, C_SPR, C_NONE, C_NONE, C_REG, 35, 4, 0, 0, 0},
{AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0},
{AMSR, C_REG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0},
{AMOVD, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0},
{AMSR, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0},
{APRFM, C_UOREG32K, C_NONE, C_NONE, C_SPR, 91, 4, 0, 0, 0},
{APRFM, C_UOREG32K, C_NONE, C_NONE, C_LCON, 91, 4, 0, 0, 0},
{ADMB, C_VCON, C_NONE, C_NONE, C_NONE, 51, 4, 0, 0, 0},
{AHINT, C_VCON, C_NONE, C_NONE, C_NONE, 52, 4, 0, 0, 0},
{ASYS, C_VCON, C_NONE, C_NONE, C_NONE, 50, 4, 0, 0, 0},
{ASYS, C_VCON, C_REG, C_NONE, C_NONE, 50, 4, 0, 0, 0},
{ASYSL, C_VCON, C_NONE, C_NONE, C_REG, 50, 4, 0, 0, 0},
/* encryption instructions */
{AAESD, C_VREG, C_NONE, C_NONE, C_VREG, 29, 4, 0, 0, 0}, // for compatibility with old code
{AAESD, C_ARNG, C_NONE, C_NONE, C_ARNG, 29, 4, 0, 0, 0}, // recommend using the new one for better readability
{ASHA1C, C_VREG, C_REG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
{ASHA1C, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
{ASHA1H, C_VREG, C_NONE, C_NONE, C_VREG, 29, 4, 0, 0, 0},
{ASHA1SU0, C_ARNG, C_ARNG, C_NONE, C_ARNG, 1, 4, 0, 0, 0},
{ASHA256H, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
{AVREV32, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0},
{AVPMULL, C_ARNG, C_ARNG, C_NONE, C_ARNG, 93, 4, 0, 0, 0},
{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0},
{obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0},
{obj.AFUNCDATA, C_VCON, C_NONE, C_NONE, C_ADDR, 0, 0, 0, 0, 0},
{obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
{obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
{obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
{obj.ANOP, C_VREG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL
{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL
{obj.APCALIGN, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // align code
{obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0},
}
/*
* valid pstate field values, and value to use in instruction
*/
var pstatefield = []struct {
reg int16
enc uint32
}{
{REG_SPSel, 0<<16 | 4<<12 | 5<<5},
{REG_DAIFSet, 3<<16 | 4<<12 | 6<<5},
{REG_DAIFClr, 3<<16 | 4<<12 | 7<<5},
}
var prfopfield = []struct {
reg int16
enc uint32
}{
{REG_PLDL1KEEP, 0},
{REG_PLDL1STRM, 1},
{REG_PLDL2KEEP, 2},
{REG_PLDL2STRM, 3},
{REG_PLDL3KEEP, 4},
{REG_PLDL3STRM, 5},
{REG_PLIL1KEEP, 8},
{REG_PLIL1STRM, 9},
{REG_PLIL2KEEP, 10},
{REG_PLIL2STRM, 11},
{REG_PLIL3KEEP, 12},
{REG_PLIL3STRM, 13},
{REG_PSTL1KEEP, 16},
{REG_PSTL1STRM, 17},
{REG_PSTL2KEEP, 18},
{REG_PSTL2STRM, 19},
{REG_PSTL3KEEP, 20},
{REG_PSTL3STRM, 21},
}
// Used for padinng NOOP instruction
const OP_NOOP = 0xd503201f
// align code to a certain length by padding bytes.
func pcAlignPadLength(pc int64, alignedValue int64, ctxt *obj.Link) int {
if !((alignedValue&(alignedValue-1) == 0) && 8 <= alignedValue && alignedValue <= 2048) {
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", alignedValue)
}
return int(-pc & (alignedValue - 1))
}
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if ctxt.Retpoline {
ctxt.Diag("-spectre=ret not supported on arm64")
ctxt.Retpoline = false // don't keep printing
}
p := cursym.Func.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
}
if oprange[AAND&obj.AMask] == nil {
ctxt.Diag("arm64 ops not initialized, call arm64.buildop first")
}
c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset & 0xffffffff), extrasize: int32(p.To.Offset >> 32)}
p.To.Offset &= 0xffffffff // extrasize is no longer needed
bflag := 1
pc := int64(0)
p.Pc = pc
var m int
var o *Optab
for p = p.Link; p != nil; p = p.Link {
if p.As == ADWORD && (pc&7) != 0 {
pc += 4
}
p.Pc = pc
o = c.oplook(p)
m = int(o.size)
if m == 0 {
switch p.As {
case obj.APCALIGN:
alignedValue := p.From.Offset
m = pcAlignPadLength(pc, alignedValue, ctxt)
// Update the current text symbol alignment value.
if int32(alignedValue) > cursym.Func.Align {
cursym.Func.Align = int32(alignedValue)
}
break
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
continue
default:
c.ctxt.Diag("zero-width instruction\n%v", p)
}
}
switch o.flag & (LFROM | LTO) {
case LFROM:
c.addpool(p, &p.From)
case LTO:
c.addpool(p, &p.To)
break
}
if p.As == AB || p.As == obj.ARET || p.As == AERET { /* TODO: other unconditional operations */
c.checkpool(p, 0)
}
pc += int64(m)
if c.blitrl != nil {
c.checkpool(p, 1)
}
}
c.cursym.Size = pc
/*
* if any procedure is large enough to
* generate a large SBRA branch, then
* generate extra passes putting branches
* around jmps to fix. this is rare.
*/
for bflag != 0 {
bflag = 0
pc = 0
for p = c.cursym.Func.Text.Link; p != nil; p = p.Link {
if p.As == ADWORD && (pc&7) != 0 {
pc += 4
}
p.Pc = pc
o = c.oplook(p)
/* very large branches */
if (o.type_ == 7 || o.type_ == 39 || o.type_ == 40) && p.To.Target() != nil { // 7: BEQ and like, 39: CBZ and like, 40: TBZ and like
otxt := p.To.Target().Pc - pc
var toofar bool
switch o.type_ {
case 7, 39: // branch instruction encodes 19 bits
toofar = otxt <= -(1<<20)+10 || otxt >= (1<<20)-10
case 40: // branch instruction encodes 14 bits
toofar = otxt <= -(1<<15)+10 || otxt >= (1<<15)-10
}
if toofar {
q := c.newprog()
q.Link = p.Link
p.Link = q
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.To.SetTarget(p.To.Target())
p.To.SetTarget(q)
q = c.newprog()
q.Link = p.Link
p.Link = q
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.To.SetTarget(q.Link.Link)
bflag = 1
}
}
m = int(o.size)
if m == 0 {
switch p.As {
case obj.APCALIGN:
alignedValue := p.From.Offset
m = pcAlignPadLength(pc, alignedValue, ctxt)
break
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
continue
default:
c.ctxt.Diag("zero-width instruction\n%v", p)
}
}
pc += int64(m)
}
}
pc += -pc & (funcAlign - 1)
c.cursym.Size = pc
/*
* lay out the code, emitting code and data relocations.
*/
c.cursym.Grow(c.cursym.Size)
bp := c.cursym.P
psz := int32(0)
var i int
var out [6]uint32
for p := c.cursym.Func.Text.Link; p != nil; p = p.Link {
c.pc = p.Pc
o = c.oplook(p)
// need to align DWORDs on 8-byte boundary. The ISA doesn't
// require it, but the various 64-bit loads we generate assume it.
if o.as == ADWORD && psz%8 != 0 {
bp[3] = 0
bp[2] = bp[3]
bp[1] = bp[2]
bp[0] = bp[1]
bp = bp[4:]
psz += 4
}
if int(o.size) > 4*len(out) {
log.Fatalf("out array in span7 is too small, need at least %d for %v", o.size/4, p)
}
if p.As == obj.APCALIGN {
alignedValue := p.From.Offset
v := pcAlignPadLength(p.Pc, alignedValue, c.ctxt)
for i = 0; i < int(v/4); i++ {
// emit ANOOP instruction by the padding size
c.ctxt.Arch.ByteOrder.PutUint32(bp, OP_NOOP)
bp = bp[4:]
psz += 4
}
} else {
c.asmout(p, o, out[:])
for i = 0; i < int(o.size/4); i++ {
c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
bp = bp[4:]
psz += 4
}
}
}
// Mark nonpreemptible instruction sequences.
// We use REGTMP as a scratch register during call injection,
// so instruction sequences that use REGTMP are unsafe to
// preempt asynchronously.
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable)
}
// isUnsafePoint returns whether p is an unsafe point.
func (c *ctxt7) isUnsafePoint(p *obj.Prog) bool {
// If p explicitly uses REGTMP, it's unsafe to preempt, because the
// preemption sequence clobbers REGTMP.
return p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP
}
// isRestartable returns whether p is a multi-instruction sequence that,
// if preempted, can be restarted.
func (c *ctxt7) isRestartable(p *obj.Prog) bool {
if c.isUnsafePoint(p) {
return false
}
// If p is a multi-instruction sequence with uses REGTMP inserted by
// the assembler in order to materialize a large constant/offset, we
// can restart p (at the start of the instruction sequence), recompute
// the content of REGTMP, upon async preemption. Currently, all cases
// of assembler-inserted REGTMP fall into this category.
// If p doesn't use REGTMP, it can be simply preempted, so we don't
// mark it.
o := c.oplook(p)
return o.size > 4 && o.flag&NOTUSETMP == 0
}
/*
* when the first reference to the literal pool threatens
* to go out of range of a 1Mb PC-relative offset
* drop the pool now, and branch round it.
*/
func (c *ctxt7) checkpool(p *obj.Prog, skip int) {
if c.pool.size >= 0xffff0 || !ispcdisp(int32(p.Pc+4+int64(c.pool.size)-int64(c.pool.start)+8)) {
c.flushpool(p, skip)
} else if p.Link == nil {
c.flushpool(p, 2)
}
}
func (c *ctxt7) flushpool(p *obj.Prog, skip int) {
if c.blitrl != nil {
if skip != 0 {
if c.ctxt.Debugvlog && skip == 1 {
fmt.Printf("note: flush literal pool at %#x: len=%d ref=%x\n", uint64(p.Pc+4), c.pool.size, c.pool.start)
}
q := c.newprog()
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.To.SetTarget(p.Link)
q.Link = c.blitrl
q.Pos = p.Pos
c.blitrl = q
} else if p.Pc+int64(c.pool.size)-int64(c.pool.start) < maxPCDisp {
return
}
// The line number for constant pool entries doesn't really matter.
// We set it to the line number of the preceding instruction so that
// there are no deltas to encode in the pc-line tables.
for q := c.blitrl; q != nil; q = q.Link {
q.Pos = p.Pos
}
c.elitrl.Link = p.Link
p.Link = c.blitrl
c.blitrl = nil /* BUG: should refer back to values until out-of-range */
c.elitrl = nil
c.pool.size = 0
c.pool.start = 0
}
}
/*
* MOVD foo(SB), R is actually
* MOVD addr, REGTMP
* MOVD REGTMP, R
* where addr is the address of the DWORD containing the address of foo.
*
* TODO: hash
*/
func (c *ctxt7) addpool(p *obj.Prog, a *obj.Addr) {
cls := c.aclass(a)
lit := c.instoffset
t := c.newprog()
t.As = AWORD
sz := 4
if a.Type == obj.TYPE_CONST {
if lit != int64(int32(lit)) && uint64(lit) != uint64(uint32(lit)) {
// out of range -0x80000000 ~ 0xffffffff, must store 64-bit
t.As = ADWORD
sz = 8
} // else store 32-bit
} else if p.As == AMOVD && a.Type != obj.TYPE_MEM || cls == C_ADDR || cls == C_VCON || lit != int64(int32(lit)) || uint64(lit) != uint64(uint32(lit)) {
// conservative: don't know if we want signed or unsigned extension.
// in case of ambiguity, store 64-bit
t.As = ADWORD
sz = 8
}
switch cls {
// TODO(aram): remove.
default:
if a.Name != obj.NAME_EXTERN {
fmt.Printf("addpool: %v in %v shouldn't go to default case\n", DRconv(cls), p)
}
t.To.Offset = a.Offset
t.To.Sym = a.Sym
t.To.Type = a.Type
t.To.Name = a.Name
/* This is here because MOV uint12<<12, R is disabled in optab.
Because of this, we need to load the constant from memory. */
case C_ADDCON:
fallthrough
case C_ZAUTO,
C_PSAUTO,
C_PSAUTO_8,
C_PSAUTO_4,
C_PPAUTO,
C_UAUTO4K_8,
C_UAUTO4K_4,
C_UAUTO4K_2,
C_UAUTO4K,
C_UAUTO8K_8,
C_UAUTO8K_4,
C_UAUTO8K,
C_UAUTO16K_8,
C_UAUTO16K,
C_UAUTO32K,
C_NSAUTO_8,
C_NSAUTO_4,
C_NSAUTO,
C_NPAUTO,
C_NAUTO4K,
C_LAUTO,
C_PPOREG,
C_PSOREG,
C_PSOREG_4,
C_PSOREG_8,
C_UOREG4K_8,
C_UOREG4K_4,
C_UOREG4K_2,
C_UOREG4K,
C_UOREG8K_8,
C_UOREG8K_4,
C_UOREG8K,
C_UOREG16K_8,
C_UOREG16K,
C_UOREG32K,
C_NSOREG_8,
C_NSOREG_4,
C_NSOREG,
C_NPOREG,
C_NOREG4K,
C_LOREG,
C_LACON,
C_ADDCON2,
C_LCON,
C_VCON:
if a.Name == obj.NAME_EXTERN {
fmt.Printf("addpool: %v in %v needs reloc\n", DRconv(cls), p)
}
t.To.Type = obj.TYPE_CONST
t.To.Offset = lit
break
}
for q := c.blitrl; q != nil; q = q.Link { /* could hash on t.t0.offset */
if q.To == t.To {
p.Pool = q
return
}
}
q := c.newprog()
*q = *t
q.Pc = int64(c.pool.size)
if c.blitrl == nil {
c.blitrl = q
c.pool.start = uint32(p.Pc)
} else {
c.elitrl.Link = q
}
c.elitrl = q
c.pool.size = -c.pool.size & (funcAlign - 1)
c.pool.size += uint32(sz)
p.Pool = q
}
func (c *ctxt7) regoff(a *obj.Addr) uint32 {
c.instoffset = 0
c.aclass(a)
return uint32(c.instoffset)
}
func isSTLXRop(op obj.As) bool {
switch op {
case ASTLXR, ASTLXRW, ASTLXRB, ASTLXRH,
ASTXR, ASTXRW, ASTXRB, ASTXRH:
return true
}
return false
}
func isSTXPop(op obj.As) bool {
switch op {
case ASTXP, ASTLXP, ASTXPW, ASTLXPW:
return true
}
return false
}
func isANDop(op obj.As) bool {
switch op {
case AAND, AORR, AEOR, AANDS, ATST,
ABIC, AEON, AORN, ABICS:
return true
}
return false
}
func isANDWop(op obj.As) bool {
switch op {
case AANDW, AORRW, AEORW, AANDSW, ATSTW,
ABICW, AEONW, AORNW, ABICSW:
return true
}
return false
}
func isADDop(op obj.As) bool {
switch op {
case AADD, AADDS, ASUB, ASUBS, ACMN, ACMP:
return true
}
return false
}
func isADDWop(op obj.As) bool {
switch op {
case AADDW, AADDSW, ASUBW, ASUBSW, ACMNW, ACMPW:
return true
}
return false
}
func isRegShiftOrExt(a *obj.Addr) bool {
return (a.Index-obj.RBaseARM64)&REG_EXT != 0 || (a.Index-obj.RBaseARM64)&REG_LSL != 0
}
// Maximum PC-relative displacement.
// The actual limit is ±2²⁰, but we are conservative
// to avoid needing to recompute the literal pool flush points
// as span-dependent jumps are enlarged.
const maxPCDisp = 512 * 1024
// ispcdisp reports whether v is a valid PC-relative displacement.
func ispcdisp(v int32) bool {
return -maxPCDisp < v && v < maxPCDisp && v&3 == 0
}
func isaddcon(v int64) bool {
/* uimm12 or uimm24? */
if v < 0 {
return false
}
if (v & 0xFFF) == 0 {
v >>= 12
}
return v <= 0xFFF
}
// isbitcon reports whether a constant can be encoded into a logical instruction.
// bitcon has a binary form of repetition of a bit sequence of length 2, 4, 8, 16, 32, or 64,
// which itself is a rotate (w.r.t. the length of the unit) of a sequence of ones.
// special cases: 0 and -1 are not bitcon.
// this function needs to run against virtually all the constants, so it needs to be fast.
// for this reason, bitcon testing and bitcon encoding are separate functions.
func isbitcon(x uint64) bool {
if x == 1<<64-1 || x == 0 {
return false
}
// determine the period and sign-extend a unit to 64 bits
switch {
case x != x>>32|x<<32:
// period is 64
// nothing to do
case x != x>>16|x<<48:
// period is 32
x = uint64(int64(int32(x)))
case x != x>>8|x<<56:
// period is 16
x = uint64(int64(int16(x)))
case x != x>>4|x<<60:
// period is 8
x = uint64(int64(int8(x)))
default:
// period is 4 or 2, always true
// 0001, 0010, 0100, 1000 -- 0001 rotate
// 0011, 0110, 1100, 1001 -- 0011 rotate
// 0111, 1011, 1101, 1110 -- 0111 rotate
// 0101, 1010 -- 01 rotate, repeat
return true
}
return sequenceOfOnes(x) || sequenceOfOnes(^x)
}
// sequenceOfOnes tests whether a constant is a sequence of ones in binary, with leading and trailing zeros
func sequenceOfOnes(x uint64) bool {
y := x & -x // lowest set bit of x. x is good iff x+y is a power of 2
y += x
return (y-1)&y == 0
}
// bitconEncode returns the encoding of a bitcon used in logical instructions
// x is known to be a bitcon
// a bitcon is a sequence of n ones at low bits (i.e. 1<<n-1), right rotated
// by R bits, and repeated with period of 64, 32, 16, 8, 4, or 2.
// it is encoded in logical instructions with 3 bitfields
// N (1 bit) : R (6 bits) : S (6 bits), where
// N=1 -- period=64
// N=0, S=0xxxxx -- period=32
// N=0, S=10xxxx -- period=16
// N=0, S=110xxx -- period=8
// N=0, S=1110xx -- period=4
// N=0, S=11110x -- period=2
// R is the shift amount, low bits of S = n-1
func bitconEncode(x uint64, mode int) uint32 {
var period uint32
// determine the period and sign-extend a unit to 64 bits
switch {
case x != x>>32|x<<32:
period = 64
case x != x>>16|x<<48:
period = 32
x = uint64(int64(int32(x)))
case x != x>>8|x<<56:
period = 16
x = uint64(int64(int16(x)))
case x != x>>4|x<<60:
period = 8
x = uint64(int64(int8(x)))
case x != x>>2|x<<62:
period = 4
x = uint64(int64(x<<60) >> 60)
default:
period = 2
x = uint64(int64(x<<62) >> 62)
}
neg := false
if int64(x) < 0 {
x = ^x
neg = true
}
y := x & -x // lowest set bit of x.
s := log2(y)
n := log2(x+y) - s // x (or ^x) is a sequence of n ones left shifted by s bits
if neg {
// ^x is a sequence of n ones left shifted by s bits
// adjust n, s for x
s = n + s
n = period - n
}
N := uint32(0)
if mode == 64 && period == 64 {
N = 1
}
R := (period - s) & (period - 1) & uint32(mode-1) // shift amount of right rotate
S := (n - 1) | 63&^(period<<1-1) // low bits = #ones - 1, high bits encodes period
return N<<22 | R<<16 | S<<10
}
func log2(x uint64) uint32 {
if x == 0 {
panic("log2 of 0")
}
n := uint32(0)
if x >= 1<<32 {
x >>= 32
n += 32
}
if x >= 1<<16 {
x >>= 16
n += 16
}
if x >= 1<<8 {
x >>= 8
n += 8
}
if x >= 1<<4 {
x >>= 4
n += 4
}
if x >= 1<<2 {
x >>= 2
n += 2
}
if x >= 1<<1 {
x >>= 1
n += 1
}
return n
}
func autoclass(l int64) int {
if l == 0 {
return C_ZAUTO
}
if l < 0 {
if l >= -256 && (l&7) == 0 {
return C_NSAUTO_8
}
if l >= -256 && (l&3) == 0 {
return C_NSAUTO_4
}
if l >= -256 {
return C_NSAUTO
}
if l >= -512 && (l&7) == 0 {
return C_NPAUTO
}
if l >= -4095 {
return C_NAUTO4K
}
return C_LAUTO
}
if l <= 255 {
if (l & 7) == 0 {
return C_PSAUTO_8
}
if (l & 3) == 0 {
return C_PSAUTO_4
}
return C_PSAUTO
}
if l <= 504 && l&7 == 0 {
return C_PPAUTO
}
if l <= 4095 {
if l&7 == 0 {
return C_UAUTO4K_8
}
if l&3 == 0 {
return C_UAUTO4K_4
}
if l&1 == 0 {
return C_UAUTO4K_2
}
return C_UAUTO4K
}
if l <= 8190 {
if l&7 == 0 {
return C_UAUTO8K_8
}
if l&3 == 0 {
return C_UAUTO8K_4
}
if l&1 == 0 {
return C_UAUTO8K
}
}
if l <= 16380 {
if l&7 == 0 {
return C_UAUTO16K_8
}
if l&3 == 0 {
return C_UAUTO16K
}
}
if l <= 32760 && (l&7) == 0 {
return C_UAUTO32K
}
return C_LAUTO
}
func oregclass(l int64) int {
return autoclass(l) - C_ZAUTO + C_ZOREG
}
/*
* given an offset v and a class c (see above)
* return the offset value to use in the instruction,
* scaled if necessary
*/
func (c *ctxt7) offsetshift(p *obj.Prog, v int64, cls int) int64 {
s := 0
if cls >= C_SEXT1 && cls <= C_SEXT16 {
s = cls - C_SEXT1
} else {
switch cls {
case C_UAUTO4K, C_UOREG4K, C_ZOREG:
s = 0
case C_UAUTO8K, C_UOREG8K:
s = 1
case C_UAUTO16K, C_UOREG16K:
s = 2
case C_UAUTO32K, C_UOREG32K:
s = 3
default:
c.ctxt.Diag("bad class: %v\n%v", DRconv(cls), p)
}
}
vs := v >> uint(s)
if vs<<uint(s) != v {
c.ctxt.Diag("odd offset: %d\n%v", v, p)
}
return vs
}
/*
* if v contains a single 16-bit value aligned
* on a 16-bit field, and thus suitable for movk/movn,
* return the field index 0 to 3; otherwise return -1
*/
func movcon(v int64) int {
for s := 0; s < 64; s += 16 {
if (uint64(v) &^ (uint64(0xFFFF) << uint(s))) == 0 {
return s / 16
}
}
return -1
}
func rclass(r int16) int {
switch {
case REG_R0 <= r && r <= REG_R30: // not 31
return C_REG
case r == REGZERO:
return C_ZCON
case REG_F0 <= r && r <= REG_F31:
return C_FREG
case REG_V0 <= r && r <= REG_V31:
return C_VREG
case COND_EQ <= r && r <= COND_NV:
return C_COND
case r == REGSP:
return C_RSP
case r >= REG_ARNG && r < REG_ELEM:
return C_ARNG
case r >= REG_ELEM && r < REG_ELEM_END:
return C_ELEM
case r >= REG_UXTB && r < REG_SPECIAL:
return C_EXTREG
case r >= REG_SPECIAL:
return C_SPR
}
return C_GOK
}
// con32class reclassifies the constant of 32-bit instruction. Because the constant type is 32-bit,
// but saved in Offset which type is int64, con32class treats it as uint32 type and reclassifies it.
func (c *ctxt7) con32class(a *obj.Addr) int {
v := uint32(a.Offset)
if v == 0 {
return C_ZCON
}
if isaddcon(int64(v)) {
if v <= 0xFFF {
if isbitcon(uint64(a.Offset)) {
return C_ABCON0
}
return C_ADDCON0
}
if isbitcon(uint64(a.Offset)) {
return C_ABCON
}
if movcon(int64(v)) >= 0 {
return C_AMCON
}
if movcon(int64(^v)) >= 0 {
return C_AMCON
}
return C_ADDCON
}
t := movcon(int64(v))
if t >= 0 {
if isbitcon(uint64(a.Offset)) {
return C_MBCON
}
return C_MOVCON
}
t = movcon(int64(^v))
if t >= 0 {
if isbitcon(uint64(a.Offset)) {
return C_MBCON
}
return C_MOVCON
}
if isbitcon(uint64(a.Offset)) {
return C_BITCON
}
if 0 <= v && v <= 0xffffff {
return C_ADDCON2
}
return C_LCON
}
// con64class reclassifies the constant of C_VCON and C_LCON class.
func (c *ctxt7) con64class(a *obj.Addr) int {
zeroCount := 0
negCount := 0
for i := uint(0); i < 4; i++ {
immh := uint32(a.Offset >> (i * 16) & 0xffff)
if immh == 0 {
zeroCount++
} else if immh == 0xffff {
negCount++
}
}
if zeroCount >= 3 || negCount >= 3 {
return C_MOVCON
} else if zeroCount == 2 || negCount == 2 {
return C_MOVCON2
} else if zeroCount == 1 || negCount == 1 {
return C_MOVCON3
} else {
return C_VCON
}
}
func (c *ctxt7) aclass(a *obj.Addr) int {
switch a.Type {
case obj.TYPE_NONE:
return C_NONE
case obj.TYPE_REG:
return rclass(a.Reg)
case obj.TYPE_REGREG:
return C_PAIR
case obj.TYPE_SHIFT:
return C_SHIFT
case obj.TYPE_REGLIST:
return C_LIST
case obj.TYPE_MEM:
// The base register should be an integer register.
if int16(REG_F0) <= a.Reg && a.Reg <= int16(REG_V31) {
break
}
switch a.Name {
case obj.NAME_EXTERN, obj.NAME_STATIC:
if a.Sym == nil {
break
}
c.instoffset = a.Offset
if a.Sym != nil { // use relocation
if a.Sym.Type == objabi.STLSBSS {
if c.ctxt.Flag_shared {
return C_TLS_IE
} else {
return C_TLS_LE
}
}
return C_ADDR
}
return C_LEXT
case obj.NAME_GOTREF:
return C_GOTADDR
case obj.NAME_AUTO:
if a.Reg == REGSP {
// unset base register for better printing, since
// a.Offset is still relative to pseudo-SP.
a.Reg = obj.REG_NONE
}
// The frame top 8 or 16 bytes are for FP
c.instoffset = int64(c.autosize) + a.Offset - int64(c.extrasize)
return autoclass(c.instoffset)
case obj.NAME_PARAM:
if a.Reg == REGSP {
// unset base register for better printing, since
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
c.instoffset = int64(c.autosize) + a.Offset + 8
return autoclass(c.instoffset)
case obj.NAME_NONE:
if a.Index != 0 {
if a.Offset != 0 {
if isRegShiftOrExt(a) {
// extended or shifted register offset, (Rn)(Rm.UXTW<<2) or (Rn)(Rm<<2).
return C_ROFF
}
return C_GOK
}
// register offset, (Rn)(Rm)
return C_ROFF
}
c.instoffset = a.Offset
return oregclass(c.instoffset)
}
return C_GOK
case obj.TYPE_FCONST:
return C_FCON
case obj.TYPE_TEXTSIZE:
return C_TEXTSIZE
case obj.TYPE_CONST, obj.TYPE_ADDR:
switch a.Name {
case obj.NAME_NONE:
c.instoffset = a.Offset
if a.Reg != 0 && a.Reg != REGZERO {
break
}
v := c.instoffset
if v == 0 {
return C_ZCON
}
if isaddcon(v) {
if v <= 0xFFF {
if isbitcon(uint64(v)) {
return C_ABCON0
}
return C_ADDCON0
}
if isbitcon(uint64(v)) {
return C_ABCON
}
if movcon(v) >= 0 {
return C_AMCON
}
if movcon(^v) >= 0 {
return C_AMCON
}
return C_ADDCON
}
t := movcon(v)
if t >= 0 {
if isbitcon(uint64(v)) {
return C_MBCON
}
return C_MOVCON
}
t = movcon(^v)
if t >= 0 {
if isbitcon(uint64(v)) {
return C_MBCON
}
return C_MOVCON
}
if isbitcon(uint64(v)) {
return C_BITCON
}
if 0 <= v && v <= 0xffffff {
return C_ADDCON2
}
if uint64(v) == uint64(uint32(v)) || v == int64(int32(v)) {
return C_LCON
}
return C_VCON
case obj.NAME_EXTERN, obj.NAME_STATIC:
if a.Sym == nil {
return C_GOK
}
if a.Sym.Type == objabi.STLSBSS {
c.ctxt.Diag("taking address of TLS variable is not supported")
}
c.instoffset = a.Offset
return C_VCONADDR
case obj.NAME_AUTO:
if a.Reg == REGSP {
// unset base register for better printing, since
// a.Offset is still relative to pseudo-SP.
a.Reg = obj.REG_NONE
}
// The frame top 8 or 16 bytes are for FP
c.instoffset = int64(c.autosize) + a.Offset - int64(c.extrasize)
case obj.NAME_PARAM:
if a.Reg == REGSP {
// unset base register for better printing, since
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
c.instoffset = int64(c.autosize) + a.Offset + 8
default:
return C_GOK
}
if isaddcon(c.instoffset) {
return C_AACON
}
return C_LACON
case obj.TYPE_BRANCH:
return C_SBRA
}
return C_GOK
}
func oclass(a *obj.Addr) int {
return int(a.Class) - 1
}
func (c *ctxt7) oplook(p *obj.Prog) *Optab {
a1 := int(p.Optab)
if a1 != 0 {
return &optab[a1-1]
}
a1 = int(p.From.Class)
if a1 == 0 {
a0 := c.aclass(&p.From)
// do not break C_ADDCON2 when S bit is set
if (p.As == AADDS || p.As == AADDSW || p.As == ASUBS || p.As == ASUBSW) && a0 == C_ADDCON2 {
a0 = C_LCON
}
a1 = a0 + 1
p.From.Class = int8(a1)
// more specific classification of 32-bit integers
if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE {
if p.As == AMOVW || isADDWop(p.As) {
ra0 := c.con32class(&p.From)
// do not break C_ADDCON2 when S bit is set
if (p.As == AADDSW || p.As == ASUBSW) && ra0 == C_ADDCON2 {
ra0 = C_LCON
}
a1 = ra0 + 1
p.From.Class = int8(a1)
}
if isANDWop(p.As) && a0 != C_BITCON {
// For 32-bit logical instruction with constant,
// the BITCON test is special in that it looks at
// the 64-bit which has the high 32-bit as a copy
// of the low 32-bit. We have handled that and
// don't pass it to con32class.
a1 = c.con32class(&p.From) + 1
p.From.Class = int8(a1)
}
if ((p.As == AMOVD) || isANDop(p.As) || isADDop(p.As)) && (a0 == C_LCON || a0 == C_VCON) {
a1 = c.con64class(&p.From) + 1
p.From.Class = int8(a1)
}
}
}
a1--
a3 := C_NONE + 1
if p.GetFrom3() != nil {
a3 = int(p.GetFrom3().Class)
if a3 == 0 {
a3 = c.aclass(p.GetFrom3()) + 1
p.GetFrom3().Class = int8(a3)
}
}
a3--
a4 := int(p.To.Class)
if a4 == 0 {
a4 = c.aclass(&p.To) + 1
p.To.Class = int8(a4)
}
a4--
a2 := C_NONE
if p.Reg != 0 {
a2 = rclass(p.Reg)
}
if false {
fmt.Printf("oplook %v %d %d %d %d\n", p.As, a1, a2, a3, a4)
fmt.Printf("\t\t%d %d\n", p.From.Type, p.To.Type)
}
ops := oprange[p.As&obj.AMask]
c1 := &xcmp[a1]
c2 := &xcmp[a2]
c3 := &xcmp[a3]
c4 := &xcmp[a4]
c5 := &xcmp[p.Scond>>5]
for i := range ops {
op := &ops[i]
if (int(op.a2) == a2 || c2[op.a2]) && c5[op.scond>>5] && c1[op.a1] && c3[op.a3] && c4[op.a4] {
p.Optab = uint16(cap(optab) - cap(ops) + i + 1)
return op
}
}
c.ctxt.Diag("illegal combination: %v %v %v %v %v, %d %d", p, DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4), p.From.Type, p.To.Type)
// Turn illegal instruction into an UNDEF, avoid crashing in asmout
return &Optab{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0}
}
func cmp(a int, b int) bool {
if a == b {
return true
}
switch a {
case C_RSP:
if b == C_REG {
return true
}
case C_REG:
if b == C_ZCON {
return true
}
case C_ADDCON0:
if b == C_ZCON || b == C_ABCON0 {
return true
}
case C_ADDCON:
if b == C_ZCON || b == C_ABCON0 || b == C_ADDCON0 || b == C_ABCON || b == C_AMCON {
return true
}
case C_BITCON:
if b == C_ABCON0 || b == C_ABCON || b == C_MBCON {
return true
}
case C_MOVCON:
if b == C_MBCON || b == C_ZCON || b == C_ADDCON0 || b == C_AMCON {
return true
}
case C_ADDCON2:
if b == C_ZCON || b == C_ADDCON || b == C_ADDCON0 {
return true
}
case C_LCON:
if b == C_ZCON || b == C_BITCON || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_ABCON0 || b == C_MBCON || b == C_MOVCON || b == C_ADDCON2 || b == C_AMCON {
return true
}
case C_MOVCON2:
return cmp(C_LCON, b)
case C_VCON:
return cmp(C_LCON, b)
case C_LACON:
if b == C_AACON {
return true
}
case C_SEXT2:
if b == C_SEXT1 {
return true
}
case C_SEXT4:
if b == C_SEXT1 || b == C_SEXT2 {
return true
}
case C_SEXT8:
if b >= C_SEXT1 && b <= C_SEXT4 {
return true
}
case C_SEXT16:
if b >= C_SEXT1 && b <= C_SEXT8 {
return true
}
case C_LEXT:
if b >= C_SEXT1 && b <= C_SEXT16 {
return true
}
case C_NSAUTO_4:
if b == C_NSAUTO_8 {
return true
}
case C_NSAUTO:
switch b {
case C_NSAUTO_4, C_NSAUTO_8:
return true
}
case C_NPAUTO:
switch b {
case C_NSAUTO_8:
return true
}
case C_NAUTO4K:
switch b {
case C_NSAUTO_8, C_NSAUTO_4, C_NSAUTO, C_NPAUTO:
return true
}
case C_PSAUTO_8:
if b == C_ZAUTO {
return true
}
case C_PSAUTO_4:
switch b {
case C_ZAUTO, C_PSAUTO_8:
return true
}
case C_PSAUTO:
switch b {
case C_ZAUTO, C_PSAUTO_8, C_PSAUTO_4:
return true
}
case C_PPAUTO:
switch b {
case C_ZAUTO, C_PSAUTO_8:
return true
}
case C_UAUTO4K:
switch b {
case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8,
C_PPAUTO, C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8:
return true
}
case C_UAUTO8K:
switch b {
case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO,
C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO8K_4, C_UAUTO8K_8:
return true
}
case C_UAUTO16K:
switch b {
case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO,
C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO8K_4, C_UAUTO8K_8, C_UAUTO16K_8:
return true
}
case C_UAUTO32K:
switch b {
case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8,
C_PPAUTO, C_UAUTO4K_8, C_UAUTO8K_8, C_UAUTO16K_8:
return true
}
case C_LAUTO:
switch b {
case C_ZAUTO, C_NSAUTO, C_NSAUTO_4, C_NSAUTO_8, C_NPAUTO,
C_NAUTO4K, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO,
C_UAUTO4K, C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8,
C_UAUTO8K, C_UAUTO8K_4, C_UAUTO8K_8,
C_UAUTO16K, C_UAUTO16K_8,
C_UAUTO32K:
return true
}
case C_NSOREG_4:
if b == C_NSOREG_8 {
return true
}
case C_NSOREG:
switch b {
case C_NSOREG_4, C_NSOREG_8:
return true
}
case C_NPOREG:
switch b {
case C_NSOREG_8:
return true
}
case C_NOREG4K:
switch b {
case C_NSOREG_8, C_NSOREG_4, C_NSOREG, C_NPOREG:
return true
}
case C_PSOREG_4:
switch b {
case C_ZOREG, C_PSOREG_8:
return true
}
case C_PSOREG:
switch b {
case C_ZOREG, C_PSOREG_8, C_PSOREG_4:
return true
}
case C_PPOREG:
switch b {
case C_ZOREG, C_PSOREG_8:
return true
}
case C_UOREG4K:
switch b {
case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG,
C_PPOREG, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8:
return true
}
case C_UOREG8K:
switch b {
case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG,
C_PPOREG, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8,
C_UOREG8K_4, C_UOREG8K_8:
return true
}
case C_UOREG16K:
switch b {
case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG,
C_PPOREG, C_UOREG4K_4, C_UOREG4K_8, C_UOREG8K_4,
C_UOREG8K_8, C_UOREG16K_8:
return true
}
case C_UOREG32K:
switch b {
case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG,
C_PPOREG, C_UOREG4K_8, C_UOREG8K_8, C_UOREG16K_8:
return true
}
case C_LOREG:
switch b {
case C_ZOREG, C_NSOREG, C_NSOREG_4, C_NSOREG_8, C_NPOREG,
C_NOREG4K, C_PSOREG_4, C_PSOREG_8, C_PSOREG, C_PPOREG,
C_UOREG4K, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8,
C_UOREG8K, C_UOREG8K_4, C_UOREG8K_8,
C_UOREG16K, C_UOREG16K_8,
C_UOREG32K:
return true
}
case C_LBRA:
if b == C_SBRA {
return true
}
}
return false
}
type ocmp []Optab
func (x ocmp) Len() int {
return len(x)
}
func (x ocmp) Swap(i, j int) {
x[i], x[j] = x[j], x[i]
}
func (x ocmp) Less(i, j int) bool {
p1 := &x[i]
p2 := &x[j]
if p1.as != p2.as {
return p1.as < p2.as
}
if p1.a1 != p2.a1 {
return p1.a1 < p2.a1
}
if p1.a2 != p2.a2 {
return p1.a2 < p2.a2
}
if p1.a3 != p2.a3 {
return p1.a3 < p2.a3
}
if p1.a4 != p2.a4 {
return p1.a4 < p2.a4
}
if p1.scond != p2.scond {
return p1.scond < p2.scond
}
return false
}
func oprangeset(a obj.As, t []Optab) {
oprange[a&obj.AMask] = t
}
func buildop(ctxt *obj.Link) {
if oprange[AAND&obj.AMask] != nil {
// Already initialized; stop now.
// This happens in the cmd/asm tests,
// each of which re-initializes the arch.
return
}
var n int
for i := 0; i < C_GOK; i++ {
for n = 0; n < C_GOK; n++ {
if cmp(n, i) {
xcmp[i][n] = true
}
}
}
for n = 0; optab[n].as != obj.AXXX; n++ {
}
sort.Sort(ocmp(optab[:n]))
for i := 0; i < n; i++ {
r := optab[i].as
start := i
for optab[i].as == r {
i++
}
t := optab[start:i]
i--
oprangeset(r, t)
switch r {
default:
ctxt.Diag("unknown op in build: %v", r)
ctxt.DiagFlush()
log.Fatalf("bad code")
case AADD:
oprangeset(AADDS, t)
oprangeset(ASUB, t)
oprangeset(ASUBS, t)
oprangeset(AADDW, t)
oprangeset(AADDSW, t)
oprangeset(ASUBW, t)
oprangeset(ASUBSW, t)
case AAND: /* logical immediate, logical shifted register */
oprangeset(AANDW, t)
oprangeset(AEOR, t)
oprangeset(AEORW, t)
oprangeset(AORR, t)
oprangeset(AORRW, t)
oprangeset(ABIC, t)
oprangeset(ABICW, t)
oprangeset(AEON, t)
oprangeset(AEONW, t)
oprangeset(AORN, t)
oprangeset(AORNW, t)
case AANDS: /* logical immediate, logical shifted register, set flags, cannot target RSP */
oprangeset(AANDSW, t)
oprangeset(ABICS, t)
oprangeset(ABICSW, t)
case ANEG:
oprangeset(ANEGS, t)
oprangeset(ANEGSW, t)
oprangeset(ANEGW, t)
case AADC: /* rn=Rd */
oprangeset(AADCW, t)
oprangeset(AADCS, t)
oprangeset(AADCSW, t)
oprangeset(ASBC, t)
oprangeset(ASBCW, t)
oprangeset(ASBCS, t)
oprangeset(ASBCSW, t)
case ANGC: /* rn=REGZERO */
oprangeset(ANGCW, t)
oprangeset(ANGCS, t)
oprangeset(ANGCSW, t)
case ACMP:
oprangeset(ACMPW, t)
oprangeset(ACMN, t)
oprangeset(ACMNW, t)
case ATST:
oprangeset(ATSTW, t)
/* register/register, and shifted */
case AMVN:
oprangeset(AMVNW, t)
case AMOVK:
oprangeset(AMOVKW, t)
oprangeset(AMOVN, t)
oprangeset(AMOVNW, t)
oprangeset(AMOVZ, t)
oprangeset(AMOVZW, t)
case ASWPD:
for i := range atomicInstructions {
oprangeset(i, t)
}
case ABEQ:
oprangeset(ABNE, t)
oprangeset(ABCS, t)
oprangeset(ABHS, t)
oprangeset(ABCC, t)
oprangeset(ABLO, t)
oprangeset(ABMI, t)
oprangeset(ABPL, t)
oprangeset(ABVS, t)
oprangeset(ABVC, t)
oprangeset(ABHI, t)
oprangeset(ABLS, t)
oprangeset(ABGE, t)
oprangeset(ABLT, t)
oprangeset(ABGT, t)
oprangeset(ABLE, t)
case ALSL:
oprangeset(ALSLW, t)
oprangeset(ALSR, t)
oprangeset(ALSRW, t)
oprangeset(AASR, t)
oprangeset(AASRW, t)
oprangeset(AROR, t)
oprangeset(ARORW, t)
case ACLS:
oprangeset(ACLSW, t)
oprangeset(ACLZ, t)
oprangeset(ACLZW, t)
oprangeset(ARBIT, t)
oprangeset(ARBITW, t)
oprangeset(AREV, t)
oprangeset(AREVW, t)
oprangeset(AREV16, t)
oprangeset(AREV16W, t)
oprangeset(AREV32, t)
case ASDIV:
oprangeset(ASDIVW, t)
oprangeset(AUDIV, t)
oprangeset(AUDIVW, t)
oprangeset(ACRC32B, t)
oprangeset(ACRC32CB, t)
oprangeset(ACRC32CH, t)
oprangeset(ACRC32CW, t)
oprangeset(ACRC32CX, t)
oprangeset(ACRC32H, t)
oprangeset(ACRC32W, t)
oprangeset(ACRC32X, t)
case AMADD:
oprangeset(AMADDW, t)
oprangeset(AMSUB, t)
oprangeset(AMSUBW, t)
oprangeset(ASMADDL, t)
oprangeset(ASMSUBL, t)
oprangeset(AUMADDL, t)
oprangeset(AUMSUBL, t)
case AREM:
oprangeset(AREMW, t)
oprangeset(AUREM, t)
oprangeset(AUREMW, t)
case AMUL:
oprangeset(AMULW, t)
oprangeset(AMNEG, t)
oprangeset(AMNEGW, t)
oprangeset(ASMNEGL, t)
oprangeset(ASMULL, t)
oprangeset(ASMULH, t)
oprangeset(AUMNEGL, t)
oprangeset(AUMULH, t)
oprangeset(AUMULL, t)
case AMOVB:
oprangeset(AMOVBU, t)
case AMOVH:
oprangeset(AMOVHU, t)
case AMOVW:
oprangeset(AMOVWU, t)
case ABFM:
oprangeset(ABFMW, t)
oprangeset(ASBFM, t)
oprangeset(ASBFMW, t)
oprangeset(AUBFM, t)
oprangeset(AUBFMW, t)
case ABFI:
oprangeset(ABFIW, t)
oprangeset(ABFXIL, t)
oprangeset(ABFXILW, t)
oprangeset(ASBFIZ, t)
oprangeset(ASBFIZW, t)
oprangeset(ASBFX, t)
oprangeset(ASBFXW, t)
oprangeset(AUBFIZ, t)
oprangeset(AUBFIZW, t)
oprangeset(AUBFX, t)
oprangeset(AUBFXW, t)
case AEXTR:
oprangeset(AEXTRW, t)
case ASXTB:
oprangeset(ASXTBW, t)
oprangeset(ASXTH, t)
oprangeset(ASXTHW, t)
oprangeset(ASXTW, t)
oprangeset(AUXTB, t)
oprangeset(AUXTH, t)
oprangeset(AUXTW, t)
oprangeset(AUXTBW, t)
oprangeset(AUXTHW, t)
case ACCMN:
oprangeset(ACCMNW, t)
oprangeset(ACCMP, t)
oprangeset(ACCMPW, t)
case ACSEL:
oprangeset(ACSELW, t)
oprangeset(ACSINC, t)
oprangeset(ACSINCW, t)
oprangeset(ACSINV, t)
oprangeset(ACSINVW, t)
oprangeset(ACSNEG, t)
oprangeset(ACSNEGW, t)
case ACINC:
// aliases Rm=Rn, !cond
oprangeset(ACINCW, t)
oprangeset(ACINV, t)
oprangeset(ACINVW, t)
oprangeset(ACNEG, t)
oprangeset(ACNEGW, t)
// aliases, Rm=Rn=REGZERO, !cond
case ACSET:
oprangeset(ACSETW, t)
oprangeset(ACSETM, t)
oprangeset(ACSETMW, t)
case AMOVD,
AMOVBU,
AB,
ABL,
AWORD,
ADWORD,
obj.ARET,
obj.ATEXT:
break
case ALDP:
oprangeset(AFLDPD, t)
case ASTP:
oprangeset(AFSTPD, t)
case ASTPW:
oprangeset(AFSTPS, t)
case ALDPW:
oprangeset(ALDPSW, t)
oprangeset(AFLDPS, t)
case AERET:
oprangeset(AWFE, t)
oprangeset(AWFI, t)
oprangeset(AYIELD, t)
oprangeset(ASEV, t)
oprangeset(ASEVL, t)
oprangeset(ANOOP, t)
oprangeset(ADRPS, t)
case ACBZ:
oprangeset(ACBZW, t)
oprangeset(ACBNZ, t)
oprangeset(ACBNZW, t)
case ATBZ:
oprangeset(ATBNZ, t)
case AADR, AADRP:
break
case ACLREX:
break
case ASVC:
oprangeset(AHVC, t)
oprangeset(AHLT, t)
oprangeset(ASMC, t)
oprangeset(ABRK, t)
oprangeset(ADCPS1, t)
oprangeset(ADCPS2, t)
oprangeset(ADCPS3, t)
case AFADDS:
oprangeset(AFADDD, t)
oprangeset(AFSUBS, t)
oprangeset(AFSUBD, t)
oprangeset(AFMULS, t)
oprangeset(AFMULD, t)
oprangeset(AFNMULS, t)
oprangeset(AFNMULD, t)
oprangeset(AFDIVS, t)
oprangeset(AFMAXD, t)
oprangeset(AFMAXS, t)
oprangeset(AFMIND, t)
oprangeset(AFMINS, t)
oprangeset(AFMAXNMD, t)
oprangeset(AFMAXNMS, t)
oprangeset(AFMINNMD, t)
oprangeset(AFMINNMS, t)
oprangeset(AFDIVD, t)
case AFMSUBD:
oprangeset(AFMSUBS, t)
oprangeset(AFMADDS, t)
oprangeset(AFMADDD, t)
oprangeset(AFNMSUBS, t)
oprangeset(AFNMSUBD, t)
oprangeset(AFNMADDS, t)
oprangeset(AFNMADDD, t)
case AFCVTSD:
oprangeset(AFCVTDS, t)
oprangeset(AFABSD, t)
oprangeset(AFABSS, t)
oprangeset(AFNEGD, t)
oprangeset(AFNEGS, t)
oprangeset(AFSQRTD, t)
oprangeset(AFSQRTS, t)
oprangeset(AFRINTNS, t)
oprangeset(AFRINTND, t)
oprangeset(AFRINTPS, t)
oprangeset(AFRINTPD, t)
oprangeset(AFRINTMS, t)
oprangeset(AFRINTMD, t)
oprangeset(AFRINTZS, t)
oprangeset(AFRINTZD, t)
oprangeset(AFRINTAS, t)
oprangeset(AFRINTAD, t)
oprangeset(AFRINTXS, t)
oprangeset(AFRINTXD, t)
oprangeset(AFRINTIS, t)
oprangeset(AFRINTID, t)
oprangeset(AFCVTDH, t)
oprangeset(AFCVTHS, t)
oprangeset(AFCVTHD, t)
oprangeset(AFCVTSH, t)
case AFCMPS:
oprangeset(AFCMPD, t)
oprangeset(AFCMPES, t)
oprangeset(AFCMPED, t)
case AFCCMPS:
oprangeset(AFCCMPD, t)
oprangeset(AFCCMPES, t)
oprangeset(AFCCMPED, t)
case AFCSELD:
oprangeset(AFCSELS, t)
case AFMOVS, AFMOVD:
break
case AFCVTZSD:
oprangeset(AFCVTZSDW, t)
oprangeset(AFCVTZSS, t)
oprangeset(AFCVTZSSW, t)
oprangeset(AFCVTZUD, t)
oprangeset(AFCVTZUDW, t)
oprangeset(AFCVTZUS, t)
oprangeset(AFCVTZUSW, t)
case ASCVTFD:
oprangeset(ASCVTFS, t)
oprangeset(ASCVTFWD, t)
oprangeset(ASCVTFWS, t)
oprangeset(AUCVTFD, t)
oprangeset(AUCVTFS, t)
oprangeset(AUCVTFWD, t)
oprangeset(AUCVTFWS, t)
case ASYS:
oprangeset(AAT, t)
oprangeset(ADC, t)
oprangeset(AIC, t)
oprangeset(ATLBI, t)
case ASYSL, AHINT:
break
case ADMB:
oprangeset(ADSB, t)
oprangeset(AISB, t)
case AMRS, AMSR:
break
case ALDAR:
oprangeset(ALDARW, t)
oprangeset(ALDARB, t)
oprangeset(ALDARH, t)
fallthrough
case ALDXR:
oprangeset(ALDXRB, t)
oprangeset(ALDXRH, t)
oprangeset(ALDXRW, t)
case ALDAXR:
oprangeset(ALDAXRB, t)
oprangeset(ALDAXRH, t)
oprangeset(ALDAXRW, t)
case ALDXP:
oprangeset(ALDXPW, t)
oprangeset(ALDAXP, t)
oprangeset(ALDAXPW, t)
case ASTLR:
oprangeset(ASTLRB, t)
oprangeset(ASTLRH, t)
oprangeset(ASTLRW, t)
case ASTXR:
oprangeset(ASTXRB, t)
oprangeset(ASTXRH, t)
oprangeset(ASTXRW, t)
case ASTLXR:
oprangeset(ASTLXRB, t)
oprangeset(ASTLXRH, t)
oprangeset(ASTLXRW, t)
case ASTXP:
oprangeset(ASTLXP, t)
oprangeset(ASTLXPW, t)
oprangeset(ASTXPW, t)
case AVADDP:
oprangeset(AVAND, t)
oprangeset(AVCMEQ, t)
oprangeset(AVORR, t)
oprangeset(AVEOR, t)
case AVADD:
oprangeset(AVSUB, t)
case AAESD:
oprangeset(AAESE, t)
oprangeset(AAESMC, t)
oprangeset(AAESIMC, t)
oprangeset(ASHA1SU1, t)
oprangeset(ASHA256SU0, t)
oprangeset(ASHA512SU0, t)
case ASHA1C:
oprangeset(ASHA1P, t)
oprangeset(ASHA1M, t)
case ASHA256H:
oprangeset(ASHA256H2, t)
oprangeset(ASHA512H, t)
oprangeset(ASHA512H2, t)
case ASHA1SU0:
oprangeset(ASHA256SU1, t)
oprangeset(ASHA512SU1, t)
case AVADDV:
oprangeset(AVUADDLV, t)
case AVFMLA:
oprangeset(AVFMLS, t)
case AVPMULL:
oprangeset(AVPMULL2, t)
case AVUSHR:
oprangeset(AVSHL, t)
oprangeset(AVSRI, t)
case AVREV32:
oprangeset(AVCNT, t)
oprangeset(AVRBIT, t)
oprangeset(AVREV64, t)
oprangeset(AVREV16, t)
case AVZIP1:
oprangeset(AVZIP2, t)
case AVLD1R:
oprangeset(AVLD2, t)
oprangeset(AVLD2R, t)
oprangeset(AVLD3, t)
oprangeset(AVLD3R, t)
oprangeset(AVLD4, t)
oprangeset(AVLD4R, t)
case ASHA1H,
AVCNT,
AVMOV,
AVLD1,
AVST1,
AVST2,
AVST3,
AVST4,
AVTBL,
AVDUP,
AVMOVI,
APRFM,
AVEXT:
break
case obj.ANOP,
obj.AUNDEF,
obj.AFUNCDATA,
obj.APCALIGN,
obj.APCDATA,
obj.ADUFFZERO,
obj.ADUFFCOPY:
break
}
}
}
// chipfloat7() checks if the immediate constants available in FMOVS/FMOVD instructions.
// For details of the range of constants available, see
// http://infocenter.arm.com/help/topic/com.arm.doc.dui0473m/dom1359731199385.html.
func (c *ctxt7) chipfloat7(e float64) int {
ei := math.Float64bits(e)
l := uint32(int32(ei))
h := uint32(int32(ei >> 32))
if l != 0 || h&0xffff != 0 {
return -1
}
h1 := h & 0x7fc00000
if h1 != 0x40000000 && h1 != 0x3fc00000 {
return -1
}
n := 0
// sign bit (a)
if h&0x80000000 != 0 {
n |= 1 << 7
}
// exp sign bit (b)
if h1 == 0x3fc00000 {
n |= 1 << 6
}
// rest of exp and mantissa (cd-efgh)
n |= int((h >> 16) & 0x3f)
//print("match %.8lux %.8lux %d\n", l, h, n);
return n
}
/* form offset parameter to SYS; special register number */
func SYSARG5(op0 int, op1 int, Cn int, Cm int, op2 int) int {
return op0<<19 | op1<<16 | Cn<<12 | Cm<<8 | op2<<5
}
func SYSARG4(op1 int, Cn int, Cm int, op2 int) int {
return SYSARG5(0, op1, Cn, Cm, op2)
}
// checkUnpredictable checks if the sourse and transfer registers are the same register.
// ARM64 manual says it is "constrained unpredictable" if the src and dst registers of STP/LDP are same.
func (c *ctxt7) checkUnpredictable(p *obj.Prog, isload bool, wback bool, rn int16, rt1 int16, rt2 int16) {
if wback && rn != REGSP && (rn == rt1 || rn == rt2) {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
if isload && rt1 == rt2 {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
}
/* checkindex checks if index >= 0 && index <= maxindex */
func (c *ctxt7) checkindex(p *obj.Prog, index, maxindex int) {
if index < 0 || index > maxindex {
c.ctxt.Diag("register element index out of range 0 to %d: %v", maxindex, p)
}
}
/* checkoffset checks whether the immediate offset is valid for VLD[1-4].P and VST[1-4].P */
func (c *ctxt7) checkoffset(p *obj.Prog, as obj.As) {
var offset, list, n, expect int64
switch as {
case AVLD1, AVLD2, AVLD3, AVLD4, AVLD1R, AVLD2R, AVLD3R, AVLD4R:
offset = p.From.Offset
list = p.To.Offset
case AVST1, AVST2, AVST3, AVST4:
offset = p.To.Offset
list = p.From.Offset
default:
c.ctxt.Diag("invalid operation on op %v", p.As)
}
opcode := (list >> 12) & 15
q := (list >> 30) & 1
if offset == 0 {
return
}
switch opcode {
case 0x7:
n = 1 // one register
case 0xa:
n = 2 // two registers
case 0x6:
n = 3 // three registers
case 0x2:
n = 4 // four registers
default:
c.ctxt.Diag("invalid register numbers in ARM64 register list: %v", p)
}
if !(q == 0 && offset == n*8) && !(q == 1 && offset == n*16) {
c.ctxt.Diag("invalid post-increment offset: %v", p)
}
switch as {
case AVLD1, AVST1:
return
case AVLD1R:
expect = 1
case AVLD2, AVST2, AVLD2R:
expect = 2
case AVLD3, AVST3, AVLD3R:
expect = 3
case AVLD4, AVST4, AVLD4R:
expect = 4
}
if expect != n {
c.ctxt.Diag("expected %d registers, got %d: %v.", expect, n, p)
}
}
/* checkShiftAmount checks whether the index shift amount is valid */
/* for load with register offset instructions */
func (c *ctxt7) checkShiftAmount(p *obj.Prog, a *obj.Addr) {
var amount int16
amount = (a.Index >> 5) & 7
switch p.As {
case AMOVB, AMOVBU:
if amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
case AMOVH, AMOVHU:
if amount != 1 && amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
case AMOVW, AMOVWU, AFMOVS:
if amount != 2 && amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
case AMOVD, AFMOVD:
if amount != 3 && amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
default:
panic("invalid operation")
}
}
func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
var os [5]uint32
o1 := uint32(0)
o2 := uint32(0)
o3 := uint32(0)
o4 := uint32(0)
o5 := uint32(0)
if false { /*debug['P']*/
fmt.Printf("%x: %v\ttype %d\n", uint32(p.Pc), p, o.type_)
}
switch o.type_ {
default:
c.ctxt.Diag("%v: unknown asm %d", p, o.type_)
case 0: /* pseudo ops */
break
case 1: /* op Rm,[Rn],Rd; default Rn=Rd -> op Rm<<0,[Rn,]Rd (shifted register) */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
rt := int(p.To.Reg)
r := int(p.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
if r == 0 {
r = rt
}
o1 |= (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 2: /* add/sub $(uimm12|uimm24)[,R],R; cmp $(uimm12|uimm24),R */
o1 = c.opirr(p, p.As)
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
if (o1 & Sbit) == 0 {
c.ctxt.Diag("ineffective ZR destination\n%v", p)
}
rt = REGZERO
}
r := int(p.Reg)
if r == 0 {
r = rt
}
v := int32(c.regoff(&p.From))
o1 = c.oaddi(p, int32(o1), v, r, rt)
case 3: /* op R<<n[,R],R (shifted register) */
o1 = c.oprrr(p, p.As)
amount := (p.From.Offset >> 10) & 63
is64bit := o1 & (1 << 31)
if is64bit == 0 && amount >= 32 {
c.ctxt.Diag("shift amount out of range 0 to 31: %v", p)
}
o1 |= uint32(p.From.Offset) /* includes reg, op, etc */
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
r := int(p.Reg)
if p.As == AMVN || p.As == AMVNW {
r = REGZERO
} else if r == 0 {
r = rt
}
o1 |= (uint32(r&31) << 5) | uint32(rt&31)
case 4: /* mov $addcon, R; mov $recon, R; mov $racon, R */
o1 = c.opirr(p, p.As)
rt := int(p.To.Reg)
r := int(o.param)
if r == 0 {
r = REGZERO
} else if r == REGFROM {
r = int(p.From.Reg)
}
if r == 0 {
r = REGSP
}
v := int32(c.regoff(&p.From))
if (v & 0xFFF000) != 0 {
v >>= 12
o1 |= 1 << 22 /* shift, by 12 */
}
o1 |= ((uint32(v) & 0xFFF) << 10) | (uint32(r&31) << 5) | uint32(rt&31)
case 5: /* b s; bl s */
o1 = c.opbra(p, p.As)
if p.To.Sym == nil {
o1 |= uint32(c.brdist(p, 0, 26, 2))
break
}
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 4
rel.Sym = p.To.Sym
rel.Add = p.To.Offset
rel.Type = objabi.R_CALLARM64
case 6: /* b ,O(R); bl ,O(R) */
o1 = c.opbrr(p, p.As)
o1 |= uint32(p.To.Reg&31) << 5
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 0
rel.Type = objabi.R_CALLIND
case 7: /* beq s */
o1 = c.opbra(p, p.As)
o1 |= uint32(c.brdist(p, 0, 19, 2) << 5)
case 8: /* lsl $c,[R],R -> ubfm $(W-1)-c,$(-c MOD (W-1)),Rn,Rd */
rt := int(p.To.Reg)
rf := int(p.Reg)
if rf == 0 {
rf = rt
}
v := int32(p.From.Offset)
switch p.As {
case AASR:
o1 = c.opbfm(p, ASBFM, int(v), 63, rf, rt)
case AASRW:
o1 = c.opbfm(p, ASBFMW, int(v), 31, rf, rt)
case ALSL:
o1 = c.opbfm(p, AUBFM, int((64-v)&63), int(63-v), rf, rt)
case ALSLW:
o1 = c.opbfm(p, AUBFMW, int((32-v)&31), int(31-v), rf, rt)
case ALSR:
o1 = c.opbfm(p, AUBFM, int(v), 63, rf, rt)
case ALSRW:
o1 = c.opbfm(p, AUBFMW, int(v), 31, rf, rt)
case AROR:
o1 = c.opextr(p, AEXTR, v, rf, rf, rt)
case ARORW:
o1 = c.opextr(p, AEXTRW, v, rf, rf, rt)
default:
c.ctxt.Diag("bad shift $con\n%v", p)
break
}
case 9: /* lsl Rm,[Rn],Rd -> lslv Rm, Rn, Rd */
o1 = c.oprrr(p, p.As)
r := int(p.Reg)
if r == 0 {
r = int(p.To.Reg)
}
o1 |= (uint32(p.From.Reg&31) << 16) | (uint32(r&31) << 5) | uint32(p.To.Reg&31)
case 10: /* brk/hvc/.../svc [$con] */
o1 = c.opimm(p, p.As)
if p.From.Type != obj.TYPE_NONE {
o1 |= uint32((p.From.Offset & 0xffff) << 5)
}
case 11: /* dword */
c.aclass(&p.To)
o1 = uint32(c.instoffset)
o2 = uint32(c.instoffset >> 32)
if p.To.Sym != nil {
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.To.Sym
rel.Add = p.To.Offset
rel.Type = objabi.R_ADDR
o2 = 0
o1 = o2
}
case 12: /* movT $vcon, reg */
// NOTE: this case does not use REGTMP. If it ever does,
// remove the NOTUSETMP flag in optab.
num := c.omovlconst(p.As, p, &p.From, int(p.To.Reg), os[:])
if num == 0 {
c.ctxt.Diag("invalid constant: %v", p)
}
o1 = os[0]
o2 = os[1]
o3 = os[2]
o4 = os[3]
case 13: /* addop $vcon, [R], R (64 bit literal); cmp $lcon,R -> addop $lcon,R, ZR */
o := uint32(0)
num := uint8(0)
cls := oclass(&p.From)
if isADDWop(p.As) {
if !cmp(C_LCON, cls) {
c.ctxt.Diag("illegal combination: %v", p)
}
num = c.omovlconst(AMOVW, p, &p.From, REGTMP, os[:])
} else {
num = c.omovlconst(AMOVD, p, &p.From, REGTMP, os[:])
}
if num == 0 {
c.ctxt.Diag("invalid constant: %v", p)
}
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
r := int(p.Reg)
if r == 0 {
r = rt
}
if p.To.Type != obj.TYPE_NONE && (p.To.Reg == REGSP || r == REGSP) {
o = c.opxrrr(p, p.As, false)
o |= REGTMP & 31 << 16
o |= LSL0_64
} else {
o = c.oprrr(p, p.As)
o |= REGTMP & 31 << 16 /* shift is 0 */
}
o |= uint32(r&31) << 5
o |= uint32(rt & 31)
os[num] = o
o1 = os[0]
o2 = os[1]
o3 = os[2]
o4 = os[3]
o5 = os[4]
case 14: /* word */
if c.aclass(&p.To) == C_ADDR {
c.ctxt.Diag("address constant needs DWORD\n%v", p)
}
o1 = uint32(c.instoffset)
if p.To.Sym != nil {
// This case happens with words generated
// in the PC stream as part of the literal pool.
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 4
rel.Sym = p.To.Sym
rel.Add = p.To.Offset
rel.Type = objabi.R_ADDR
o1 = 0
}
case 15: /* mul/mneg/umulh/umull r,[r,]r; madd/msub/fmadd/fmsub/fnmadd/fnmsub Rm,Ra,Rn,Rd */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
rt := int(p.To.Reg)
var r int
var ra int
if p.From3Type() == obj.TYPE_REG {
r = int(p.GetFrom3().Reg)
ra = int(p.Reg)
if ra == 0 {
ra = REGZERO
}
} else {
r = int(p.Reg)
if r == 0 {
r = rt
}
ra = REGZERO
}
o1 |= (uint32(rf&31) << 16) | (uint32(ra&31) << 10) | (uint32(r&31) << 5) | uint32(rt&31)
case 16: /* XremY R[,R],R -> XdivY; XmsubY */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
rt := int(p.To.Reg)
r := int(p.Reg)
if r == 0 {
r = rt
}
o1 |= (uint32(rf&31) << 16) | (uint32(r&31) << 5) | REGTMP&31
o2 = c.oprrr(p, AMSUBW)
o2 |= o1 & (1 << 31) /* same size */
o2 |= (uint32(rf&31) << 16) | (uint32(r&31) << 10) | (REGTMP & 31 << 5) | uint32(rt&31)
case 17: /* op Rm,[Rn],Rd; default Rn=ZR */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
rt := int(p.To.Reg)
r := int(p.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
if r == 0 {
r = REGZERO
}
o1 |= (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 18: /* csel cond,Rn,Rm,Rd; cinc/cinv/cneg cond,Rn,Rd; cset cond,Rd */
o1 = c.oprrr(p, p.As)
cond := int(p.From.Reg)
if cond < COND_EQ || cond > COND_NV {
c.ctxt.Diag("invalid condition: %v", p)
} else {
cond -= COND_EQ
}
r := int(p.Reg)
var rf int
if r != 0 {
if p.From3Type() == obj.TYPE_NONE {
/* CINC/CINV/CNEG */
rf = r
cond ^= 1
} else {
rf = int(p.GetFrom3().Reg) /* CSEL */
}
} else {
/* CSET */
rf = REGZERO
r = rf
cond ^= 1
}
rt := int(p.To.Reg)
o1 |= (uint32(rf&31) << 16) | (uint32(cond&15) << 12) | (uint32(r&31) << 5) | uint32(rt&31)
case 19: /* CCMN cond, (Rm|uimm5),Rn, uimm4 -> ccmn Rn,Rm,uimm4,cond */
nzcv := int(p.To.Offset)
cond := int(p.From.Reg)
if cond < COND_EQ || cond > COND_NV {
c.ctxt.Diag("invalid condition\n%v", p)
} else {
cond -= COND_EQ
}
var rf int
if p.GetFrom3().Type == obj.TYPE_REG {
o1 = c.oprrr(p, p.As)
rf = int(p.GetFrom3().Reg) /* Rm */
} else {
o1 = c.opirr(p, p.As)
rf = int(p.GetFrom3().Offset & 0x1F)
}
o1 |= (uint32(rf&31) << 16) | (uint32(cond&15) << 12) | (uint32(p.Reg&31) << 5) | uint32(nzcv)
case 20: /* movT R,O(R) -> strT */
v := int32(c.regoff(&p.To))
sz := int32(1 << uint(movesize(p.As)))
r := int(p.To.Reg)
if r == 0 {
r = int(o.param)
}
if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */
o1 = c.olsr9s(p, int32(c.opstr9(p, p.As)), v, r, int(p.From.Reg))
} else {
v = int32(c.offsetshift(p, int64(v), int(o.a4)))
o1 = c.olsr12u(p, int32(c.opstr12(p, p.As)), v, r, int(p.From.Reg))
}
case 21: /* movT O(R),R -> ldrT */
v := int32(c.regoff(&p.From))
sz := int32(1 << uint(movesize(p.As)))
r := int(p.From.Reg)
if r == 0 {
r = int(o.param)
}
if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */
o1 = c.olsr9s(p, int32(c.opldr9(p, p.As)), v, r, int(p.To.Reg))
} else {
v = int32(c.offsetshift(p, int64(v), int(o.a1)))
//print("offset=%lld v=%ld a1=%d\n", instoffset, v, o->a1);
o1 = c.olsr12u(p, int32(c.opldr12(p, p.As)), v, r, int(p.To.Reg))
}
case 22: /* movT (R)O!,R; movT O(R)!, R -> ldrT */
if p.From.Reg != REGSP && p.From.Reg == p.To.Reg {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
v := int32(p.From.Offset)
if v < -256 || v > 255 {
c.ctxt.Diag("offset out of range [-255,254]: %v", p)
}
o1 = c.opldrpp(p, p.As)
if o.scond == C_XPOST {
o1 |= 1 << 10
} else {
o1 |= 3 << 10
}
o1 |= ((uint32(v) & 0x1FF) << 12) | (uint32(p.From.Reg&31) << 5) | uint32(p.To.Reg&31)
case 23: /* movT R,(R)O!; movT O(R)!, R -> strT */
if p.To.Reg != REGSP && p.From.Reg == p.To.Reg {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
v := int32(p.To.Offset)
if v < -256 || v > 255 {
c.ctxt.Diag("offset out of range [-255,254]: %v", p)
}
o1 = LD2STR(c.opldrpp(p, p.As))
if o.scond == C_XPOST {
o1 |= 1 << 10
} else {
o1 |= 3 << 10
}
o1 |= ((uint32(v) & 0x1FF) << 12) | (uint32(p.To.Reg&31) << 5) | uint32(p.From.Reg&31)
case 24: /* mov/mvn Rs,Rd -> add $0,Rs,Rd or orr Rs,ZR,Rd */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
s := rf == REGSP || rt == REGSP
if p.As == AMVN || p.As == AMVNW {
if s {
c.ctxt.Diag("illegal SP reference\n%v", p)
}
o1 = c.oprrr(p, p.As)
o1 |= (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31)
} else if s {
o1 = c.opirr(p, p.As)
o1 |= (uint32(rf&31) << 5) | uint32(rt&31)
} else {
o1 = c.oprrr(p, p.As)
o1 |= (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31)
}
case 25: /* negX Rs, Rd -> subX Rs<<0, ZR, Rd */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
if rf == C_NONE {
rf = int(p.To.Reg)
}
rt := int(p.To.Reg)
o1 |= (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31)
case 26: /* negX Rm<<s, Rd -> subX Rm<<s, ZR, Rd */
o1 = c.oprrr(p, p.As)
o1 |= uint32(p.From.Offset) /* includes reg, op, etc */
rt := int(p.To.Reg)
o1 |= (REGZERO & 31 << 5) | uint32(rt&31)
case 27: /* op Rm<<n[,Rn],Rd (extended register) */
if (p.From.Reg-obj.RBaseARM64)&REG_EXT != 0 {
amount := (p.From.Reg >> 5) & 7
if amount > 4 {
c.ctxt.Diag("shift amount out of range 0 to 4: %v", p)
}
o1 = c.opxrrr(p, p.As, true)
o1 |= c.encRegShiftOrExt(&p.From, p.From.Reg) /* includes reg, op, etc */
} else {
o1 = c.opxrrr(p, p.As, false)
o1 |= uint32(p.From.Reg&31) << 16
}
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
r := int(p.Reg)
if r == 0 {
r = rt
}
o1 |= (uint32(r&31) << 5) | uint32(rt&31)
case 28: /* logop $vcon, [R], R (64 bit literal) */
o := uint32(0)
num := uint8(0)
cls := oclass(&p.From)
if isANDWop(p.As) {
if !cmp(C_LCON, cls) {
c.ctxt.Diag("illegal combination: %v", p)
}
num = c.omovlconst(AMOVW, p, &p.From, REGTMP, os[:])
} else {
num = c.omovlconst(AMOVD, p, &p.From, REGTMP, os[:])
}
if num == 0 {
c.ctxt.Diag("invalid constant: %v", p)
}
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
r := int(p.Reg)
if r == 0 {
r = rt
}
o = c.oprrr(p, p.As)
o |= REGTMP & 31 << 16 /* shift is 0 */
o |= uint32(r&31) << 5
o |= uint32(rt & 31)
os[num] = o
o1 = os[0]
o2 = os[1]
o3 = os[2]
o4 = os[3]
o5 = os[4]
case 29: /* op Rn, Rd */
fc := c.aclass(&p.From)
tc := c.aclass(&p.To)
if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZCON || tc == C_REG || tc == C_ZCON) {
// FMOV Rx, Fy or FMOV Fy, Rx
o1 = FPCVTI(0, 0, 0, 0, 6)
if p.As == AFMOVD {
o1 |= 1<<31 | 1<<22 // 64-bit
}
if fc == C_REG || fc == C_ZCON {
o1 |= 1 << 16 // FMOV Rx, Fy
}
} else {
o1 = c.oprrr(p, p.As)
}
o1 |= uint32(p.From.Reg&31)<<5 | uint32(p.To.Reg&31)
case 30: /* movT R,L(R) -> strT */
// if offset L can be split into hi+lo, and both fit into instructions, do
// add $hi, R, Rtmp
// str R, lo(Rtmp)
// otherwise, use constant pool
// mov $L, Rtmp (from constant pool)
// str R, (R+Rtmp)
s := movesize(o.as)
if s < 0 {
c.ctxt.Diag("unexpected long move, op %v tab %v\n%v", p.As, o.as, p)
}
r := int(p.To.Reg)
if r == 0 {
r = int(o.param)
}
v := int32(c.regoff(&p.To))
var hi int32
if v < 0 || (v&((1<<uint(s))-1)) != 0 {
// negative or unaligned offset, use constant pool
goto storeusepool
}
hi = v - (v & (0xFFF << uint(s)))
if hi&0xFFF != 0 {
c.ctxt.Diag("internal: miscalculated offset %d [%d]\n%v", v, s, p)
}
if hi&^0xFFF000 != 0 {
// hi doesn't fit into an ADD instruction
goto storeusepool
}
o1 = c.oaddi(p, int32(c.opirr(p, AADD)), hi, r, REGTMP)
o2 = c.olsr12u(p, int32(c.opstr12(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.From.Reg))
break
storeusepool:
if r == REGTMP || p.From.Reg == REGTMP {
c.ctxt.Diag("REGTMP used in large offset store: %v", p)
}
o1 = c.omovlit(AMOVD, p, &p.To, REGTMP)
o2 = c.olsxrr(p, int32(c.opstrr(p, p.As, false)), int(p.From.Reg), r, REGTMP)
case 31: /* movT L(R), R -> ldrT */
// if offset L can be split into hi+lo, and both fit into instructions, do
// add $hi, R, Rtmp
// ldr lo(Rtmp), R
// otherwise, use constant pool
// mov $L, Rtmp (from constant pool)
// ldr (R+Rtmp), R
s := movesize(o.as)
if s < 0 {
c.ctxt.Diag("unexpected long move, op %v tab %v\n%v", p.As, o.as, p)
}
r := int(p.From.Reg)
if r == 0 {
r = int(o.param)
}
v := int32(c.regoff(&p.From))
var hi int32
if v < 0 || (v&((1<<uint(s))-1)) != 0 {
// negative or unaligned offset, use constant pool
goto loadusepool
}
hi = v - (v & (0xFFF << uint(s)))
if (hi & 0xFFF) != 0 {
c.ctxt.Diag("internal: miscalculated offset %d [%d]\n%v", v, s, p)
}
if hi&^0xFFF000 != 0 {
// hi doesn't fit into an ADD instruction
goto loadusepool
}
o1 = c.oaddi(p, int32(c.opirr(p, AADD)), hi, r, REGTMP)
o2 = c.olsr12u(p, int32(c.opldr12(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.To.Reg))
break
loadusepool:
if r == REGTMP || p.From.Reg == REGTMP {
c.ctxt.Diag("REGTMP used in large offset load: %v", p)
}
o1 = c.omovlit(AMOVD, p, &p.From, REGTMP)
o2 = c.olsxrr(p, int32(c.opldrr(p, p.As, false)), int(p.To.Reg), r, REGTMP)
case 32: /* mov $con, R -> movz/movn */
o1 = c.omovconst(p.As, p, &p.From, int(p.To.Reg))
case 33: /* movk $uimm16 << pos */
o1 = c.opirr(p, p.As)
d := p.From.Offset
s := movcon(d)
if s < 0 || s >= 4 {
c.ctxt.Diag("bad constant for MOVK: %#x\n%v", uint64(d), p)
}
if (o1&S64) == 0 && s >= 2 {
c.ctxt.Diag("illegal bit position\n%v", p)
}
if ((d >> uint(s*16)) >> 16) != 0 {
c.ctxt.Diag("requires uimm16\n%v", p)
}
rt := int(p.To.Reg)
o1 |= uint32((((d >> uint(s*16)) & 0xFFFF) << 5) | int64((uint32(s)&3)<<21) | int64(rt&31))
case 34: /* mov $lacon,R */
o1 = c.omovlit(AMOVD, p, &p.From, REGTMP)
if o1 == 0 {
break
}
o2 = c.opxrrr(p, AADD, false)
o2 |= REGTMP & 31 << 16
o2 |= LSL0_64
r := int(p.From.Reg)
if r == 0 {
r = int(o.param)
}
o2 |= uint32(r&31) << 5
o2 |= uint32(p.To.Reg & 31)
case 35: /* mov SPR,R -> mrs */
o1 = c.oprrr(p, AMRS)
// SysRegEnc function returns the system register encoding and accessFlags.
_, v, accessFlags := SysRegEnc(p.From.Reg)
if v == 0 {
c.ctxt.Diag("illegal system register:\n%v", p)
}
if (o1 & (v &^ (3 << 19))) != 0 {
c.ctxt.Diag("MRS register value overlap\n%v", p)
}
if accessFlags&SR_READ == 0 {
c.ctxt.Diag("system register is not readable: %v", p)
}
o1 |= v
o1 |= uint32(p.To.Reg & 31)
case 36: /* mov R,SPR */
o1 = c.oprrr(p, AMSR)
// SysRegEnc function returns the system register encoding and accessFlags.
_, v, accessFlags := SysRegEnc(p.To.Reg)
if v == 0 {
c.ctxt.Diag("illegal system register:\n%v", p)
}
if (o1 & (v &^ (3 << 19))) != 0 {
c.ctxt.Diag("MSR register value overlap\n%v", p)
}
if accessFlags&SR_WRITE == 0 {
c.ctxt.Diag("system register is not writable: %v", p)
}
o1 |= v
o1 |= uint32(p.From.Reg & 31)
case 37: /* mov $con,PSTATEfield -> MSR [immediate] */
if (uint64(p.From.Offset) &^ uint64(0xF)) != 0 {
c.ctxt.Diag("illegal immediate for PSTATE field\n%v", p)
}
o1 = c.opirr(p, AMSR)
o1 |= uint32((p.From.Offset & 0xF) << 8) /* Crm */
v := uint32(0)
for i := 0; i < len(pstatefield); i++ {
if pstatefield[i].reg == p.To.Reg {
v = pstatefield[i].enc
break
}
}
if v == 0 {
c.ctxt.Diag("illegal PSTATE field for immediate move\n%v", p)
}
o1 |= v
case 38: /* clrex [$imm] */
o1 = c.opimm(p, p.As)
if p.To.Type == obj.TYPE_NONE {
o1 |= 0xF << 8
} else {
o1 |= uint32((p.To.Offset & 0xF) << 8)
}
case 39: /* cbz R, rel */
o1 = c.opirr(p, p.As)
o1 |= uint32(p.From.Reg & 31)
o1 |= uint32(c.brdist(p, 0, 19, 2) << 5)
case 40: /* tbz */
o1 = c.opirr(p, p.As)
v := int32(p.From.Offset)
if v < 0 || v > 63 {
c.ctxt.Diag("illegal bit number\n%v", p)
}
o1 |= ((uint32(v) & 0x20) << (31 - 5)) | ((uint32(v) & 0x1F) << 19)
o1 |= uint32(c.brdist(p, 0, 14, 2) << 5)
o1 |= uint32(p.Reg & 31)
case 41: /* eret, nop, others with no operands */
o1 = c.op0(p, p.As)
case 42: /* bfm R,r,s,R */
o1 = c.opbfm(p, p.As, int(p.From.Offset), int(p.GetFrom3().Offset), int(p.Reg), int(p.To.Reg))
case 43: /* bfm aliases */
r := int(p.From.Offset)
s := int(p.GetFrom3().Offset)
rf := int(p.Reg)
rt := int(p.To.Reg)
if rf == 0 {
rf = rt
}
switch p.As {
case ABFI:
if r != 0 {
r = 64 - r
}
o1 = c.opbfm(p, ABFM, r, s-1, rf, rt)
case ABFIW:
if r != 0 {
r = 32 - r
}
o1 = c.opbfm(p, ABFMW, r, s-1, rf, rt)
case ABFXIL:
o1 = c.opbfm(p, ABFM, r, r+s-1, rf, rt)
case ABFXILW:
o1 = c.opbfm(p, ABFMW, r, r+s-1, rf, rt)
case ASBFIZ:
if r != 0 {
r = 64 - r
}
o1 = c.opbfm(p, ASBFM, r, s-1, rf, rt)
case ASBFIZW:
if r != 0 {
r = 32 - r
}
o1 = c.opbfm(p, ASBFMW, r, s-1, rf, rt)
case ASBFX:
o1 = c.opbfm(p, ASBFM, r, r+s-1, rf, rt)
case ASBFXW:
o1 = c.opbfm(p, ASBFMW, r, r+s-1, rf, rt)
case AUBFIZ:
if r != 0 {
r = 64 - r
}
o1 = c.opbfm(p, AUBFM, r, s-1, rf, rt)