summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/arm
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/internal/obj/arm')
-rw-r--r--src/cmd/internal/obj/arm/a.out.go410
-rw-r--r--src/cmd/internal/obj/arm/anames.go144
-rw-r--r--src/cmd/internal/obj/arm/anames5.go77
-rw-r--r--src/cmd/internal/obj/arm/asm5.go3123
-rw-r--r--src/cmd/internal/obj/arm/list5.go124
-rw-r--r--src/cmd/internal/obj/arm/obj5.go839
6 files changed, 4717 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/arm/a.out.go b/src/cmd/internal/obj/arm/a.out.go
new file mode 100644
index 0000000..fd695ad
--- /dev/null
+++ b/src/cmd/internal/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 "cmd/internal/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 /* <=0xff rotated constant offset from auto */
+ C_LACON /* Large Auto CONstant, i.e. large offset from SP */
+
+ 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/src/cmd/internal/obj/arm/anames.go b/src/cmd/internal/obj/arm/anames.go
new file mode 100644
index 0000000..f5e92de
--- /dev/null
+++ b/src/cmd/internal/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 "cmd/internal/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/src/cmd/internal/obj/arm/anames5.go b/src/cmd/internal/obj/arm/anames5.go
new file mode 100644
index 0000000..78fcd55
--- /dev/null
+++ b/src/cmd/internal/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/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
new file mode 100644
index 0000000..4e6eff9
--- /dev/null
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -0,0 +1,3123 @@
+// 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 (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "fmt"
+ "internal/buildcfg"
+ "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 (
+ 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_HOREG,
+ C_FOREG,
+ C_HFOREG,
+ C_SOREG,
+ C_ROREG,
+ C_SROREG,
+ C_LOREG,
+ C_HAUTO,
+ C_FAUTO,
+ C_HFAUTO,
+ 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 && buildcfg.GOARM.Version == 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)
+ }
+
+ if (p.As == ASRL || p.As == ASRA) && p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
+ // Right shifts are weird - a shift that looks like "shift by constant 0" actually
+ // means "shift by constant 32". Use left shift in this situation instead.
+ // See issue 64715.
+ // TODO: rotate by 0? Not currently supported, but if we ever do then include it here.
+ p.As = ASLL
+ }
+ if p.As != AMOVB && p.As != AMOVBS && p.As != AMOVBU && p.As != AMOVH && p.As != AMOVHS && p.As != AMOVHU && p.As != AXTAB && p.As != AXTABU && p.As != AXTAH && p.As != AXTAHU {
+ // Same here, but for shifts encoded in Addrs.
+ // Don't do it for the extension ops, which
+ // need to keep their RR shifts.
+ fixShift := func(a *obj.Addr) {
+ if a.Type == obj.TYPE_SHIFT {
+ typ := a.Offset & SHIFT_RR
+ isConst := a.Offset&(1<<4) == 0
+ amount := a.Offset >> 7 & 0x1f
+ if isConst && amount == 0 && (typ == SHIFT_LR || typ == SHIFT_AR || typ == SHIFT_RR) {
+ a.Offset -= typ
+ a.Offset += SHIFT_LL
+ }
+ }
+ }
+ fixShift(&p.From)
+ fixShift(&p.To)
+ }
+
+ 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
+ }
+
+ 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.Version=7 and !GOARM.SoftFloat to gate the use of VFPv3 vmov (imm) instructions.
+ if buildcfg.GOARM.Version < 7 || buildcfg.GOARM.SoftFloat || math.Float64bits(e) != 0 {
+ return -1
+ }
+ return 0
+}
+
+func (c *ctxt5) chipfloat5(e float64) int {
+ // We use GOARM.Version=7 and !GOARM.SoftFloat to gate the use of VFPv3 vmov (imm) instructions.
+ if buildcfg.GOARM.Version < 7 || buildcfg.GOARM.SoftFloat {
+ 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/src/cmd/internal/obj/arm/list5.go b/src/cmd/internal/obj/arm/list5.go
new file mode 100644
index 0000000..6d630f6
--- /dev/null
+++ b/src/cmd/internal/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 (
+ "cmd/internal/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/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
new file mode 100644
index 0000000..def4f52
--- /dev/null
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -0,0 +1,839 @@
+// 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 (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "internal/abi"
+ "internal/buildcfg"
+ "log"
+)
+
+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 buildcfg.GOARM.Version < 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.To.Name = obj.NAME_NONE
+ 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
+ }
+ }
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
+ c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
+ }
+}
+
+func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
+ if c.ctxt.Flag_maymorestack != "" {
+ // Save LR and make room for REGCTXT.
+ const frameSize = 8
+ // MOVW.W R14,$-8(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 = -frameSize
+ p.To.Reg = REGSP
+ p.Spadj = frameSize
+
+ // MOVW REGCTXT, 4(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVW
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 4
+ p.To.Reg = REGSP
+
+ // CALL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = obj.ACALL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+
+ // Restore REGCTXT and LR.
+
+ // MOVW 4(SP), REGCTXT
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVW
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 4
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // MOVW.P 8(SP), R14
+ p.As = AMOVW
+ p.Scond |= C_PBIT
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = frameSize
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+ p.Spadj = -frameSize
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
+ // 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 <= abi.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 <= abi.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) - abi.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 underflow.
+ // The runtime guarantees SP > objabi.StackBig, but
+ // framesize is large enough that SP-framesize may
+ // underflow, causing a direct comparison with the
+ // stack guard to incorrectly succeed. We explicitly
+ // guard against underflow.
+ //
+ // // Try subtracting from SP and check for underflow.
+ // // If this underflows, it sets C to 0.
+ // SUB.S $(framesize-StackSmall), SP, R2
+ // // If C is 1 (unsigned >=), compare with guard.
+ // CMP.HS stackguard, R2
+
+ p = obj.Appendp(p, c.newprog)
+ p.As = ASUB
+ p.Scond = C_SBIT
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(framesize) - abi.StackSmall
+ p.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R2
+
+ p = obj.Appendp(p, c.newprog)
+ p.As = ACMP
+ p.Scond = C_SCOND_HS
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REG_R1
+ p.Reg = REG_R2
+ }
+
+ // BLS call-to-morestack (C is 0 or Z is 1)
+ 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(startPred.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,
+}