7141 lines
		
	
	
		
			173 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			7141 lines
		
	
	
		
			173 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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 (
 | |
| 	"github.com/twitchyliquid64/golang-asm/obj"
 | |
| 	"github.com/twitchyliquid64/golang-asm/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_RSP, 4, 4, REGFROM, 0, 0},
 | |
| 	{AMOVD, C_AACON2, C_NONE, C_NONE, C_RSP, 4, 8, REGFROM, 0, 0},
 | |
| 
 | |
| 	/* load long effective stack address (load int32 offset and add) */
 | |
| 	{AMOVD, C_LACON, C_NONE, C_NONE, C_RSP, 34, 8, REGSP, LFROM, 0},
 | |
| 
 | |
| 	// Move a large constant to a Vn.
 | |
| 	{AFMOVQ, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
 | |
| 	{AFMOVD, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
 | |
| 	{AFMOVS, C_LCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 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},
 | |
| 	{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},
 | |
| 
 | |
| 	// get a PC-relative address
 | |
| 	{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},
 | |
| 
 | |
| 	{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},
 | |
| 	{AVUSHLL, C_VCON, C_ARNG, C_NONE, C_ARNG, 102, 4, 0, 0, 0},
 | |
| 	{AVUXTL, C_ARNG, C_NONE, C_NONE, C_ARNG, 102, 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},
 | |
| 
 | |
| 	/* 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)®_EXT != 0 || (a.Index-obj.RBaseARM64)®_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
 | |
| }
 | |
| 
 | |
| func isaddcon2(v int64) bool {
 | |
| 	return 0 <= v && v <= 0xFFFFFF
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 		}
 | |
| 		cf := c.instoffset
 | |
| 		if isaddcon(cf) || isaddcon(-cf) {
 | |
| 			return C_AACON
 | |
| 		}
 | |
| 		if isaddcon2(cf) {
 | |
| 			return C_AACON2
 | |
| 		}
 | |
| 
 | |
| 		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 || b == C_AACON2 {
 | |
| 			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, AFMOVQ:
 | |
| 			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)
 | |
| 			oprangeset(AVBSL, t)
 | |
| 			oprangeset(AVBIT, t)
 | |
| 			oprangeset(AVCMTST, t)
 | |
| 			oprangeset(AVUZP1, t)
 | |
| 			oprangeset(AVUZP2, t)
 | |
| 			oprangeset(AVBIF, 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 AVUXTL:
 | |
| 			oprangeset(AVUXTL2, t)
 | |
| 
 | |
| 		case AVUSHLL:
 | |
| 			oprangeset(AVUSHLL2, 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
 | |
| 	size := (list >> 10) & 3
 | |
| 	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)
 | |
| 	}
 | |
| 
 | |
| 	switch as {
 | |
| 	case AVLD1R, AVLD2R, AVLD3R, AVLD4R:
 | |
| 		if offset != n*(1<<uint(size)) {
 | |
| 			c.ctxt.Diag("invalid post-increment offset: %v", p)
 | |
| 		}
 | |
| 	default:
 | |
| 		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; 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 {
 | |
| 			o1 = c.oaddi(p, op, v&0xfff000, r, REGTMP)
 | |
| 			o2 = c.oaddi(p, op, v&0x000fff, REGTMP, 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
 | |
| 		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)®_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)
 | |
| 
 | |
| 		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/LDANDx/LDEORx/LDORx Rs, (Rb), Rt */
 | |
| 		rs := p.From.Reg
 | |
| 		rt := p.RegTo2
 | |
| 		rb := p.To.Reg
 | |
| 
 | |
| 		fields := atomicInstructions[p.As]
 | |
| 		// rt can't be sp. rt can't be r31 when field A is 0, A bit is the 23rd bit.
 | |
| 		if rt == REG_RSP || (rt == REGZERO && (fields&(1<<23) == 0)) {
 | |
| 			c.ctxt.Diag("illegal destination register: %v\n", p)
 | |
| 		}
 | |
| 		o1 |= fields | 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
 | |
| 		}
 | |
| 		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 isADDWop(p.As) || isANDWop(p.As) {
 | |
| 			o1 = c.omovconst(AMOVW, p, &p.From, REGTMP)
 | |
| 		} 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 |= LSL0_64
 | |
| 		} 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) */
 | |
| 		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.opstr12(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.opldr12(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.opldr12(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.opldr12(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 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)
 | |
| 			}
 | |
| 		}
 | |
| 		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
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		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 == 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)
 | |
| 		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 == 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 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:
 | |
| 			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 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 on VMOV Rn, Vd.<T>: %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 */
 | |
| 		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.opldrpp(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.<T>, Vn.<T>, Vd */
 | |
| 		af := int((p.From.Reg >> 5) & 15)
 | |
| 		at := int((p.To.Reg >> 5) & 15)
 | |
| 		a := int((p.Reg >> 5) & 15)
 | |
| 
 | |
| 		var Q, size uint32
 | |
| 		if p.As == AVPMULL {
 | |
| 			Q = 0
 | |
| 		} else {
 | |
| 			Q = 1
 | |
| 		}
 | |
| 
 | |
| 		var fArng int
 | |
| 		switch at {
 | |
| 		case ARNG_8H:
 | |
| 			if Q == 0 {
 | |
| 				fArng = ARNG_8B
 | |
| 			} else {
 | |
| 				fArng = ARNG_16B
 | |
| 			}
 | |
| 			size = 0
 | |
| 		case ARNG_1Q:
 | |
| 			if Q == 0 {
 | |
| 				fArng = ARNG_1D
 | |
| 			} else {
 | |
| 				fArng = ARNG_2D
 | |
| 			}
 | |
| 			size = 3
 | |
| 		default:
 | |
| 			c.ctxt.Diag("invalid arrangement on Vd.<T>: %v", p)
 | |
| 		}
 | |
| 
 | |
| 		if af != a || af != fArng {
 | |
| 			c.ctxt.Diag("invalid arrangement: %v", 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 $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:
 | |
| 			imm = esize*2 - shift
 | |
| 			if imm < esize || imm > imax {
 | |
| 				c.ctxt.Diag("shift out of range: %v", p)
 | |
| 			}
 | |
| 		case AVSHL:
 | |
| 			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&127) << 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: // FOMVQ/FMOVD $vcon, 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
 | |
| 		}
 | |
| 
 | |
| 		pack := func(q, x, y uint8) uint32 {
 | |
| 			return uint32(q)<<16 | uint32(x)<<8 | uint32(y)
 | |
| 		}
 | |
| 
 | |
| 		var Q uint8 = uint8(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)
 | |
| 	}
 | |
| 	out[0] = o1
 | |
| 	out[1] = o2
 | |
| 	out[2] = o3
 | |
| 	out[3] = o4
 | |
| 	out[4] = o5
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * basic Rm op Rn -> Rd (using shifted register with 0)
 | |
|  * also op Rn -> Rt
 | |
|  * also Rm*Rn op Ra -> Rd
 | |
|  * also Vm op Vn -> Vd
 | |
|  */
 | |
| func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case AADC:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case AADCW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case AADCS:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case AADCSW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case ANGC, ASBC:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case ANGCS, ASBCS:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case ANGCW, ASBCW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case ANGCSW, ASBCSW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0xd0<<21 | 0<<10
 | |
| 
 | |
| 	case AADD:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case AADDW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ACMN, AADDS:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ACMNW, AADDSW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ASUB:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ASUBW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ACMP, ASUBS:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case ACMPW, ASUBSW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 0<<21 | 0<<10
 | |
| 
 | |
| 	case AAND:
 | |
| 		return S64 | 0<<29 | 0xA<<24
 | |
| 
 | |
| 	case AANDW:
 | |
| 		return S32 | 0<<29 | 0xA<<24
 | |
| 
 | |
| 	case AMOVD, AORR:
 | |
| 		return S64 | 1<<29 | 0xA<<24
 | |
| 
 | |
| 		//	case AMOVW:
 | |
| 	case AMOVWU, AORRW:
 | |
| 		return S32 | 1<<29 | 0xA<<24
 | |
| 
 | |
| 	case AEOR:
 | |
| 		return S64 | 2<<29 | 0xA<<24
 | |
| 
 | |
| 	case AEORW:
 | |
| 		return S32 | 2<<29 | 0xA<<24
 | |
| 
 | |
| 	case AANDS, ATST:
 | |
| 		return S64 | 3<<29 | 0xA<<24
 | |
| 
 | |
| 	case AANDSW, ATSTW:
 | |
| 		return S32 | 3<<29 | 0xA<<24
 | |
| 
 | |
| 	case ABIC:
 | |
| 		return S64 | 0<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case ABICW:
 | |
| 		return S32 | 0<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case ABICS:
 | |
| 		return S64 | 3<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case ABICSW:
 | |
| 		return S32 | 3<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case AEON:
 | |
| 		return S64 | 2<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case AEONW:
 | |
| 		return S32 | 2<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case AMVN, AORN:
 | |
| 		return S64 | 1<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case AMVNW, AORNW:
 | |
| 		return S32 | 1<<29 | 0xA<<24 | 1<<21
 | |
| 
 | |
| 	case AASR:
 | |
| 		return S64 | OPDP2(10) /* also ASRV */
 | |
| 
 | |
| 	case AASRW:
 | |
| 		return S32 | OPDP2(10)
 | |
| 
 | |
| 	case ALSL:
 | |
| 		return S64 | OPDP2(8)
 | |
| 
 | |
| 	case ALSLW:
 | |
| 		return S32 | OPDP2(8)
 | |
| 
 | |
| 	case ALSR:
 | |
| 		return S64 | OPDP2(9)
 | |
| 
 | |
| 	case ALSRW:
 | |
| 		return S32 | OPDP2(9)
 | |
| 
 | |
| 	case AROR:
 | |
| 		return S64 | OPDP2(11)
 | |
| 
 | |
| 	case ARORW:
 | |
| 		return S32 | OPDP2(11)
 | |
| 
 | |
| 	case ACCMN:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0xD2<<21 | 0<<11 | 0<<10 | 0<<4 /* cond<<12 | nzcv<<0 */
 | |
| 
 | |
| 	case ACCMNW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0xD2<<21 | 0<<11 | 0<<10 | 0<<4
 | |
| 
 | |
| 	case ACCMP:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0xD2<<21 | 0<<11 | 0<<10 | 0<<4 /* imm5<<16 | cond<<12 | nzcv<<0 */
 | |
| 
 | |
| 	case ACCMPW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0xD2<<21 | 0<<11 | 0<<10 | 0<<4
 | |
| 
 | |
| 	case ACRC32B:
 | |
| 		return S32 | OPDP2(16)
 | |
| 
 | |
| 	case ACRC32H:
 | |
| 		return S32 | OPDP2(17)
 | |
| 
 | |
| 	case ACRC32W:
 | |
| 		return S32 | OPDP2(18)
 | |
| 
 | |
| 	case ACRC32X:
 | |
| 		return S64 | OPDP2(19)
 | |
| 
 | |
| 	case ACRC32CB:
 | |
| 		return S32 | OPDP2(20)
 | |
| 
 | |
| 	case ACRC32CH:
 | |
| 		return S32 | OPDP2(21)
 | |
| 
 | |
| 	case ACRC32CW:
 | |
| 		return S32 | OPDP2(22)
 | |
| 
 | |
| 	case ACRC32CX:
 | |
| 		return S64 | OPDP2(23)
 | |
| 
 | |
| 	case ACSEL:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACSELW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACSET:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case ACSETW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case ACSETM:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACSETMW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACINC, ACSINC:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case ACINCW, ACSINCW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case ACINV, ACSINV:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACINVW, ACSINVW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 0<<10
 | |
| 
 | |
| 	case ACNEG, ACSNEG:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case ACNEGW, ACSNEGW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0xD4<<21 | 0<<11 | 1<<10
 | |
| 
 | |
| 	case AMUL, AMADD:
 | |
| 		return S64 | 0<<29 | 0x1B<<24 | 0<<21 | 0<<15
 | |
| 
 | |
| 	case AMULW, AMADDW:
 | |
| 		return S32 | 0<<29 | 0x1B<<24 | 0<<21 | 0<<15
 | |
| 
 | |
| 	case AMNEG, AMSUB:
 | |
| 		return S64 | 0<<29 | 0x1B<<24 | 0<<21 | 1<<15
 | |
| 
 | |
| 	case AMNEGW, AMSUBW:
 | |
| 		return S32 | 0<<29 | 0x1B<<24 | 0<<21 | 1<<15
 | |
| 
 | |
| 	case AMRS:
 | |
| 		return SYSOP(1, 2, 0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case AMSR:
 | |
| 		return SYSOP(0, 2, 0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case ANEG:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0xB<<24 | 0<<21
 | |
| 
 | |
| 	case ANEGW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0xB<<24 | 0<<21
 | |
| 
 | |
| 	case ANEGS:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0xB<<24 | 0<<21
 | |
| 
 | |
| 	case ANEGSW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0xB<<24 | 0<<21
 | |
| 
 | |
| 	case AREM, ASDIV:
 | |
| 		return S64 | OPDP2(3)
 | |
| 
 | |
| 	case AREMW, ASDIVW:
 | |
| 		return S32 | OPDP2(3)
 | |
| 
 | |
| 	case ASMULL, ASMADDL:
 | |
| 		return OPDP3(1, 0, 1, 0)
 | |
| 
 | |
| 	case ASMNEGL, ASMSUBL:
 | |
| 		return OPDP3(1, 0, 1, 1)
 | |
| 
 | |
| 	case ASMULH:
 | |
| 		return OPDP3(1, 0, 2, 0)
 | |
| 
 | |
| 	case AUMULL, AUMADDL:
 | |
| 		return OPDP3(1, 0, 5, 0)
 | |
| 
 | |
| 	case AUMNEGL, AUMSUBL:
 | |
| 		return OPDP3(1, 0, 5, 1)
 | |
| 
 | |
| 	case AUMULH:
 | |
| 		return OPDP3(1, 0, 6, 0)
 | |
| 
 | |
| 	case AUREM, AUDIV:
 | |
| 		return S64 | OPDP2(2)
 | |
| 
 | |
| 	case AUREMW, AUDIVW:
 | |
| 		return S32 | OPDP2(2)
 | |
| 
 | |
| 	case AAESE:
 | |
| 		return 0x4E<<24 | 2<<20 | 8<<16 | 4<<12 | 2<<10
 | |
| 
 | |
| 	case AAESD:
 | |
| 		return 0x4E<<24 | 2<<20 | 8<<16 | 5<<12 | 2<<10
 | |
| 
 | |
| 	case AAESMC:
 | |
| 		return 0x4E<<24 | 2<<20 | 8<<16 | 6<<12 | 2<<10
 | |
| 
 | |
| 	case AAESIMC:
 | |
| 		return 0x4E<<24 | 2<<20 | 8<<16 | 7<<12 | 2<<10
 | |
| 
 | |
| 	case ASHA1C:
 | |
| 		return 0x5E<<24 | 0<<12
 | |
| 
 | |
| 	case ASHA1P:
 | |
| 		return 0x5E<<24 | 1<<12
 | |
| 
 | |
| 	case ASHA1M:
 | |
| 		return 0x5E<<24 | 2<<12
 | |
| 
 | |
| 	case ASHA1SU0:
 | |
| 		return 0x5E<<24 | 3<<12
 | |
| 
 | |
| 	case ASHA256H:
 | |
| 		return 0x5E<<24 | 4<<12
 | |
| 
 | |
| 	case ASHA256H2:
 | |
| 		return 0x5E<<24 | 5<<12
 | |
| 
 | |
| 	case ASHA256SU1:
 | |
| 		return 0x5E<<24 | 6<<12
 | |
| 
 | |
| 	case ASHA1H:
 | |
| 		return 0x5E<<24 | 2<<20 | 8<<16 | 0<<12 | 2<<10
 | |
| 
 | |
| 	case ASHA1SU1:
 | |
| 		return 0x5E<<24 | 2<<20 | 8<<16 | 1<<12 | 2<<10
 | |
| 
 | |
| 	case ASHA256SU0:
 | |
| 		return 0x5E<<24 | 2<<20 | 8<<16 | 2<<12 | 2<<10
 | |
| 
 | |
| 	case ASHA512H:
 | |
| 		return 0xCE<<24 | 3<<21 | 8<<12
 | |
| 
 | |
| 	case ASHA512H2:
 | |
| 		return 0xCE<<24 | 3<<21 | 8<<12 | 4<<8
 | |
| 
 | |
| 	case ASHA512SU1:
 | |
| 		return 0xCE<<24 | 3<<21 | 8<<12 | 8<<8
 | |
| 
 | |
| 	case ASHA512SU0:
 | |
| 		return 0xCE<<24 | 3<<22 | 8<<12
 | |
| 
 | |
| 	case AFCVTZSD:
 | |
| 		return FPCVTI(1, 0, 1, 3, 0)
 | |
| 
 | |
| 	case AFCVTZSDW:
 | |
| 		return FPCVTI(0, 0, 1, 3, 0)
 | |
| 
 | |
| 	case AFCVTZSS:
 | |
| 		return FPCVTI(1, 0, 0, 3, 0)
 | |
| 
 | |
| 	case AFCVTZSSW:
 | |
| 		return FPCVTI(0, 0, 0, 3, 0)
 | |
| 
 | |
| 	case AFCVTZUD:
 | |
| 		return FPCVTI(1, 0, 1, 3, 1)
 | |
| 
 | |
| 	case AFCVTZUDW:
 | |
| 		return FPCVTI(0, 0, 1, 3, 1)
 | |
| 
 | |
| 	case AFCVTZUS:
 | |
| 		return FPCVTI(1, 0, 0, 3, 1)
 | |
| 
 | |
| 	case AFCVTZUSW:
 | |
| 		return FPCVTI(0, 0, 0, 3, 1)
 | |
| 
 | |
| 	case ASCVTFD:
 | |
| 		return FPCVTI(1, 0, 1, 0, 2)
 | |
| 
 | |
| 	case ASCVTFS:
 | |
| 		return FPCVTI(1, 0, 0, 0, 2)
 | |
| 
 | |
| 	case ASCVTFWD:
 | |
| 		return FPCVTI(0, 0, 1, 0, 2)
 | |
| 
 | |
| 	case ASCVTFWS:
 | |
| 		return FPCVTI(0, 0, 0, 0, 2)
 | |
| 
 | |
| 	case AUCVTFD:
 | |
| 		return FPCVTI(1, 0, 1, 0, 3)
 | |
| 
 | |
| 	case AUCVTFS:
 | |
| 		return FPCVTI(1, 0, 0, 0, 3)
 | |
| 
 | |
| 	case AUCVTFWD:
 | |
| 		return FPCVTI(0, 0, 1, 0, 3)
 | |
| 
 | |
| 	case AUCVTFWS:
 | |
| 		return FPCVTI(0, 0, 0, 0, 3)
 | |
| 
 | |
| 	case AFADDS:
 | |
| 		return FPOP2S(0, 0, 0, 2)
 | |
| 
 | |
| 	case AFADDD:
 | |
| 		return FPOP2S(0, 0, 1, 2)
 | |
| 
 | |
| 	case AFSUBS:
 | |
| 		return FPOP2S(0, 0, 0, 3)
 | |
| 
 | |
| 	case AFSUBD:
 | |
| 		return FPOP2S(0, 0, 1, 3)
 | |
| 
 | |
| 	case AFMADDD:
 | |
| 		return FPOP3S(0, 0, 1, 0, 0)
 | |
| 
 | |
| 	case AFMADDS:
 | |
| 		return FPOP3S(0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case AFMSUBD:
 | |
| 		return FPOP3S(0, 0, 1, 0, 1)
 | |
| 
 | |
| 	case AFMSUBS:
 | |
| 		return FPOP3S(0, 0, 0, 0, 1)
 | |
| 
 | |
| 	case AFNMADDD:
 | |
| 		return FPOP3S(0, 0, 1, 1, 0)
 | |
| 
 | |
| 	case AFNMADDS:
 | |
| 		return FPOP3S(0, 0, 0, 1, 0)
 | |
| 
 | |
| 	case AFNMSUBD:
 | |
| 		return FPOP3S(0, 0, 1, 1, 1)
 | |
| 
 | |
| 	case AFNMSUBS:
 | |
| 		return FPOP3S(0, 0, 0, 1, 1)
 | |
| 
 | |
| 	case AFMULS:
 | |
| 		return FPOP2S(0, 0, 0, 0)
 | |
| 
 | |
| 	case AFMULD:
 | |
| 		return FPOP2S(0, 0, 1, 0)
 | |
| 
 | |
| 	case AFDIVS:
 | |
| 		return FPOP2S(0, 0, 0, 1)
 | |
| 
 | |
| 	case AFDIVD:
 | |
| 		return FPOP2S(0, 0, 1, 1)
 | |
| 
 | |
| 	case AFMAXS:
 | |
| 		return FPOP2S(0, 0, 0, 4)
 | |
| 
 | |
| 	case AFMINS:
 | |
| 		return FPOP2S(0, 0, 0, 5)
 | |
| 
 | |
| 	case AFMAXD:
 | |
| 		return FPOP2S(0, 0, 1, 4)
 | |
| 
 | |
| 	case AFMIND:
 | |
| 		return FPOP2S(0, 0, 1, 5)
 | |
| 
 | |
| 	case AFMAXNMS:
 | |
| 		return FPOP2S(0, 0, 0, 6)
 | |
| 
 | |
| 	case AFMAXNMD:
 | |
| 		return FPOP2S(0, 0, 1, 6)
 | |
| 
 | |
| 	case AFMINNMS:
 | |
| 		return FPOP2S(0, 0, 0, 7)
 | |
| 
 | |
| 	case AFMINNMD:
 | |
| 		return FPOP2S(0, 0, 1, 7)
 | |
| 
 | |
| 	case AFNMULS:
 | |
| 		return FPOP2S(0, 0, 0, 8)
 | |
| 
 | |
| 	case AFNMULD:
 | |
| 		return FPOP2S(0, 0, 1, 8)
 | |
| 
 | |
| 	case AFCMPS:
 | |
| 		return FPCMP(0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case AFCMPD:
 | |
| 		return FPCMP(0, 0, 1, 0, 0)
 | |
| 
 | |
| 	case AFCMPES:
 | |
| 		return FPCMP(0, 0, 0, 0, 16)
 | |
| 
 | |
| 	case AFCMPED:
 | |
| 		return FPCMP(0, 0, 1, 0, 16)
 | |
| 
 | |
| 	case AFCCMPS:
 | |
| 		return FPCCMP(0, 0, 0, 0)
 | |
| 
 | |
| 	case AFCCMPD:
 | |
| 		return FPCCMP(0, 0, 1, 0)
 | |
| 
 | |
| 	case AFCCMPES:
 | |
| 		return FPCCMP(0, 0, 0, 1)
 | |
| 
 | |
| 	case AFCCMPED:
 | |
| 		return FPCCMP(0, 0, 1, 1)
 | |
| 
 | |
| 	case AFCSELS:
 | |
| 		return 0x1E<<24 | 0<<22 | 1<<21 | 3<<10
 | |
| 
 | |
| 	case AFCSELD:
 | |
| 		return 0x1E<<24 | 1<<22 | 1<<21 | 3<<10
 | |
| 
 | |
| 	case AFMOVS:
 | |
| 		return FPOP1S(0, 0, 0, 0)
 | |
| 
 | |
| 	case AFABSS:
 | |
| 		return FPOP1S(0, 0, 0, 1)
 | |
| 
 | |
| 	case AFNEGS:
 | |
| 		return FPOP1S(0, 0, 0, 2)
 | |
| 
 | |
| 	case AFSQRTS:
 | |
| 		return FPOP1S(0, 0, 0, 3)
 | |
| 
 | |
| 	case AFCVTSD:
 | |
| 		return FPOP1S(0, 0, 0, 5)
 | |
| 
 | |
| 	case AFCVTSH:
 | |
| 		return FPOP1S(0, 0, 0, 7)
 | |
| 
 | |
| 	case AFRINTNS:
 | |
| 		return FPOP1S(0, 0, 0, 8)
 | |
| 
 | |
| 	case AFRINTPS:
 | |
| 		return FPOP1S(0, 0, 0, 9)
 | |
| 
 | |
| 	case AFRINTMS:
 | |
| 		return FPOP1S(0, 0, 0, 10)
 | |
| 
 | |
| 	case AFRINTZS:
 | |
| 		return FPOP1S(0, 0, 0, 11)
 | |
| 
 | |
| 	case AFRINTAS:
 | |
| 		return FPOP1S(0, 0, 0, 12)
 | |
| 
 | |
| 	case AFRINTXS:
 | |
| 		return FPOP1S(0, 0, 0, 14)
 | |
| 
 | |
| 	case AFRINTIS:
 | |
| 		return FPOP1S(0, 0, 0, 15)
 | |
| 
 | |
| 	case AFMOVD:
 | |
| 		return FPOP1S(0, 0, 1, 0)
 | |
| 
 | |
| 	case AFABSD:
 | |
| 		return FPOP1S(0, 0, 1, 1)
 | |
| 
 | |
| 	case AFNEGD:
 | |
| 		return FPOP1S(0, 0, 1, 2)
 | |
| 
 | |
| 	case AFSQRTD:
 | |
| 		return FPOP1S(0, 0, 1, 3)
 | |
| 
 | |
| 	case AFCVTDS:
 | |
| 		return FPOP1S(0, 0, 1, 4)
 | |
| 
 | |
| 	case AFCVTDH:
 | |
| 		return FPOP1S(0, 0, 1, 7)
 | |
| 
 | |
| 	case AFRINTND:
 | |
| 		return FPOP1S(0, 0, 1, 8)
 | |
| 
 | |
| 	case AFRINTPD:
 | |
| 		return FPOP1S(0, 0, 1, 9)
 | |
| 
 | |
| 	case AFRINTMD:
 | |
| 		return FPOP1S(0, 0, 1, 10)
 | |
| 
 | |
| 	case AFRINTZD:
 | |
| 		return FPOP1S(0, 0, 1, 11)
 | |
| 
 | |
| 	case AFRINTAD:
 | |
| 		return FPOP1S(0, 0, 1, 12)
 | |
| 
 | |
| 	case AFRINTXD:
 | |
| 		return FPOP1S(0, 0, 1, 14)
 | |
| 
 | |
| 	case AFRINTID:
 | |
| 		return FPOP1S(0, 0, 1, 15)
 | |
| 
 | |
| 	case AFCVTHS:
 | |
| 		return FPOP1S(0, 0, 3, 4)
 | |
| 
 | |
| 	case AFCVTHD:
 | |
| 		return FPOP1S(0, 0, 3, 5)
 | |
| 
 | |
| 	case AVADD:
 | |
| 		return 7<<25 | 1<<21 | 1<<15 | 1<<10
 | |
| 
 | |
| 	case AVSUB:
 | |
| 		return 0x17<<25 | 1<<21 | 1<<15 | 1<<10
 | |
| 
 | |
| 	case AVADDP:
 | |
| 		return 7<<25 | 1<<21 | 1<<15 | 15<<10
 | |
| 
 | |
| 	case AVAND:
 | |
| 		return 7<<25 | 1<<21 | 7<<10
 | |
| 
 | |
| 	case AVCMEQ:
 | |
| 		return 1<<29 | 0x71<<21 | 0x23<<10
 | |
| 
 | |
| 	case AVCNT:
 | |
| 		return 0xE<<24 | 0x10<<17 | 5<<12 | 2<<10
 | |
| 
 | |
| 	case AVZIP1:
 | |
| 		return 0xE<<24 | 3<<12 | 2<<10
 | |
| 
 | |
| 	case AVZIP2:
 | |
| 		return 0xE<<24 | 1<<14 | 3<<12 | 2<<10
 | |
| 
 | |
| 	case AVEOR:
 | |
| 		return 1<<29 | 0x71<<21 | 7<<10
 | |
| 
 | |
| 	case AVORR:
 | |
| 		return 7<<25 | 5<<21 | 7<<10
 | |
| 
 | |
| 	case AVREV16:
 | |
| 		return 3<<26 | 2<<24 | 1<<21 | 3<<11
 | |
| 
 | |
| 	case AVREV32:
 | |
| 		return 11<<26 | 2<<24 | 1<<21 | 1<<11
 | |
| 
 | |
| 	case AVREV64:
 | |
| 		return 3<<26 | 2<<24 | 1<<21 | 1<<11
 | |
| 
 | |
| 	case AVMOV:
 | |
| 		return 7<<25 | 5<<21 | 7<<10
 | |
| 
 | |
| 	case AVADDV:
 | |
| 		return 7<<25 | 3<<20 | 3<<15 | 7<<11
 | |
| 
 | |
| 	case AVUADDLV:
 | |
| 		return 1<<29 | 7<<25 | 3<<20 | 7<<11
 | |
| 
 | |
| 	case AVFMLA:
 | |
| 		return 7<<25 | 0<<23 | 1<<21 | 3<<14 | 3<<10
 | |
| 
 | |
| 	case AVFMLS:
 | |
| 		return 7<<25 | 1<<23 | 1<<21 | 3<<14 | 3<<10
 | |
| 
 | |
| 	case AVPMULL, AVPMULL2:
 | |
| 		return 0xE<<24 | 1<<21 | 0x38<<10
 | |
| 
 | |
| 	case AVRBIT:
 | |
| 		return 0x2E<<24 | 1<<22 | 0x10<<17 | 5<<12 | 2<<10
 | |
| 
 | |
| 	case AVLD1, AVLD2, AVLD3, AVLD4:
 | |
| 		return 3<<26 | 1<<22
 | |
| 
 | |
| 	case AVLD1R, AVLD3R:
 | |
| 		return 0xD<<24 | 1<<22
 | |
| 
 | |
| 	case AVLD2R, AVLD4R:
 | |
| 		return 0xD<<24 | 3<<21
 | |
| 
 | |
| 	case AVBIF:
 | |
| 		return 1<<29 | 7<<25 | 7<<21 | 7<<10
 | |
| 
 | |
| 	case AVBIT:
 | |
| 		return 1<<29 | 0x75<<21 | 7<<10
 | |
| 
 | |
| 	case AVBSL:
 | |
| 		return 1<<29 | 0x73<<21 | 7<<10
 | |
| 
 | |
| 	case AVCMTST:
 | |
| 		return 0xE<<24 | 1<<21 | 0x23<<10
 | |
| 
 | |
| 	case AVUZP1:
 | |
| 		return 7<<25 | 3<<11
 | |
| 
 | |
| 	case AVUZP2:
 | |
| 		return 7<<25 | 1<<14 | 3<<11
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad rrr %d %v", p, a, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * imm -> Rd
 | |
|  * imm op Rn -> Rd
 | |
|  */
 | |
| func (c *ctxt7) opirr(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	/* op $addcon, Rn, Rd */
 | |
| 	case AMOVD, AADD:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0x11<<24
 | |
| 
 | |
| 	case ACMN, AADDS:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0x11<<24
 | |
| 
 | |
| 	case AMOVW, AADDW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0x11<<24
 | |
| 
 | |
| 	case ACMNW, AADDSW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0x11<<24
 | |
| 
 | |
| 	case ASUB:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0x11<<24
 | |
| 
 | |
| 	case ACMP, ASUBS:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0x11<<24
 | |
| 
 | |
| 	case ASUBW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0x11<<24
 | |
| 
 | |
| 	case ACMPW, ASUBSW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0x11<<24
 | |
| 
 | |
| 		/* op $imm(SB), Rd; op label, Rd */
 | |
| 	case AADR:
 | |
| 		return 0<<31 | 0x10<<24
 | |
| 
 | |
| 	case AADRP:
 | |
| 		return 1<<31 | 0x10<<24
 | |
| 
 | |
| 		/* op $bimm, Rn, Rd */
 | |
| 	case AAND, ABIC:
 | |
| 		return S64 | 0<<29 | 0x24<<23
 | |
| 
 | |
| 	case AANDW, ABICW:
 | |
| 		return S32 | 0<<29 | 0x24<<23 | 0<<22
 | |
| 
 | |
| 	case AORR, AORN:
 | |
| 		return S64 | 1<<29 | 0x24<<23
 | |
| 
 | |
| 	case AORRW, AORNW:
 | |
| 		return S32 | 1<<29 | 0x24<<23 | 0<<22
 | |
| 
 | |
| 	case AEOR, AEON:
 | |
| 		return S64 | 2<<29 | 0x24<<23
 | |
| 
 | |
| 	case AEORW, AEONW:
 | |
| 		return S32 | 2<<29 | 0x24<<23 | 0<<22
 | |
| 
 | |
| 	case AANDS, ABICS, ATST:
 | |
| 		return S64 | 3<<29 | 0x24<<23
 | |
| 
 | |
| 	case AANDSW, ABICSW, ATSTW:
 | |
| 		return S32 | 3<<29 | 0x24<<23 | 0<<22
 | |
| 
 | |
| 	case AASR:
 | |
| 		return S64 | 0<<29 | 0x26<<23 /* alias of SBFM */
 | |
| 
 | |
| 	case AASRW:
 | |
| 		return S32 | 0<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 		/* op $width, $lsb, Rn, Rd */
 | |
| 	case ABFI:
 | |
| 		return S64 | 2<<29 | 0x26<<23 | 1<<22
 | |
| 		/* alias of BFM */
 | |
| 
 | |
| 	case ABFIW:
 | |
| 		return S32 | 2<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 		/* op $imms, $immr, Rn, Rd */
 | |
| 	case ABFM:
 | |
| 		return S64 | 1<<29 | 0x26<<23 | 1<<22
 | |
| 
 | |
| 	case ABFMW:
 | |
| 		return S32 | 1<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 	case ASBFM:
 | |
| 		return S64 | 0<<29 | 0x26<<23 | 1<<22
 | |
| 
 | |
| 	case ASBFMW:
 | |
| 		return S32 | 0<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 	case AUBFM:
 | |
| 		return S64 | 2<<29 | 0x26<<23 | 1<<22
 | |
| 
 | |
| 	case AUBFMW:
 | |
| 		return S32 | 2<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 	case ABFXIL:
 | |
| 		return S64 | 1<<29 | 0x26<<23 | 1<<22 /* alias of BFM */
 | |
| 
 | |
| 	case ABFXILW:
 | |
| 		return S32 | 1<<29 | 0x26<<23 | 0<<22
 | |
| 
 | |
| 	case AEXTR:
 | |
| 		return S64 | 0<<29 | 0x27<<23 | 1<<22 | 0<<21
 | |
| 
 | |
| 	case AEXTRW:
 | |
| 		return S32 | 0<<29 | 0x27<<23 | 0<<22 | 0<<21
 | |
| 
 | |
| 	case ACBNZ:
 | |
| 		return S64 | 0x1A<<25 | 1<<24
 | |
| 
 | |
| 	case ACBNZW:
 | |
| 		return S32 | 0x1A<<25 | 1<<24
 | |
| 
 | |
| 	case ACBZ:
 | |
| 		return S64 | 0x1A<<25 | 0<<24
 | |
| 
 | |
| 	case ACBZW:
 | |
| 		return S32 | 0x1A<<25 | 0<<24
 | |
| 
 | |
| 	case ACCMN:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0xD2<<21 | 1<<11 | 0<<10 | 0<<4 /* imm5<<16 | cond<<12 | nzcv<<0 */
 | |
| 
 | |
| 	case ACCMNW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0xD2<<21 | 1<<11 | 0<<10 | 0<<4
 | |
| 
 | |
| 	case ACCMP:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0xD2<<21 | 1<<11 | 0<<10 | 0<<4 /* imm5<<16 | cond<<12 | nzcv<<0 */
 | |
| 
 | |
| 	case ACCMPW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0xD2<<21 | 1<<11 | 0<<10 | 0<<4
 | |
| 
 | |
| 	case AMOVK:
 | |
| 		return S64 | 3<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMOVKW:
 | |
| 		return S32 | 3<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMOVN:
 | |
| 		return S64 | 0<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMOVNW:
 | |
| 		return S32 | 0<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMOVZ:
 | |
| 		return S64 | 2<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMOVZW:
 | |
| 		return S32 | 2<<29 | 0x25<<23
 | |
| 
 | |
| 	case AMSR:
 | |
| 		return SYSOP(0, 0, 0, 4, 0, 0, 0x1F) /* MSR (immediate) */
 | |
| 
 | |
| 	case AAT,
 | |
| 		ADC,
 | |
| 		AIC,
 | |
| 		ATLBI,
 | |
| 		ASYS:
 | |
| 		return SYSOP(0, 1, 0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case ASYSL:
 | |
| 		return SYSOP(1, 1, 0, 0, 0, 0, 0)
 | |
| 
 | |
| 	case ATBZ:
 | |
| 		return 0x36 << 24
 | |
| 
 | |
| 	case ATBNZ:
 | |
| 		return 0x37 << 24
 | |
| 
 | |
| 	case ADSB:
 | |
| 		return SYSOP(0, 0, 3, 3, 0, 4, 0x1F)
 | |
| 
 | |
| 	case ADMB:
 | |
| 		return SYSOP(0, 0, 3, 3, 0, 5, 0x1F)
 | |
| 
 | |
| 	case AISB:
 | |
| 		return SYSOP(0, 0, 3, 3, 0, 6, 0x1F)
 | |
| 
 | |
| 	case AHINT:
 | |
| 		return SYSOP(0, 0, 3, 2, 0, 0, 0x1F)
 | |
| 
 | |
| 	case AVEXT:
 | |
| 		return 0x2E<<24 | 0<<23 | 0<<21 | 0<<15
 | |
| 
 | |
| 	case AVUSHR:
 | |
| 		return 0x5E<<23 | 1<<10
 | |
| 
 | |
| 	case AVSHL:
 | |
| 		return 0x1E<<23 | 21<<10
 | |
| 
 | |
| 	case AVSRI:
 | |
| 		return 0x5E<<23 | 17<<10
 | |
| 
 | |
| 	case AVUSHLL, AVUXTL:
 | |
| 		return 1<<29 | 15<<24 | 0x29<<10
 | |
| 
 | |
| 	case AVUSHLL2, AVUXTL2:
 | |
| 		return 3<<29 | 15<<24 | 0x29<<10
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad irr %v", p, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opbit(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ACLS:
 | |
| 		return S64 | OPBIT(5)
 | |
| 
 | |
| 	case ACLSW:
 | |
| 		return S32 | OPBIT(5)
 | |
| 
 | |
| 	case ACLZ:
 | |
| 		return S64 | OPBIT(4)
 | |
| 
 | |
| 	case ACLZW:
 | |
| 		return S32 | OPBIT(4)
 | |
| 
 | |
| 	case ARBIT:
 | |
| 		return S64 | OPBIT(0)
 | |
| 
 | |
| 	case ARBITW:
 | |
| 		return S32 | OPBIT(0)
 | |
| 
 | |
| 	case AREV:
 | |
| 		return S64 | OPBIT(3)
 | |
| 
 | |
| 	case AREVW:
 | |
| 		return S32 | OPBIT(2)
 | |
| 
 | |
| 	case AREV16:
 | |
| 		return S64 | OPBIT(1)
 | |
| 
 | |
| 	case AREV16W:
 | |
| 		return S32 | OPBIT(1)
 | |
| 
 | |
| 	case AREV32:
 | |
| 		return S64 | OPBIT(2)
 | |
| 
 | |
| 	default:
 | |
| 		c.ctxt.Diag("bad bit op\n%v", p)
 | |
| 		return 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * add/subtract sign or zero-extended register
 | |
|  */
 | |
| func (c *ctxt7) opxrrr(p *obj.Prog, a obj.As, extend bool) uint32 {
 | |
| 	extension := uint32(0)
 | |
| 	if !extend {
 | |
| 		switch a {
 | |
| 		case AADD, ACMN, AADDS, ASUB, ACMP, ASUBS:
 | |
| 			extension = LSL0_64
 | |
| 
 | |
| 		case AADDW, ACMNW, AADDSW, ASUBW, ACMPW, ASUBSW:
 | |
| 			extension = LSL0_32
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch a {
 | |
| 	case AADD:
 | |
| 		return S64 | 0<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case AADDW:
 | |
| 		return S32 | 0<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ACMN, AADDS:
 | |
| 		return S64 | 0<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ACMNW, AADDSW:
 | |
| 		return S32 | 0<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ASUB:
 | |
| 		return S64 | 1<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ASUBW:
 | |
| 		return S32 | 1<<30 | 0<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ACMP, ASUBS:
 | |
| 		return S64 | 1<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 
 | |
| 	case ACMPW, ASUBSW:
 | |
| 		return S32 | 1<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 1<<21 | extension
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opxrrr %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opimm(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ASVC:
 | |
| 		return 0xD4<<24 | 0<<21 | 1 /* imm16<<5 */
 | |
| 
 | |
| 	case AHVC:
 | |
| 		return 0xD4<<24 | 0<<21 | 2
 | |
| 
 | |
| 	case ASMC:
 | |
| 		return 0xD4<<24 | 0<<21 | 3
 | |
| 
 | |
| 	case ABRK:
 | |
| 		return 0xD4<<24 | 1<<21 | 0
 | |
| 
 | |
| 	case AHLT:
 | |
| 		return 0xD4<<24 | 2<<21 | 0
 | |
| 
 | |
| 	case ADCPS1:
 | |
| 		return 0xD4<<24 | 5<<21 | 1
 | |
| 
 | |
| 	case ADCPS2:
 | |
| 		return 0xD4<<24 | 5<<21 | 2
 | |
| 
 | |
| 	case ADCPS3:
 | |
| 		return 0xD4<<24 | 5<<21 | 3
 | |
| 
 | |
| 	case ACLREX:
 | |
| 		return SYSOP(0, 0, 3, 3, 0, 2, 0x1F)
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad imm %v", p, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) brdist(p *obj.Prog, preshift int, flen int, shift int) int64 {
 | |
| 	v := int64(0)
 | |
| 	t := int64(0)
 | |
| 	q := p.To.Target()
 | |
| 	if q == nil {
 | |
| 		// TODO: don't use brdist for this case, as it isn't a branch.
 | |
| 		// (Calls from omovlit, and maybe adr/adrp opcodes as well.)
 | |
| 		q = p.Pool
 | |
| 	}
 | |
| 	if q != nil {
 | |
| 		v = (q.Pc >> uint(preshift)) - (c.pc >> uint(preshift))
 | |
| 		if (v & ((1 << uint(shift)) - 1)) != 0 {
 | |
| 			c.ctxt.Diag("misaligned label\n%v", p)
 | |
| 		}
 | |
| 		v >>= uint(shift)
 | |
| 		t = int64(1) << uint(flen-1)
 | |
| 		if v < -t || v >= t {
 | |
| 			c.ctxt.Diag("branch too far %#x vs %#x [%p]\n%v\n%v", v, t, c.blitrl, p, q)
 | |
| 			panic("branch too far")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return v & ((t << 1) - 1)
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * pc-relative branches
 | |
|  */
 | |
| func (c *ctxt7) opbra(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ABEQ:
 | |
| 		return OPBcc(0x0)
 | |
| 
 | |
| 	case ABNE:
 | |
| 		return OPBcc(0x1)
 | |
| 
 | |
| 	case ABCS:
 | |
| 		return OPBcc(0x2)
 | |
| 
 | |
| 	case ABHS:
 | |
| 		return OPBcc(0x2)
 | |
| 
 | |
| 	case ABCC:
 | |
| 		return OPBcc(0x3)
 | |
| 
 | |
| 	case ABLO:
 | |
| 		return OPBcc(0x3)
 | |
| 
 | |
| 	case ABMI:
 | |
| 		return OPBcc(0x4)
 | |
| 
 | |
| 	case ABPL:
 | |
| 		return OPBcc(0x5)
 | |
| 
 | |
| 	case ABVS:
 | |
| 		return OPBcc(0x6)
 | |
| 
 | |
| 	case ABVC:
 | |
| 		return OPBcc(0x7)
 | |
| 
 | |
| 	case ABHI:
 | |
| 		return OPBcc(0x8)
 | |
| 
 | |
| 	case ABLS:
 | |
| 		return OPBcc(0x9)
 | |
| 
 | |
| 	case ABGE:
 | |
| 		return OPBcc(0xa)
 | |
| 
 | |
| 	case ABLT:
 | |
| 		return OPBcc(0xb)
 | |
| 
 | |
| 	case ABGT:
 | |
| 		return OPBcc(0xc)
 | |
| 
 | |
| 	case ABLE:
 | |
| 		return OPBcc(0xd) /* imm19<<5 | cond */
 | |
| 
 | |
| 	case AB:
 | |
| 		return 0<<31 | 5<<26 /* imm26 */
 | |
| 
 | |
| 	case obj.ADUFFZERO, obj.ADUFFCOPY, ABL:
 | |
| 		return 1<<31 | 5<<26
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad bra %v", p, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opbrr(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ABL:
 | |
| 		return OPBLR(1) /* BLR */
 | |
| 
 | |
| 	case AB:
 | |
| 		return OPBLR(0) /* BR */
 | |
| 
 | |
| 	case obj.ARET:
 | |
| 		return OPBLR(2) /* RET */
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad brr %v", p, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) op0(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ADRPS:
 | |
| 		return 0x6B<<25 | 5<<21 | 0x1F<<16 | 0x1F<<5
 | |
| 
 | |
| 	case AERET:
 | |
| 		return 0x6B<<25 | 4<<21 | 0x1F<<16 | 0<<10 | 0x1F<<5
 | |
| 
 | |
| 	case ANOOP:
 | |
| 		return SYSHINT(0)
 | |
| 
 | |
| 	case AYIELD:
 | |
| 		return SYSHINT(1)
 | |
| 
 | |
| 	case AWFE:
 | |
| 		return SYSHINT(2)
 | |
| 
 | |
| 	case AWFI:
 | |
| 		return SYSHINT(3)
 | |
| 
 | |
| 	case ASEV:
 | |
| 		return SYSHINT(4)
 | |
| 
 | |
| 	case ASEVL:
 | |
| 		return SYSHINT(5)
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("%v: bad op0 %v", p, a)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * register offset
 | |
|  */
 | |
| func (c *ctxt7) opload(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ALDAR:
 | |
| 		return LDSTX(3, 1, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDARW:
 | |
| 		return LDSTX(2, 1, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDARB:
 | |
| 		return LDSTX(0, 1, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDARH:
 | |
| 		return LDSTX(1, 1, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDAXP:
 | |
| 		return LDSTX(3, 0, 1, 1, 1)
 | |
| 
 | |
| 	case ALDAXPW:
 | |
| 		return LDSTX(2, 0, 1, 1, 1)
 | |
| 
 | |
| 	case ALDAXR:
 | |
| 		return LDSTX(3, 0, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDAXRW:
 | |
| 		return LDSTX(2, 0, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDAXRB:
 | |
| 		return LDSTX(0, 0, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDAXRH:
 | |
| 		return LDSTX(1, 0, 1, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ALDXR:
 | |
| 		return LDSTX(3, 0, 1, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ALDXRB:
 | |
| 		return LDSTX(0, 0, 1, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ALDXRH:
 | |
| 		return LDSTX(1, 0, 1, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ALDXRW:
 | |
| 		return LDSTX(2, 0, 1, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ALDXP:
 | |
| 		return LDSTX(3, 0, 1, 1, 0)
 | |
| 
 | |
| 	case ALDXPW:
 | |
| 		return LDSTX(2, 0, 1, 1, 0)
 | |
| 
 | |
| 	case AMOVNP:
 | |
| 		return S64 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 | |
| 
 | |
| 	case AMOVNPW:
 | |
| 		return S32 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opload %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opstore(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case ASTLR:
 | |
| 		return LDSTX(3, 1, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLRB:
 | |
| 		return LDSTX(0, 1, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLRH:
 | |
| 		return LDSTX(1, 1, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLP:
 | |
| 		return LDSTX(3, 0, 0, 1, 1)
 | |
| 
 | |
| 	case ASTLPW:
 | |
| 		return LDSTX(2, 0, 0, 1, 1)
 | |
| 
 | |
| 	case ASTLRW:
 | |
| 		return LDSTX(2, 1, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLXP:
 | |
| 		return LDSTX(3, 0, 0, 1, 1)
 | |
| 
 | |
| 	case ASTLXPW:
 | |
| 		return LDSTX(2, 0, 0, 1, 1)
 | |
| 
 | |
| 	case ASTLXR:
 | |
| 		return LDSTX(3, 0, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLXRB:
 | |
| 		return LDSTX(0, 0, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLXRH:
 | |
| 		return LDSTX(1, 0, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTLXRW:
 | |
| 		return LDSTX(2, 0, 0, 0, 1) | 0x1F<<10
 | |
| 
 | |
| 	case ASTXR:
 | |
| 		return LDSTX(3, 0, 0, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ASTXRB:
 | |
| 		return LDSTX(0, 0, 0, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ASTXRH:
 | |
| 		return LDSTX(1, 0, 0, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case ASTXP:
 | |
| 		return LDSTX(3, 0, 0, 1, 0)
 | |
| 
 | |
| 	case ASTXPW:
 | |
| 		return LDSTX(2, 0, 0, 1, 0)
 | |
| 
 | |
| 	case ASTXRW:
 | |
| 		return LDSTX(2, 0, 0, 0, 0) | 0x1F<<10
 | |
| 
 | |
| 	case AMOVNP:
 | |
| 		return S64 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 | |
| 
 | |
| 	case AMOVNPW:
 | |
| 		return S32 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opstore %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * load/store register (unsigned immediate) C3.3.13
 | |
|  *	these produce 64-bit values (when there's an option)
 | |
|  */
 | |
| func (c *ctxt7) olsr12u(p *obj.Prog, o int32, v int32, b int, r int) uint32 {
 | |
| 	if v < 0 || v >= (1<<12) {
 | |
| 		c.ctxt.Diag("offset out of range: %d\n%v", v, p)
 | |
| 	}
 | |
| 	o |= (v & 0xFFF) << 10
 | |
| 	o |= int32(b&31) << 5
 | |
| 	o |= int32(r & 31)
 | |
| 	return uint32(o)
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opldr12(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return LDSTR12U(3, 0, 1) /* imm12<<10 | Rn<<5 | Rt */
 | |
| 
 | |
| 	case AMOVW:
 | |
| 		return LDSTR12U(2, 0, 2)
 | |
| 
 | |
| 	case AMOVWU:
 | |
| 		return LDSTR12U(2, 0, 1)
 | |
| 
 | |
| 	case AMOVH:
 | |
| 		return LDSTR12U(1, 0, 2)
 | |
| 
 | |
| 	case AMOVHU:
 | |
| 		return LDSTR12U(1, 0, 1)
 | |
| 
 | |
| 	case AMOVB:
 | |
| 		return LDSTR12U(0, 0, 2)
 | |
| 
 | |
| 	case AMOVBU:
 | |
| 		return LDSTR12U(0, 0, 1)
 | |
| 
 | |
| 	case AFMOVS:
 | |
| 		return LDSTR12U(2, 1, 1)
 | |
| 
 | |
| 	case AFMOVD:
 | |
| 		return LDSTR12U(3, 1, 1)
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opldr12 %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opstr12(p *obj.Prog, a obj.As) uint32 {
 | |
| 	return LD2STR(c.opldr12(p, a))
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * load/store register (unscaled immediate) C3.3.12
 | |
|  */
 | |
| func (c *ctxt7) olsr9s(p *obj.Prog, o int32, v int32, b int, r int) uint32 {
 | |
| 	if v < -256 || v > 255 {
 | |
| 		c.ctxt.Diag("offset out of range: %d\n%v", v, p)
 | |
| 	}
 | |
| 	o |= (v & 0x1FF) << 12
 | |
| 	o |= int32(b&31) << 5
 | |
| 	o |= int32(r & 31)
 | |
| 	return uint32(o)
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opldr9(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return LDSTR9S(3, 0, 1) /* simm9<<12 | Rn<<5 | Rt */
 | |
| 
 | |
| 	case AMOVW:
 | |
| 		return LDSTR9S(2, 0, 2)
 | |
| 
 | |
| 	case AMOVWU:
 | |
| 		return LDSTR9S(2, 0, 1)
 | |
| 
 | |
| 	case AMOVH:
 | |
| 		return LDSTR9S(1, 0, 2)
 | |
| 
 | |
| 	case AMOVHU:
 | |
| 		return LDSTR9S(1, 0, 1)
 | |
| 
 | |
| 	case AMOVB:
 | |
| 		return LDSTR9S(0, 0, 2)
 | |
| 
 | |
| 	case AMOVBU:
 | |
| 		return LDSTR9S(0, 0, 1)
 | |
| 
 | |
| 	case AFMOVS:
 | |
| 		return LDSTR9S(2, 1, 1)
 | |
| 
 | |
| 	case AFMOVD:
 | |
| 		return LDSTR9S(3, 1, 1)
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opldr9 %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opstr9(p *obj.Prog, a obj.As) uint32 {
 | |
| 	return LD2STR(c.opldr9(p, a))
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opldrpp(p *obj.Prog, a obj.As) uint32 {
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return 3<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22 /* simm9<<12 | Rn<<5 | Rt */
 | |
| 
 | |
| 	case AMOVW:
 | |
| 		return 2<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22
 | |
| 
 | |
| 	case AMOVWU:
 | |
| 		return 2<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22
 | |
| 
 | |
| 	case AMOVH:
 | |
| 		return 1<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22
 | |
| 
 | |
| 	case AMOVHU:
 | |
| 		return 1<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22
 | |
| 
 | |
| 	case AMOVB:
 | |
| 		return 0<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22
 | |
| 
 | |
| 	case AMOVBU:
 | |
| 		return 0<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22
 | |
| 
 | |
| 	case AFMOVS:
 | |
| 		return 2<<30 | 7<<27 | 1<<26 | 0<<24 | 1<<22
 | |
| 
 | |
| 	case AFMOVD:
 | |
| 		return 3<<30 | 7<<27 | 1<<26 | 0<<24 | 1<<22
 | |
| 
 | |
| 	case APRFM:
 | |
| 		return 0xf9<<24 | 2<<22
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	c.ctxt.Diag("bad opldr %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // olsxrr attaches register operands to a load/store opcode supplied in o.
 | |
| // The result either encodes a load of r from (r1+r2) or a store of r to (r1+r2).
 | |
| func (c *ctxt7) olsxrr(p *obj.Prog, o int32, r int, r1 int, r2 int) uint32 {
 | |
| 	o |= int32(r1&31) << 5
 | |
| 	o |= int32(r2&31) << 16
 | |
| 	o |= int32(r & 31)
 | |
| 	return uint32(o)
 | |
| }
 | |
| 
 | |
| // opldrr returns the ARM64 opcode encoding corresponding to the obj.As opcode
 | |
| // for load instruction with register offset.
 | |
| // The offset register can be (Rn)(Rm.UXTW<<2) or (Rn)(Rm<<2) or (Rn)(Rm).
 | |
| func (c *ctxt7) opldrr(p *obj.Prog, a obj.As, extension bool) uint32 {
 | |
| 	OptionS := uint32(0x1a)
 | |
| 	if extension {
 | |
| 		OptionS = uint32(0) // option value and S value have been encoded into p.From.Offset.
 | |
| 	}
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x1f<<27
 | |
| 	case AMOVW:
 | |
| 		return OptionS<<10 | 0x5<<21 | 0x17<<27
 | |
| 	case AMOVWU:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x17<<27
 | |
| 	case AMOVH:
 | |
| 		return OptionS<<10 | 0x5<<21 | 0x0f<<27
 | |
| 	case AMOVHU:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x0f<<27
 | |
| 	case AMOVB:
 | |
| 		return OptionS<<10 | 0x5<<21 | 0x07<<27
 | |
| 	case AMOVBU:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x07<<27
 | |
| 	case AFMOVS:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x17<<27 | 1<<26
 | |
| 	case AFMOVD:
 | |
| 		return OptionS<<10 | 0x3<<21 | 0x1f<<27 | 1<<26
 | |
| 	}
 | |
| 	c.ctxt.Diag("bad opldrr %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // opstrr returns the ARM64 opcode encoding corresponding to the obj.As opcode
 | |
| // for store instruction with register offset.
 | |
| // The offset register can be (Rn)(Rm.UXTW<<2) or (Rn)(Rm<<2) or (Rn)(Rm).
 | |
| func (c *ctxt7) opstrr(p *obj.Prog, a obj.As, extension bool) uint32 {
 | |
| 	OptionS := uint32(0x1a)
 | |
| 	if extension {
 | |
| 		OptionS = uint32(0) // option value and S value have been encoded into p.To.Offset.
 | |
| 	}
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x1f<<27
 | |
| 	case AMOVW, AMOVWU:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x17<<27
 | |
| 	case AMOVH, AMOVHU:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x0f<<27
 | |
| 	case AMOVB, AMOVBU:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x07<<27
 | |
| 	case AFMOVS:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x17<<27 | 1<<26
 | |
| 	case AFMOVD:
 | |
| 		return OptionS<<10 | 0x1<<21 | 0x1f<<27 | 1<<26
 | |
| 	}
 | |
| 	c.ctxt.Diag("bad opstrr %v\n%v", a, p)
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) oaddi(p *obj.Prog, o1 int32, v int32, r int, rt int) uint32 {
 | |
| 	if (v & 0xFFF000) != 0 {
 | |
| 		if v&0xFFF != 0 {
 | |
| 			c.ctxt.Diag("%v misuses oaddi", p)
 | |
| 		}
 | |
| 		v >>= 12
 | |
| 		o1 |= 1 << 22
 | |
| 	}
 | |
| 
 | |
| 	o1 |= ((v & 0xFFF) << 10) | (int32(r&31) << 5) | int32(rt&31)
 | |
| 	return uint32(o1)
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * load a literal value into dr
 | |
|  */
 | |
| func (c *ctxt7) omovlit(as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32 {
 | |
| 	var o1 int32
 | |
| 	if p.Pool == nil { /* not in literal pool */
 | |
| 		c.aclass(a)
 | |
| 		c.ctxt.Logf("omovlit add %d (%#x)\n", c.instoffset, uint64(c.instoffset))
 | |
| 
 | |
| 		/* TODO: could be clever, and use general constant builder */
 | |
| 		o1 = int32(c.opirr(p, AADD))
 | |
| 
 | |
| 		v := int32(c.instoffset)
 | |
| 		if v != 0 && (v&0xFFF) == 0 {
 | |
| 			v >>= 12
 | |
| 			o1 |= 1 << 22 /* shift, by 12 */
 | |
| 		}
 | |
| 
 | |
| 		o1 |= ((v & 0xFFF) << 10) | (REGZERO & 31 << 5) | int32(dr&31)
 | |
| 	} else {
 | |
| 		fp, w := 0, 0
 | |
| 		switch as {
 | |
| 		case AFMOVS:
 | |
| 			fp = 1
 | |
| 			w = 0 /* 32-bit SIMD/FP */
 | |
| 
 | |
| 		case AFMOVD:
 | |
| 			fp = 1
 | |
| 			w = 1 /* 64-bit SIMD/FP */
 | |
| 
 | |
| 		case AFMOVQ:
 | |
| 			fp = 1
 | |
| 			w = 2 /* 128-bit SIMD/FP */
 | |
| 
 | |
| 		case AMOVD:
 | |
| 			if p.Pool.As == ADWORD {
 | |
| 				w = 1 /* 64-bit */
 | |
| 			} else if p.Pool.To.Offset < 0 {
 | |
| 				w = 2 /* 32-bit, sign-extended to 64-bit */
 | |
| 			} else if p.Pool.To.Offset >= 0 {
 | |
| 				w = 0 /* 32-bit, zero-extended to 64-bit */
 | |
| 			} else {
 | |
| 				c.ctxt.Diag("invalid operand %v in %v", a, p)
 | |
| 			}
 | |
| 
 | |
| 		case AMOVBU, AMOVHU, AMOVWU:
 | |
| 			w = 0 /* 32-bit, zero-extended to 64-bit */
 | |
| 
 | |
| 		case AMOVB, AMOVH, AMOVW:
 | |
| 			w = 2 /* 32-bit, sign-extended to 64-bit */
 | |
| 
 | |
| 		default:
 | |
| 			c.ctxt.Diag("invalid operation %v in %v", as, p)
 | |
| 		}
 | |
| 
 | |
| 		v := int32(c.brdist(p, 0, 19, 2))
 | |
| 		o1 = (int32(w) << 30) | (int32(fp) << 26) | (3 << 27)
 | |
| 		o1 |= (v & 0x7FFFF) << 5
 | |
| 		o1 |= int32(dr & 31)
 | |
| 	}
 | |
| 
 | |
| 	return uint32(o1)
 | |
| }
 | |
| 
 | |
| // load a constant (MOVCON or BITCON) in a into rt
 | |
| func (c *ctxt7) omovconst(as obj.As, p *obj.Prog, a *obj.Addr, rt int) (o1 uint32) {
 | |
| 	if cls := oclass(a); cls == C_BITCON || cls == C_ABCON || cls == C_ABCON0 {
 | |
| 		// or $bitcon, REGZERO, rt
 | |
| 		mode := 64
 | |
| 		var as1 obj.As
 | |
| 		switch as {
 | |
| 		case AMOVW:
 | |
| 			as1 = AORRW
 | |
| 			mode = 32
 | |
| 		case AMOVD:
 | |
| 			as1 = AORR
 | |
| 		}
 | |
| 		o1 = c.opirr(p, as1)
 | |
| 		o1 |= bitconEncode(uint64(a.Offset), mode) | uint32(REGZERO&31)<<5 | uint32(rt&31)
 | |
| 		return o1
 | |
| 	}
 | |
| 
 | |
| 	if as == AMOVW {
 | |
| 		d := uint32(a.Offset)
 | |
| 		s := movcon(int64(d))
 | |
| 		if s < 0 || 16*s >= 32 {
 | |
| 			d = ^d
 | |
| 			s = movcon(int64(d))
 | |
| 			if s < 0 || 16*s >= 32 {
 | |
| 				c.ctxt.Diag("impossible 32-bit move wide: %#x\n%v", uint32(a.Offset), p)
 | |
| 			}
 | |
| 			o1 = c.opirr(p, AMOVNW)
 | |
| 		} else {
 | |
| 			o1 = c.opirr(p, AMOVZW)
 | |
| 		}
 | |
| 		o1 |= MOVCONST(int64(d), s, rt)
 | |
| 	}
 | |
| 	if as == AMOVD {
 | |
| 		d := a.Offset
 | |
| 		s := movcon(d)
 | |
| 		if s < 0 || 16*s >= 64 {
 | |
| 			d = ^d
 | |
| 			s = movcon(d)
 | |
| 			if s < 0 || 16*s >= 64 {
 | |
| 				c.ctxt.Diag("impossible 64-bit move wide: %#x\n%v", uint64(a.Offset), p)
 | |
| 			}
 | |
| 			o1 = c.opirr(p, AMOVN)
 | |
| 		} else {
 | |
| 			o1 = c.opirr(p, AMOVZ)
 | |
| 		}
 | |
| 		o1 |= MOVCONST(d, s, rt)
 | |
| 	}
 | |
| 	return o1
 | |
| }
 | |
| 
 | |
| // load a 32-bit/64-bit large constant (LCON or VCON) in a.Offset into rt
 | |
| // put the instruction sequence in os and return the number of instructions.
 | |
| func (c *ctxt7) omovlconst(as obj.As, p *obj.Prog, a *obj.Addr, rt int, os []uint32) (num uint8) {
 | |
| 	switch as {
 | |
| 	case AMOVW:
 | |
| 		d := uint32(a.Offset)
 | |
| 		// use MOVZW and MOVKW to load a constant to rt
 | |
| 		os[0] = c.opirr(p, AMOVZW)
 | |
| 		os[0] |= MOVCONST(int64(d), 0, rt)
 | |
| 		os[1] = c.opirr(p, AMOVKW)
 | |
| 		os[1] |= MOVCONST(int64(d), 1, rt)
 | |
| 		return 2
 | |
| 
 | |
| 	case AMOVD:
 | |
| 		d := a.Offset
 | |
| 		dn := ^d
 | |
| 		var immh [4]uint64
 | |
| 		var i int
 | |
| 		zeroCount := int(0)
 | |
| 		negCount := int(0)
 | |
| 		for i = 0; i < 4; i++ {
 | |
| 			immh[i] = uint64((d >> uint(i*16)) & 0xffff)
 | |
| 			if immh[i] == 0 {
 | |
| 				zeroCount++
 | |
| 			} else if immh[i] == 0xffff {
 | |
| 				negCount++
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if zeroCount == 4 || negCount == 4 {
 | |
| 			c.ctxt.Diag("the immediate should be MOVCON: %v", p)
 | |
| 		}
 | |
| 		switch {
 | |
| 		case zeroCount == 3:
 | |
| 			// one MOVZ
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0 {
 | |
| 					os[0] = c.opirr(p, AMOVZ)
 | |
| 					os[0] |= MOVCONST(d, i, rt)
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			return 1
 | |
| 
 | |
| 		case negCount == 3:
 | |
| 			// one MOVN
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0xffff {
 | |
| 					os[0] = c.opirr(p, AMOVN)
 | |
| 					os[0] |= MOVCONST(dn, i, rt)
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			return 1
 | |
| 
 | |
| 		case zeroCount == 2:
 | |
| 			// one MOVZ and one MOVK
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0 {
 | |
| 					os[0] = c.opirr(p, AMOVZ)
 | |
| 					os[0] |= MOVCONST(d, i, rt)
 | |
| 					i++
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			for ; i < 4; i++ {
 | |
| 				if immh[i] != 0 {
 | |
| 					os[1] = c.opirr(p, AMOVK)
 | |
| 					os[1] |= MOVCONST(d, i, rt)
 | |
| 				}
 | |
| 			}
 | |
| 			return 2
 | |
| 
 | |
| 		case negCount == 2:
 | |
| 			// one MOVN and one MOVK
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0xffff {
 | |
| 					os[0] = c.opirr(p, AMOVN)
 | |
| 					os[0] |= MOVCONST(dn, i, rt)
 | |
| 					i++
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			for ; i < 4; i++ {
 | |
| 				if immh[i] != 0xffff {
 | |
| 					os[1] = c.opirr(p, AMOVK)
 | |
| 					os[1] |= MOVCONST(d, i, rt)
 | |
| 				}
 | |
| 			}
 | |
| 			return 2
 | |
| 
 | |
| 		case zeroCount == 1:
 | |
| 			// one MOVZ and two MOVKs
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0 {
 | |
| 					os[0] = c.opirr(p, AMOVZ)
 | |
| 					os[0] |= MOVCONST(d, i, rt)
 | |
| 					i++
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			for j := 1; i < 4; i++ {
 | |
| 				if immh[i] != 0 {
 | |
| 					os[j] = c.opirr(p, AMOVK)
 | |
| 					os[j] |= MOVCONST(d, i, rt)
 | |
| 					j++
 | |
| 				}
 | |
| 			}
 | |
| 			return 3
 | |
| 
 | |
| 		case negCount == 1:
 | |
| 			// one MOVN and two MOVKs
 | |
| 			for i = 0; i < 4; i++ {
 | |
| 				if immh[i] != 0xffff {
 | |
| 					os[0] = c.opirr(p, AMOVN)
 | |
| 					os[0] |= MOVCONST(dn, i, rt)
 | |
| 					i++
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			for j := 1; i < 4; i++ {
 | |
| 				if immh[i] != 0xffff {
 | |
| 					os[j] = c.opirr(p, AMOVK)
 | |
| 					os[j] |= MOVCONST(d, i, rt)
 | |
| 					j++
 | |
| 				}
 | |
| 			}
 | |
| 			return 3
 | |
| 
 | |
| 		default:
 | |
| 			// one MOVZ and 3 MOVKs
 | |
| 			os[0] = c.opirr(p, AMOVZ)
 | |
| 			os[0] |= MOVCONST(d, 0, rt)
 | |
| 			for i = 1; i < 4; i++ {
 | |
| 				os[i] = c.opirr(p, AMOVK)
 | |
| 				os[i] |= MOVCONST(d, i, rt)
 | |
| 			}
 | |
| 			return 4
 | |
| 		}
 | |
| 	default:
 | |
| 		return 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opbfm(p *obj.Prog, a obj.As, r int, s int, rf int, rt int) uint32 {
 | |
| 	var b uint32
 | |
| 	o := c.opirr(p, a)
 | |
| 	if (o & (1 << 31)) == 0 {
 | |
| 		b = 32
 | |
| 	} else {
 | |
| 		b = 64
 | |
| 	}
 | |
| 	if r < 0 || uint32(r) >= b {
 | |
| 		c.ctxt.Diag("illegal bit number\n%v", p)
 | |
| 	}
 | |
| 	o |= (uint32(r) & 0x3F) << 16
 | |
| 	if s < 0 || uint32(s) >= b {
 | |
| 		c.ctxt.Diag("illegal bit number\n%v", p)
 | |
| 	}
 | |
| 	o |= (uint32(s) & 0x3F) << 10
 | |
| 	o |= (uint32(rf&31) << 5) | uint32(rt&31)
 | |
| 	return o
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) opextr(p *obj.Prog, a obj.As, v int32, rn int, rm int, rt int) uint32 {
 | |
| 	var b uint32
 | |
| 	o := c.opirr(p, a)
 | |
| 	if (o & (1 << 31)) != 0 {
 | |
| 		b = 63
 | |
| 	} else {
 | |
| 		b = 31
 | |
| 	}
 | |
| 	if v < 0 || uint32(v) > b {
 | |
| 		c.ctxt.Diag("illegal bit number\n%v", p)
 | |
| 	}
 | |
| 	o |= uint32(v) << 10
 | |
| 	o |= uint32(rn&31) << 5
 | |
| 	o |= uint32(rm&31) << 16
 | |
| 	o |= uint32(rt & 31)
 | |
| 	return o
 | |
| }
 | |
| 
 | |
| /* genrate instruction encoding for LDP/LDPW/LDPSW/STP/STPW */
 | |
| func (c *ctxt7) opldpstp(p *obj.Prog, o *Optab, vo int32, rbase, rl, rh, ldp uint32) uint32 {
 | |
| 	wback := false
 | |
| 	if o.scond == C_XPOST || o.scond == C_XPRE {
 | |
| 		wback = true
 | |
| 	}
 | |
| 	switch p.As {
 | |
| 	case ALDP, ALDPW, ALDPSW:
 | |
| 		c.checkUnpredictable(p, true, wback, p.From.Reg, p.To.Reg, int16(p.To.Offset))
 | |
| 	case ASTP, ASTPW:
 | |
| 		if wback == true {
 | |
| 			c.checkUnpredictable(p, false, true, p.To.Reg, p.From.Reg, int16(p.From.Offset))
 | |
| 		}
 | |
| 	case AFLDPD, AFLDPS:
 | |
| 		c.checkUnpredictable(p, true, false, p.From.Reg, p.To.Reg, int16(p.To.Offset))
 | |
| 	}
 | |
| 	var ret uint32
 | |
| 	// check offset
 | |
| 	switch p.As {
 | |
| 	case AFLDPD, AFSTPD:
 | |
| 		if vo < -512 || vo > 504 || vo%8 != 0 {
 | |
| 			c.ctxt.Diag("invalid offset %v\n", p)
 | |
| 		}
 | |
| 		vo /= 8
 | |
| 		ret = 1<<30 | 1<<26
 | |
| 	case ALDP, ASTP:
 | |
| 		if vo < -512 || vo > 504 || vo%8 != 0 {
 | |
| 			c.ctxt.Diag("invalid offset %v\n", p)
 | |
| 		}
 | |
| 		vo /= 8
 | |
| 		ret = 2 << 30
 | |
| 	case AFLDPS, AFSTPS:
 | |
| 		if vo < -256 || vo > 252 || vo%4 != 0 {
 | |
| 			c.ctxt.Diag("invalid offset %v\n", p)
 | |
| 		}
 | |
| 		vo /= 4
 | |
| 		ret = 1 << 26
 | |
| 	case ALDPW, ASTPW:
 | |
| 		if vo < -256 || vo > 252 || vo%4 != 0 {
 | |
| 			c.ctxt.Diag("invalid offset %v\n", p)
 | |
| 		}
 | |
| 		vo /= 4
 | |
| 		ret = 0
 | |
| 	case ALDPSW:
 | |
| 		if vo < -256 || vo > 252 || vo%4 != 0 {
 | |
| 			c.ctxt.Diag("invalid offset %v\n", p)
 | |
| 		}
 | |
| 		vo /= 4
 | |
| 		ret = 1 << 30
 | |
| 	default:
 | |
| 		c.ctxt.Diag("invalid instruction %v\n", p)
 | |
| 	}
 | |
| 	// check register pair
 | |
| 	switch p.As {
 | |
| 	case AFLDPD, AFLDPS, AFSTPD, AFSTPS:
 | |
| 		if rl < REG_F0 || REG_F31 < rl || rh < REG_F0 || REG_F31 < rh {
 | |
| 			c.ctxt.Diag("invalid register pair %v\n", p)
 | |
| 		}
 | |
| 	case ALDP, ALDPW, ALDPSW:
 | |
| 		if rl < REG_R0 || REG_R30 < rl || rh < REG_R0 || REG_R30 < rh {
 | |
| 			c.ctxt.Diag("invalid register pair %v\n", p)
 | |
| 		}
 | |
| 	case ASTP, ASTPW:
 | |
| 		if rl < REG_R0 || REG_R31 < rl || rh < REG_R0 || REG_R31 < rh {
 | |
| 			c.ctxt.Diag("invalid register pair %v\n", p)
 | |
| 		}
 | |
| 	}
 | |
| 	// other conditional flag bits
 | |
| 	switch o.scond {
 | |
| 	case C_XPOST:
 | |
| 		ret |= 1 << 23
 | |
| 	case C_XPRE:
 | |
| 		ret |= 3 << 23
 | |
| 	default:
 | |
| 		ret |= 2 << 23
 | |
| 	}
 | |
| 	ret |= 5<<27 | (ldp&1)<<22 | uint32(vo&0x7f)<<15 | (rh&31)<<10 | (rbase&31)<<5 | (rl & 31)
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func (c *ctxt7) maskOpvldvst(p *obj.Prog, o1 uint32) uint32 {
 | |
| 	if p.As == AVLD1 || p.As == AVST1 {
 | |
| 		return o1
 | |
| 	}
 | |
| 
 | |
| 	o1 &^= 0xf000 // mask out "opcode" field (bit 12-15)
 | |
| 	switch p.As {
 | |
| 	case AVLD1R, AVLD2R:
 | |
| 		o1 |= 0xC << 12
 | |
| 	case AVLD3R, AVLD4R:
 | |
| 		o1 |= 0xE << 12
 | |
| 	case AVLD2, AVST2:
 | |
| 		o1 |= 8 << 12
 | |
| 	case AVLD3, AVST3:
 | |
| 		o1 |= 4 << 12
 | |
| 	case AVLD4, AVST4:
 | |
| 	default:
 | |
| 		c.ctxt.Diag("unsupported instruction:%v\n", p.As)
 | |
| 	}
 | |
| 	return o1
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * size in log2(bytes)
 | |
|  */
 | |
| func movesize(a obj.As) int {
 | |
| 	switch a {
 | |
| 	case AMOVD:
 | |
| 		return 3
 | |
| 
 | |
| 	case AMOVW, AMOVWU:
 | |
| 		return 2
 | |
| 
 | |
| 	case AMOVH, AMOVHU:
 | |
| 		return 1
 | |
| 
 | |
| 	case AMOVB, AMOVBU:
 | |
| 		return 0
 | |
| 
 | |
| 	case AFMOVS:
 | |
| 		return 2
 | |
| 
 | |
| 	case AFMOVD:
 | |
| 		return 3
 | |
| 
 | |
| 	default:
 | |
| 		return -1
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // rm is the Rm register value, o is the extension, amount is the left shift value.
 | |
| func roff(rm int16, o uint32, amount int16) uint32 {
 | |
| 	return uint32(rm&31)<<16 | o<<13 | uint32(amount)<<10
 | |
| }
 | |
| 
 | |
| // encRegShiftOrExt returns the encoding of shifted/extended register, Rx<<n and Rx.UXTW<<n, etc.
 | |
| func (c *ctxt7) encRegShiftOrExt(a *obj.Addr, r int16) uint32 {
 | |
| 	var num, rm int16
 | |
| 	num = (r >> 5) & 7
 | |
| 	rm = r & 31
 | |
| 	switch {
 | |
| 	case REG_UXTB <= r && r < REG_UXTH:
 | |
| 		return roff(rm, 0, num)
 | |
| 	case REG_UXTH <= r && r < REG_UXTW:
 | |
| 		return roff(rm, 1, num)
 | |
| 	case REG_UXTW <= r && r < REG_UXTX:
 | |
| 		if a.Type == obj.TYPE_MEM {
 | |
| 			if num == 0 {
 | |
| 				return roff(rm, 2, 2)
 | |
| 			} else {
 | |
| 				return roff(rm, 2, 6)
 | |
| 			}
 | |
| 		} else {
 | |
| 			return roff(rm, 2, num)
 | |
| 		}
 | |
| 	case REG_UXTX <= r && r < REG_SXTB:
 | |
| 		return roff(rm, 3, num)
 | |
| 	case REG_SXTB <= r && r < REG_SXTH:
 | |
| 		return roff(rm, 4, num)
 | |
| 	case REG_SXTH <= r && r < REG_SXTW:
 | |
| 		return roff(rm, 5, num)
 | |
| 	case REG_SXTW <= r && r < REG_SXTX:
 | |
| 		if a.Type == obj.TYPE_MEM {
 | |
| 			if num == 0 {
 | |
| 				return roff(rm, 6, 2)
 | |
| 			} else {
 | |
| 				return roff(rm, 6, 6)
 | |
| 			}
 | |
| 		} else {
 | |
| 			return roff(rm, 6, num)
 | |
| 		}
 | |
| 	case REG_SXTX <= r && r < REG_SPECIAL:
 | |
| 		if a.Type == obj.TYPE_MEM {
 | |
| 			if num == 0 {
 | |
| 				return roff(rm, 7, 2)
 | |
| 			} else {
 | |
| 				return roff(rm, 7, 6)
 | |
| 			}
 | |
| 		} else {
 | |
| 			return roff(rm, 7, num)
 | |
| 		}
 | |
| 	case REG_LSL <= r && r < (REG_LSL+1<<8):
 | |
| 		return roff(rm, 3, 6)
 | |
| 	default:
 | |
| 		c.ctxt.Diag("unsupported register extension type.")
 | |
| 	}
 | |
| 
 | |
| 	return 0
 | |
| }
 |