in src/cmd/internal/obj/arm64/asm7.go [3188:5557]
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 */
if p.To.Reg == REG_RSP && isADDSop(p.As) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
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)
}
shift := (p.From.Offset >> 22) & 3
if (shift > 2 || shift < 0) && (isADDop(p.As) || isADDWop(p.As) || isNEGop(p.As)) {
c.ctxt.Diag("unsupported shift operator: %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 || isNEGop(p.As) {
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; mov $addcon2, R */
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))
var op int32
if v < 0 {
v = -v
op = int32(c.opirr(p, ASUB))
} else {
op = int32(c.opirr(p, AADD))
}
if int(o.size) == 8 {
// NOTE: this case does not use REGTMP. If it ever does,
// remove the NOTUSETMP flag in optab.
o1 = c.oaddi(p, op, v&0xfff000, r, rt)
o2 = c.oaddi(p, op, v&0x000fff, rt, rt)
break
}
o1 = c.oaddi(p, op, v, r, rt)
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
if p.As == obj.ACALL {
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 */
if p.Reg == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
}
if p.To.Reg == REG_RSP && isADDSop(p.As) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
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)
// AL and NV are not allowed for CINC/CINV/CNEG/CSET/CSETM instructions
if cond < COND_EQ || cond > COND_NV || (cond == COND_AL || cond == COND_NV) && p.From3Type() == obj.TYPE_NONE {
c.ctxt.Diag("invalid condition: %v", p)
} else {
cond -= COND_EQ
}
r := int(p.Reg)
var rf int = r
if p.From3Type() == obj.TYPE_NONE {
/* CINC/CINV/CNEG or CSET/CSETM*/
if r == 0 {
/* CSET/CSETM */
rf = REGZERO
r = rf
}
cond ^= 1
} else {
rf = int(p.GetFrom3().Reg) /* CSEL */
}
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.opstr(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.opstr(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.opldr(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.opldr(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 [-256,255]: %v", p)
}
o1 = c.opldr(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 [-256,255]: %v", p)
}
o1 = c.opstr(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: // op R<<n, RSP, RSP (extended register)
// Refer to ARM reference manual, if "Rd" or "Rn" is RSP,
// it can be encoded as op(extended regster) instruction.
if !(p.To.Reg == REGSP || p.Reg == REGSP) {
c.ctxt.Diag("expected SP reference: %v", p)
break
}
if p.To.Reg == REGSP && (p.As == AADDS || p.As == AADDSW || p.As == ASUBS || p.As == ASUBSW) {
c.ctxt.Diag("unexpected SP reference: %v", p)
break
}
amount := (p.From.Offset >> 10) & 63
shift := (p.From.Offset >> 22) & 3
if shift != 0 {
c.ctxt.Diag("illegal combination: %v", p)
break
}
if amount > 4 {
c.ctxt.Diag("the left shift amount out of range 0 to 4: %v", p)
break
}
rf := (p.From.Offset >> 16) & 31
rt := int(p.To.Reg)
r := int(p.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
if r == 0 {
r = rt
}
o1 = c.opxrrr(p, p.As, false)
o1 |= uint32(rf)<<16 | uint32(amount&7)<<10 | (uint32(r&31) << 5) | uint32(rt&31)
case 27: /* op Rm<<n[,Rn],Rd (extended register) */
if p.To.Reg == REG_RSP && isADDSop(p.As) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
if (p.From.Reg-obj.RBaseARM64)®_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) */
if p.Reg == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
}
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.opstr(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.opldr(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
if d == 0 {
c.ctxt.Diag("zero shifts cannot be handled correctly: %v", p)
}
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)
case AUBFIZW:
if r != 0 {
r = 32 - r
}
o1 = c.opbfm(p, AUBFMW, r, s-1, rf, rt)
case AUBFX:
o1 = c.opbfm(p, AUBFM, r, r+s-1, rf, rt)
case AUBFXW:
o1 = c.opbfm(p, AUBFMW, r, r+s-1, rf, rt)
default:
c.ctxt.Diag("bad bfm alias\n%v", p)
break
}
case 44: /* extr $b, Rn, Rm, Rd */
o1 = c.opextr(p, p.As, int32(p.From.Offset), int(p.GetFrom3().Reg), int(p.Reg), int(p.To.Reg))
case 45: /* sxt/uxt[bhw] R,R; movT R,R -> sxtT R,R */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
as := p.As
if rf == REGZERO {
as = AMOVWU /* clearer in disassembly */
}
switch as {
case AMOVB, ASXTB:
o1 = c.opbfm(p, ASBFM, 0, 7, rf, rt)
case AMOVH, ASXTH:
o1 = c.opbfm(p, ASBFM, 0, 15, rf, rt)
case AMOVW, ASXTW:
o1 = c.opbfm(p, ASBFM, 0, 31, rf, rt)
case AMOVBU, AUXTB:
o1 = c.opbfm(p, AUBFM, 0, 7, rf, rt)
case AMOVHU, AUXTH:
o1 = c.opbfm(p, AUBFM, 0, 15, rf, rt)
case AMOVWU:
o1 = c.oprrr(p, as) | (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31)
case AUXTW:
o1 = c.opbfm(p, AUBFM, 0, 31, rf, rt)
case ASXTBW:
o1 = c.opbfm(p, ASBFMW, 0, 7, rf, rt)
case ASXTHW:
o1 = c.opbfm(p, ASBFMW, 0, 15, rf, rt)
case AUXTBW:
o1 = c.opbfm(p, AUBFMW, 0, 7, rf, rt)
case AUXTHW:
o1 = c.opbfm(p, AUBFMW, 0, 15, rf, rt)
default:
c.ctxt.Diag("bad sxt %v", as)
break
}
case 46: /* cls */
o1 = c.opbit(p, p.As)
o1 |= uint32(p.From.Reg&31) << 5
o1 |= uint32(p.To.Reg & 31)
case 47: // SWPx/LDADDx/LDCLRx/LDEORx/LDORx/CASx Rs, (Rb), Rt
rs := p.From.Reg
rt := p.RegTo2
rb := p.To.Reg
// rt can't be sp.
if rt == REG_RSP {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
if enc, ok := atomicLDADD[p.As]; ok {
// for LDADDx-like instructions, rt can't be r31 when field.enc A is 0, A bit is the 23rd bit.
if (rt == REGZERO) && (enc&(1<<23) == 0) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
o1 |= enc
} else if enc, ok := atomicSWP[p.As]; ok {
o1 |= enc
} else {
c.ctxt.Diag("invalid atomic instructions: %v\n", p)
}
o1 |= uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31)
case 48: /* ADD $C_ADDCON2, Rm, Rd */
// NOTE: this case does not use REGTMP. If it ever does,
// remove the NOTUSETMP flag in optab.
op := c.opirr(p, p.As)
if op&Sbit != 0 {
c.ctxt.Diag("can not break addition/subtraction when S bit is set", p)
}
rt := int(p.To.Reg)
r := int(p.Reg)
if r == 0 {
r = rt
}
o1 = c.oaddi(p, int32(op), int32(c.regoff(&p.From))&0x000fff, r, rt)
o2 = c.oaddi(p, int32(op), int32(c.regoff(&p.From))&0xfff000, rt, rt)
case 50: /* sys/sysl */
o1 = c.opirr(p, p.As)
if (p.From.Offset &^ int64(SYSARG4(0x7, 0xF, 0xF, 0x7))) != 0 {
c.ctxt.Diag("illegal SYS argument\n%v", p)
}
o1 |= uint32(p.From.Offset)
if p.To.Type == obj.TYPE_REG {
o1 |= uint32(p.To.Reg & 31)
} else if p.Reg != 0 {
o1 |= uint32(p.Reg & 31)
} else {
o1 |= 0x1F
}
case 51: /* dmb */
o1 = c.opirr(p, p.As)
if p.From.Type == obj.TYPE_CONST {
o1 |= uint32((p.From.Offset & 0xF) << 8)
}
case 52: /* hint */
o1 = c.opirr(p, p.As)
o1 |= uint32((p.From.Offset & 0x7F) << 5)
case 53: /* and/or/eor/bic/tst/... $bitcon, Rn, Rd */
a := p.As
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
r := int(p.Reg)
if r == 0 {
r = rt
}
if r == REG_RSP {
c.ctxt.Diag("illegal source register: %v", p)
break
}
mode := 64
v := uint64(p.From.Offset)
switch p.As {
case AANDW, AORRW, AEORW, AANDSW, ATSTW:
mode = 32
case ABIC, AORN, AEON, ABICS:
v = ^v
case ABICW, AORNW, AEONW, ABICSW:
v = ^v
mode = 32
}
o1 = c.opirr(p, a)
o1 |= bitconEncode(v, mode) | uint32(r&31)<<5 | uint32(rt&31)
case 54: /* floating point arith */
o1 = c.oprrr(p, p.As)
rf := int(p.From.Reg)
rt := int(p.To.Reg)
r := int(p.Reg)
if (o1&(0x1F<<24)) == (0x1E<<24) && (o1&(1<<11)) == 0 { /* monadic */
r = rf
rf = 0
} else if r == 0 {
r = rt
}
o1 |= (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 55: /* floating-point constant */
var rf int
o1 = 0xf<<25 | 1<<21 | 1<<12
rf = c.chipfloat7(p.From.Val.(float64))
if rf < 0 {
c.ctxt.Diag("invalid floating-point immediate\n%v", p)
}
if p.As == AFMOVD {
o1 |= 1 << 22
}
o1 |= (uint32(rf&0xff) << 13) | uint32(p.To.Reg&31)
case 56: /* floating point compare */
o1 = c.oprrr(p, p.As)
var rf int
if p.From.Type == obj.TYPE_FCONST {
o1 |= 8 /* zero */
rf = 0
} else {
rf = int(p.From.Reg)
}
rt := int(p.Reg)
o1 |= uint32(rf&31)<<16 | uint32(rt&31)<<5
case 57: /* floating point conditional compare */
o1 = c.oprrr(p, p.As)
cond := int(p.From.Reg)
if cond < COND_EQ || cond > COND_NV {
c.ctxt.Diag("invalid condition\n%v", p)
} else {
cond -= COND_EQ
}
nzcv := int(p.To.Offset)
if nzcv&^0xF != 0 {
c.ctxt.Diag("implausible condition\n%v", p)
}
rf := int(p.Reg)
if p.GetFrom3() == nil || p.GetFrom3().Reg < REG_F0 || p.GetFrom3().Reg > REG_F31 {
c.ctxt.Diag("illegal FCCMP\n%v", p)
break
}
rt := int(p.GetFrom3().Reg)
o1 |= uint32(rf&31)<<16 | uint32(cond&15)<<12 | uint32(rt&31)<<5 | uint32(nzcv)
case 58: /* ldar/ldarb/ldarh/ldaxp/ldxp/ldaxr/ldxr */
o1 = c.opload(p, p.As)
o1 |= 0x1F << 16
o1 |= uint32(p.From.Reg&31) << 5
if p.As == ALDXP || p.As == ALDXPW || p.As == ALDAXP || p.As == ALDAXPW {
if int(p.To.Reg) == int(p.To.Offset) {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
o1 |= uint32(p.To.Offset&31) << 10
} else {
o1 |= 0x1F << 10
}
o1 |= uint32(p.To.Reg & 31)
case 59: /* stxr/stlxr/stxp/stlxp */
s := p.RegTo2
n := p.To.Reg
t := p.From.Reg
if isSTLXRop(p.As) {
if s == t || (s == n && n != REGSP) {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
} else if isSTXPop(p.As) {
t2 := int16(p.From.Offset)
if (s == t || s == t2) || (s == n && n != REGSP) {
c.ctxt.Diag("constrained unpredictable behavior: %v", p)
}
}
if s == REG_RSP {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
o1 = c.opstore(p, p.As)
if p.RegTo2 != obj.REG_NONE {
o1 |= uint32(p.RegTo2&31) << 16
} else {
o1 |= 0x1F << 16
}
if isSTXPop(p.As) {
o1 |= uint32(p.From.Offset&31) << 10
}
o1 |= uint32(p.To.Reg&31)<<5 | uint32(p.From.Reg&31)
case 60: /* adrp label,r */
d := c.brdist(p, 12, 21, 0)
o1 = ADR(1, uint32(d), uint32(p.To.Reg))
case 61: /* adr label, r */
d := c.brdist(p, 0, 21, 0)
o1 = ADR(0, uint32(d), uint32(p.To.Reg))
case 62: /* op $movcon, [R], R -> mov $movcon, REGTMP + op REGTMP, [R], R */
if p.Reg == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
}
if p.To.Reg == REG_RSP && isADDSop(p.As) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
lsl0 := LSL0_64
if isADDWop(p.As) || isANDWop(p.As) {
o1 = c.omovconst(AMOVW, p, &p.From, REGTMP)
lsl0 = LSL0_32
} else {
o1 = c.omovconst(AMOVD, p, &p.From, REGTMP)
}
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.Reg == REGSP || r == REGSP {
o2 = c.opxrrr(p, p.As, false)
o2 |= REGTMP & 31 << 16
o2 |= uint32(lsl0)
} else {
o2 = c.oprrr(p, p.As)
o2 |= REGTMP & 31 << 16 /* shift is 0 */
}
o2 |= uint32(r&31) << 5
o2 |= uint32(rt & 31)
/* reloc ops */
case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */
if p.From.Reg == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
}
o1 = ADR(1, 0, REGTMP)
o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
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_ADDRARM64
o3 = c.olsr12u(p, int32(c.opstr(p, p.As)), 0, REGTMP, int(p.From.Reg))
case 65: /* movT addr,R -> adrp + add + movT (REGTMP), R */
o1 = ADR(1, 0, REGTMP)
o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Add = p.From.Offset
rel.Type = objabi.R_ADDRARM64
o3 = c.olsr12u(p, int32(c.opldr(p, p.As)), 0, REGTMP, int(p.To.Reg))
case 66: /* ldp O(R)!, (r1, r2); ldp (R)O!, (r1, r2) */
v := int32(c.regoff(&p.From))
r := int(p.From.Reg)
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid ldp source: %v\n", p)
}
o1 |= c.opldpstp(p, o, v, uint32(r), uint32(p.To.Reg), uint32(p.To.Offset), 1)
case 67: /* stp (r1, r2), O(R)!; stp (r1, r2), (R)O! */
r := int(p.To.Reg)
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid stp destination: %v\n", p)
}
v := int32(c.regoff(&p.To))
o1 = c.opldpstp(p, o, v, uint32(r), uint32(p.From.Reg), uint32(p.From.Offset), 0)
case 68: /* movT $vconaddr(SB), reg -> adrp + add + reloc */
// NOTE: this case does not use REGTMP. If it ever does,
// remove the NOTUSETMP flag in optab.
if p.As == AMOVW {
c.ctxt.Diag("invalid load of 32-bit address: %v", p)
}
o1 = ADR(1, 0, uint32(p.To.Reg))
o2 = c.opirr(p, AADD) | uint32(p.To.Reg&31)<<5 | uint32(p.To.Reg&31)
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Add = p.From.Offset
rel.Type = objabi.R_ADDRARM64
case 69: /* LE model movd $tlsvar, reg -> movz reg, 0 + reloc */
o1 = c.opirr(p, AMOVZ)
o1 |= uint32(p.To.Reg & 31)
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 4
rel.Sym = p.From.Sym
rel.Type = objabi.R_ARM64_TLS_LE
if p.From.Offset != 0 {
c.ctxt.Diag("invalid offset on MOVW $tlsvar")
}
case 70: /* IE model movd $tlsvar, reg -> adrp REGTMP, 0; ldr reg, [REGTMP, #0] + relocs */
o1 = ADR(1, 0, REGTMP)
o2 = c.olsr12u(p, int32(c.opldr(p, AMOVD)), 0, REGTMP, int(p.To.Reg))
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Add = 0
rel.Type = objabi.R_ARM64_TLS_IE
if p.From.Offset != 0 {
c.ctxt.Diag("invalid offset on MOVW $tlsvar")
}
case 71: /* movd sym@GOT, reg -> adrp REGTMP, #0; ldr reg, [REGTMP, #0] + relocs */
o1 = ADR(1, 0, REGTMP)
o2 = c.olsr12u(p, int32(c.opldr(p, AMOVD)), 0, REGTMP, int(p.To.Reg))
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Add = 0
rel.Type = objabi.R_ARM64_GOTPCREL
case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2/vrax1 Vm.<T>, Vn.<T>, Vd.<T> */
af := int((p.From.Reg >> 5) & 15)
af3 := int((p.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
if af != af3 || af != at {
c.ctxt.Diag("operand mismatch: %v", p)
break
}
o1 = c.oprrr(p, p.As)
rf := int((p.From.Reg) & 31)
rt := int((p.To.Reg) & 31)
r := int((p.Reg) & 31)
Q := 0
size := 0
switch af {
case ARNG_16B:
Q = 1
size = 0
case ARNG_2D:
Q = 1
size = 3
case ARNG_2S:
Q = 0
size = 2
case ARNG_4H:
Q = 0
size = 1
case ARNG_4S:
Q = 1
size = 2
case ARNG_8B:
Q = 0
size = 0
case ARNG_8H:
Q = 1
size = 1
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
switch p.As {
case AVORR, AVAND, AVEOR, AVBIT, AVBSL, AVBIF:
if af != ARNG_16B && af != ARNG_8B {
c.ctxt.Diag("invalid arrangement: %v", p)
}
case AVFMLA, AVFMLS:
if af != ARNG_2D && af != ARNG_2S && af != ARNG_4S {
c.ctxt.Diag("invalid arrangement: %v", p)
}
case AVUMAX, AVUMIN:
if af == ARNG_2D {
c.ctxt.Diag("invalid arrangement: %v", p)
}
}
switch p.As {
case AVAND, AVEOR:
size = 0
case AVBSL:
size = 1
case AVORR, AVBIT, AVBIF:
size = 2
case AVFMLA, AVFMLS:
if af == ARNG_2D {
size = 1
} else {
size = 0
}
case AVRAX1:
if af != ARNG_2D {
c.ctxt.Diag("invalid arrangement: %v", p)
}
size = 0
Q = 0
}
o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 73: /* vmov V.<T>[index], R */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
imm5 := 0
o1 = 7<<25 | 0xf<<10
index := int(p.From.Index)
switch (p.From.Reg >> 5) & 15 {
case ARNG_B:
c.checkindex(p, index, 15)
imm5 |= 1
imm5 |= index << 1
case ARNG_H:
c.checkindex(p, index, 7)
imm5 |= 2
imm5 |= index << 2
case ARNG_S:
c.checkindex(p, index, 3)
imm5 |= 4
imm5 |= index << 3
case ARNG_D:
c.checkindex(p, index, 1)
imm5 |= 8
imm5 |= index << 4
o1 |= 1 << 30
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
o1 |= (uint32(imm5&0x1f) << 16) | (uint32(rf&31) << 5) | uint32(rt&31)
case 74:
// add $O, R, Rtmp or sub $O, R, Rtmp
// ldp (Rtmp), (R1, R2)
r := int(p.From.Reg)
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid ldp source: %v", p)
}
v := int32(c.regoff(&p.From))
if v > 0 {
if v > 4095 {
c.ctxt.Diag("offset out of range: %v", p)
}
o1 = c.oaddi(p, int32(c.opirr(p, AADD)), v, r, REGTMP)
}
if v < 0 {
if v < -4095 {
c.ctxt.Diag("offset out of range: %v", p)
}
o1 = c.oaddi(p, int32(c.opirr(p, ASUB)), -v, r, REGTMP)
}
o2 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.To.Reg), uint32(p.To.Offset), 1)
case 75:
// mov $L, Rtmp (from constant pool)
// add Rtmp, R, Rtmp
// ldp (Rtmp), (R1, R2)
r := int(p.From.Reg)
if r == REGTMP {
c.ctxt.Diag("REGTMP used in large offset load: %v", p)
}
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid ldp source: %v", p)
}
o1 = c.omovlit(AMOVD, p, &p.From, REGTMP)
o2 = c.opxrrr(p, AADD, false)
o2 |= (REGTMP & 31) << 16
o2 |= uint32(r&31) << 5
o2 |= uint32(REGTMP & 31)
o3 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.To.Reg), uint32(p.To.Offset), 1)
case 76:
// add $O, R, Rtmp or sub $O, R, Rtmp
// stp (R1, R2), (Rtmp)
if p.From.Reg == REGTMP || p.From.Offset == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v", p)
}
r := int(p.To.Reg)
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid stp destination: %v", p)
}
v := int32(c.regoff(&p.To))
if v > 0 {
if v > 4095 {
c.ctxt.Diag("offset out of range: %v", p)
}
o1 = c.oaddi(p, int32(c.opirr(p, AADD)), v, r, REGTMP)
}
if v < 0 {
if v < -4095 {
c.ctxt.Diag("offset out of range: %v", p)
}
o1 = c.oaddi(p, int32(c.opirr(p, ASUB)), -v, r, REGTMP)
}
o2 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.From.Reg), uint32(p.From.Offset), 0)
case 77:
// mov $L, Rtmp (from constant pool)
// add Rtmp, R, Rtmp
// stp (R1, R2), (Rtmp)
r := int(p.To.Reg)
if r == REGTMP || p.From.Reg == REGTMP || p.From.Offset == REGTMP {
c.ctxt.Diag("REGTMP used in large offset store: %v", p)
}
if r == obj.REG_NONE {
r = int(o.param)
}
if r == obj.REG_NONE {
c.ctxt.Diag("invalid stp destination: %v", p)
}
o1 = c.omovlit(AMOVD, p, &p.To, REGTMP)
o2 = c.opxrrr(p, AADD, false)
o2 |= REGTMP & 31 << 16
o2 |= uint32(r&31) << 5
o2 |= uint32(REGTMP & 31)
o3 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.From.Reg), uint32(p.From.Offset), 0)
case 78: /* vmov R, V.<T>[index] */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
imm5 := 0
o1 = 1<<30 | 7<<25 | 7<<10
index := int(p.To.Index)
switch (p.To.Reg >> 5) & 15 {
case ARNG_B:
c.checkindex(p, index, 15)
imm5 |= 1
imm5 |= index << 1
case ARNG_H:
c.checkindex(p, index, 7)
imm5 |= 2
imm5 |= index << 2
case ARNG_S:
c.checkindex(p, index, 3)
imm5 |= 4
imm5 |= index << 3
case ARNG_D:
c.checkindex(p, index, 1)
imm5 |= 8
imm5 |= index << 4
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
o1 |= (uint32(imm5&0x1f) << 16) | (uint32(rf&31) << 5) | uint32(rt&31)
case 79: /* vdup Vn.<T>[index], Vd.<T> */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
o1 = 7<<25 | 1<<10
var imm5, Q int
index := int(p.From.Index)
switch (p.To.Reg >> 5) & 15 {
case ARNG_16B:
c.checkindex(p, index, 15)
Q = 1
imm5 = 1
imm5 |= index << 1
case ARNG_2D:
c.checkindex(p, index, 1)
Q = 1
imm5 = 8
imm5 |= index << 4
case ARNG_2S:
c.checkindex(p, index, 3)
Q = 0
imm5 = 4
imm5 |= index << 3
case ARNG_4H:
c.checkindex(p, index, 7)
Q = 0
imm5 = 2
imm5 |= index << 2
case ARNG_4S:
c.checkindex(p, index, 3)
Q = 1
imm5 = 4
imm5 |= index << 3
case ARNG_8B:
c.checkindex(p, index, 15)
Q = 0
imm5 = 1
imm5 |= index << 1
case ARNG_8H:
c.checkindex(p, index, 7)
Q = 1
imm5 = 2
imm5 |= index << 2
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
o1 |= (uint32(Q&1) << 30) | (uint32(imm5&0x1f) << 16)
o1 |= (uint32(rf&31) << 5) | uint32(rt&31)
case 80: /* vmov/vdup V.<T>[index], Vn */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
imm5 := 0
index := int(p.From.Index)
switch p.As {
case AVMOV, AVDUP:
o1 = 1<<30 | 15<<25 | 1<<10
switch (p.From.Reg >> 5) & 15 {
case ARNG_B:
c.checkindex(p, index, 15)
imm5 |= 1
imm5 |= index << 1
case ARNG_H:
c.checkindex(p, index, 7)
imm5 |= 2
imm5 |= index << 2
case ARNG_S:
c.checkindex(p, index, 3)
imm5 |= 4
imm5 |= index << 3
case ARNG_D:
c.checkindex(p, index, 1)
imm5 |= 8
imm5 |= index << 4
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
default:
c.ctxt.Diag("unsupported op %v", p.As)
}
o1 |= (uint32(imm5&0x1f) << 16) | (uint32(rf&31) << 5) | uint32(rt&31)
case 81: /* vld[1-4]|vld[1-4]r (Rn), [Vt1.<T>, Vt2.<T>, ...] */
c.checkoffset(p, p.As)
r := int(p.From.Reg)
o1 = c.oprrr(p, p.As)
if o.scond == C_XPOST {
o1 |= 1 << 23
if p.From.Index == 0 {
// immediate offset variant
o1 |= 0x1f << 16
} else {
// register offset variant
if isRegShiftOrExt(&p.From) {
c.ctxt.Diag("invalid extended register op: %v\n", p)
}
o1 |= uint32(p.From.Index&0x1f) << 16
}
}
o1 |= uint32(p.To.Offset)
// cmd/asm/internal/arch/arm64.go:ARM64RegisterListOffset
// add opcode(bit 12-15) for vld1, mask it off if it's not vld1
o1 = c.maskOpvldvst(p, o1)
o1 |= uint32(r&31) << 5
case 82: /* vmov/vdup Rn, Vd.<T> */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
o1 = 7<<25 | 3<<10
var imm5, Q uint32
switch (p.To.Reg >> 5) & 15 {
case ARNG_16B:
Q = 1
imm5 = 1
case ARNG_2D:
Q = 1
imm5 = 8
case ARNG_2S:
Q = 0
imm5 = 4
case ARNG_4H:
Q = 0
imm5 = 2
case ARNG_4S:
Q = 1
imm5 = 4
case ARNG_8B:
Q = 0
imm5 = 1
case ARNG_8H:
Q = 1
imm5 = 2
default:
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
o1 |= (Q & 1 << 30) | (imm5 & 0x1f << 16)
o1 |= (uint32(rf&31) << 5) | uint32(rt&31)
case 83: /* vmov Vn.<T>, Vd.<T> */
af := int((p.From.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
if af != at {
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
o1 = c.oprrr(p, p.As)
rf := int((p.From.Reg) & 31)
rt := int((p.To.Reg) & 31)
var Q, size uint32
switch af {
case ARNG_8B:
Q = 0
size = 0
case ARNG_16B:
Q = 1
size = 0
case ARNG_4H:
Q = 0
size = 1
case ARNG_8H:
Q = 1
size = 1
case ARNG_2S:
Q = 0
size = 2
case ARNG_4S:
Q = 1
size = 2
default:
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
if (p.As == AVMOV || p.As == AVRBIT || p.As == AVCNT) && (af != ARNG_16B && af != ARNG_8B) {
c.ctxt.Diag("invalid arrangement: %v", p)
}
if p.As == AVREV32 && (af == ARNG_2S || af == ARNG_4S) {
c.ctxt.Diag("invalid arrangement: %v", p)
}
if p.As == AVREV16 && af != ARNG_8B && af != ARNG_16B {
c.ctxt.Diag("invalid arrangement: %v", p)
}
if p.As == AVMOV {
o1 |= uint32(rf&31) << 16
}
if p.As == AVRBIT {
size = 1
}
o1 |= (Q&1)<<30 | (size&3)<<22 | uint32(rf&31)<<5 | uint32(rt&31)
case 84: /* vst[1-4] [Vt1.<T>, Vt2.<T>, ...], (Rn) */
c.checkoffset(p, p.As)
r := int(p.To.Reg)
o1 = 3 << 26
if o.scond == C_XPOST {
o1 |= 1 << 23
if p.To.Index == 0 {
// immediate offset variant
o1 |= 0x1f << 16
} else {
// register offset variant
if isRegShiftOrExt(&p.To) {
c.ctxt.Diag("invalid extended register: %v\n", p)
}
o1 |= uint32(p.To.Index&31) << 16
}
}
o1 |= uint32(p.From.Offset)
// cmd/asm/internal/arch/arm64.go:ARM64RegisterListOffset
// add opcode(bit 12-15) for vst1, mask it off if it's not vst1
o1 = c.maskOpvldvst(p, o1)
o1 |= uint32(r&31) << 5
case 85: /* vaddv/vuaddlv Vn.<T>, Vd*/
af := int((p.From.Reg >> 5) & 15)
o1 = c.oprrr(p, p.As)
rf := int((p.From.Reg) & 31)
rt := int((p.To.Reg) & 31)
Q := 0
size := 0
switch af {
case ARNG_8B:
Q = 0
size = 0
case ARNG_16B:
Q = 1
size = 0
case ARNG_4H:
Q = 0
size = 1
case ARNG_8H:
Q = 1
size = 1
case ARNG_4S:
Q = 1
size = 2
default:
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 5) | uint32(rt&31)
case 86: /* vmovi $imm8, Vd.<T>*/
at := int((p.To.Reg >> 5) & 15)
r := int(p.From.Offset)
if r > 255 || r < 0 {
c.ctxt.Diag("immediate constant out of range: %v\n", p)
}
rt := int((p.To.Reg) & 31)
Q := 0
switch at {
case ARNG_8B:
Q = 0
case ARNG_16B:
Q = 1
default:
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
o1 = 0xf<<24 | 0xe<<12 | 1<<10
o1 |= (uint32(Q&1) << 30) | (uint32((r>>5)&7) << 16) | (uint32(r&0x1f) << 5) | uint32(rt&31)
case 87: /* stp (r,r), addr(SB) -> adrp + add + stp */
if p.From.Reg == REGTMP || p.From.Offset == REGTMP {
c.ctxt.Diag("cannot use REGTMP as source: %v", p)
}
o1 = ADR(1, 0, REGTMP)
o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
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_ADDRARM64
o3 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.From.Reg), uint32(p.From.Offset), 0)
case 88: /* ldp addr(SB), (r,r) -> adrp + add + ldp */
o1 = ADR(1, 0, REGTMP)
o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Add = p.From.Offset
rel.Type = objabi.R_ADDRARM64
o3 |= c.opldpstp(p, o, 0, uint32(REGTMP), uint32(p.To.Reg), uint32(p.To.Offset), 1)
case 89: /* vadd/vsub Vm, Vn, Vd */
switch p.As {
case AVADD:
o1 = 5<<28 | 7<<25 | 7<<21 | 1<<15 | 1<<10
case AVSUB:
o1 = 7<<28 | 7<<25 | 7<<21 | 1<<15 | 1<<10
default:
c.ctxt.Diag("bad opcode: %v\n", p)
break
}
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) | uint32(rt&31)
// This is supposed to be something that stops execution.
// It's not supposed to be reached, ever, but if it is, we'd
// like to be able to tell how we got there. Assemble as
// 0xbea71700 which is guaranteed to raise undefined instruction
// exception.
case 90:
o1 = 0xbea71700
case 91: /* prfm imm(Rn), <prfop | $imm5> */
imm := uint32(p.From.Offset)
r := p.From.Reg
v := uint32(0xff)
if p.To.Type == obj.TYPE_CONST {
v = uint32(p.To.Offset)
if v > 31 {
c.ctxt.Diag("illegal prefetch operation\n%v", p)
}
} else {
for i := 0; i < len(prfopfield); i++ {
if prfopfield[i].reg == p.To.Reg {
v = prfopfield[i].enc
break
}
}
if v == 0xff {
c.ctxt.Diag("illegal prefetch operation:\n%v", p)
}
}
o1 = c.opirr(p, p.As)
o1 |= (uint32(r&31) << 5) | (uint32((imm>>3)&0xfff) << 10) | (uint32(v & 31))
case 92: /* vmov Vn.<T>[index], Vd.<T>[index] */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
imm4 := 0
imm5 := 0
o1 = 3<<29 | 7<<25 | 1<<10
index1 := int(p.To.Index)
index2 := int(p.From.Index)
if ((p.To.Reg >> 5) & 15) != ((p.From.Reg >> 5) & 15) {
c.ctxt.Diag("operand mismatch: %v", p)
}
switch (p.To.Reg >> 5) & 15 {
case ARNG_B:
c.checkindex(p, index1, 15)
c.checkindex(p, index2, 15)
imm5 |= 1
imm5 |= index1 << 1
imm4 |= index2
case ARNG_H:
c.checkindex(p, index1, 7)
c.checkindex(p, index2, 7)
imm5 |= 2
imm5 |= index1 << 2
imm4 |= index2 << 1
case ARNG_S:
c.checkindex(p, index1, 3)
c.checkindex(p, index2, 3)
imm5 |= 4
imm5 |= index1 << 3
imm4 |= index2 << 2
case ARNG_D:
c.checkindex(p, index1, 1)
c.checkindex(p, index2, 1)
imm5 |= 8
imm5 |= index1 << 4
imm4 |= index2 << 3
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
o1 |= (uint32(imm5&0x1f) << 16) | (uint32(imm4&0xf) << 11) | (uint32(rf&31) << 5) | uint32(rt&31)
case 93: /* vpmull{2} Vm.<Tb>, Vn.<Tb>, Vd.<Ta> */
af := uint8((p.From.Reg >> 5) & 15)
at := uint8((p.To.Reg >> 5) & 15)
a := uint8((p.Reg >> 5) & 15)
if af != a {
c.ctxt.Diag("invalid arrangement: %v", p)
}
var Q, size uint32
if p.As == AVPMULL2 {
Q = 1
}
switch pack(Q, at, af) {
case pack(0, ARNG_8H, ARNG_8B), pack(1, ARNG_8H, ARNG_16B):
size = 0
case pack(0, ARNG_1Q, ARNG_1D), pack(1, ARNG_1Q, ARNG_2D):
size = 3
default:
c.ctxt.Diag("operand mismatch: %v\n", p)
}
o1 = c.oprrr(p, p.As)
rf := int((p.From.Reg) & 31)
rt := int((p.To.Reg) & 31)
r := int((p.Reg) & 31)
o1 |= ((Q & 1) << 30) | ((size & 3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 94: /* vext $imm4, Vm.<T>, Vn.<T>, Vd.<T> */
af := int(((p.GetFrom3().Reg) >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
a := int((p.Reg >> 5) & 15)
index := int(p.From.Offset)
if af != a || af != at {
c.ctxt.Diag("invalid arrangement: %v", p)
break
}
var Q uint32
var b int
if af == ARNG_8B {
Q = 0
b = 7
} else if af == ARNG_16B {
Q = 1
b = 15
} else {
c.ctxt.Diag("invalid arrangement, should be B8 or B16: %v", p)
break
}
if index < 0 || index > b {
c.ctxt.Diag("illegal offset: %v", p)
}
o1 = c.opirr(p, p.As)
rf := int((p.GetFrom3().Reg) & 31)
rt := int((p.To.Reg) & 31)
r := int((p.Reg) & 31)
o1 |= ((Q & 1) << 30) | (uint32(r&31) << 16) | (uint32(index&15) << 11) | (uint32(rf&31) << 5) | uint32(rt&31)
case 95: /* vushr/vshl/vsri/vsli/vusra $shift, Vn.<T>, Vd.<T> */
at := int((p.To.Reg >> 5) & 15)
af := int((p.Reg >> 5) & 15)
shift := int(p.From.Offset)
if af != at {
c.ctxt.Diag("invalid arrangement on op Vn.<T>, Vd.<T>: %v", p)
}
var Q uint32
var imax, esize int
switch af {
case ARNG_8B, ARNG_4H, ARNG_2S:
Q = 0
case ARNG_16B, ARNG_8H, ARNG_4S, ARNG_2D:
Q = 1
default:
c.ctxt.Diag("invalid arrangement on op Vn.<T>, Vd.<T>: %v", p)
}
switch af {
case ARNG_8B, ARNG_16B:
imax = 15
esize = 8
case ARNG_4H, ARNG_8H:
imax = 31
esize = 16
case ARNG_2S, ARNG_4S:
imax = 63
esize = 32
case ARNG_2D:
imax = 127
esize = 64
}
imm := 0
switch p.As {
case AVUSHR, AVSRI, AVUSRA:
imm = esize*2 - shift
if imm < esize || imm > imax {
c.ctxt.Diag("shift out of range: %v", p)
}
case AVSHL, AVSLI:
imm = esize + shift
if imm > imax {
c.ctxt.Diag("shift out of range: %v", p)
}
default:
c.ctxt.Diag("invalid instruction %v\n", p)
}
o1 = c.opirr(p, p.As)
rt := int((p.To.Reg) & 31)
rf := int((p.Reg) & 31)
o1 |= ((Q & 1) << 30) | (uint32(imm&0x7f) << 16) | (uint32(rf&31) << 5) | uint32(rt&31)
case 96: /* vst1 Vt1.<T>[index], offset(Rn) */
af := int((p.From.Reg >> 5) & 15)
rt := int((p.From.Reg) & 31)
rf := int((p.To.Reg) & 31)
r := int(p.To.Index & 31)
index := int(p.From.Index)
offset := int32(c.regoff(&p.To))
if o.scond == C_XPOST {
if (p.To.Index != 0) && (offset != 0) {
c.ctxt.Diag("invalid offset: %v", p)
}
if p.To.Index == 0 && offset == 0 {
c.ctxt.Diag("invalid offset: %v", p)
}
}
if offset != 0 {
r = 31
}
var Q, S, size int
var opcode uint32
switch af {
case ARNG_B:
c.checkindex(p, index, 15)
if o.scond == C_XPOST && offset != 0 && offset != 1 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 3
S = (index >> 2) & 1
size = index & 3
opcode = 0
case ARNG_H:
c.checkindex(p, index, 7)
if o.scond == C_XPOST && offset != 0 && offset != 2 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 2
S = (index >> 1) & 1
size = (index & 1) << 1
opcode = 2
case ARNG_S:
c.checkindex(p, index, 3)
if o.scond == C_XPOST && offset != 0 && offset != 4 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 1
S = index & 1
size = 0
opcode = 4
case ARNG_D:
c.checkindex(p, index, 1)
if o.scond == C_XPOST && offset != 0 && offset != 8 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index
S = 0
size = 1
opcode = 4
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
if o.scond == C_XPOST {
o1 |= 27 << 23
} else {
o1 |= 26 << 23
}
o1 |= (uint32(Q&1) << 30) | (uint32(r&31) << 16) | ((opcode & 7) << 13) | (uint32(S&1) << 12) | (uint32(size&3) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
case 97: /* vld1 offset(Rn), vt.<T>[index] */
at := int((p.To.Reg >> 5) & 15)
rt := int((p.To.Reg) & 31)
rf := int((p.From.Reg) & 31)
r := int(p.From.Index & 31)
index := int(p.To.Index)
offset := int32(c.regoff(&p.From))
if o.scond == C_XPOST {
if (p.From.Index != 0) && (offset != 0) {
c.ctxt.Diag("invalid offset: %v", p)
}
if p.From.Index == 0 && offset == 0 {
c.ctxt.Diag("invalid offset: %v", p)
}
}
if offset != 0 {
r = 31
}
Q := 0
S := 0
size := 0
var opcode uint32
switch at {
case ARNG_B:
c.checkindex(p, index, 15)
if o.scond == C_XPOST && offset != 0 && offset != 1 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 3
S = (index >> 2) & 1
size = index & 3
opcode = 0
case ARNG_H:
c.checkindex(p, index, 7)
if o.scond == C_XPOST && offset != 0 && offset != 2 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 2
S = (index >> 1) & 1
size = (index & 1) << 1
opcode = 2
case ARNG_S:
c.checkindex(p, index, 3)
if o.scond == C_XPOST && offset != 0 && offset != 4 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index >> 1
S = index & 1
size = 0
opcode = 4
case ARNG_D:
c.checkindex(p, index, 1)
if o.scond == C_XPOST && offset != 0 && offset != 8 {
c.ctxt.Diag("invalid offset: %v", p)
}
Q = index
S = 0
size = 1
opcode = 4
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
if o.scond == C_XPOST {
o1 |= 110 << 21
} else {
o1 |= 106 << 21
}
o1 |= (uint32(Q&1) << 30) | (uint32(r&31) << 16) | ((opcode & 7) << 13) | (uint32(S&1) << 12) | (uint32(size&3) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
case 98: /* MOVD (Rn)(Rm.SXTW[<<amount]),Rd */
if isRegShiftOrExt(&p.From) {
// extended or shifted offset register.
c.checkShiftAmount(p, &p.From)
o1 = c.opldrr(p, p.As, true)
o1 |= c.encRegShiftOrExt(&p.From, p.From.Index) /* includes reg, op, etc */
} else {
// (Rn)(Rm), no extension or shift.
o1 = c.opldrr(p, p.As, false)
o1 |= uint32(p.From.Index&31) << 16
}
o1 |= uint32(p.From.Reg&31) << 5
rt := int(p.To.Reg)
o1 |= uint32(rt & 31)
case 99: /* MOVD Rt, (Rn)(Rm.SXTW[<<amount]) */
if isRegShiftOrExt(&p.To) {
// extended or shifted offset register.
c.checkShiftAmount(p, &p.To)
o1 = c.opstrr(p, p.As, true)
o1 |= c.encRegShiftOrExt(&p.To, p.To.Index) /* includes reg, op, etc */
} else {
// (Rn)(Rm), no extension or shift.
o1 = c.opstrr(p, p.As, false)
o1 |= uint32(p.To.Index&31) << 16
}
o1 |= uint32(p.To.Reg&31) << 5
rf := int(p.From.Reg)
o1 |= uint32(rf & 31)
case 100: /* VTBL Vn.<T>, [Vt1.<T>, Vt2.<T>, ...], Vd.<T> */
af := int((p.From.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
if af != at {
c.ctxt.Diag("invalid arrangement: %v\n", p)
}
var q, len uint32
switch af {
case ARNG_8B:
q = 0
case ARNG_16B:
q = 1
default:
c.ctxt.Diag("invalid arrangement: %v", p)
}
rf := int(p.From.Reg)
rt := int(p.To.Reg)
offset := int(p.GetFrom3().Offset)
opcode := (offset >> 12) & 15
switch opcode {
case 0x7:
len = 0 // one register
case 0xa:
len = 1 // two register
case 0x6:
len = 2 // three registers
case 0x2:
len = 3 // four registers
default:
c.ctxt.Diag("invalid register numbers in ARM64 register list: %v", p)
}
o1 = q<<30 | 0xe<<24 | len<<13
o1 |= (uint32(rf&31) << 16) | uint32(offset&31)<<5 | uint32(rt&31)
case 101: // VMOVQ $vcon1, $vcon2, Vd or VMOVD|VMOVS $vcon, Vd -> FMOVQ/FMOVD/FMOVS pool(PC), Vd: load from constant pool.
o1 = c.omovlit(p.As, p, &p.From, int(p.To.Reg))
case 102: /* vushll, vushll2, vuxtl, vuxtl2 */
o1 = c.opirr(p, p.As)
rf := p.Reg
af := uint8((p.Reg >> 5) & 15)
at := uint8((p.To.Reg >> 5) & 15)
shift := int(p.From.Offset)
if p.As == AVUXTL || p.As == AVUXTL2 {
rf = p.From.Reg
af = uint8((p.From.Reg >> 5) & 15)
shift = 0
}
Q := (o1 >> 30) & 1
var immh, width uint8
switch pack(Q, af, at) {
case pack(0, ARNG_8B, ARNG_8H):
immh, width = 1, 8
case pack(1, ARNG_16B, ARNG_8H):
immh, width = 1, 8
case pack(0, ARNG_4H, ARNG_4S):
immh, width = 2, 16
case pack(1, ARNG_8H, ARNG_4S):
immh, width = 2, 16
case pack(0, ARNG_2S, ARNG_2D):
immh, width = 4, 32
case pack(1, ARNG_4S, ARNG_2D):
immh, width = 4, 32
default:
c.ctxt.Diag("operand mismatch: %v\n", p)
}
if !(0 <= shift && shift <= int(width-1)) {
c.ctxt.Diag("shift amount out of range: %v\n", p)
}
o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31)
case 103: /* VEOR3/VBCAX Va.B16, Vm.B16, Vn.B16, Vd.B16 */
ta := (p.From.Reg >> 5) & 15
tm := (p.Reg >> 5) & 15
td := (p.To.Reg >> 5) & 15
tn := ((p.GetFrom3().Reg) >> 5) & 15
if ta != tm || ta != tn || ta != td || ta != ARNG_16B {
c.ctxt.Diag("invalid arrangement: %v", p)
break
}
o1 = c.oprrr(p, p.As)
ra := int(p.From.Reg)
rm := int(p.Reg)
rn := int(p.GetFrom3().Reg)
rd := int(p.To.Reg)
o1 |= uint32(rm&31)<<16 | uint32(ra&31)<<10 | uint32(rn&31)<<5 | uint32(rd)&31
case 104: /* vxar $imm4, Vm.<T>, Vn.<T>, Vd.<T> */
af := ((p.GetFrom3().Reg) >> 5) & 15
at := (p.To.Reg >> 5) & 15
a := (p.Reg >> 5) & 15
index := int(p.From.Offset)
if af != a || af != at {
c.ctxt.Diag("invalid arrangement: %v", p)
break
}
if af != ARNG_2D {
c.ctxt.Diag("invalid arrangement, should be D2: %v", p)
break
}
if index < 0 || index > 63 {
c.ctxt.Diag("illegal offset: %v", p)
}
o1 = c.opirr(p, p.As)
rf := (p.GetFrom3().Reg) & 31
rt := (p.To.Reg) & 31
r := (p.Reg) & 31
o1 |= (uint32(r&31) << 16) | (uint32(index&63) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
case 105: /* vuaddw{2} Vm.<Tb>, Vn.<Ta>, Vd.<Ta> */
af := uint8((p.From.Reg >> 5) & 15)
at := uint8((p.To.Reg >> 5) & 15)
a := uint8((p.Reg >> 5) & 15)
if at != a {
c.ctxt.Diag("invalid arrangement: %v", p)
break
}
var Q, size uint32
if p.As == AVUADDW2 {
Q = 1
}
switch pack(Q, at, af) {
case pack(0, ARNG_8H, ARNG_8B), pack(1, ARNG_8H, ARNG_16B):
size = 0
case pack(0, ARNG_4S, ARNG_4H), pack(1, ARNG_4S, ARNG_8H):
size = 1
case pack(0, ARNG_2D, ARNG_2S), pack(1, ARNG_2D, ARNG_4S):
size = 2
default:
c.ctxt.Diag("operand mismatch: %v\n", p)
}
o1 = c.oprrr(p, p.As)
rf := int((p.From.Reg) & 31)
rt := int((p.To.Reg) & 31)
r := int((p.Reg) & 31)
o1 |= ((Q & 1) << 30) | ((size & 3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
case 106: // CASPx (Rs, Rs+1), (Rb), (Rt, Rt+1)
rs := p.From.Reg
rt := p.GetTo2().Reg
rb := p.To.Reg
rs1 := int16(p.From.Offset)
rt1 := int16(p.GetTo2().Offset)
enc, ok := atomicCASP[p.As]
if !ok {
c.ctxt.Diag("invalid CASP-like atomic instructions: %v\n", p)
}
// for CASPx-like instructions, Rs<0> != 1 && Rt<0> != 1
switch {
case rs&1 != 0:
c.ctxt.Diag("source register pair must start from even register: %v\n", p)
break
case rt&1 != 0:
c.ctxt.Diag("destination register pair must start from even register: %v\n", p)
break
case rs != rs1-1:
c.ctxt.Diag("source register pair must be contiguous: %v\n", p)
break
case rt != rt1-1:
c.ctxt.Diag("destination register pair must be contiguous: %v\n", p)
break
}
// rt can't be sp.
if rt == REG_RSP {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
o1 |= enc | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31)
}
out[0] = o1
out[1] = o2
out[2] = o3
out[3] = o4
out[4] = o5
}