diff options
Diffstat (limited to 'vendor/github.com/twitchyliquid64/golang-asm/obj/arm')
6 files changed, 4635 insertions, 0 deletions
diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/a.out.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/a.out.go new file mode 100644 index 000000000..8f8b8dbc4 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/a.out.go @@ -0,0 +1,410 @@ +// Inferno utils/5c/5.out.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/5.out.h +// +//	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 arm + +import "github.com/twitchyliquid64/golang-asm/obj" + +//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p arm + +const ( +	NSNAME = 8 +	NSYM   = 50 +	NREG   = 16 +) + +/* -1 disables use of REGARG */ +const ( +	REGARG = -1 +) + +const ( +	REG_R0 = obj.RBaseARM + iota // must be 16-aligned +	REG_R1 +	REG_R2 +	REG_R3 +	REG_R4 +	REG_R5 +	REG_R6 +	REG_R7 +	REG_R8 +	REG_R9 +	REG_R10 +	REG_R11 +	REG_R12 +	REG_R13 +	REG_R14 +	REG_R15 + +	REG_F0 // must be 16-aligned +	REG_F1 +	REG_F2 +	REG_F3 +	REG_F4 +	REG_F5 +	REG_F6 +	REG_F7 +	REG_F8 +	REG_F9 +	REG_F10 +	REG_F11 +	REG_F12 +	REG_F13 +	REG_F14 +	REG_F15 + +	REG_FPSR // must be 2-aligned +	REG_FPCR + +	REG_CPSR // must be 2-aligned +	REG_SPSR + +	REGRET = REG_R0 +	/* compiler allocates R1 up as temps */ +	/* compiler allocates register variables R3 up */ +	/* compiler allocates external registers R10 down */ +	REGEXT = REG_R10 +	/* these two registers are declared in runtime.h */ +	REGG = REGEXT - 0 +	REGM = REGEXT - 1 + +	REGCTXT = REG_R7 +	REGTMP  = REG_R11 +	REGSP   = REG_R13 +	REGLINK = REG_R14 +	REGPC   = REG_R15 + +	NFREG = 16 +	/* compiler allocates register variables F0 up */ +	/* compiler allocates external registers F7 down */ +	FREGRET = REG_F0 +	FREGEXT = REG_F7 +	FREGTMP = REG_F15 +) + +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040b/IHI0040B_aadwarf.pdf +var ARMDWARFRegisters = map[int16]int16{} + +func init() { +	// f assigns dwarfregisters[from:to] = (base):(step*(to-from)+base) +	f := func(from, to, base, step int16) { +		for r := int16(from); r <= to; r++ { +			ARMDWARFRegisters[r] = step*(r-from) + base +		} +	} +	f(REG_R0, REG_R15, 0, 1) +	f(REG_F0, REG_F15, 64, 2) // Use d0 through D15, aka S0, S2, ..., S30 +} + +// Special registers, after subtracting obj.RBaseARM, bit 9 indicates +// a special register and the low bits select the register. +const ( +	REG_SPECIAL = obj.RBaseARM + 1<<9 + iota +	REG_MB_SY +	REG_MB_ST +	REG_MB_ISH +	REG_MB_ISHST +	REG_MB_NSH +	REG_MB_NSHST +	REG_MB_OSH +	REG_MB_OSHST + +	MAXREG +) + +const ( +	C_NONE = iota +	C_REG +	C_REGREG +	C_REGREG2 +	C_REGLIST +	C_SHIFT     /* register shift R>>x */ +	C_SHIFTADDR /* memory address with shifted offset R>>x(R) */ +	C_FREG +	C_PSR +	C_FCR +	C_SPR /* REG_MB_SY */ + +	C_RCON   /* 0xff rotated */ +	C_NCON   /* ~RCON */ +	C_RCON2A /* OR of two disjoint C_RCON constants */ +	C_RCON2S /* subtraction of two disjoint C_RCON constants */ +	C_SCON   /* 0xffff */ +	C_LCON +	C_LCONADDR +	C_ZFCON +	C_SFCON +	C_LFCON + +	C_RACON +	C_LACON + +	C_SBRA +	C_LBRA + +	C_HAUTO  /* halfword insn offset (-0xff to 0xff) */ +	C_FAUTO  /* float insn offset (0 to 0x3fc, word aligned) */ +	C_HFAUTO /* both H and F */ +	C_SAUTO  /* -0xfff to 0xfff */ +	C_LAUTO + +	C_HOREG +	C_FOREG +	C_HFOREG +	C_SOREG +	C_ROREG +	C_SROREG /* both nil and R */ +	C_LOREG + +	C_PC +	C_SP +	C_HREG + +	C_ADDR /* reference to relocatable address */ + +	// TLS "var" in local exec mode: will become a constant offset from +	// thread local base that is ultimately chosen by the program linker. +	C_TLS_LE + +	// TLS "var" in initial exec mode: will become a memory address (chosen +	// by the program linker) that the dynamic linker will fill with the +	// offset from the thread local base. +	C_TLS_IE + +	C_TEXTSIZE + +	C_GOK + +	C_NCLASS /* must be the last */ +) + +const ( +	AAND = obj.ABaseARM + obj.A_ARCHSPECIFIC + iota +	AEOR +	ASUB +	ARSB +	AADD +	AADC +	ASBC +	ARSC +	ATST +	ATEQ +	ACMP +	ACMN +	AORR +	ABIC + +	AMVN + +	/* +	 * Do not reorder or fragment the conditional branch +	 * opcodes, or the predication code will break +	 */ +	ABEQ +	ABNE +	ABCS +	ABHS +	ABCC +	ABLO +	ABMI +	ABPL +	ABVS +	ABVC +	ABHI +	ABLS +	ABGE +	ABLT +	ABGT +	ABLE + +	AMOVWD +	AMOVWF +	AMOVDW +	AMOVFW +	AMOVFD +	AMOVDF +	AMOVF +	AMOVD + +	ACMPF +	ACMPD +	AADDF +	AADDD +	ASUBF +	ASUBD +	AMULF +	AMULD +	ANMULF +	ANMULD +	AMULAF +	AMULAD +	ANMULAF +	ANMULAD +	AMULSF +	AMULSD +	ANMULSF +	ANMULSD +	AFMULAF +	AFMULAD +	AFNMULAF +	AFNMULAD +	AFMULSF +	AFMULSD +	AFNMULSF +	AFNMULSD +	ADIVF +	ADIVD +	ASQRTF +	ASQRTD +	AABSF +	AABSD +	ANEGF +	ANEGD + +	ASRL +	ASRA +	ASLL +	AMULU +	ADIVU +	AMUL +	AMMUL +	ADIV +	AMOD +	AMODU +	ADIVHW +	ADIVUHW + +	AMOVB +	AMOVBS +	AMOVBU +	AMOVH +	AMOVHS +	AMOVHU +	AMOVW +	AMOVM +	ASWPBU +	ASWPW + +	ARFE +	ASWI +	AMULA +	AMULS +	AMMULA +	AMMULS + +	AWORD + +	AMULL +	AMULAL +	AMULLU +	AMULALU + +	ABX +	ABXRET +	ADWORD + +	ALDREX +	ASTREX +	ALDREXD +	ASTREXD + +	ADMB + +	APLD + +	ACLZ +	AREV +	AREV16 +	AREVSH +	ARBIT + +	AXTAB +	AXTAH +	AXTABU +	AXTAHU + +	ABFX +	ABFXU +	ABFC +	ABFI + +	AMULWT +	AMULWB +	AMULBB +	AMULAWT +	AMULAWB +	AMULABB + +	AMRC // MRC/MCR + +	ALAST + +	// aliases +	AB  = obj.AJMP +	ABL = obj.ACALL +) + +/* scond byte */ +const ( +	C_SCOND = (1 << 4) - 1 +	C_SBIT  = 1 << 4 +	C_PBIT  = 1 << 5 +	C_WBIT  = 1 << 6 +	C_FBIT  = 1 << 7 /* psr flags-only */ +	C_UBIT  = 1 << 7 /* up bit, unsigned bit */ + +	// These constants are the ARM condition codes encodings, +	// XORed with 14 so that C_SCOND_NONE has value 0, +	// so that a zeroed Prog.scond means "always execute". +	C_SCOND_XOR = 14 + +	C_SCOND_EQ   = 0 ^ C_SCOND_XOR +	C_SCOND_NE   = 1 ^ C_SCOND_XOR +	C_SCOND_HS   = 2 ^ C_SCOND_XOR +	C_SCOND_LO   = 3 ^ C_SCOND_XOR +	C_SCOND_MI   = 4 ^ C_SCOND_XOR +	C_SCOND_PL   = 5 ^ C_SCOND_XOR +	C_SCOND_VS   = 6 ^ C_SCOND_XOR +	C_SCOND_VC   = 7 ^ C_SCOND_XOR +	C_SCOND_HI   = 8 ^ C_SCOND_XOR +	C_SCOND_LS   = 9 ^ C_SCOND_XOR +	C_SCOND_GE   = 10 ^ C_SCOND_XOR +	C_SCOND_LT   = 11 ^ C_SCOND_XOR +	C_SCOND_GT   = 12 ^ C_SCOND_XOR +	C_SCOND_LE   = 13 ^ C_SCOND_XOR +	C_SCOND_NONE = 14 ^ C_SCOND_XOR +	C_SCOND_NV   = 15 ^ C_SCOND_XOR + +	/* D_SHIFT type */ +	SHIFT_LL = 0 << 5 +	SHIFT_LR = 1 << 5 +	SHIFT_AR = 2 << 5 +	SHIFT_RR = 3 << 5 +) diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames.go new file mode 100644 index 000000000..8a05133e9 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames.go @@ -0,0 +1,144 @@ +// Code generated by stringer -i a.out.go -o anames.go -p arm; DO NOT EDIT. + +package arm + +import "github.com/twitchyliquid64/golang-asm/obj" + +var Anames = []string{ +	obj.A_ARCHSPECIFIC: "AND", +	"EOR", +	"SUB", +	"RSB", +	"ADD", +	"ADC", +	"SBC", +	"RSC", +	"TST", +	"TEQ", +	"CMP", +	"CMN", +	"ORR", +	"BIC", +	"MVN", +	"BEQ", +	"BNE", +	"BCS", +	"BHS", +	"BCC", +	"BLO", +	"BMI", +	"BPL", +	"BVS", +	"BVC", +	"BHI", +	"BLS", +	"BGE", +	"BLT", +	"BGT", +	"BLE", +	"MOVWD", +	"MOVWF", +	"MOVDW", +	"MOVFW", +	"MOVFD", +	"MOVDF", +	"MOVF", +	"MOVD", +	"CMPF", +	"CMPD", +	"ADDF", +	"ADDD", +	"SUBF", +	"SUBD", +	"MULF", +	"MULD", +	"NMULF", +	"NMULD", +	"MULAF", +	"MULAD", +	"NMULAF", +	"NMULAD", +	"MULSF", +	"MULSD", +	"NMULSF", +	"NMULSD", +	"FMULAF", +	"FMULAD", +	"FNMULAF", +	"FNMULAD", +	"FMULSF", +	"FMULSD", +	"FNMULSF", +	"FNMULSD", +	"DIVF", +	"DIVD", +	"SQRTF", +	"SQRTD", +	"ABSF", +	"ABSD", +	"NEGF", +	"NEGD", +	"SRL", +	"SRA", +	"SLL", +	"MULU", +	"DIVU", +	"MUL", +	"MMUL", +	"DIV", +	"MOD", +	"MODU", +	"DIVHW", +	"DIVUHW", +	"MOVB", +	"MOVBS", +	"MOVBU", +	"MOVH", +	"MOVHS", +	"MOVHU", +	"MOVW", +	"MOVM", +	"SWPBU", +	"SWPW", +	"RFE", +	"SWI", +	"MULA", +	"MULS", +	"MMULA", +	"MMULS", +	"WORD", +	"MULL", +	"MULAL", +	"MULLU", +	"MULALU", +	"BX", +	"BXRET", +	"DWORD", +	"LDREX", +	"STREX", +	"LDREXD", +	"STREXD", +	"DMB", +	"PLD", +	"CLZ", +	"REV", +	"REV16", +	"REVSH", +	"RBIT", +	"XTAB", +	"XTAH", +	"XTABU", +	"XTAHU", +	"BFX", +	"BFXU", +	"BFC", +	"BFI", +	"MULWT", +	"MULWB", +	"MULBB", +	"MULAWT", +	"MULAWB", +	"MULABB", +	"MRC", +	"LAST", +} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames5.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames5.go new file mode 100644 index 000000000..78fcd55f7 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/anames5.go @@ -0,0 +1,77 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package arm + +var cnames5 = []string{ +	"NONE", +	"REG", +	"REGREG", +	"REGREG2", +	"REGLIST", +	"SHIFT", +	"SHIFTADDR", +	"FREG", +	"PSR", +	"FCR", +	"SPR", +	"RCON", +	"NCON", +	"RCON2A", +	"RCON2S", +	"SCON", +	"LCON", +	"LCONADDR", +	"ZFCON", +	"SFCON", +	"LFCON", +	"RACON", +	"LACON", +	"SBRA", +	"LBRA", +	"HAUTO", +	"FAUTO", +	"HFAUTO", +	"SAUTO", +	"LAUTO", +	"HOREG", +	"FOREG", +	"HFOREG", +	"SOREG", +	"ROREG", +	"SROREG", +	"LOREG", +	"PC", +	"SP", +	"HREG", +	"ADDR", +	"C_TLS_LE", +	"C_TLS_IE", +	"TEXTSIZE", +	"GOK", +	"NCLASS", +	"SCOND = (1<<4)-1", +	"SBIT = 1<<4", +	"PBIT = 1<<5", +	"WBIT = 1<<6", +	"FBIT = 1<<7", +	"UBIT = 1<<7", +	"SCOND_XOR = 14", +	"SCOND_EQ = 0 ^ C_SCOND_XOR", +	"SCOND_NE = 1 ^ C_SCOND_XOR", +	"SCOND_HS = 2 ^ C_SCOND_XOR", +	"SCOND_LO = 3 ^ C_SCOND_XOR", +	"SCOND_MI = 4 ^ C_SCOND_XOR", +	"SCOND_PL = 5 ^ C_SCOND_XOR", +	"SCOND_VS = 6 ^ C_SCOND_XOR", +	"SCOND_VC = 7 ^ C_SCOND_XOR", +	"SCOND_HI = 8 ^ C_SCOND_XOR", +	"SCOND_LS = 9 ^ C_SCOND_XOR", +	"SCOND_GE = 10 ^ C_SCOND_XOR", +	"SCOND_LT = 11 ^ C_SCOND_XOR", +	"SCOND_GT = 12 ^ C_SCOND_XOR", +	"SCOND_LE = 13 ^ C_SCOND_XOR", +	"SCOND_NONE = 14 ^ C_SCOND_XOR", +	"SCOND_NV = 15 ^ C_SCOND_XOR", +} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/asm5.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/asm5.go new file mode 100644 index 000000000..924657fb3 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/asm5.go @@ -0,0 +1,3096 @@ +// Inferno utils/5l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/span.c +// +//	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 arm + +import ( +	"github.com/twitchyliquid64/golang-asm/obj" +	"github.com/twitchyliquid64/golang-asm/objabi" +	"fmt" +	"log" +	"math" +	"sort" +) + +// ctxt5 holds state while assembling a single function. +// Each function gets a fresh ctxt5. +// This allows for multiple functions to be safely concurrently assembled. +type ctxt5 struct { +	ctxt       *obj.Link +	newprog    obj.ProgAlloc +	cursym     *obj.LSym +	printp     *obj.Prog +	blitrl     *obj.Prog +	elitrl     *obj.Prog +	autosize   int64 +	instoffset int64 +	pc         int64 +	pool       struct { +		start uint32 +		size  uint32 +		extra uint32 +	} +} + +type Optab struct { +	as       obj.As +	a1       uint8 +	a2       int8 +	a3       uint8 +	type_    uint8 +	size     int8 +	param    int16 +	flag     int8 +	pcrelsiz uint8 +	scond    uint8 // optional flags accepted by the instruction +} + +type Opcross [32][2][32]uint8 + +const ( +	LFROM  = 1 << 0 +	LTO    = 1 << 1 +	LPOOL  = 1 << 2 +	LPCREL = 1 << 3 +) + +var optab = []Optab{ +	/* struct Optab: +	OPCODE, from, prog->reg, to, type, size, param, flag, extra data size, optional suffix */ +	{obj.ATEXT, C_ADDR, C_NONE, C_TEXTSIZE, 0, 0, 0, 0, 0, 0}, +	{AADD, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AADD, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{AMVN, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, C_SBIT}, +	{ACMP, C_REG, C_REG, C_NONE, 1, 4, 0, 0, 0, 0}, +	{AADD, C_RCON, C_REG, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AADD, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_RCON, C_REG, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_RCON, C_REG, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0, C_SBIT}, +	{AMOVW, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0, 0}, +	{AMVN, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0, 0}, +	{ACMP, C_RCON, C_REG, C_NONE, 2, 4, 0, 0, 0, 0}, +	{AADD, C_SHIFT, C_REG, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AADD, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_SHIFT, C_REG, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AAND, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_SHIFT, C_REG, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AORR, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{AMVN, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0, C_SBIT}, +	{ACMP, C_SHIFT, C_REG, C_NONE, 3, 4, 0, 0, 0, 0}, +	{AMOVW, C_RACON, C_NONE, C_REG, 4, 4, REGSP, 0, 0, C_SBIT}, +	{AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL, 0, 0}, +	{ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, +	{ABX, C_NONE, C_NONE, C_SBRA, 74, 20, 0, 0, 0, 0}, +	{ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, +	{ABEQ, C_RCON, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, // prediction hinted form, hint ignored +	{AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL, 0, 0}, +	{ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0, 0, 0, 0}, +	{ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0, 0, 0, 0}, +	{ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0, 0, 0, 0}, +	{ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0, 0, 0, 0}, +	{ASLL, C_RCON, C_REG, C_REG, 8, 4, 0, 0, 0, C_SBIT}, +	{ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0, 0, 0, C_SBIT}, +	{ASLL, C_REG, C_NONE, C_REG, 9, 4, 0, 0, 0, C_SBIT}, +	{ASLL, C_REG, C_REG, C_REG, 9, 4, 0, 0, 0, C_SBIT}, +	{ASWI, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0, 0}, +	{ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0, 0, 0, 0}, +	{AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0, 0, 0, 0}, +	{AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0, 0, 0, 0}, +	{AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0, 0, 0, 0}, +	{AWORD, C_NONE, C_NONE, C_TLS_LE, 103, 4, 0, 0, 0, 0}, +	{AWORD, C_NONE, C_NONE, C_TLS_IE, 104, 4, 0, 0, 0, 0}, +	{AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0, 0, 0, 0}, +	{AMOVW, C_SCON, C_NONE, C_REG, 12, 4, 0, 0, 0, 0}, +	{AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM, 0, 0}, +	{AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4, 0}, +	{AMVN, C_NCON, C_NONE, C_REG, 12, 4, 0, 0, 0, 0}, +	{AADD, C_NCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AADD, C_NCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AAND, C_NCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AAND, C_NCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AORR, C_NCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AORR, C_NCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{ACMP, C_NCON, C_REG, C_NONE, 13, 8, 0, 0, 0, 0}, +	{AADD, C_SCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AADD, C_SCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AAND, C_SCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AAND, C_SCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AORR, C_SCON, C_REG, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AORR, C_SCON, C_NONE, C_REG, 13, 8, 0, 0, 0, C_SBIT}, +	{AMVN, C_SCON, C_NONE, C_REG, 13, 8, 0, 0, 0, 0}, +	{ACMP, C_SCON, C_REG, C_NONE, 13, 8, 0, 0, 0, 0}, +	{AADD, C_RCON2A, C_REG, C_REG, 106, 8, 0, 0, 0, 0}, +	{AADD, C_RCON2A, C_NONE, C_REG, 106, 8, 0, 0, 0, 0}, +	{AORR, C_RCON2A, C_REG, C_REG, 106, 8, 0, 0, 0, 0}, +	{AORR, C_RCON2A, C_NONE, C_REG, 106, 8, 0, 0, 0, 0}, +	{AADD, C_RCON2S, C_REG, C_REG, 107, 8, 0, 0, 0, 0}, +	{AADD, C_RCON2S, C_NONE, C_REG, 107, 8, 0, 0, 0, 0}, +	{AADD, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AADD, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AAND, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AAND, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AORR, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AORR, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0, C_SBIT}, +	{AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0, 0}, +	{ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM, 0, 0}, +	{AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, 0}, +	{AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0, 0}, +	{AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0, 0, 0, 0}, +	{AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0, 0}, +	{AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0, 0}, +	{AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0, 0}, +	{AMUL, C_REG, C_REG, C_REG, 15, 4, 0, 0, 0, C_SBIT}, +	{AMUL, C_REG, C_NONE, C_REG, 15, 4, 0, 0, 0, C_SBIT}, +	{ADIV, C_REG, C_REG, C_REG, 16, 4, 0, 0, 0, 0}, +	{ADIV, C_REG, C_NONE, C_REG, 16, 4, 0, 0, 0, 0}, +	{ADIVHW, C_REG, C_REG, C_REG, 105, 4, 0, 0, 0, 0}, +	{ADIVHW, C_REG, C_NONE, C_REG, 105, 4, 0, 0, 0, 0}, +	{AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0, 0, 0, C_SBIT}, +	{ABFX, C_LCON, C_REG, C_REG, 18, 4, 0, 0, 0, 0},  // width in From, LSB in From3 +	{ABFX, C_LCON, C_NONE, C_REG, 18, 4, 0, 0, 0, 0}, // width in From, LSB in From3 +	{AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_SAUTO, C_NONE, C_REG, 21, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_SOREG, C_NONE, C_REG, 21, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_SAUTO, C_NONE, C_REG, 21, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_SOREG, C_NONE, C_REG, 21, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AXTAB, C_SHIFT, C_REG, C_REG, 22, 4, 0, 0, 0, 0}, +	{AXTAB, C_SHIFT, C_NONE, C_REG, 22, 4, 0, 0, 0, 0}, +	{AMOVW, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, C_SBIT}, +	{AMOVB, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVBS, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVBU, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVH, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVHS, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVHU, C_SHIFT, C_NONE, C_REG, 23, 4, 0, 0, 0, 0}, +	{AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_TLS_LE, C_NONE, C_REG, 101, 4, 0, LFROM, 0, 0}, +	{AMOVW, C_TLS_IE, C_NONE, C_REG, 102, 8, 0, LFROM, 0, 0}, +	{AMOVW, C_LAUTO, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_LOREG, C_NONE, C_REG, 31, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_LAUTO, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_LOREG, C_NONE, C_REG, 31, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_LACON, C_NONE, C_REG, 34, 8, REGSP, LFROM, 0, C_SBIT}, +	{AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0, 0, 0, 0}, +	{AMOVW, C_REG, C_NONE, C_PSR, 36, 4, 0, 0, 0, 0}, +	{AMOVW, C_RCON, C_NONE, C_PSR, 37, 4, 0, 0, 0, 0}, +	{AMOVM, C_REGLIST, C_NONE, C_SOREG, 38, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVM, C_SOREG, C_NONE, C_REGLIST, 39, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{ASWPW, C_SOREG, C_REG, C_REG, 40, 4, 0, 0, 0, 0}, +	{ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0, 0}, +	{AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FAUTO, C_NONE, C_FREG, 51, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FOREG, C_NONE, C_FREG, 51, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_LAUTO, C_NONE, C_FREG, 53, 12, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_LOREG, C_NONE, C_FREG, 53, 12, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0, 0}, +	{AADDF, C_FREG, C_FREG, C_FREG, 54, 4, 0, 0, 0, 0}, +	{AMOVF, C_FREG, C_NONE, C_FREG, 55, 4, 0, 0, 0, 0}, +	{ANEGF, C_FREG, C_NONE, C_FREG, 55, 4, 0, 0, 0, 0}, +	{AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0, 0, 0, 0}, +	{AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0, 0, 0, 0}, +	{AMOVW, C_SHIFTADDR, C_NONE, C_REG, 59, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_SHIFTADDR, C_NONE, C_REG, 59, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_SHIFTADDR, C_NONE, C_REG, 60, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_SHIFTADDR, C_NONE, C_REG, 60, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_SHIFTADDR, C_NONE, C_REG, 60, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_SHIFTADDR, C_NONE, C_REG, 60, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_SHIFTADDR, C_NONE, C_REG, 60, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVW, C_REG, C_NONE, C_SHIFTADDR, 61, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_REG, C_NONE, C_SHIFTADDR, 61, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_REG, C_NONE, C_SHIFTADDR, 61, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBU, C_REG, C_NONE, C_SHIFTADDR, 61, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_SHIFTADDR, 62, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_SHIFTADDR, 62, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_SHIFTADDR, 62, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0, C_PBIT | C_WBIT | C_UBIT}, +	{AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4, C_PBIT | C_WBIT | C_UBIT}, +	{ALDREX, C_SOREG, C_NONE, C_REG, 77, 4, 0, 0, 0, 0}, +	{ASTREX, C_SOREG, C_REG, C_REG, 78, 4, 0, 0, 0, 0}, +	{ADMB, C_NONE, C_NONE, C_NONE, 110, 4, 0, 0, 0, 0}, +	{ADMB, C_LCON, C_NONE, C_NONE, 110, 4, 0, 0, 0, 0}, +	{ADMB, C_SPR, C_NONE, C_NONE, 110, 4, 0, 0, 0, 0}, +	{AMOVF, C_ZFCON, C_NONE, C_FREG, 80, 8, 0, 0, 0, 0}, +	{AMOVF, C_SFCON, C_NONE, C_FREG, 81, 4, 0, 0, 0, 0}, +	{ACMPF, C_FREG, C_FREG, C_NONE, 82, 8, 0, 0, 0, 0}, +	{ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0, 0, 0, 0}, +	{AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0, 0, 0, C_UBIT}, +	{AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0, 0, 0, C_UBIT}, +	{AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0, 0, 0, C_UBIT}, +	{AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0, 0, 0, C_UBIT}, +	{AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0, 0, 0, 0}, +	{AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0, 0, 0, 0}, +	{ALDREXD, C_SOREG, C_NONE, C_REG, 91, 4, 0, 0, 0, 0}, +	{ASTREXD, C_SOREG, C_REG, C_REG, 92, 4, 0, 0, 0, 0}, +	{APLD, C_SOREG, C_NONE, C_NONE, 95, 4, 0, 0, 0, 0}, +	{obj.AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0, 0, 0, 0}, +	{ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0, 0, 0, 0}, +	{AMULWT, C_REG, C_REG, C_REG, 98, 4, 0, 0, 0, 0}, +	{AMULA, C_REG, C_REG, C_REGREG2, 99, 4, 0, 0, 0, C_SBIT}, +	{AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0, 0, 0, 0}, +	{obj.APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0, 0, 0, 0}, +	{obj.AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0, 0, 0, 0}, +	{obj.ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0}, +	{obj.ANOP, C_LCON, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0}, // nop variants, see #40689 +	{obj.ANOP, C_REG, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0}, +	{obj.ANOP, C_FREG, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0}, +	{obj.ADUFFZERO, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, // same as ABL +	{obj.ADUFFCOPY, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, // same as ABL +	{obj.AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0, 0}, +} + +var mbOp = []struct { +	reg int16 +	enc uint32 +}{ +	{REG_MB_SY, 15}, +	{REG_MB_ST, 14}, +	{REG_MB_ISH, 11}, +	{REG_MB_ISHST, 10}, +	{REG_MB_NSH, 7}, +	{REG_MB_NSHST, 6}, +	{REG_MB_OSH, 3}, +	{REG_MB_OSHST, 2}, +} + +var oprange [ALAST & obj.AMask][]Optab + +var xcmp [C_GOK + 1][C_GOK + 1]bool + +var ( +	deferreturn *obj.LSym +	symdiv      *obj.LSym +	symdivu     *obj.LSym +	symmod      *obj.LSym +	symmodu     *obj.LSym +) + +// Note about encoding: Prog.scond holds the condition encoding, +// but XOR'ed with C_SCOND_XOR, so that C_SCOND_NONE == 0. +// The code that shifts the value << 28 has the responsibility +// for XORing with C_SCOND_XOR too. + +func checkSuffix(c *ctxt5, p *obj.Prog, o *Optab) { +	if p.Scond&C_SBIT != 0 && o.scond&C_SBIT == 0 { +		c.ctxt.Diag("invalid .S suffix: %v", p) +	} +	if p.Scond&C_PBIT != 0 && o.scond&C_PBIT == 0 { +		c.ctxt.Diag("invalid .P suffix: %v", p) +	} +	if p.Scond&C_WBIT != 0 && o.scond&C_WBIT == 0 { +		c.ctxt.Diag("invalid .W suffix: %v", p) +	} +	if p.Scond&C_UBIT != 0 && o.scond&C_UBIT == 0 { +		c.ctxt.Diag("invalid .U suffix: %v", p) +	} +} + +func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { +	if ctxt.Retpoline { +		ctxt.Diag("-spectre=ret not supported on arm") +		ctxt.Retpoline = false // don't keep printing +	} + +	var p *obj.Prog +	var op *obj.Prog + +	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("arm ops not initialized, call arm.buildop first") +	} + +	c := ctxt5{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: p.To.Offset + 4} +	pc := int32(0) + +	op = p +	p = p.Link +	var m int +	var o *Optab +	for ; p != nil || c.blitrl != nil; op, p = p, p.Link { +		if p == nil { +			if c.checkpool(op, pc) { +				p = op +				continue +			} + +			// can't happen: blitrl is not nil, but checkpool didn't flushpool +			ctxt.Diag("internal inconsistency") + +			break +		} + +		p.Pc = int64(pc) +		o = c.oplook(p) +		m = int(o.size) + +		if m%4 != 0 || p.Pc%4 != 0 { +			ctxt.Diag("!pc invalid: %v size=%d", p, m) +		} + +		// must check literal pool here in case p generates many instructions +		if c.blitrl != nil { +			// Emit the constant pool just before p if p +			// would push us over the immediate size limit. +			if c.checkpool(op, pc+int32(m)) { +				// Back up to the instruction just +				// before the pool and continue with +				// the first instruction of the pool. +				p = op +				continue +			} +		} + +		if m == 0 && (p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.ANOP) { +			ctxt.Diag("zero-width instruction\n%v", p) +			continue +		} + +		switch o.flag & (LFROM | LTO | LPOOL) { +		case LFROM: +			c.addpool(p, &p.From) + +		case LTO: +			c.addpool(p, &p.To) + +		case LPOOL: +			if p.Scond&C_SCOND == C_SCOND_NONE { +				c.flushpool(p, 0, 0) +			} +		} + +		if p.As == AMOVW && p.To.Type == obj.TYPE_REG && p.To.Reg == REGPC && p.Scond&C_SCOND == C_SCOND_NONE { +			c.flushpool(p, 0, 0) +		} + +		pc += int32(m) +	} + +	c.cursym.Size = int64(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. +	 */ +	times := 0 + +	var bflag int +	var opc int32 +	var out [6 + 3]uint32 +	for { +		bflag = 0 +		pc = 0 +		times++ +		c.cursym.Func.Text.Pc = 0 // force re-layout the code. +		for p = c.cursym.Func.Text; p != nil; p = p.Link { +			o = c.oplook(p) +			if int64(pc) > p.Pc { +				p.Pc = int64(pc) +			} + +			/* very large branches +			if(o->type == 6 && p->pcond) { +				otxt = p->pcond->pc - c; +				if(otxt < 0) +					otxt = -otxt; +				if(otxt >= (1L<<17) - 10) { +					q = emallocz(sizeof(Prog)); +					q->link = p->link; +					p->link = q; +					q->as = AB; +					q->to.type = TYPE_BRANCH; +					q->pcond = p->pcond; +					p->pcond = q; +					q = emallocz(sizeof(Prog)); +					q->link = p->link; +					p->link = q; +					q->as = AB; +					q->to.type = TYPE_BRANCH; +					q->pcond = q->link->link; +					bflag = 1; +				} +			} +			*/ +			opc = int32(p.Pc) +			m = int(o.size) +			if p.Pc != int64(opc) { +				bflag = 1 +			} + +			//print("%v pc changed %d to %d in iter. %d\n", p, opc, (int32)p->pc, times); +			pc = int32(p.Pc + int64(m)) + +			if m%4 != 0 || p.Pc%4 != 0 { +				ctxt.Diag("pc invalid: %v size=%d", p, m) +			} + +			if m/4 > len(out) { +				ctxt.Diag("instruction size too large: %d > %d", m/4, len(out)) +			} +			if m == 0 && (p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.ANOP) { +				if p.As == obj.ATEXT { +					c.autosize = p.To.Offset + 4 +					continue +				} + +				ctxt.Diag("zero-width instruction\n%v", p) +				continue +			} +		} + +		c.cursym.Size = int64(pc) +		if bflag == 0 { +			break +		} +	} + +	if pc%4 != 0 { +		ctxt.Diag("sym->size=%d, invalid", pc) +	} + +	/* +	 * lay out the code.  all the pc-relative code references, +	 * even cross-function, are resolved now; +	 * only data references need to be relocated. +	 * with more work we could leave cross-function +	 * code references to be relocated too, and then +	 * perhaps we'd be able to parallelize the span loop above. +	 */ + +	p = c.cursym.Func.Text +	c.autosize = p.To.Offset + 4 +	c.cursym.Grow(c.cursym.Size) + +	bp := c.cursym.P +	pc = int32(p.Pc) // even p->link might need extra padding +	var v int +	for p = p.Link; p != nil; p = p.Link { +		c.pc = p.Pc +		o = c.oplook(p) +		opc = int32(p.Pc) +		c.asmout(p, o, out[:]) +		m = int(o.size) + +		if m%4 != 0 || p.Pc%4 != 0 { +			ctxt.Diag("final stage: pc invalid: %v size=%d", p, m) +		} + +		if int64(pc) > p.Pc { +			ctxt.Diag("PC padding invalid: want %#d, has %#d: %v", p.Pc, pc, p) +		} +		for int64(pc) != p.Pc { +			// emit 0xe1a00000 (MOVW R0, R0) +			bp[0] = 0x00 +			bp = bp[1:] + +			bp[0] = 0x00 +			bp = bp[1:] +			bp[0] = 0xa0 +			bp = bp[1:] +			bp[0] = 0xe1 +			bp = bp[1:] +			pc += 4 +		} + +		for i := 0; i < m/4; i++ { +			v = int(out[i]) +			bp[0] = byte(v) +			bp = bp[1:] +			bp[0] = byte(v >> 8) +			bp = bp[1:] +			bp[0] = byte(v >> 16) +			bp = bp[1:] +			bp[0] = byte(v >> 24) +			bp = bp[1:] +		} + +		pc += int32(m) +	} +} + +// checkpool flushes the literal pool when the first reference to +// it threatens to go out of range of a 12-bit PC-relative offset. +// +// nextpc is the tentative next PC at which the pool could be emitted. +// checkpool should be called *before* emitting the instruction that +// would cause the PC to reach nextpc. +// If nextpc is too far from the first pool reference, checkpool will +// flush the pool immediately after p. +// The caller should resume processing a p.Link. +func (c *ctxt5) checkpool(p *obj.Prog, nextpc int32) bool { +	poolLast := nextpc +	poolLast += 4                      // the AB instruction to jump around the pool +	poolLast += int32(c.pool.size) - 4 // the offset of the last pool entry + +	refPC := int32(c.pool.start) // PC of the first pool reference + +	v := poolLast - refPC - 8 // 12-bit PC-relative offset (see omvl) + +	if c.pool.size >= 0xff0 || immaddr(v) == 0 { +		return c.flushpool(p, 1, 0) +	} else if p.Link == nil { +		return c.flushpool(p, 2, 0) +	} +	return false +} + +func (c *ctxt5) flushpool(p *obj.Prog, skip int, force int) bool { +	if c.blitrl != nil { +		if skip != 0 { +			if false && 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 force == 0 && (p.Pc+int64(c.pool.size)-int64(c.pool.start) < 2048) { +			return false +		} + +		// 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 +		c.pool.extra = 0 +		return true +	} + +	return false +} + +func (c *ctxt5) addpool(p *obj.Prog, a *obj.Addr) { +	t := c.newprog() +	t.As = AWORD + +	switch c.aclass(a) { +	default: +		t.To.Offset = a.Offset +		t.To.Sym = a.Sym +		t.To.Type = a.Type +		t.To.Name = a.Name + +		if c.ctxt.Flag_shared && t.To.Sym != nil { +			t.Rel = p +		} + +	case C_SROREG, +		C_LOREG, +		C_ROREG, +		C_FOREG, +		C_SOREG, +		C_HOREG, +		C_FAUTO, +		C_SAUTO, +		C_LAUTO, +		C_LACON: +		t.To.Type = obj.TYPE_CONST +		t.To.Offset = c.instoffset +	} + +	if t.Rel == nil { +		for q := c.blitrl; q != nil; q = q.Link { /* could hash on t.t0.offset */ +			if q.Rel == nil && 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 += 4 + +	// Store the link to the pool entry in Pool. +	p.Pool = q +} + +func (c *ctxt5) regoff(a *obj.Addr) int32 { +	c.instoffset = 0 +	c.aclass(a) +	return int32(c.instoffset) +} + +func immrot(v uint32) int32 { +	for i := 0; i < 16; i++ { +		if v&^0xff == 0 { +			return int32(uint32(int32(i)<<8) | v | 1<<25) +		} +		v = v<<2 | v>>30 +	} + +	return 0 +} + +// immrot2a returns bits encoding the immediate constant fields of two instructions, +// such that the encoded constants x, y satisfy x|y==v, x&y==0. +// Returns 0,0 if no such decomposition of v exists. +func immrot2a(v uint32) (uint32, uint32) { +	for i := uint(1); i < 32; i++ { +		m := uint32(1<<i - 1) +		if x, y := immrot(v&m), immrot(v&^m); x != 0 && y != 0 { +			return uint32(x), uint32(y) +		} +	} +	// TODO: handle some more cases, like where +	// the wraparound from the rotate could help. +	return 0, 0 +} + +// immrot2s returns bits encoding the immediate constant fields of two instructions, +// such that the encoded constants y, x satisfy y-x==v, y&x==0. +// Returns 0,0 if no such decomposition of v exists. +func immrot2s(v uint32) (uint32, uint32) { +	if immrot(v) != 0 { +		return v, 0 +	} +	// suppose v in the form of {leading 00, upper effective bits, lower 8 effective bits, trailing 00} +	// omit trailing 00 +	var i uint32 +	for i = 2; i < 32; i += 2 { +		if v&(1<<i-1) != 0 { +			break +		} +	} +	// i must be <= 24, then adjust i just above lower 8 effective bits of v +	i += 6 +	// let x = {the complement of lower 8 effective bits, trailing 00}, y = x + v +	x := 1<<i - v&(1<<i-1) +	y := v + x +	if y, x = uint32(immrot(y)), uint32(immrot(x)); y != 0 && x != 0 { +		return y, x +	} +	return 0, 0 +} + +func immaddr(v int32) int32 { +	if v >= 0 && v <= 0xfff { +		return v&0xfff | 1<<24 | 1<<23 /* pre indexing */ /* pre indexing, up */ +	} +	if v >= -0xfff && v < 0 { +		return -v&0xfff | 1<<24 /* pre indexing */ +	} +	return 0 +} + +func immfloat(v int32) bool { +	return v&0xC03 == 0 /* offset will fit in floating-point load/store */ +} + +func immhalf(v int32) bool { +	if v >= 0 && v <= 0xff { +		return v|1<<24|1<<23 != 0 /* pre indexing */ /* pre indexing, up */ +	} +	if v >= -0xff && v < 0 { +		return -v&0xff|1<<24 != 0 /* pre indexing */ +	} +	return false +} + +func (c *ctxt5) aclass(a *obj.Addr) int { +	switch a.Type { +	case obj.TYPE_NONE: +		return C_NONE + +	case obj.TYPE_REG: +		c.instoffset = 0 +		if REG_R0 <= a.Reg && a.Reg <= REG_R15 { +			return C_REG +		} +		if REG_F0 <= a.Reg && a.Reg <= REG_F15 { +			return C_FREG +		} +		if a.Reg == REG_FPSR || a.Reg == REG_FPCR { +			return C_FCR +		} +		if a.Reg == REG_CPSR || a.Reg == REG_SPSR { +			return C_PSR +		} +		if a.Reg >= REG_SPECIAL { +			return C_SPR +		} +		return C_GOK + +	case obj.TYPE_REGREG: +		return C_REGREG + +	case obj.TYPE_REGREG2: +		return C_REGREG2 + +	case obj.TYPE_REGLIST: +		return C_REGLIST + +	case obj.TYPE_SHIFT: +		if a.Reg == 0 { +			// register shift R>>i +			return C_SHIFT +		} else { +			// memory address with shifted offset R>>i(R) +			return C_SHIFTADDR +		} + +	case obj.TYPE_MEM: +		switch a.Name { +		case obj.NAME_EXTERN, +			obj.NAME_GOTREF, +			obj.NAME_STATIC: +			if a.Sym == nil || a.Sym.Name == "" { +				fmt.Printf("null sym external\n") +				return C_GOK +			} + +			c.instoffset = 0 // s.b. unused but just in case +			if a.Sym.Type == objabi.STLSBSS { +				if c.ctxt.Flag_shared { +					return C_TLS_IE +				} else { +					return C_TLS_LE +				} +			} + +			return C_ADDR + +		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 +			} +			c.instoffset = c.autosize + a.Offset +			if t := immaddr(int32(c.instoffset)); t != 0 { +				if immhalf(int32(c.instoffset)) { +					if immfloat(t) { +						return C_HFAUTO +					} +					return C_HAUTO +				} + +				if immfloat(t) { +					return C_FAUTO +				} +				return C_SAUTO +			} + +			return C_LAUTO + +		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 = c.autosize + a.Offset + 4 +			if t := immaddr(int32(c.instoffset)); t != 0 { +				if immhalf(int32(c.instoffset)) { +					if immfloat(t) { +						return C_HFAUTO +					} +					return C_HAUTO +				} + +				if immfloat(t) { +					return C_FAUTO +				} +				return C_SAUTO +			} + +			return C_LAUTO + +		case obj.NAME_NONE: +			c.instoffset = a.Offset +			if t := immaddr(int32(c.instoffset)); t != 0 { +				if immhalf(int32(c.instoffset)) { /* n.b. that it will also satisfy immrot */ +					if immfloat(t) { +						return C_HFOREG +					} +					return C_HOREG +				} + +				if immfloat(t) { +					return C_FOREG /* n.b. that it will also satisfy immrot */ +				} +				if immrot(uint32(c.instoffset)) != 0 { +					return C_SROREG +				} +				if immhalf(int32(c.instoffset)) { +					return C_HOREG +				} +				return C_SOREG +			} + +			if immrot(uint32(c.instoffset)) != 0 { +				return C_ROREG +			} +			return C_LOREG +		} + +		return C_GOK + +	case obj.TYPE_FCONST: +		if c.chipzero5(a.Val.(float64)) >= 0 { +			return C_ZFCON +		} +		if c.chipfloat5(a.Val.(float64)) >= 0 { +			return C_SFCON +		} +		return C_LFCON + +	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 { +				return c.aconsize() +			} + +			if immrot(uint32(c.instoffset)) != 0 { +				return C_RCON +			} +			if immrot(^uint32(c.instoffset)) != 0 { +				return C_NCON +			} +			if uint32(c.instoffset) <= 0xffff && objabi.GOARM == 7 { +				return C_SCON +			} +			if x, y := immrot2a(uint32(c.instoffset)); x != 0 && y != 0 { +				return C_RCON2A +			} +			if y, x := immrot2s(uint32(c.instoffset)); x != 0 && y != 0 { +				return C_RCON2S +			} +			return C_LCON + +		case obj.NAME_EXTERN, +			obj.NAME_GOTREF, +			obj.NAME_STATIC: +			s := a.Sym +			if s == nil { +				break +			} +			c.instoffset = 0 // s.b. unused but just in case +			return C_LCONADDR + +		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 +			} +			c.instoffset = c.autosize + a.Offset +			return c.aconsize() + +		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 = c.autosize + a.Offset + 4 +			return c.aconsize() +		} + +		return C_GOK + +	case obj.TYPE_BRANCH: +		return C_SBRA +	} + +	return C_GOK +} + +func (c *ctxt5) aconsize() int { +	if immrot(uint32(c.instoffset)) != 0 { +		return C_RACON +	} +	if immrot(uint32(-c.instoffset)) != 0 { +		return C_RACON +	} +	return C_LACON +} + +func (c *ctxt5) oplook(p *obj.Prog) *Optab { +	a1 := int(p.Optab) +	if a1 != 0 { +		return &optab[a1-1] +	} +	a1 = int(p.From.Class) +	if a1 == 0 { +		a1 = c.aclass(&p.From) + 1 +		p.From.Class = int8(a1) +	} + +	a1-- +	a3 := int(p.To.Class) +	if a3 == 0 { +		a3 = c.aclass(&p.To) + 1 +		p.To.Class = int8(a3) +	} + +	a3-- +	a2 := C_NONE +	if p.Reg != 0 { +		switch { +		case REG_F0 <= p.Reg && p.Reg <= REG_F15: +			a2 = C_FREG +		case REG_R0 <= p.Reg && p.Reg <= REG_R15: +			a2 = C_REG +		default: +			c.ctxt.Diag("invalid register in %v", p) +		} +	} + +	// check illegal base register +	switch a1 { +	case C_SOREG, C_LOREG, C_HOREG, C_FOREG, C_ROREG, C_HFOREG, C_SROREG, C_SHIFTADDR: +		if p.From.Reg < REG_R0 || REG_R15 < p.From.Reg { +			c.ctxt.Diag("illegal base register: %v", p) +		} +	default: +	} +	switch a3 { +	case C_SOREG, C_LOREG, C_HOREG, C_FOREG, C_ROREG, C_HFOREG, C_SROREG, C_SHIFTADDR: +		if p.To.Reg < REG_R0 || REG_R15 < p.To.Reg { +			c.ctxt.Diag("illegal base register: %v", p) +		} +	default: +	} + +	// If current instruction has a .S suffix (flags update), +	// we must use the constant pool instead of splitting it. +	if (a1 == C_RCON2A || a1 == C_RCON2S) && p.Scond&C_SBIT != 0 { +		a1 = C_LCON +	} +	if (a3 == C_RCON2A || a3 == C_RCON2S) && p.Scond&C_SBIT != 0 { +		a3 = C_LCON +	} + +	if false { /*debug['O']*/ +		fmt.Printf("oplook %v %v %v %v\n", p.As, DRconv(a1), DRconv(a2), DRconv(a3)) +		fmt.Printf("\t\t%d %d\n", p.From.Type, p.To.Type) +	} + +	ops := oprange[p.As&obj.AMask] +	c1 := &xcmp[a1] +	c3 := &xcmp[a3] +	for i := range ops { +		op := &ops[i] +		if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] { +			p.Optab = uint16(cap(optab) - cap(ops) + i + 1) +			checkSuffix(c, p, op) +			return op +		} +	} + +	c.ctxt.Diag("illegal combination %v; %v %v %v; from %d %d; to %d %d", p, DRconv(a1), DRconv(a2), DRconv(a3), p.From.Type, p.From.Name, p.To.Type, p.To.Name) +	if ops == nil { +		ops = optab +	} +	return &ops[0] +} + +func cmp(a int, b int) bool { +	if a == b { +		return true +	} +	switch a { +	case C_LCON: +		if b == C_RCON || b == C_NCON || b == C_SCON || b == C_RCON2A || b == C_RCON2S { +			return true +		} + +	case C_LACON: +		if b == C_RACON { +			return true +		} + +	case C_LFCON: +		if b == C_ZFCON || b == C_SFCON { +			return true +		} + +	case C_HFAUTO: +		return b == C_HAUTO || b == C_FAUTO + +	case C_FAUTO, C_HAUTO: +		return b == C_HFAUTO + +	case C_SAUTO: +		return cmp(C_HFAUTO, b) + +	case C_LAUTO: +		return cmp(C_SAUTO, b) + +	case C_HFOREG: +		return b == C_HOREG || b == C_FOREG + +	case C_FOREG, C_HOREG: +		return b == C_HFOREG + +	case C_SROREG: +		return cmp(C_SOREG, b) || cmp(C_ROREG, b) + +	case C_SOREG, C_ROREG: +		return b == C_SROREG || cmp(C_HFOREG, b) + +	case C_LOREG: +		return cmp(C_SROREG, b) + +	case C_LBRA: +		if b == C_SBRA { +			return true +		} + +	case C_HREG: +		return cmp(C_SP, b) || cmp(C_PC, b) +	} + +	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] +	n := int(p1.as) - int(p2.as) +	if n != 0 { +		return n < 0 +	} +	n = int(p1.a1) - int(p2.a1) +	if n != 0 { +		return n < 0 +	} +	n = int(p1.a2) - int(p2.a2) +	if n != 0 { +		return n < 0 +	} +	n = int(p1.a3) - int(p2.a3) +	if n != 0 { +		return n < 0 +	} +	return false +} + +func opset(a, b0 obj.As) { +	oprange[a&obj.AMask] = oprange[b0] +} + +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 +	} + +	deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) + +	symdiv = ctxt.Lookup("runtime._div") +	symdivu = ctxt.Lookup("runtime._divu") +	symmod = ctxt.Lookup("runtime._mod") +	symmodu = ctxt.Lookup("runtime._modu") + +	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++ { +		if optab[n].flag&LPCREL != 0 { +			if ctxt.Flag_shared { +				optab[n].size += int8(optab[n].pcrelsiz) +			} else { +				optab[n].flag &^= LPCREL +			} +		} +	} + +	sort.Sort(ocmp(optab[:n])) +	for i := 0; i < n; i++ { +		r := optab[i].as +		r0 := r & obj.AMask +		start := i +		for optab[i].as == r { +			i++ +		} +		oprange[r0] = optab[start:i] +		i-- + +		switch r { +		default: +			ctxt.Diag("unknown op in build: %v", r) +			ctxt.DiagFlush() +			log.Fatalf("bad code") + +		case AADD: +			opset(ASUB, r0) +			opset(ARSB, r0) +			opset(AADC, r0) +			opset(ASBC, r0) +			opset(ARSC, r0) + +		case AORR: +			opset(AEOR, r0) +			opset(ABIC, r0) + +		case ACMP: +			opset(ATEQ, r0) +			opset(ACMN, r0) +			opset(ATST, r0) + +		case AMVN: +			break + +		case ABEQ: +			opset(ABNE, r0) +			opset(ABCS, r0) +			opset(ABHS, r0) +			opset(ABCC, r0) +			opset(ABLO, r0) +			opset(ABMI, r0) +			opset(ABPL, r0) +			opset(ABVS, r0) +			opset(ABVC, r0) +			opset(ABHI, r0) +			opset(ABLS, r0) +			opset(ABGE, r0) +			opset(ABLT, r0) +			opset(ABGT, r0) +			opset(ABLE, r0) + +		case ASLL: +			opset(ASRL, r0) +			opset(ASRA, r0) + +		case AMUL: +			opset(AMULU, r0) + +		case ADIV: +			opset(AMOD, r0) +			opset(AMODU, r0) +			opset(ADIVU, r0) + +		case ADIVHW: +			opset(ADIVUHW, r0) + +		case AMOVW, +			AMOVB, +			AMOVBS, +			AMOVBU, +			AMOVH, +			AMOVHS, +			AMOVHU: +			break + +		case ASWPW: +			opset(ASWPBU, r0) + +		case AB, +			ABL, +			ABX, +			ABXRET, +			obj.ADUFFZERO, +			obj.ADUFFCOPY, +			ASWI, +			AWORD, +			AMOVM, +			ARFE, +			obj.ATEXT: +			break + +		case AADDF: +			opset(AADDD, r0) +			opset(ASUBF, r0) +			opset(ASUBD, r0) +			opset(AMULF, r0) +			opset(AMULD, r0) +			opset(ANMULF, r0) +			opset(ANMULD, r0) +			opset(AMULAF, r0) +			opset(AMULAD, r0) +			opset(AMULSF, r0) +			opset(AMULSD, r0) +			opset(ANMULAF, r0) +			opset(ANMULAD, r0) +			opset(ANMULSF, r0) +			opset(ANMULSD, r0) +			opset(AFMULAF, r0) +			opset(AFMULAD, r0) +			opset(AFMULSF, r0) +			opset(AFMULSD, r0) +			opset(AFNMULAF, r0) +			opset(AFNMULAD, r0) +			opset(AFNMULSF, r0) +			opset(AFNMULSD, r0) +			opset(ADIVF, r0) +			opset(ADIVD, r0) + +		case ANEGF: +			opset(ANEGD, r0) +			opset(ASQRTF, r0) +			opset(ASQRTD, r0) +			opset(AMOVFD, r0) +			opset(AMOVDF, r0) +			opset(AABSF, r0) +			opset(AABSD, r0) + +		case ACMPF: +			opset(ACMPD, r0) + +		case AMOVF: +			opset(AMOVD, r0) + +		case AMOVFW: +			opset(AMOVDW, r0) + +		case AMOVWF: +			opset(AMOVWD, r0) + +		case AMULL: +			opset(AMULAL, r0) +			opset(AMULLU, r0) +			opset(AMULALU, r0) + +		case AMULWT: +			opset(AMULWB, r0) +			opset(AMULBB, r0) +			opset(AMMUL, r0) + +		case AMULAWT: +			opset(AMULAWB, r0) +			opset(AMULABB, r0) +			opset(AMULS, r0) +			opset(AMMULA, r0) +			opset(AMMULS, r0) + +		case ABFX: +			opset(ABFXU, r0) +			opset(ABFC, r0) +			opset(ABFI, r0) + +		case ACLZ: +			opset(AREV, r0) +			opset(AREV16, r0) +			opset(AREVSH, r0) +			opset(ARBIT, r0) + +		case AXTAB: +			opset(AXTAH, r0) +			opset(AXTABU, r0) +			opset(AXTAHU, r0) + +		case ALDREX, +			ASTREX, +			ALDREXD, +			ASTREXD, +			ADMB, +			APLD, +			AAND, +			AMULA, +			obj.AUNDEF, +			obj.AFUNCDATA, +			obj.APCDATA, +			obj.ANOP: +			break +		} +	} +} + +func (c *ctxt5) asmout(p *obj.Prog, o *Optab, out []uint32) { +	c.printp = p +	o1 := uint32(0) +	o2 := uint32(0) +	o3 := uint32(0) +	o4 := uint32(0) +	o5 := uint32(0) +	o6 := 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 */ +		if false { /*debug['G']*/ +			fmt.Printf("%x: %s: arm\n", uint32(p.Pc), p.From.Sym.Name) +		} + +	case 1: /* op R,[R],R */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if p.To.Type == obj.TYPE_NONE { +			rt = 0 +		} +		if p.As == AMOVB || p.As == AMOVH || p.As == AMOVW || p.As == AMVN { +			r = 0 +		} else if r == 0 { +			r = rt +		} +		o1 |= (uint32(rf)&15)<<0 | (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 + +	case 2: /* movbu $I,[R],R */ +		c.aclass(&p.From) + +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		o1 |= uint32(immrot(uint32(c.instoffset))) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if p.To.Type == obj.TYPE_NONE { +			rt = 0 +		} +		if p.As == AMOVW || p.As == AMVN { +			r = 0 +		} else if r == 0 { +			r = rt +		} +		o1 |= (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 + +	case 106: /* op $I,R,R where I can be decomposed into 2 immediates */ +		c.aclass(&p.From) +		r := int(p.Reg) +		rt := int(p.To.Reg) +		if r == 0 { +			r = rt +		} +		x, y := immrot2a(uint32(c.instoffset)) +		var as2 obj.As +		switch p.As { +		case AADD, ASUB, AORR, AEOR, ABIC: +			as2 = p.As // ADD, SUB, ORR, EOR, BIC +		case ARSB: +			as2 = AADD // RSB -> RSB/ADD pair +		case AADC: +			as2 = AADD // ADC -> ADC/ADD pair +		case ASBC: +			as2 = ASUB // SBC -> SBC/SUB pair +		case ARSC: +			as2 = AADD // RSC -> RSC/ADD pair +		default: +			c.ctxt.Diag("unknown second op for %v", p) +		} +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		o2 = c.oprrr(p, as2, int(p.Scond)) +		o1 |= (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 +		o2 |= (uint32(rt)&15)<<16 | (uint32(rt)&15)<<12 +		o1 |= x +		o2 |= y + +	case 107: /* op $I,R,R where I can be decomposed into 2 immediates */ +		c.aclass(&p.From) +		r := int(p.Reg) +		rt := int(p.To.Reg) +		if r == 0 { +			r = rt +		} +		y, x := immrot2s(uint32(c.instoffset)) +		var as2 obj.As +		switch p.As { +		case AADD: +			as2 = ASUB // ADD -> ADD/SUB pair +		case ASUB: +			as2 = AADD // SUB -> SUB/ADD pair +		case ARSB: +			as2 = ASUB // RSB -> RSB/SUB pair +		case AADC: +			as2 = ASUB // ADC -> ADC/SUB pair +		case ASBC: +			as2 = AADD // SBC -> SBC/ADD pair +		case ARSC: +			as2 = ASUB // RSC -> RSC/SUB pair +		default: +			c.ctxt.Diag("unknown second op for %v", p) +		} +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		o2 = c.oprrr(p, as2, int(p.Scond)) +		o1 |= (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 +		o2 |= (uint32(rt)&15)<<16 | (uint32(rt)&15)<<12 +		o1 |= y +		o2 |= x + +	case 3: /* add R<<[IR],[R],R */ +		o1 = c.mov(p) + +	case 4: /* MOVW $off(R), R -> add $off,[R],R */ +		c.aclass(&p.From) +		if c.instoffset < 0 { +			o1 = c.oprrr(p, ASUB, int(p.Scond)) +			o1 |= uint32(immrot(uint32(-c.instoffset))) +		} else { +			o1 = c.oprrr(p, AADD, int(p.Scond)) +			o1 |= uint32(immrot(uint32(c.instoffset))) +		} +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 |= (uint32(r) & 15) << 16 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 5: /* bra s */ +		o1 = c.opbra(p, p.As, int(p.Scond)) + +		v := int32(-8) +		if p.To.Sym != nil { +			rel := obj.Addrel(c.cursym) +			rel.Off = int32(c.pc) +			rel.Siz = 4 +			rel.Sym = p.To.Sym +			v += int32(p.To.Offset) +			rel.Add = int64(o1) | (int64(v)>>2)&0xffffff +			rel.Type = objabi.R_CALLARM +			break +		} + +		if p.To.Target() != nil { +			v = int32((p.To.Target().Pc - c.pc) - 8) +		} +		o1 |= (uint32(v) >> 2) & 0xffffff + +	case 6: /* b ,O(R) -> add $O,R,PC */ +		c.aclass(&p.To) + +		o1 = c.oprrr(p, AADD, int(p.Scond)) +		o1 |= uint32(immrot(uint32(c.instoffset))) +		o1 |= (uint32(p.To.Reg) & 15) << 16 +		o1 |= (REGPC & 15) << 12 + +	case 7: /* bl (R) -> blx R */ +		c.aclass(&p.To) + +		if c.instoffset != 0 { +			c.ctxt.Diag("%v: doesn't support BL offset(REG) with non-zero offset %d", p, c.instoffset) +		} +		o1 = c.oprrr(p, ABL, int(p.Scond)) +		o1 |= (uint32(p.To.Reg) & 15) << 0 +		rel := obj.Addrel(c.cursym) +		rel.Off = int32(c.pc) +		rel.Siz = 0 +		rel.Type = objabi.R_CALLIND + +	case 8: /* sll $c,[R],R -> mov (R<<$c),R */ +		c.aclass(&p.From) + +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		r := int(p.Reg) +		if r == 0 { +			r = int(p.To.Reg) +		} +		o1 |= (uint32(r) & 15) << 0 +		o1 |= uint32((c.instoffset & 31) << 7) +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 9: /* sll R,[R],R -> mov (R<<R),R */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		r := int(p.Reg) +		if r == 0 { +			r = int(p.To.Reg) +		} +		o1 |= (uint32(r) & 15) << 0 +		o1 |= (uint32(p.From.Reg)&15)<<8 | 1<<4 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 10: /* swi [$con] */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		if p.To.Type != obj.TYPE_NONE { +			c.aclass(&p.To) +			o1 |= uint32(c.instoffset & 0xffffff) +		} + +	case 11: /* word */ +		c.aclass(&p.To) + +		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 (c.pool). +			rel := obj.Addrel(c.cursym) + +			rel.Off = int32(c.pc) +			rel.Siz = 4 +			rel.Sym = p.To.Sym +			rel.Add = p.To.Offset + +			if c.ctxt.Flag_shared { +				if p.To.Name == obj.NAME_GOTREF { +					rel.Type = objabi.R_GOTPCREL +				} else { +					rel.Type = objabi.R_PCREL +				} +				rel.Add += c.pc - p.Rel.Pc - 8 +			} else { +				rel.Type = objabi.R_ADDR +			} +			o1 = 0 +		} + +	case 12: /* movw $lcon, reg */ +		if o.a1 == C_SCON { +			o1 = c.omvs(p, &p.From, int(p.To.Reg)) +		} else if p.As == AMVN { +			o1 = c.omvr(p, &p.From, int(p.To.Reg)) +		} else { +			o1 = c.omvl(p, &p.From, int(p.To.Reg)) +		} + +		if o.flag&LPCREL != 0 { +			o2 = c.oprrr(p, AADD, int(p.Scond)) | (uint32(p.To.Reg)&15)<<0 | (REGPC&15)<<16 | (uint32(p.To.Reg)&15)<<12 +		} + +	case 13: /* op $lcon, [R], R */ +		if o.a1 == C_SCON { +			o1 = c.omvs(p, &p.From, REGTMP) +		} else { +			o1 = c.omvl(p, &p.From, REGTMP) +		} + +		if o1 == 0 { +			break +		} +		o2 = c.oprrr(p, p.As, int(p.Scond)) +		o2 |= REGTMP & 15 +		r := int(p.Reg) +		if p.As == AMVN { +			r = 0 +		} else if r == 0 { +			r = int(p.To.Reg) +		} +		o2 |= (uint32(r) & 15) << 16 +		if p.To.Type != obj.TYPE_NONE { +			o2 |= (uint32(p.To.Reg) & 15) << 12 +		} + +	case 14: /* movb/movbu/movh/movhu R,R */ +		o1 = c.oprrr(p, ASLL, int(p.Scond)) + +		if p.As == AMOVBU || p.As == AMOVHU { +			o2 = c.oprrr(p, ASRL, int(p.Scond)) +		} else { +			o2 = c.oprrr(p, ASRA, int(p.Scond)) +		} + +		r := int(p.To.Reg) +		o1 |= (uint32(p.From.Reg)&15)<<0 | (uint32(r)&15)<<12 +		o2 |= uint32(r)&15 | (uint32(r)&15)<<12 +		if p.As == AMOVB || p.As == AMOVBS || p.As == AMOVBU { +			o1 |= 24 << 7 +			o2 |= 24 << 7 +		} else { +			o1 |= 16 << 7 +			o2 |= 16 << 7 +		} + +	case 15: /* mul r,[r,]r */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if r == 0 { +			r = rt +		} + +		o1 |= (uint32(rf)&15)<<8 | (uint32(r)&15)<<0 | (uint32(rt)&15)<<16 + +	case 16: /* div r,[r,]r */ +		o1 = 0xf << 28 + +		o2 = 0 + +	case 17: +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) +		rt2 := int(p.To.Offset) +		r := int(p.Reg) +		o1 |= (uint32(rf)&15)<<8 | (uint32(r)&15)<<0 | (uint32(rt)&15)<<16 | (uint32(rt2)&15)<<12 + +	case 18: /* BFX/BFXU/BFC/BFI */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if r == 0 { +			r = rt +		} else if p.As == ABFC { // only "BFC $width, $lsb, Reg" is accepted, p.Reg must be 0 +			c.ctxt.Diag("illegal combination: %v", p) +		} +		if p.GetFrom3() == nil || p.GetFrom3().Type != obj.TYPE_CONST { +			c.ctxt.Diag("%v: missing or wrong LSB", p) +			break +		} +		lsb := p.GetFrom3().Offset +		width := p.From.Offset +		if lsb < 0 || lsb > 31 || width <= 0 || (lsb+width) > 32 { +			c.ctxt.Diag("%v: wrong width or LSB", p) +		} +		switch p.As { +		case ABFX, ABFXU: // (width-1) is encoded +			o1 |= (uint32(r)&15)<<0 | (uint32(rt)&15)<<12 | uint32(lsb)<<7 | uint32(width-1)<<16 +		case ABFC, ABFI: // MSB is encoded +			o1 |= (uint32(r)&15)<<0 | (uint32(rt)&15)<<12 | uint32(lsb)<<7 | uint32(lsb+width-1)<<16 +		default: +			c.ctxt.Diag("illegal combination: %v", p) +		} + +	case 20: /* mov/movb/movbu R,O(R) */ +		c.aclass(&p.To) + +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.osr(p.As, int(p.From.Reg), int32(c.instoffset), r, int(p.Scond)) + +	case 21: /* mov/movbu O(R),R -> lr */ +		c.aclass(&p.From) + +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.olr(int32(c.instoffset), r, int(p.To.Reg), int(p.Scond)) +		if p.As != AMOVW { +			o1 |= 1 << 22 +		} + +	case 22: /* XTAB R@>i, [R], R */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		switch p.From.Offset &^ 0xf { +		// only 0/8/16/24 bits rotation is accepted +		case SHIFT_RR, SHIFT_RR | 8<<7, SHIFT_RR | 16<<7, SHIFT_RR | 24<<7: +			o1 |= uint32(p.From.Offset) & 0xc0f +		default: +			c.ctxt.Diag("illegal shift: %v", p) +		} +		rt := p.To.Reg +		r := p.Reg +		if r == 0 { +			r = rt +		} +		o1 |= (uint32(rt)&15)<<12 | (uint32(r)&15)<<16 + +	case 23: /* MOVW/MOVB/MOVH R@>i, R */ +		switch p.As { +		case AMOVW: +			o1 = c.mov(p) +		case AMOVBU, AMOVBS, AMOVB, AMOVHU, AMOVHS, AMOVH: +			o1 = c.movxt(p) +		default: +			c.ctxt.Diag("illegal combination: %v", p) +		} + +	case 30: /* mov/movb/movbu R,L(R) */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.osrr(int(p.From.Reg), REGTMP&15, r, int(p.Scond)) +		if p.As != AMOVW { +			o2 |= 1 << 22 +		} + +	case 31: /* mov/movbu L(R),R -> lr[b] */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.olrr(REGTMP&15, r, int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVBU || p.As == AMOVBS || p.As == AMOVB { +			o2 |= 1 << 22 +		} + +	case 34: /* mov $lacon,R */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} + +		o2 = c.oprrr(p, AADD, int(p.Scond)) +		o2 |= REGTMP & 15 +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 |= (uint32(r) & 15) << 16 +		if p.To.Type != obj.TYPE_NONE { +			o2 |= (uint32(p.To.Reg) & 15) << 12 +		} + +	case 35: /* mov PSR,R */ +		o1 = 2<<23 | 0xf<<16 | 0<<0 + +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +		o1 |= (uint32(p.From.Reg) & 1) << 22 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 36: /* mov R,PSR */ +		o1 = 2<<23 | 0x2cf<<12 | 0<<4 + +		if p.Scond&C_FBIT != 0 { +			o1 ^= 0x010 << 12 +		} +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +		o1 |= (uint32(p.To.Reg) & 1) << 22 +		o1 |= (uint32(p.From.Reg) & 15) << 0 + +	case 37: /* mov $con,PSR */ +		c.aclass(&p.From) + +		o1 = 2<<23 | 0x2cf<<12 | 0<<4 +		if p.Scond&C_FBIT != 0 { +			o1 ^= 0x010 << 12 +		} +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +		o1 |= uint32(immrot(uint32(c.instoffset))) +		o1 |= (uint32(p.To.Reg) & 1) << 22 +		o1 |= (uint32(p.From.Reg) & 15) << 0 + +	case 38, 39: +		switch o.type_ { +		case 38: /* movm $con,oreg -> stm */ +			o1 = 0x4 << 25 + +			o1 |= uint32(p.From.Offset & 0xffff) +			o1 |= (uint32(p.To.Reg) & 15) << 16 +			c.aclass(&p.To) + +		case 39: /* movm oreg,$con -> ldm */ +			o1 = 0x4<<25 | 1<<20 + +			o1 |= uint32(p.To.Offset & 0xffff) +			o1 |= (uint32(p.From.Reg) & 15) << 16 +			c.aclass(&p.From) +		} + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in MOVM; %v", p) +		} +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +		if p.Scond&C_PBIT != 0 { +			o1 |= 1 << 24 +		} +		if p.Scond&C_UBIT != 0 { +			o1 |= 1 << 23 +		} +		if p.Scond&C_WBIT != 0 { +			o1 |= 1 << 21 +		} + +	case 40: /* swp oreg,reg,reg */ +		c.aclass(&p.From) + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in SWP") +		} +		o1 = 0x2<<23 | 0x9<<4 +		if p.As != ASWPW { +			o1 |= 1 << 22 +		} +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.Reg) & 15) << 0 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ +		o1 = 0xe8fd8000 + +	case 50: /* floating point store */ +		v := c.regoff(&p.To) + +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.ofsr(p.As, int(p.From.Reg), v, r, int(p.Scond), p) + +	case 51: /* floating point load */ +		v := c.regoff(&p.From) + +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.ofsr(p.As, int(p.To.Reg), v, r, int(p.Scond), p) | 1<<20 + +	case 52: /* floating point store, int32 offset UGLY */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.oprrr(p, AADD, int(p.Scond)) | (REGTMP&15)<<12 | (REGTMP&15)<<16 | (uint32(r)&15)<<0 +		o3 = c.ofsr(p.As, int(p.From.Reg), 0, REGTMP, int(p.Scond), p) + +	case 53: /* floating point load, int32 offset UGLY */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.oprrr(p, AADD, int(p.Scond)) | (REGTMP&15)<<12 | (REGTMP&15)<<16 | (uint32(r)&15)<<0 +		o3 = c.ofsr(p.As, int(p.To.Reg), 0, (REGTMP&15), int(p.Scond), p) | 1<<20 + +	case 54: /* floating point arith */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if r == 0 { +			switch p.As { +			case AMULAD, AMULAF, AMULSF, AMULSD, ANMULAF, ANMULAD, ANMULSF, ANMULSD, +				AFMULAD, AFMULAF, AFMULSF, AFMULSD, AFNMULAF, AFNMULAD, AFNMULSF, AFNMULSD: +				c.ctxt.Diag("illegal combination: %v", p) +			default: +				r = rt +			} +		} + +		o1 |= (uint32(rf)&15)<<0 | (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 + +	case 55: /* negf freg, freg */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) + +		o1 |= (uint32(rf)&15)<<0 | (uint32(rt)&15)<<12 + +	case 56: /* move to FP[CS]R */ +		o1 = ((uint32(p.Scond)&C_SCOND)^C_SCOND_XOR)<<28 | 0xee1<<16 | 0xa1<<4 + +		o1 |= (uint32(p.From.Reg) & 15) << 12 + +	case 57: /* move from FP[CS]R */ +		o1 = ((uint32(p.Scond)&C_SCOND)^C_SCOND_XOR)<<28 | 0xef1<<16 | 0xa1<<4 + +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 58: /* movbu R,R */ +		o1 = c.oprrr(p, AAND, int(p.Scond)) + +		o1 |= uint32(immrot(0xff)) +		rt := int(p.To.Reg) +		r := int(p.From.Reg) +		if p.To.Type == obj.TYPE_NONE { +			rt = 0 +		} +		if r == 0 { +			r = rt +		} +		o1 |= (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 + +	case 59: /* movw/bu R<<I(R),R -> ldr indexed */ +		if p.From.Reg == 0 { +			c.ctxt.Diag("source operand is not a memory address: %v", p) +			break +		} +		if p.From.Offset&(1<<4) != 0 { +			c.ctxt.Diag("bad shift in LDR") +			break +		} +		o1 = c.olrr(int(p.From.Offset), int(p.From.Reg), int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVBU { +			o1 |= 1 << 22 +		} + +	case 60: /* movb R(R),R -> ldrsb indexed */ +		if p.From.Reg == 0 { +			c.ctxt.Diag("source operand is not a memory address: %v", p) +			break +		} +		if p.From.Offset&(^0xf) != 0 { +			c.ctxt.Diag("bad shift: %v", p) +			break +		} +		o1 = c.olhrr(int(p.From.Offset), int(p.From.Reg), int(p.To.Reg), int(p.Scond)) +		switch p.As { +		case AMOVB, AMOVBS: +			o1 ^= 1<<5 | 1<<6 +		case AMOVH, AMOVHS: +			o1 ^= 1 << 6 +		default: +		} +		if p.Scond&C_UBIT != 0 { +			o1 &^= 1 << 23 +		} + +	case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */ +		if p.To.Reg == 0 { +			c.ctxt.Diag("MOV to shifter operand") +		} +		o1 = c.osrr(int(p.From.Reg), int(p.To.Offset), int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVB || p.As == AMOVBS || p.As == AMOVBU { +			o1 |= 1 << 22 +		} + +	case 62: /* MOVH/MOVHS/MOVHU Reg, Reg<<0(Reg) -> strh */ +		if p.To.Reg == 0 { +			c.ctxt.Diag("MOV to shifter operand") +		} +		if p.To.Offset&(^0xf) != 0 { +			c.ctxt.Diag("bad shift: %v", p) +		} +		o1 = c.olhrr(int(p.To.Offset), int(p.To.Reg), int(p.From.Reg), int(p.Scond)) +		o1 ^= 1 << 20 +		if p.Scond&C_UBIT != 0 { +			o1 &^= 1 << 23 +		} + +		/* reloc ops */ +	case 64: /* mov/movb/movbu R,addr */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.osr(p.As, int(p.From.Reg), 0, REGTMP, int(p.Scond)) +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +	case 65: /* mov/movbu addr,R */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.olr(0, REGTMP, int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVBU || p.As == AMOVBS || p.As == AMOVB { +			o2 |= 1 << 22 +		} +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +	case 101: /* movw tlsvar,R, local exec*/ +		o1 = c.omvl(p, &p.From, int(p.To.Reg)) + +	case 102: /* movw tlsvar,R, initial exec*/ +		o1 = c.omvl(p, &p.From, int(p.To.Reg)) +		o2 = c.olrr(int(p.To.Reg)&15, (REGPC & 15), int(p.To.Reg), int(p.Scond)) + +	case 103: /* word tlsvar, local exec */ +		if p.To.Sym == nil { +			c.ctxt.Diag("nil sym in tls %v", p) +		} +		if p.To.Offset != 0 { +			c.ctxt.Diag("offset against tls var in %v", p) +		} +		// This case happens with words generated in the PC stream as part of +		// the literal c.pool. +		rel := obj.Addrel(c.cursym) + +		rel.Off = int32(c.pc) +		rel.Siz = 4 +		rel.Sym = p.To.Sym +		rel.Type = objabi.R_TLS_LE +		o1 = 0 + +	case 104: /* word tlsvar, initial exec */ +		if p.To.Sym == nil { +			c.ctxt.Diag("nil sym in tls %v", p) +		} +		if p.To.Offset != 0 { +			c.ctxt.Diag("offset against tls var in %v", p) +		} +		rel := obj.Addrel(c.cursym) +		rel.Off = int32(c.pc) +		rel.Siz = 4 +		rel.Sym = p.To.Sym +		rel.Type = objabi.R_TLS_IE +		rel.Add = c.pc - p.Rel.Pc - 8 - int64(rel.Siz) + +	case 68: /* floating point store -> ADDR */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.ofsr(p.As, int(p.From.Reg), 0, REGTMP, int(p.Scond), p) +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +	case 69: /* floating point load <- ADDR */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.ofsr(p.As, int(p.To.Reg), 0, (REGTMP&15), int(p.Scond), p) | 1<<20 +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +		/* ArmV4 ops: */ +	case 70: /* movh/movhu R,O(R) -> strh */ +		c.aclass(&p.To) + +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.oshr(int(p.From.Reg), int32(c.instoffset), r, int(p.Scond)) + +	case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ +		c.aclass(&p.From) + +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o1 = c.olhr(int32(c.instoffset), r, int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVB || p.As == AMOVBS { +			o1 ^= 1<<5 | 1<<6 +		} else if p.As == AMOVH || p.As == AMOVHS { +			o1 ^= (1 << 6) +		} + +	case 72: /* movh/movhu R,L(R) -> strh */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.To.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.oshrr(int(p.From.Reg), REGTMP&15, r, int(p.Scond)) + +	case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		r := int(p.From.Reg) +		if r == 0 { +			r = int(o.param) +		} +		o2 = c.olhrr(REGTMP&15, r, int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVB || p.As == AMOVBS { +			o2 ^= 1<<5 | 1<<6 +		} else if p.As == AMOVH || p.As == AMOVHS { +			o2 ^= (1 << 6) +		} + +	case 74: /* bx $I */ +		c.ctxt.Diag("ABX $I") + +	case 75: /* bx O(R) */ +		c.aclass(&p.To) + +		if c.instoffset != 0 { +			c.ctxt.Diag("non-zero offset in ABX") +		} + +		/* +			o1 = 	c.oprrr(p, AADD, p->scond) | immrot(0) | ((REGPC&15)<<16) | ((REGLINK&15)<<12);	// mov PC, LR +			o2 = (((p->scond&C_SCOND) ^ C_SCOND_XOR)<<28) | (0x12fff<<8) | (1<<4) | ((p->to.reg&15) << 0);		// BX R +		*/ +		// p->to.reg may be REGLINK +		o1 = c.oprrr(p, AADD, int(p.Scond)) + +		o1 |= uint32(immrot(uint32(c.instoffset))) +		o1 |= (uint32(p.To.Reg) & 15) << 16 +		o1 |= (REGTMP & 15) << 12 +		o2 = c.oprrr(p, AADD, int(p.Scond)) | uint32(immrot(0)) | (REGPC&15)<<16 | (REGLINK&15)<<12 // mov PC, LR +		o3 = ((uint32(p.Scond)&C_SCOND)^C_SCOND_XOR)<<28 | 0x12fff<<8 | 1<<4 | REGTMP&15            // BX Rtmp + +	case 76: /* bx O(R) when returning from fn*/ +		c.ctxt.Diag("ABXRET") + +	case 77: /* ldrex oreg,reg */ +		c.aclass(&p.From) + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in LDREX") +		} +		o1 = 0x19<<20 | 0xf9f +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 78: /* strex reg,oreg,reg */ +		c.aclass(&p.From) + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in STREX") +		} +		if p.To.Reg == p.From.Reg || p.To.Reg == p.Reg { +			c.ctxt.Diag("cannot use same register as both source and destination: %v", p) +		} +		o1 = 0x18<<20 | 0xf90 +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.Reg) & 15) << 0 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 80: /* fmov zfcon,freg */ +		if p.As == AMOVD { +			o1 = 0xeeb00b00 // VMOV imm 64 +			o2 = c.oprrr(p, ASUBD, int(p.Scond)) +		} else { +			o1 = 0x0eb00a00 // VMOV imm 32 +			o2 = c.oprrr(p, ASUBF, int(p.Scond)) +		} + +		v := int32(0x70) // 1.0 +		r := (int(p.To.Reg) & 15) << 0 + +		// movf $1.0, r +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +		o1 |= (uint32(r) & 15) << 12 +		o1 |= (uint32(v) & 0xf) << 0 +		o1 |= (uint32(v) & 0xf0) << 12 + +		// subf r,r,r +		o2 |= (uint32(r)&15)<<0 | (uint32(r)&15)<<16 | (uint32(r)&15)<<12 + +	case 81: /* fmov sfcon,freg */ +		o1 = 0x0eb00a00 // VMOV imm 32 +		if p.As == AMOVD { +			o1 = 0xeeb00b00 // VMOV imm 64 +		} +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		v := int32(c.chipfloat5(p.From.Val.(float64))) +		o1 |= (uint32(v) & 0xf) << 0 +		o1 |= (uint32(v) & 0xf0) << 12 + +	case 82: /* fcmp freg,freg, */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.Reg)&15)<<12 | (uint32(p.From.Reg)&15)<<0 +		o2 = 0x0ef1fa10 // VMRS R15 +		o2 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 83: /* fcmp freg,, */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg)&15)<<12 | 1<<16 +		o2 = 0x0ef1fa10 // VMRS R15 +		o2 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 84: /* movfw freg,freg - truncate float-to-fix */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 0 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 85: /* movwf freg,freg - fix-to-float */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 0 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +		// macro for movfw freg,FTMP; movw FTMP,reg +	case 86: /* movfw freg,reg - truncate float-to-fix */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 0 +		o1 |= (FREGTMP & 15) << 12 +		o2 = c.oprrr(p, -AMOVFW, int(p.Scond)) +		o2 |= (FREGTMP & 15) << 16 +		o2 |= (uint32(p.To.Reg) & 15) << 12 + +		// macro for movw reg,FTMP; movwf FTMP,freg +	case 87: /* movwf reg,freg - fix-to-float */ +		o1 = c.oprrr(p, -AMOVWF, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 12 +		o1 |= (FREGTMP & 15) << 16 +		o2 = c.oprrr(p, p.As, int(p.Scond)) +		o2 |= (FREGTMP & 15) << 0 +		o2 |= (uint32(p.To.Reg) & 15) << 12 + +	case 88: /* movw reg,freg  */ +		o1 = c.oprrr(p, -AMOVWF, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 12 +		o1 |= (uint32(p.To.Reg) & 15) << 16 + +	case 89: /* movw freg,reg  */ +		o1 = c.oprrr(p, -AMOVFW, int(p.Scond)) + +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.To.Reg) & 15) << 12 + +	case 91: /* ldrexd oreg,reg */ +		c.aclass(&p.From) + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in LDREX") +		} +		o1 = 0x1b<<20 | 0xf9f +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 92: /* strexd reg,oreg,reg */ +		c.aclass(&p.From) + +		if c.instoffset != 0 { +			c.ctxt.Diag("offset must be zero in STREX") +		} +		if p.Reg&1 != 0 { +			c.ctxt.Diag("source register must be even in STREXD: %v", p) +		} +		if p.To.Reg == p.From.Reg || p.To.Reg == p.Reg || p.To.Reg == p.Reg+1 { +			c.ctxt.Diag("cannot use same register as both source and destination: %v", p) +		} +		o1 = 0x1a<<20 | 0xf90 +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		o1 |= (uint32(p.Reg) & 15) << 0 +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 + +	case 93: /* movb/movh/movhu addr,R -> ldrsb/ldrsh/ldrh */ +		o1 = c.omvl(p, &p.From, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.olhr(0, REGTMP, int(p.To.Reg), int(p.Scond)) +		if p.As == AMOVB || p.As == AMOVBS { +			o2 ^= 1<<5 | 1<<6 +		} else if p.As == AMOVH || p.As == AMOVHS { +			o2 ^= (1 << 6) +		} +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +	case 94: /* movh/movhu R,addr -> strh */ +		o1 = c.omvl(p, &p.To, REGTMP) + +		if o1 == 0 { +			break +		} +		o2 = c.oshr(int(p.From.Reg), 0, REGTMP, int(p.Scond)) +		if o.flag&LPCREL != 0 { +			o3 = o2 +			o2 = c.oprrr(p, AADD, int(p.Scond)) | REGTMP&15 | (REGPC&15)<<16 | (REGTMP&15)<<12 +		} + +	case 95: /* PLD off(reg) */ +		o1 = 0xf5d0f000 + +		o1 |= (uint32(p.From.Reg) & 15) << 16 +		if p.From.Offset < 0 { +			o1 &^= (1 << 23) +			o1 |= uint32((-p.From.Offset) & 0xfff) +		} else { +			o1 |= uint32(p.From.Offset & 0xfff) +		} + +	// 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 +	// 0xf7fabcfd which is guaranteed to raise undefined instruction +	// exception. +	case 96: /* UNDEF */ +		o1 = 0xf7fabcfd + +	case 97: /* CLZ Rm, Rd */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.To.Reg) & 15) << 12 +		o1 |= (uint32(p.From.Reg) & 15) << 0 + +	case 98: /* MULW{T,B} Rs, Rm, Rd */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.To.Reg) & 15) << 16 +		o1 |= (uint32(p.From.Reg) & 15) << 8 +		o1 |= (uint32(p.Reg) & 15) << 0 + +	case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) + +		o1 |= (uint32(p.To.Reg) & 15) << 16 +		o1 |= (uint32(p.From.Reg) & 15) << 8 +		o1 |= (uint32(p.Reg) & 15) << 0 +		o1 |= uint32((p.To.Offset & 15) << 12) + +	case 105: /* divhw r,[r,]r */ +		o1 = c.oprrr(p, p.As, int(p.Scond)) +		rf := int(p.From.Reg) +		rt := int(p.To.Reg) +		r := int(p.Reg) +		if r == 0 { +			r = rt +		} +		o1 |= (uint32(rf)&15)<<8 | (uint32(r)&15)<<0 | (uint32(rt)&15)<<16 + +	case 110: /* dmb [mbop | $con] */ +		o1 = 0xf57ff050 +		mbop := uint32(0) + +		switch c.aclass(&p.From) { +		case C_SPR: +			for _, f := range mbOp { +				if f.reg == p.From.Reg { +					mbop = f.enc +					break +				} +			} +		case C_RCON: +			for _, f := range mbOp { +				enc := uint32(c.instoffset) +				if f.enc == enc { +					mbop = enc +					break +				} +			} +		case C_NONE: +			mbop = 0xf +		} + +		if mbop == 0 { +			c.ctxt.Diag("illegal mb option:\n%v", p) +		} +		o1 |= mbop +	} + +	out[0] = o1 +	out[1] = o2 +	out[2] = o3 +	out[3] = o4 +	out[4] = o5 +	out[5] = o6 +} + +func (c *ctxt5) movxt(p *obj.Prog) uint32 { +	o1 := ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +	switch p.As { +	case AMOVB, AMOVBS: +		o1 |= 0x6af<<16 | 0x7<<4 +	case AMOVH, AMOVHS: +		o1 |= 0x6bf<<16 | 0x7<<4 +	case AMOVBU: +		o1 |= 0x6ef<<16 | 0x7<<4 +	case AMOVHU: +		o1 |= 0x6ff<<16 | 0x7<<4 +	default: +		c.ctxt.Diag("illegal combination: %v", p) +	} +	switch p.From.Offset &^ 0xf { +	// only 0/8/16/24 bits rotation is accepted +	case SHIFT_RR, SHIFT_RR | 8<<7, SHIFT_RR | 16<<7, SHIFT_RR | 24<<7: +		o1 |= uint32(p.From.Offset) & 0xc0f +	default: +		c.ctxt.Diag("illegal shift: %v", p) +	} +	o1 |= (uint32(p.To.Reg) & 15) << 12 +	return o1 +} + +func (c *ctxt5) mov(p *obj.Prog) uint32 { +	c.aclass(&p.From) +	o1 := c.oprrr(p, p.As, int(p.Scond)) +	o1 |= uint32(p.From.Offset) +	rt := int(p.To.Reg) +	if p.To.Type == obj.TYPE_NONE { +		rt = 0 +	} +	r := int(p.Reg) +	if p.As == AMOVW || p.As == AMVN { +		r = 0 +	} else if r == 0 { +		r = rt +	} +	o1 |= (uint32(r)&15)<<16 | (uint32(rt)&15)<<12 +	return o1 +} + +func (c *ctxt5) oprrr(p *obj.Prog, a obj.As, sc int) uint32 { +	o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28 +	if sc&C_SBIT != 0 { +		o |= 1 << 20 +	} +	switch a { +	case ADIVHW: +		return o | 0x71<<20 | 0xf<<12 | 0x1<<4 +	case ADIVUHW: +		return o | 0x73<<20 | 0xf<<12 | 0x1<<4 +	case AMMUL: +		return o | 0x75<<20 | 0xf<<12 | 0x1<<4 +	case AMULS: +		return o | 0x6<<20 | 0x9<<4 +	case AMMULA: +		return o | 0x75<<20 | 0x1<<4 +	case AMMULS: +		return o | 0x75<<20 | 0xd<<4 +	case AMULU, AMUL: +		return o | 0x0<<21 | 0x9<<4 +	case AMULA: +		return o | 0x1<<21 | 0x9<<4 +	case AMULLU: +		return o | 0x4<<21 | 0x9<<4 +	case AMULL: +		return o | 0x6<<21 | 0x9<<4 +	case AMULALU: +		return o | 0x5<<21 | 0x9<<4 +	case AMULAL: +		return o | 0x7<<21 | 0x9<<4 +	case AAND: +		return o | 0x0<<21 +	case AEOR: +		return o | 0x1<<21 +	case ASUB: +		return o | 0x2<<21 +	case ARSB: +		return o | 0x3<<21 +	case AADD: +		return o | 0x4<<21 +	case AADC: +		return o | 0x5<<21 +	case ASBC: +		return o | 0x6<<21 +	case ARSC: +		return o | 0x7<<21 +	case ATST: +		return o | 0x8<<21 | 1<<20 +	case ATEQ: +		return o | 0x9<<21 | 1<<20 +	case ACMP: +		return o | 0xa<<21 | 1<<20 +	case ACMN: +		return o | 0xb<<21 | 1<<20 +	case AORR: +		return o | 0xc<<21 + +	case AMOVB, AMOVH, AMOVW: +		if sc&(C_PBIT|C_WBIT) != 0 { +			c.ctxt.Diag("invalid .P/.W suffix: %v", p) +		} +		return o | 0xd<<21 +	case ABIC: +		return o | 0xe<<21 +	case AMVN: +		return o | 0xf<<21 +	case ASLL: +		return o | 0xd<<21 | 0<<5 +	case ASRL: +		return o | 0xd<<21 | 1<<5 +	case ASRA: +		return o | 0xd<<21 | 2<<5 +	case ASWI: +		return o | 0xf<<24 + +	case AADDD: +		return o | 0xe<<24 | 0x3<<20 | 0xb<<8 | 0<<4 +	case AADDF: +		return o | 0xe<<24 | 0x3<<20 | 0xa<<8 | 0<<4 +	case ASUBD: +		return o | 0xe<<24 | 0x3<<20 | 0xb<<8 | 4<<4 +	case ASUBF: +		return o | 0xe<<24 | 0x3<<20 | 0xa<<8 | 4<<4 +	case AMULD: +		return o | 0xe<<24 | 0x2<<20 | 0xb<<8 | 0<<4 +	case AMULF: +		return o | 0xe<<24 | 0x2<<20 | 0xa<<8 | 0<<4 +	case ANMULD: +		return o | 0xe<<24 | 0x2<<20 | 0xb<<8 | 0x4<<4 +	case ANMULF: +		return o | 0xe<<24 | 0x2<<20 | 0xa<<8 | 0x4<<4 +	case AMULAD: +		return o | 0xe<<24 | 0xb<<8 +	case AMULAF: +		return o | 0xe<<24 | 0xa<<8 +	case AMULSD: +		return o | 0xe<<24 | 0xb<<8 | 0x4<<4 +	case AMULSF: +		return o | 0xe<<24 | 0xa<<8 | 0x4<<4 +	case ANMULAD: +		return o | 0xe<<24 | 0x1<<20 | 0xb<<8 | 0x4<<4 +	case ANMULAF: +		return o | 0xe<<24 | 0x1<<20 | 0xa<<8 | 0x4<<4 +	case ANMULSD: +		return o | 0xe<<24 | 0x1<<20 | 0xb<<8 +	case ANMULSF: +		return o | 0xe<<24 | 0x1<<20 | 0xa<<8 +	case AFMULAD: +		return o | 0xe<<24 | 0xa<<20 | 0xb<<8 +	case AFMULAF: +		return o | 0xe<<24 | 0xa<<20 | 0xa<<8 +	case AFMULSD: +		return o | 0xe<<24 | 0xa<<20 | 0xb<<8 | 0x4<<4 +	case AFMULSF: +		return o | 0xe<<24 | 0xa<<20 | 0xa<<8 | 0x4<<4 +	case AFNMULAD: +		return o | 0xe<<24 | 0x9<<20 | 0xb<<8 | 0x4<<4 +	case AFNMULAF: +		return o | 0xe<<24 | 0x9<<20 | 0xa<<8 | 0x4<<4 +	case AFNMULSD: +		return o | 0xe<<24 | 0x9<<20 | 0xb<<8 +	case AFNMULSF: +		return o | 0xe<<24 | 0x9<<20 | 0xa<<8 +	case ADIVD: +		return o | 0xe<<24 | 0x8<<20 | 0xb<<8 | 0<<4 +	case ADIVF: +		return o | 0xe<<24 | 0x8<<20 | 0xa<<8 | 0<<4 +	case ASQRTD: +		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xb<<8 | 0xc<<4 +	case ASQRTF: +		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xa<<8 | 0xc<<4 +	case AABSD: +		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xb<<8 | 0xc<<4 +	case AABSF: +		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xa<<8 | 0xc<<4 +	case ANEGD: +		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xb<<8 | 0x4<<4 +	case ANEGF: +		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xa<<8 | 0x4<<4 +	case ACMPD: +		return o | 0xe<<24 | 0xb<<20 | 4<<16 | 0xb<<8 | 0xc<<4 +	case ACMPF: +		return o | 0xe<<24 | 0xb<<20 | 4<<16 | 0xa<<8 | 0xc<<4 + +	case AMOVF: +		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xa<<8 | 4<<4 +	case AMOVD: +		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xb<<8 | 4<<4 + +	case AMOVDF: +		return o | 0xe<<24 | 0xb<<20 | 7<<16 | 0xa<<8 | 0xc<<4 | 1<<8 // dtof +	case AMOVFD: +		return o | 0xe<<24 | 0xb<<20 | 7<<16 | 0xa<<8 | 0xc<<4 | 0<<8 // dtof + +	case AMOVWF: +		if sc&C_UBIT == 0 { +			o |= 1 << 7 /* signed */ +		} +		return o | 0xe<<24 | 0xb<<20 | 8<<16 | 0xa<<8 | 4<<4 | 0<<18 | 0<<8 // toint, double + +	case AMOVWD: +		if sc&C_UBIT == 0 { +			o |= 1 << 7 /* signed */ +		} +		return o | 0xe<<24 | 0xb<<20 | 8<<16 | 0xa<<8 | 4<<4 | 0<<18 | 1<<8 // toint, double + +	case AMOVFW: +		if sc&C_UBIT == 0 { +			o |= 1 << 16 /* signed */ +		} +		return o | 0xe<<24 | 0xb<<20 | 8<<16 | 0xa<<8 | 4<<4 | 1<<18 | 0<<8 | 1<<7 // toint, double, trunc + +	case AMOVDW: +		if sc&C_UBIT == 0 { +			o |= 1 << 16 /* signed */ +		} +		return o | 0xe<<24 | 0xb<<20 | 8<<16 | 0xa<<8 | 4<<4 | 1<<18 | 1<<8 | 1<<7 // toint, double, trunc + +	case -AMOVWF: // copy WtoF +		return o | 0xe<<24 | 0x0<<20 | 0xb<<8 | 1<<4 + +	case -AMOVFW: // copy FtoW +		return o | 0xe<<24 | 0x1<<20 | 0xb<<8 | 1<<4 + +	case -ACMP: // cmp imm +		return o | 0x3<<24 | 0x5<<20 + +	case ABFX: +		return o | 0x3d<<21 | 0x5<<4 + +	case ABFXU: +		return o | 0x3f<<21 | 0x5<<4 + +	case ABFC: +		return o | 0x3e<<21 | 0x1f + +	case ABFI: +		return o | 0x3e<<21 | 0x1<<4 + +	case AXTAB: +		return o | 0x6a<<20 | 0x7<<4 + +	case AXTAH: +		return o | 0x6b<<20 | 0x7<<4 + +	case AXTABU: +		return o | 0x6e<<20 | 0x7<<4 + +	case AXTAHU: +		return o | 0x6f<<20 | 0x7<<4 + +		// CLZ doesn't support .nil +	case ACLZ: +		return o&(0xf<<28) | 0x16f<<16 | 0xf1<<4 + +	case AREV: +		return o&(0xf<<28) | 0x6bf<<16 | 0xf3<<4 + +	case AREV16: +		return o&(0xf<<28) | 0x6bf<<16 | 0xfb<<4 + +	case AREVSH: +		return o&(0xf<<28) | 0x6ff<<16 | 0xfb<<4 + +	case ARBIT: +		return o&(0xf<<28) | 0x6ff<<16 | 0xf3<<4 + +	case AMULWT: +		return o&(0xf<<28) | 0x12<<20 | 0xe<<4 + +	case AMULWB: +		return o&(0xf<<28) | 0x12<<20 | 0xa<<4 + +	case AMULBB: +		return o&(0xf<<28) | 0x16<<20 | 0x8<<4 + +	case AMULAWT: +		return o&(0xf<<28) | 0x12<<20 | 0xc<<4 + +	case AMULAWB: +		return o&(0xf<<28) | 0x12<<20 | 0x8<<4 + +	case AMULABB: +		return o&(0xf<<28) | 0x10<<20 | 0x8<<4 + +	case ABL: // BLX REG +		return o&(0xf<<28) | 0x12fff3<<4 +	} + +	c.ctxt.Diag("%v: bad rrr %d", p, a) +	return 0 +} + +func (c *ctxt5) opbra(p *obj.Prog, a obj.As, sc int) uint32 { +	sc &= C_SCOND +	sc ^= C_SCOND_XOR +	if a == ABL || a == obj.ADUFFZERO || a == obj.ADUFFCOPY { +		return uint32(sc)<<28 | 0x5<<25 | 0x1<<24 +	} +	if sc != 0xe { +		c.ctxt.Diag("%v: .COND on bcond instruction", p) +	} +	switch a { +	case ABEQ: +		return 0x0<<28 | 0x5<<25 +	case ABNE: +		return 0x1<<28 | 0x5<<25 +	case ABCS: +		return 0x2<<28 | 0x5<<25 +	case ABHS: +		return 0x2<<28 | 0x5<<25 +	case ABCC: +		return 0x3<<28 | 0x5<<25 +	case ABLO: +		return 0x3<<28 | 0x5<<25 +	case ABMI: +		return 0x4<<28 | 0x5<<25 +	case ABPL: +		return 0x5<<28 | 0x5<<25 +	case ABVS: +		return 0x6<<28 | 0x5<<25 +	case ABVC: +		return 0x7<<28 | 0x5<<25 +	case ABHI: +		return 0x8<<28 | 0x5<<25 +	case ABLS: +		return 0x9<<28 | 0x5<<25 +	case ABGE: +		return 0xa<<28 | 0x5<<25 +	case ABLT: +		return 0xb<<28 | 0x5<<25 +	case ABGT: +		return 0xc<<28 | 0x5<<25 +	case ABLE: +		return 0xd<<28 | 0x5<<25 +	case AB: +		return 0xe<<28 | 0x5<<25 +	} + +	c.ctxt.Diag("%v: bad bra %v", p, a) +	return 0 +} + +func (c *ctxt5) olr(v int32, b int, r int, sc int) uint32 { +	o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28 +	if sc&C_PBIT == 0 { +		o |= 1 << 24 +	} +	if sc&C_UBIT == 0 { +		o |= 1 << 23 +	} +	if sc&C_WBIT != 0 { +		o |= 1 << 21 +	} +	o |= 1<<26 | 1<<20 +	if v < 0 { +		if sc&C_UBIT != 0 { +			c.ctxt.Diag(".U on neg offset") +		} +		v = -v +		o ^= 1 << 23 +	} + +	if v >= 1<<12 || v < 0 { +		c.ctxt.Diag("literal span too large: %d (R%d)\n%v", v, b, c.printp) +	} +	o |= uint32(v) +	o |= (uint32(b) & 15) << 16 +	o |= (uint32(r) & 15) << 12 +	return o +} + +func (c *ctxt5) olhr(v int32, b int, r int, sc int) uint32 { +	o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28 +	if sc&C_PBIT == 0 { +		o |= 1 << 24 +	} +	if sc&C_WBIT != 0 { +		o |= 1 << 21 +	} +	o |= 1<<23 | 1<<20 | 0xb<<4 +	if v < 0 { +		v = -v +		o ^= 1 << 23 +	} + +	if v >= 1<<8 || v < 0 { +		c.ctxt.Diag("literal span too large: %d (R%d)\n%v", v, b, c.printp) +	} +	o |= uint32(v)&0xf | (uint32(v)>>4)<<8 | 1<<22 +	o |= (uint32(b) & 15) << 16 +	o |= (uint32(r) & 15) << 12 +	return o +} + +func (c *ctxt5) osr(a obj.As, r int, v int32, b int, sc int) uint32 { +	o := c.olr(v, b, r, sc) ^ (1 << 20) +	if a != AMOVW { +		o |= 1 << 22 +	} +	return o +} + +func (c *ctxt5) oshr(r int, v int32, b int, sc int) uint32 { +	o := c.olhr(v, b, r, sc) ^ (1 << 20) +	return o +} + +func (c *ctxt5) osrr(r int, i int, b int, sc int) uint32 { +	return c.olr(int32(i), b, r, sc) ^ (1<<25 | 1<<20) +} + +func (c *ctxt5) oshrr(r int, i int, b int, sc int) uint32 { +	return c.olhr(int32(i), b, r, sc) ^ (1<<22 | 1<<20) +} + +func (c *ctxt5) olrr(i int, b int, r int, sc int) uint32 { +	return c.olr(int32(i), b, r, sc) ^ (1 << 25) +} + +func (c *ctxt5) olhrr(i int, b int, r int, sc int) uint32 { +	return c.olhr(int32(i), b, r, sc) ^ (1 << 22) +} + +func (c *ctxt5) ofsr(a obj.As, r int, v int32, b int, sc int, p *obj.Prog) uint32 { +	o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28 +	if sc&C_PBIT == 0 { +		o |= 1 << 24 +	} +	if sc&C_WBIT != 0 { +		o |= 1 << 21 +	} +	o |= 6<<25 | 1<<24 | 1<<23 | 10<<8 +	if v < 0 { +		v = -v +		o ^= 1 << 23 +	} + +	if v&3 != 0 { +		c.ctxt.Diag("odd offset for floating point op: %d\n%v", v, p) +	} else if v >= 1<<10 || v < 0 { +		c.ctxt.Diag("literal span too large: %d\n%v", v, p) +	} +	o |= (uint32(v) >> 2) & 0xFF +	o |= (uint32(b) & 15) << 16 +	o |= (uint32(r) & 15) << 12 + +	switch a { +	default: +		c.ctxt.Diag("bad fst %v", a) +		fallthrough + +	case AMOVD: +		o |= 1 << 8 +		fallthrough + +	case AMOVF: +		break +	} + +	return o +} + +// MOVW $"lower 16-bit", Reg +func (c *ctxt5) omvs(p *obj.Prog, a *obj.Addr, dr int) uint32 { +	o1 := ((uint32(p.Scond) & C_SCOND) ^ C_SCOND_XOR) << 28 +	o1 |= 0x30 << 20 +	o1 |= (uint32(dr) & 15) << 12 +	o1 |= uint32(a.Offset) & 0x0fff +	o1 |= (uint32(a.Offset) & 0xf000) << 4 +	return o1 +} + +// MVN $C_NCON, Reg -> MOVW $C_RCON, Reg +func (c *ctxt5) omvr(p *obj.Prog, a *obj.Addr, dr int) uint32 { +	o1 := c.oprrr(p, AMOVW, int(p.Scond)) +	o1 |= (uint32(dr) & 15) << 12 +	v := immrot(^uint32(a.Offset)) +	if v == 0 { +		c.ctxt.Diag("%v: missing literal", p) +		return 0 +	} +	o1 |= uint32(v) +	return o1 +} + +func (c *ctxt5) omvl(p *obj.Prog, a *obj.Addr, dr int) uint32 { +	var o1 uint32 +	if p.Pool == nil { +		c.aclass(a) +		v := immrot(^uint32(c.instoffset)) +		if v == 0 { +			c.ctxt.Diag("%v: missing literal", p) +			return 0 +		} + +		o1 = c.oprrr(p, AMVN, int(p.Scond)&C_SCOND) +		o1 |= uint32(v) +		o1 |= (uint32(dr) & 15) << 12 +	} else { +		v := int32(p.Pool.Pc - p.Pc - 8) +		o1 = c.olr(v, REGPC, dr, int(p.Scond)&C_SCOND) +	} + +	return o1 +} + +func (c *ctxt5) chipzero5(e float64) int { +	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. +	if objabi.GOARM < 7 || math.Float64bits(e) != 0 { +		return -1 +	} +	return 0 +} + +func (c *ctxt5) chipfloat5(e float64) int { +	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. +	if objabi.GOARM < 7 { +		return -1 +	} + +	ei := math.Float64bits(e) +	l := uint32(ei) +	h := uint32(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 +} + +func nocache(p *obj.Prog) { +	p.Optab = 0 +	p.From.Class = 0 +	if p.GetFrom3() != nil { +		p.GetFrom3().Class = 0 +	} +	p.To.Class = 0 +} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/list5.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/list5.go new file mode 100644 index 000000000..30e03001a --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/list5.go @@ -0,0 +1,124 @@ +// Inferno utils/5c/list.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/list.c +// +//	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 arm + +import ( +	"github.com/twitchyliquid64/golang-asm/obj" +	"fmt" +) + +func init() { +	obj.RegisterRegister(obj.RBaseARM, MAXREG, rconv) +	obj.RegisterOpcode(obj.ABaseARM, Anames) +	obj.RegisterRegisterList(obj.RegListARMLo, obj.RegListARMHi, rlconv) +	obj.RegisterOpSuffix("arm", obj.CConvARM) +} + +func rconv(r int) string { +	if r == 0 { +		return "NONE" +	} +	if r == REGG { +		// Special case. +		return "g" +	} +	if REG_R0 <= r && r <= REG_R15 { +		return fmt.Sprintf("R%d", r-REG_R0) +	} +	if REG_F0 <= r && r <= REG_F15 { +		return fmt.Sprintf("F%d", r-REG_F0) +	} + +	switch r { +	case REG_FPSR: +		return "FPSR" + +	case REG_FPCR: +		return "FPCR" + +	case REG_CPSR: +		return "CPSR" + +	case REG_SPSR: +		return "SPSR" + +	case REG_MB_SY: +		return "MB_SY" +	case REG_MB_ST: +		return "MB_ST" +	case REG_MB_ISH: +		return "MB_ISH" +	case REG_MB_ISHST: +		return "MB_ISHST" +	case REG_MB_NSH: +		return "MB_NSH" +	case REG_MB_NSHST: +		return "MB_NSHST" +	case REG_MB_OSH: +		return "MB_OSH" +	case REG_MB_OSHST: +		return "MB_OSHST" +	} + +	return fmt.Sprintf("Rgok(%d)", r-obj.RBaseARM) +} + +func DRconv(a int) string { +	s := "C_??" +	if a >= C_NONE && a <= C_NCLASS { +		s = cnames5[a] +	} +	var fp string +	fp += s +	return fp +} + +func rlconv(list int64) string { +	str := "" +	for i := 0; i < 16; i++ { +		if list&(1<<uint(i)) != 0 { +			if str == "" { +				str += "[" +			} else { +				str += "," +			} +			// This is ARM-specific; R10 is g. +			if i == REGG-REG_R0 { +				str += "g" +			} else { +				str += fmt.Sprintf("R%d", i) +			} +		} +	} + +	str += "]" +	return str +} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/obj5.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/obj5.go new file mode 100644 index 000000000..50e0698a3 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/arm/obj5.go @@ -0,0 +1,784 @@ +// Derived from Inferno utils/5c/swt.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/swt.c +// +//	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 arm + +import ( +	"github.com/twitchyliquid64/golang-asm/obj" +	"github.com/twitchyliquid64/golang-asm/objabi" +	"github.com/twitchyliquid64/golang-asm/sys" +) + +var progedit_tlsfallback *obj.LSym + +func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { +	p.From.Class = 0 +	p.To.Class = 0 + +	c := ctxt5{ctxt: ctxt, newprog: newprog} + +	// Rewrite B/BL to symbol as TYPE_BRANCH. +	switch p.As { +	case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY: +		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { +			p.To.Type = obj.TYPE_BRANCH +		} +	} + +	// Replace TLS register fetches on older ARM processors. +	switch p.As { +	// Treat MRC 15, 0, <reg>, C13, C0, 3 specially. +	case AMRC: +		if p.To.Offset&0xffff0fff == 0xee1d0f70 { +			// Because the instruction might be rewritten to a BL which returns in R0 +			// the register must be zero. +			if p.To.Offset&0xf000 != 0 { +				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) +			} + +			if objabi.GOARM < 7 { +				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. +				if progedit_tlsfallback == nil { +					progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback") +				} + +				// MOVW	LR, R11 +				p.As = AMOVW + +				p.From.Type = obj.TYPE_REG +				p.From.Reg = REGLINK +				p.To.Type = obj.TYPE_REG +				p.To.Reg = REGTMP + +				// BL	runtime.read_tls_fallback(SB) +				p = obj.Appendp(p, newprog) + +				p.As = ABL +				p.To.Type = obj.TYPE_BRANCH +				p.To.Sym = progedit_tlsfallback +				p.To.Offset = 0 + +				// MOVW	R11, LR +				p = obj.Appendp(p, newprog) + +				p.As = AMOVW +				p.From.Type = obj.TYPE_REG +				p.From.Reg = REGTMP +				p.To.Type = obj.TYPE_REG +				p.To.Reg = REGLINK +				break +			} +		} + +		// Otherwise, MRC/MCR instructions need no further treatment. +		p.As = AWORD +	} + +	// Rewrite float constants to values stored in memory. +	switch p.As { +	case AMOVF: +		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { +			f32 := float32(p.From.Val.(float64)) +			p.From.Type = obj.TYPE_MEM +			p.From.Sym = ctxt.Float32Sym(f32) +			p.From.Name = obj.NAME_EXTERN +			p.From.Offset = 0 +		} + +	case AMOVD: +		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { +			p.From.Type = obj.TYPE_MEM +			p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64)) +			p.From.Name = obj.NAME_EXTERN +			p.From.Offset = 0 +		} +	} + +	if ctxt.Flag_dynlink { +		c.rewriteToUseGot(p) +	} +} + +// Rewrite p, if necessary, to access global data via the global offset table. +func (c *ctxt5) rewriteToUseGot(p *obj.Prog) { +	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { +		//     ADUFFxxx $offset +		// becomes +		//     MOVW runtime.duffxxx@GOT, R9 +		//     ADD $offset, R9 +		//     CALL (R9) +		var sym *obj.LSym +		if p.As == obj.ADUFFZERO { +			sym = c.ctxt.Lookup("runtime.duffzero") +		} else { +			sym = c.ctxt.Lookup("runtime.duffcopy") +		} +		offset := p.To.Offset +		p.As = AMOVW +		p.From.Type = obj.TYPE_MEM +		p.From.Name = obj.NAME_GOTREF +		p.From.Sym = sym +		p.To.Type = obj.TYPE_REG +		p.To.Reg = REG_R9 +		p.To.Name = obj.NAME_NONE +		p.To.Offset = 0 +		p.To.Sym = nil +		p1 := obj.Appendp(p, c.newprog) +		p1.As = AADD +		p1.From.Type = obj.TYPE_CONST +		p1.From.Offset = offset +		p1.To.Type = obj.TYPE_REG +		p1.To.Reg = REG_R9 +		p2 := obj.Appendp(p1, c.newprog) +		p2.As = obj.ACALL +		p2.To.Type = obj.TYPE_MEM +		p2.To.Reg = REG_R9 +		return +	} + +	// We only care about global data: NAME_EXTERN means a global +	// symbol in the Go sense, and p.Sym.Local is true for a few +	// internally defined symbols. +	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { +		// MOVW $sym, Rx becomes MOVW sym@GOT, Rx +		// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx +		if p.As != AMOVW { +			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) +		} +		if p.To.Type != obj.TYPE_REG { +			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) +		} +		p.From.Type = obj.TYPE_MEM +		p.From.Name = obj.NAME_GOTREF +		if p.From.Offset != 0 { +			q := obj.Appendp(p, c.newprog) +			q.As = AADD +			q.From.Type = obj.TYPE_CONST +			q.From.Offset = p.From.Offset +			q.To = p.To +			p.From.Offset = 0 +		} +	} +	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { +		c.ctxt.Diag("don't know how to handle %v with -dynlink", p) +	} +	var source *obj.Addr +	// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry +	// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9) +	// An addition may be inserted between the two MOVs if there is an offset. +	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { +		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { +			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) +		} +		source = &p.From +	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { +		source = &p.To +	} else { +		return +	} +	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { +		return +	} +	if source.Sym.Type == objabi.STLSBSS { +		return +	} +	if source.Type != obj.TYPE_MEM { +		c.ctxt.Diag("don't know how to handle %v with -dynlink", p) +	} +	p1 := obj.Appendp(p, c.newprog) +	p2 := obj.Appendp(p1, c.newprog) + +	p1.As = AMOVW +	p1.From.Type = obj.TYPE_MEM +	p1.From.Sym = source.Sym +	p1.From.Name = obj.NAME_GOTREF +	p1.To.Type = obj.TYPE_REG +	p1.To.Reg = REG_R9 + +	p2.As = p.As +	p2.From = p.From +	p2.To = p.To +	if p.From.Name == obj.NAME_EXTERN { +		p2.From.Reg = REG_R9 +		p2.From.Name = obj.NAME_NONE +		p2.From.Sym = nil +	} else if p.To.Name == obj.NAME_EXTERN { +		p2.To.Reg = REG_R9 +		p2.To.Name = obj.NAME_NONE +		p2.To.Sym = nil +	} else { +		return +	} +	obj.Nopout(p) +} + +// Prog.mark +const ( +	FOLL  = 1 << 0 +	LABEL = 1 << 1 +	LEAF  = 1 << 2 +) + +func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { +	autosize := int32(0) + +	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { +		return +	} + +	c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog} + +	p := c.cursym.Func.Text +	autoffset := int32(p.To.Offset) +	if autoffset == -4 { +		// Historical way to mark NOFRAME. +		p.From.Sym.Set(obj.AttrNoFrame, true) +		autoffset = 0 +	} +	if autoffset < 0 || autoffset%4 != 0 { +		c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset) +	} +	if p.From.Sym.NoFrame() { +		if autoffset != 0 { +			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset) +		} +	} + +	cursym.Func.Locals = autoffset +	cursym.Func.Args = p.To.Val.(int32) + +	/* +	 * find leaf subroutines +	 */ +	for p := cursym.Func.Text; p != nil; p = p.Link { +		switch p.As { +		case obj.ATEXT: +			p.Mark |= LEAF + +		case ADIV, ADIVU, AMOD, AMODU: +			cursym.Func.Text.Mark &^= LEAF + +		case ABL, +			ABX, +			obj.ADUFFZERO, +			obj.ADUFFCOPY: +			cursym.Func.Text.Mark &^= LEAF +		} +	} + +	var q2 *obj.Prog +	for p := cursym.Func.Text; p != nil; p = p.Link { +		o := p.As +		switch o { +		case obj.ATEXT: +			autosize = autoffset + +			if p.Mark&LEAF != 0 && autosize == 0 { +				// A leaf function with no locals has no frame. +				p.From.Sym.Set(obj.AttrNoFrame, true) +			} + +			if !p.From.Sym.NoFrame() { +				// If there is a stack frame at all, it includes +				// space to save the LR. +				autosize += 4 +			} + +			if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 { +				// A very few functions that do not return to their caller +				// are not identified as leaves but still have no frame. +				if ctxt.Debugvlog { +					ctxt.Logf("save suppressed in: %s\n", cursym.Name) +				} + +				cursym.Func.Text.Mark |= LEAF +			} + +			// FP offsets need an updated p.To.Offset. +			p.To.Offset = int64(autosize) - 4 + +			if cursym.Func.Text.Mark&LEAF != 0 { +				cursym.Set(obj.AttrLeaf, true) +				if p.From.Sym.NoFrame() { +					break +				} +			} + +			if !p.From.Sym.NoSplit() { +				p = c.stacksplit(p, autosize) // emit split check +			} + +			// MOVW.W		R14,$-autosize(SP) +			p = obj.Appendp(p, c.newprog) + +			p.As = AMOVW +			p.Scond |= C_WBIT +			p.From.Type = obj.TYPE_REG +			p.From.Reg = REGLINK +			p.To.Type = obj.TYPE_MEM +			p.To.Offset = int64(-autosize) +			p.To.Reg = REGSP +			p.Spadj = autosize + +			if cursym.Func.Text.From.Sym.Wrapper() { +				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame +				// +				//	MOVW g_panic(g), R1 +				//	CMP  $0, R1 +				//	B.NE checkargp +				// end: +				//	NOP +				// ... function ... +				// checkargp: +				//	MOVW panic_argp(R1), R2 +				//	ADD  $(autosize+4), R13, R3 +				//	CMP  R2, R3 +				//	B.NE end +				//	ADD  $4, R13, R4 +				//	MOVW R4, panic_argp(R1) +				//	B    end +				// +				// The NOP is needed to give the jumps somewhere to land. +				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. + +				p = obj.Appendp(p, newprog) +				p.As = AMOVW +				p.From.Type = obj.TYPE_MEM +				p.From.Reg = REGG +				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic +				p.To.Type = obj.TYPE_REG +				p.To.Reg = REG_R1 + +				p = obj.Appendp(p, newprog) +				p.As = ACMP +				p.From.Type = obj.TYPE_CONST +				p.From.Offset = 0 +				p.Reg = REG_R1 + +				// B.NE checkargp +				bne := obj.Appendp(p, newprog) +				bne.As = ABNE +				bne.To.Type = obj.TYPE_BRANCH + +				// end: NOP +				end := obj.Appendp(bne, newprog) +				end.As = obj.ANOP + +				// find end of function +				var last *obj.Prog +				for last = end; last.Link != nil; last = last.Link { +				} + +				// MOVW panic_argp(R1), R2 +				mov := obj.Appendp(last, newprog) +				mov.As = AMOVW +				mov.From.Type = obj.TYPE_MEM +				mov.From.Reg = REG_R1 +				mov.From.Offset = 0 // Panic.argp +				mov.To.Type = obj.TYPE_REG +				mov.To.Reg = REG_R2 + +				// B.NE branch target is MOVW above +				bne.To.SetTarget(mov) + +				// ADD $(autosize+4), R13, R3 +				p = obj.Appendp(mov, newprog) +				p.As = AADD +				p.From.Type = obj.TYPE_CONST +				p.From.Offset = int64(autosize) + 4 +				p.Reg = REG_R13 +				p.To.Type = obj.TYPE_REG +				p.To.Reg = REG_R3 + +				// CMP R2, R3 +				p = obj.Appendp(p, newprog) +				p.As = ACMP +				p.From.Type = obj.TYPE_REG +				p.From.Reg = REG_R2 +				p.Reg = REG_R3 + +				// B.NE end +				p = obj.Appendp(p, newprog) +				p.As = ABNE +				p.To.Type = obj.TYPE_BRANCH +				p.To.SetTarget(end) + +				// ADD $4, R13, R4 +				p = obj.Appendp(p, newprog) +				p.As = AADD +				p.From.Type = obj.TYPE_CONST +				p.From.Offset = 4 +				p.Reg = REG_R13 +				p.To.Type = obj.TYPE_REG +				p.To.Reg = REG_R4 + +				// MOVW R4, panic_argp(R1) +				p = obj.Appendp(p, newprog) +				p.As = AMOVW +				p.From.Type = obj.TYPE_REG +				p.From.Reg = REG_R4 +				p.To.Type = obj.TYPE_MEM +				p.To.Reg = REG_R1 +				p.To.Offset = 0 // Panic.argp + +				// B end +				p = obj.Appendp(p, newprog) +				p.As = AB +				p.To.Type = obj.TYPE_BRANCH +				p.To.SetTarget(end) + +				// reset for subsequent passes +				p = end +			} + +		case obj.ARET: +			nocache(p) +			if cursym.Func.Text.Mark&LEAF != 0 { +				if autosize == 0 { +					p.As = AB +					p.From = obj.Addr{} +					if p.To.Sym != nil { // retjmp +						p.To.Type = obj.TYPE_BRANCH +					} else { +						p.To.Type = obj.TYPE_MEM +						p.To.Offset = 0 +						p.To.Reg = REGLINK +					} + +					break +				} +			} + +			p.As = AMOVW +			p.Scond |= C_PBIT +			p.From.Type = obj.TYPE_MEM +			p.From.Offset = int64(autosize) +			p.From.Reg = REGSP +			p.To.Type = obj.TYPE_REG +			p.To.Reg = REGPC + +			// If there are instructions following +			// this ARET, they come from a branch +			// with the same stackframe, so no spadj. +			if p.To.Sym != nil { // retjmp +				p.To.Reg = REGLINK +				q2 = obj.Appendp(p, newprog) +				q2.As = AB +				q2.To.Type = obj.TYPE_BRANCH +				q2.To.Sym = p.To.Sym +				p.To.Sym = nil +				p = q2 +			} + +		case AADD: +			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { +				p.Spadj = int32(-p.From.Offset) +			} + +		case ASUB: +			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { +				p.Spadj = int32(p.From.Offset) +			} + +		case ADIV, ADIVU, AMOD, AMODU: +			if cursym.Func.Text.From.Sym.NoSplit() { +				ctxt.Diag("cannot divide in NOSPLIT function") +			} +			const debugdivmod = false +			if debugdivmod { +				break +			} +			if p.From.Type != obj.TYPE_REG { +				break +			} +			if p.To.Type != obj.TYPE_REG { +				break +			} + +			// Make copy because we overwrite p below. +			q1 := *p +			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { +				ctxt.Diag("div already using REGTMP: %v", p) +			} + +			/* MOV m(g),REGTMP */ +			p.As = AMOVW +			p.Pos = q1.Pos +			p.From.Type = obj.TYPE_MEM +			p.From.Reg = REGG +			p.From.Offset = 6 * 4 // offset of g.m +			p.Reg = 0 +			p.To.Type = obj.TYPE_REG +			p.To.Reg = REGTMP + +			/* MOV a,m_divmod(REGTMP) */ +			p = obj.Appendp(p, newprog) +			p.As = AMOVW +			p.Pos = q1.Pos +			p.From.Type = obj.TYPE_REG +			p.From.Reg = q1.From.Reg +			p.To.Type = obj.TYPE_MEM +			p.To.Reg = REGTMP +			p.To.Offset = 8 * 4 // offset of m.divmod + +			/* MOV b, R8 */ +			p = obj.Appendp(p, newprog) +			p.As = AMOVW +			p.Pos = q1.Pos +			p.From.Type = obj.TYPE_REG +			p.From.Reg = q1.Reg +			if q1.Reg == 0 { +				p.From.Reg = q1.To.Reg +			} +			p.To.Type = obj.TYPE_REG +			p.To.Reg = REG_R8 +			p.To.Offset = 0 + +			/* CALL appropriate */ +			p = obj.Appendp(p, newprog) +			p.As = ABL +			p.Pos = q1.Pos +			p.To.Type = obj.TYPE_BRANCH +			switch o { +			case ADIV: +				p.To.Sym = symdiv +			case ADIVU: +				p.To.Sym = symdivu +			case AMOD: +				p.To.Sym = symmod +			case AMODU: +				p.To.Sym = symmodu +			} + +			/* MOV REGTMP, b */ +			p = obj.Appendp(p, newprog) +			p.As = AMOVW +			p.Pos = q1.Pos +			p.From.Type = obj.TYPE_REG +			p.From.Reg = REGTMP +			p.From.Offset = 0 +			p.To.Type = obj.TYPE_REG +			p.To.Reg = q1.To.Reg + +		case AMOVW: +			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { +				p.Spadj = int32(-p.To.Offset) +			} +			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { +				p.Spadj = int32(-p.From.Offset) +			} +			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { +				p.Spadj = int32(-p.From.Offset) +			} + +		case obj.AGETCALLERPC: +			if cursym.Leaf() { +				/* MOVW LR, Rd */ +				p.As = AMOVW +				p.From.Type = obj.TYPE_REG +				p.From.Reg = REGLINK +			} else { +				/* MOVW (RSP), Rd */ +				p.As = AMOVW +				p.From.Type = obj.TYPE_MEM +				p.From.Reg = REGSP +			} +		} +	} +} + +func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { +	// MOVW g_stackguard(g), R1 +	p = obj.Appendp(p, c.newprog) + +	p.As = AMOVW +	p.From.Type = obj.TYPE_MEM +	p.From.Reg = REGG +	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 +	if c.cursym.CFunc() { +		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 +	} +	p.To.Type = obj.TYPE_REG +	p.To.Reg = REG_R1 + +	// Mark the stack bound check and morestack call async nonpreemptible. +	// If we get preempted here, when resumed the preemption request is +	// cleared, but we'll still call morestack, which will double the stack +	// unnecessarily. See issue #35470. +	p = c.ctxt.StartUnsafePoint(p, c.newprog) + +	if framesize <= objabi.StackSmall { +		// small stack: SP < stackguard +		//	CMP	stackguard, SP +		p = obj.Appendp(p, c.newprog) + +		p.As = ACMP +		p.From.Type = obj.TYPE_REG +		p.From.Reg = REG_R1 +		p.Reg = REGSP +	} else if framesize <= objabi.StackBig { +		// large stack: SP-framesize < stackguard-StackSmall +		//	MOVW $-(framesize-StackSmall)(SP), R2 +		//	CMP stackguard, R2 +		p = obj.Appendp(p, c.newprog) + +		p.As = AMOVW +		p.From.Type = obj.TYPE_ADDR +		p.From.Reg = REGSP +		p.From.Offset = -(int64(framesize) - objabi.StackSmall) +		p.To.Type = obj.TYPE_REG +		p.To.Reg = REG_R2 + +		p = obj.Appendp(p, c.newprog) +		p.As = ACMP +		p.From.Type = obj.TYPE_REG +		p.From.Reg = REG_R1 +		p.Reg = REG_R2 +	} else { +		// Such a large stack we need to protect against wraparound +		// if SP is close to zero. +		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) +		// The +StackGuard on both sides is required to keep the left side positive: +		// SP is allowed to be slightly below stackguard. See stack.h. +		//	CMP     $StackPreempt, R1 +		//	MOVW.NE $StackGuard(SP), R2 +		//	SUB.NE  R1, R2 +		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 +		//	CMP.NE  R3, R2 +		p = obj.Appendp(p, c.newprog) + +		p.As = ACMP +		p.From.Type = obj.TYPE_CONST +		p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1))) +		p.Reg = REG_R1 + +		p = obj.Appendp(p, c.newprog) +		p.As = AMOVW +		p.From.Type = obj.TYPE_ADDR +		p.From.Reg = REGSP +		p.From.Offset = int64(objabi.StackGuard) +		p.To.Type = obj.TYPE_REG +		p.To.Reg = REG_R2 +		p.Scond = C_SCOND_NE + +		p = obj.Appendp(p, c.newprog) +		p.As = ASUB +		p.From.Type = obj.TYPE_REG +		p.From.Reg = REG_R1 +		p.To.Type = obj.TYPE_REG +		p.To.Reg = REG_R2 +		p.Scond = C_SCOND_NE + +		p = obj.Appendp(p, c.newprog) +		p.As = AMOVW +		p.From.Type = obj.TYPE_ADDR +		p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall) +		p.To.Type = obj.TYPE_REG +		p.To.Reg = REG_R3 +		p.Scond = C_SCOND_NE + +		p = obj.Appendp(p, c.newprog) +		p.As = ACMP +		p.From.Type = obj.TYPE_REG +		p.From.Reg = REG_R3 +		p.Reg = REG_R2 +		p.Scond = C_SCOND_NE +	} + +	// BLS call-to-morestack +	bls := obj.Appendp(p, c.newprog) +	bls.As = ABLS +	bls.To.Type = obj.TYPE_BRANCH + +	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) + +	var last *obj.Prog +	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { +	} + +	// Now we are at the end of the function, but logically +	// we are still in function prologue. We need to fix the +	// SP data and PCDATA. +	spfix := obj.Appendp(last, c.newprog) +	spfix.As = obj.ANOP +	spfix.Spadj = -framesize + +	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog) +	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog) + +	// MOVW	LR, R3 +	movw := obj.Appendp(pcdata, c.newprog) +	movw.As = AMOVW +	movw.From.Type = obj.TYPE_REG +	movw.From.Reg = REGLINK +	movw.To.Type = obj.TYPE_REG +	movw.To.Reg = REG_R3 + +	bls.To.SetTarget(movw) + +	// BL runtime.morestack +	call := obj.Appendp(movw, c.newprog) +	call.As = obj.ACALL +	call.To.Type = obj.TYPE_BRANCH +	morestack := "runtime.morestack" +	switch { +	case c.cursym.CFunc(): +		morestack = "runtime.morestackc" +	case !c.cursym.Func.Text.From.Sym.NeedCtxt(): +		morestack = "runtime.morestack_noctxt" +	} +	call.To.Sym = c.ctxt.Lookup(morestack) + +	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1) + +	// B start +	b := obj.Appendp(pcdata, c.newprog) +	b.As = obj.AJMP +	b.To.Type = obj.TYPE_BRANCH +	b.To.SetTarget(c.cursym.Func.Text.Link) +	b.Spadj = +framesize + +	return end +} + +var unaryDst = map[obj.As]bool{ +	ASWI:  true, +	AWORD: true, +} + +var Linkarm = obj.LinkArch{ +	Arch:           sys.ArchARM, +	Init:           buildop, +	Preprocess:     preprocess, +	Assemble:       span5, +	Progedit:       progedit, +	UnaryDst:       unaryDst, +	DWARFRegisters: ARMDWARFRegisters, +}  | 
