diff options
| author | Russ Cox <rsc@golang.org> | 2015-01-19 14:34:58 -0500 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2015-01-21 03:02:27 +0000 |
| commit | d6f6e420fcea98a84a9c6a69a7bcf81ddca7ea74 (patch) | |
| tree | 1248e9ea9a782ad165f211f7142ebe9c48bccb4c /src/cmd/internal/obj/arm | |
| parent | db52315c88e588b87895dcb159d1b4886f355e92 (diff) | |
| download | go-d6f6e420fcea98a84a9c6a69a7bcf81ddca7ea74.tar.xz | |
[dev.cc] cmd/internal/obj: convert liblink C to Go
This CL adds the real cmd/internal/obj packages.
Collectively they correspond to the liblink library.
The conversion was done using rsc.io/c2go's run script
at rsc.io/c2go repo version 706fac7.
This is not the final conversion, just the first working draft.
There will be more updates, but this works well enough
to use with go tool objwriter and pass all.bash.
Change-Id: I9359e835425f995a392bb2fcdbebf29511477bed
Reviewed-on: https://go-review.googlesource.com/3046
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/cmd/internal/obj/arm')
| -rw-r--r-- | src/cmd/internal/obj/arm/5.out.go | 305 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm/anames5.go | 196 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm/asm5.go | 3003 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm/list5.go | 311 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm/obj5.go | 1175 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm/util.go | 12 |
6 files changed, 5002 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/arm/5.out.go b/src/cmd/internal/obj/arm/5.out.go new file mode 100644 index 0000000000..a1edfffe03 --- /dev/null +++ b/src/cmd/internal/obj/arm/5.out.go @@ -0,0 +1,305 @@ +// Inferno utils/5c/5.out.h +// http://code.google.com/p/inferno-os/source/browse/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 + +// list[5689].c + +// obj.c + +// objfile.c + +// pass.c + +// pcln.c + +// sym.c + +// TODO(ality): remove this workaround. +// It's here because Pconv in liblink/list?.c references %L. + +const ( + NSNAME = 8 + NSYM = 50 + NREG = 16 +) + +/* -1 disables use of REGARG */ +const ( + REGARG = -1 +) + +const ( + REGRET = 0 + REGEXT = 10 + REGG = REGEXT - 0 + REGM = REGEXT - 1 + REGTMP = 11 + REGSP = 13 + REGLINK = 14 + REGPC = 15 + NFREG = 16 + FREGRET = 0 + FREGEXT = 7 + FREGTMP = 15 +) + +/* compiler allocates register variables F0 up */ +/* compiler allocates external registers F7 down */ +const ( + C_NONE = iota + C_REG + C_REGREG + C_REGREG2 + C_SHIFT + C_FREG + C_PSR + C_FCR + C_RCON + C_NCON + C_SCON + C_LCON + C_LCONADDR + C_ZFCON + C_SFCON + C_LFCON + C_RACON + C_LACON + C_SBRA + C_LBRA + C_HAUTO + C_FAUTO + C_HFAUTO + C_SAUTO + C_LAUTO + C_HOREG + C_FOREG + C_HFOREG + C_SOREG + C_ROREG + C_SROREG + C_LOREG + C_PC + C_SP + C_HREG + C_ADDR + C_GOK + C_NCLASS +) + +const ( + AXXX = iota + AAND + AEOR + ASUB + ARSB + AADD + AADC + ASBC + ARSC + ATST + ATEQ + ACMP + ACMN + AORR + ABIC + AMVN + AB + ABL + 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 + ADIVF + ADIVD + ASQRTF + ASQRTD + AABSF + AABSD + ASRL + ASRA + ASLL + AMULU + ADIVU + AMUL + ADIV + AMOD + AMODU + AMOVB + AMOVBS + AMOVBU + AMOVH + AMOVHS + AMOVHU + AMOVW + AMOVM + ASWPBU + ASWPW + ANOP + ARFE + ASWI + AMULA + ADATA + AGLOBL + AGOK + AHISTORY + ANAME + ARET + ATEXT + AWORD + ADYNT_ + AINIT_ + ABCASE + ACASE + AEND + AMULL + AMULAL + AMULLU + AMULALU + ABX + ABXRET + ADWORD + ASIGNAME + ALDREX + ASTREX + ALDREXD + ASTREXD + APLD + AUNDEF + ACLZ + AMULWT + AMULWB + AMULAWT + AMULAWB + AUSEFIELD + ATYPE + AFUNCDATA + APCDATA + ACHECKNIL + AVARDEF + AVARKILL + ADUFFCOPY + ADUFFZERO + ADATABUNDLE + ADATABUNDLEEND + AMRC + ALAST +) + +/* scond byte */ +const ( + C_SCOND = (1 << 4) - 1 + C_SBIT = 1 << 4 + C_PBIT = 1 << 5 + C_WBIT = 1 << 6 + C_FBIT = 1 << 7 + C_UBIT = 1 << 7 + C_SCOND_EQ = 0 + C_SCOND_NE = 1 + C_SCOND_HS = 2 + C_SCOND_LO = 3 + C_SCOND_MI = 4 + C_SCOND_PL = 5 + C_SCOND_VS = 6 + C_SCOND_VC = 7 + C_SCOND_HI = 8 + C_SCOND_LS = 9 + C_SCOND_GE = 10 + C_SCOND_LT = 11 + C_SCOND_GT = 12 + C_SCOND_LE = 13 + C_SCOND_NONE = 14 + C_SCOND_NV = 15 + SHIFT_LL = 0 << 5 + SHIFT_LR = 1 << 5 + SHIFT_AR = 2 << 5 + SHIFT_RR = 3 << 5 +) + +const ( + D_GOK = 0 + D_NONE = 1 + D_BRANCH = D_NONE + 1 + D_OREG = D_NONE + 2 + D_CONST = D_NONE + 7 + D_FCONST = D_NONE + 8 + D_SCONST = D_NONE + 9 + D_PSR = D_NONE + 10 + D_REG = D_NONE + 12 + D_FREG = D_NONE + 13 + D_FILE = D_NONE + 16 + D_OCONST = D_NONE + 17 + D_FILE1 = D_NONE + 18 + D_SHIFT = D_NONE + 19 + D_FPCR = D_NONE + 20 + D_REGREG = D_NONE + 21 + D_ADDR = D_NONE + 22 + D_SBIG = D_NONE + 23 + D_CONST2 = D_NONE + 24 + D_REGREG2 = D_NONE + 25 + D_EXTERN = D_NONE + 3 + D_STATIC = D_NONE + 4 + D_AUTO = D_NONE + 5 + D_PARAM = D_NONE + 6 + D_LAST = D_NONE + 26 +) + +/* + * this is the ranlib header + */ +var SYMDEF string diff --git a/src/cmd/internal/obj/arm/anames5.go b/src/cmd/internal/obj/arm/anames5.go new file mode 100644 index 0000000000..4c82a2d2e3 --- /dev/null +++ b/src/cmd/internal/obj/arm/anames5.go @@ -0,0 +1,196 @@ +package arm + +var anames5 = []string{ + "XXX", + "AND", + "EOR", + "SUB", + "RSB", + "ADD", + "ADC", + "SBC", + "RSC", + "TST", + "TEQ", + "CMP", + "CMN", + "ORR", + "BIC", + "MVN", + "B", + "BL", + "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", + "DIVF", + "DIVD", + "SQRTF", + "SQRTD", + "ABSF", + "ABSD", + "SRL", + "SRA", + "SLL", + "MULU", + "DIVU", + "MUL", + "DIV", + "MOD", + "MODU", + "MOVB", + "MOVBS", + "MOVBU", + "MOVH", + "MOVHS", + "MOVHU", + "MOVW", + "MOVM", + "SWPBU", + "SWPW", + "NOP", + "RFE", + "SWI", + "MULA", + "DATA", + "GLOBL", + "GOK", + "HISTORY", + "NAME", + "RET", + "TEXT", + "WORD", + "DYNT_", + "INIT_", + "BCASE", + "CASE", + "END", + "MULL", + "MULAL", + "MULLU", + "MULALU", + "BX", + "BXRET", + "DWORD", + "SIGNAME", + "LDREX", + "STREX", + "LDREXD", + "STREXD", + "PLD", + "UNDEF", + "CLZ", + "MULWT", + "MULWB", + "MULAWT", + "MULAWB", + "USEFIELD", + "TYPE", + "FUNCDATA", + "PCDATA", + "CHECKNIL", + "VARDEF", + "VARKILL", + "DUFFCOPY", + "DUFFZERO", + "DATABUNDLE", + "DATABUNDLEEND", + "MRC", + "LAST", +} + +var cnames5 = []string{ + "NONE", + "REG", + "REGREG", + "REGREG2", + "SHIFT", + "FREG", + "PSR", + "FCR", + "RCON", + "NCON", + "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", + "GOK", + "NCLASS", + "SCOND = (1<<4)-1", + "SBIT = 1<<4", + "PBIT = 1<<5", + "WBIT = 1<<6", + "FBIT = 1<<7", + "UBIT = 1<<7", + "SCOND_EQ = 0", + "SCOND_NE = 1", + "SCOND_HS = 2", + "SCOND_LO = 3", + "SCOND_MI = 4", + "SCOND_PL = 5", + "SCOND_VS = 6", + "SCOND_VC = 7", + "SCOND_HI = 8", + "SCOND_LS = 9", + "SCOND_GE = 10", + "SCOND_LT = 11", + "SCOND_GT = 12", + "SCOND_LE = 13", + "SCOND_NONE = 14", + "SCOND_NV = 15", +} + +var dnames5 = []string{ + D_GOK: "GOK", + D_NONE: "NONE", +} diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go new file mode 100644 index 0000000000..6a20cc8aae --- /dev/null +++ b/src/cmd/internal/obj/arm/asm5.go @@ -0,0 +1,3003 @@ +// Inferno utils/5l/span.c +// http://code.google.com/p/inferno-os/source/browse/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" + "fmt" + "log" + "math" + "sort" +) + +type Optab struct { + as uint8 + a1 uint8 + a2 int8 + a3 uint8 + type_ uint8 + size int8 + param int8 + flag int8 + pcrelsiz uint8 +} + +type Oprang struct { + start []Optab + stop []Optab +} + +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 */ + Optab{ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0, 0, 0}, + Optab{ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0, 0, 0}, + Optab{AADD, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0}, + Optab{AADD, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + Optab{AMVN, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + Optab{ACMP, C_REG, C_REG, C_NONE, 1, 4, 0, 0, 0}, + Optab{AADD, C_RCON, C_REG, C_REG, 2, 4, 0, 0, 0}, + Optab{AADD, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0}, + Optab{AMOVW, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0}, + Optab{AMVN, C_RCON, C_NONE, C_REG, 2, 4, 0, 0, 0}, + Optab{ACMP, C_RCON, C_REG, C_NONE, 2, 4, 0, 0, 0}, + Optab{AADD, C_SHIFT, C_REG, C_REG, 3, 4, 0, 0, 0}, + Optab{AADD, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0}, + Optab{AMVN, C_SHIFT, C_NONE, C_REG, 3, 4, 0, 0, 0}, + Optab{ACMP, C_SHIFT, C_REG, C_NONE, 3, 4, 0, 0, 0}, + Optab{AMOVW, C_RACON, C_NONE, C_REG, 4, 4, REGSP, 0, 0}, + Optab{AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL, 0}, + Optab{ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, + Optab{ABX, C_NONE, C_NONE, C_SBRA, 74, 20, 0, 0, 0}, + Optab{ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, + Optab{AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL, 0}, + Optab{ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0, 0, 0}, + Optab{ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0, 0, 0}, + Optab{ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0, 0, 0}, + Optab{ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0, 0, 0}, + Optab{ASLL, C_RCON, C_REG, C_REG, 8, 4, 0, 0, 0}, + Optab{ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0, 0, 0}, + Optab{ASLL, C_REG, C_NONE, C_REG, 9, 4, 0, 0, 0}, + Optab{ASLL, C_REG, C_REG, C_REG, 9, 4, 0, 0, 0}, + Optab{ASWI, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0}, + Optab{ASWI, C_NONE, C_NONE, C_LOREG, 10, 4, 0, 0, 0}, + Optab{ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0, 0, 0}, + Optab{AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0, 0, 0}, + Optab{AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0, 0, 0}, + Optab{AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0, 0, 0}, + Optab{AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0, 0, 0}, + Optab{AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM, 0}, + Optab{AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4}, + Optab{AADD, C_NCON, C_REG, C_REG, 13, 8, 0, 0, 0}, + Optab{AADD, C_NCON, C_NONE, C_REG, 13, 8, 0, 0, 0}, + Optab{AMVN, C_NCON, C_NONE, C_REG, 13, 8, 0, 0, 0}, + Optab{ACMP, C_NCON, C_REG, C_NONE, 13, 8, 0, 0, 0}, + Optab{AADD, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM, 0}, + Optab{AADD, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0}, + Optab{AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM, 0}, + Optab{ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM, 0}, + Optab{AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0, 0, 0}, + Optab{AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + Optab{AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0}, + Optab{AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0, 0, 0}, + Optab{AMUL, C_REG, C_REG, C_REG, 15, 4, 0, 0, 0}, + Optab{AMUL, C_REG, C_NONE, C_REG, 15, 4, 0, 0, 0}, + Optab{ADIV, C_REG, C_REG, C_REG, 16, 4, 0, 0, 0}, + Optab{ADIV, C_REG, C_NONE, C_REG, 16, 4, 0, 0, 0}, + Optab{AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0, 0, 0}, + Optab{AMULA, C_REG, C_REG, C_REGREG2, 17, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0}, + Optab{AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0}, + Optab{AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP, 0, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0, 0, 0}, + Optab{AMOVW, C_SAUTO, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + Optab{AMOVW, C_SOREG, C_NONE, C_REG, 21, 4, 0, 0, 0}, + Optab{AMOVBU, C_SAUTO, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + Optab{AMOVBU, C_SOREG, C_NONE, C_REG, 21, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + Optab{AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + Optab{AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + Optab{AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + Optab{AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVW, C_LAUTO, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, + Optab{AMOVW, C_LOREG, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, + Optab{AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVBU, C_LAUTO, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, + Optab{AMOVBU, C_LOREG, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, + Optab{AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVW, C_LACON, C_NONE, C_REG, 34, 8, REGSP, LFROM, 0}, + Optab{AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_PSR, 36, 4, 0, 0, 0}, + Optab{AMOVW, C_RCON, C_NONE, C_PSR, 37, 4, 0, 0, 0}, + Optab{AMOVM, C_LCON, C_NONE, C_SOREG, 38, 4, 0, 0, 0}, + Optab{AMOVM, C_SOREG, C_NONE, C_LCON, 39, 4, 0, 0, 0}, + Optab{ASWPW, C_SOREG, C_REG, C_REG, 40, 4, 0, 0, 0}, + Optab{ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP, 0, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0, 0, 0}, + Optab{AMOVF, C_FAUTO, C_NONE, C_FREG, 51, 4, REGSP, 0, 0}, + Optab{AMOVF, C_FOREG, C_NONE, C_FREG, 51, 4, 0, 0, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO, 0}, + Optab{AMOVF, C_LAUTO, C_NONE, C_FREG, 53, 12, REGSP, LFROM, 0}, + Optab{AMOVF, C_LOREG, C_NONE, C_FREG, 53, 12, 0, LFROM, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4}, + Optab{AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0}, + Optab{AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0, 0, 0}, + Optab{AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0, 0, 0}, + Optab{AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0, 0, 0}, + Optab{AMOVW, C_SHIFT, C_NONE, C_REG, 59, 4, 0, 0, 0}, + Optab{AMOVBU, C_SHIFT, C_NONE, C_REG, 59, 4, 0, 0, 0}, + Optab{AMOVB, C_SHIFT, C_NONE, C_REG, 60, 4, 0, 0, 0}, + Optab{AMOVBS, C_SHIFT, C_NONE, C_REG, 60, 4, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0, 0, 0}, + Optab{AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0, 0, 0}, + Optab{AMOVBS, C_REG, C_NONE, C_SHIFT, 61, 4, 0, 0, 0}, + Optab{AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0, 0, 0}, + Optab{ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8}, + Optab{ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0, LPCREL, 0}, + Optab{AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0}, + Optab{AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0}, + Optab{AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0}, + Optab{AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0}, + Optab{AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0, 0}, + Optab{AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0, 0}, + Optab{AMOVB, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0}, + Optab{AMOVB, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0}, + Optab{AMOVBS, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0}, + Optab{AMOVBS, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0}, + Optab{AMOVH, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0}, + Optab{AMOVH, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0}, + Optab{AMOVHS, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0}, + Optab{AMOVHS, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0}, + Optab{AMOVHU, C_HAUTO, C_NONE, C_REG, 71, 4, REGSP, 0, 0}, + Optab{AMOVHU, C_HOREG, C_NONE, C_REG, 71, 4, 0, 0, 0}, + Optab{AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0}, + Optab{AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0}, + Optab{AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0}, + Optab{AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0}, + Optab{AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO, 0}, + Optab{AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO, 0}, + Optab{AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4}, + Optab{AMOVB, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0}, + Optab{AMOVB, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0}, + Optab{AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVBS, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0}, + Optab{AMOVBS, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0}, + Optab{AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVH, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0}, + Optab{AMOVH, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0}, + Optab{AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVHS, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0}, + Optab{AMOVHS, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0}, + Optab{AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4}, + Optab{AMOVHU, C_LAUTO, C_NONE, C_REG, 73, 8, REGSP, LFROM, 0}, + Optab{AMOVHU, C_LOREG, C_NONE, C_REG, 73, 8, 0, LFROM, 0}, + Optab{AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4}, + Optab{ALDREX, C_SOREG, C_NONE, C_REG, 77, 4, 0, 0, 0}, + Optab{ASTREX, C_SOREG, C_REG, C_REG, 78, 4, 0, 0, 0}, + Optab{AMOVF, C_ZFCON, C_NONE, C_FREG, 80, 8, 0, 0, 0}, + Optab{AMOVF, C_SFCON, C_NONE, C_FREG, 81, 4, 0, 0, 0}, + Optab{ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0, 0, 0}, + Optab{ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0, 0, 0}, + Optab{AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0, 0, 0}, + Optab{AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0, 0, 0}, + Optab{AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0, 0, 0}, + Optab{AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0, 0, 0}, + Optab{AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0, 0, 0}, + Optab{AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0, 0, 0}, + Optab{ATST, C_REG, C_NONE, C_NONE, 90, 4, 0, 0, 0}, + Optab{ALDREXD, C_SOREG, C_NONE, C_REG, 91, 4, 0, 0, 0}, + Optab{ASTREXD, C_SOREG, C_REG, C_REG, 92, 4, 0, 0, 0}, + Optab{APLD, C_SOREG, C_NONE, C_NONE, 95, 4, 0, 0, 0}, + Optab{AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0, 0, 0}, + Optab{ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0, 0, 0}, + Optab{AMULWT, C_REG, C_REG, C_REG, 98, 4, 0, 0, 0}, + Optab{AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0, 0, 0}, + Optab{AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0, 0, 0}, + Optab{APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0, 0, 0}, + Optab{AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0, 0, 0}, + Optab{ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, + Optab{ADUFFZERO, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as ABL + Optab{ADUFFCOPY, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as ABL + + Optab{ADATABUNDLE, C_NONE, C_NONE, C_NONE, 100, 4, 0, 0, 0}, + Optab{ADATABUNDLEEND, C_NONE, C_NONE, C_NONE, 100, 0, 0, 0, 0}, + Optab{AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0}, +} + +var pool struct { + start uint32 + size uint32 + extra uint32 +} + +var oprange [ALAST]Oprang + +var xcmp [C_GOK + 1][C_GOK + 1]uint8 + +var zprg = obj.Prog{ + As: AGOK, + Scond: C_SCOND_NONE, + Reg: NREG, + From: obj.Addr{ + Name: D_NONE, + Type_: D_NONE, + Reg: NREG, + }, + To: obj.Addr{ + Name: D_NONE, + Type_: D_NONE, + Reg: NREG, + }, +} + +var deferreturn *obj.LSym + +func nocache(p *obj.Prog) { + p.Optab = 0 + p.From.Class = 0 + p.To.Class = 0 +} + +/* size of a case statement including jump table */ +func casesz(ctxt *obj.Link, p *obj.Prog) int32 { + + var jt int = 0 + var n int32 = 0 + var o *Optab + + for ; p != nil; p = p.Link { + if p.As == ABCASE { + jt = 1 + } else if jt != 0 { + break + } + o = oplook(ctxt, p) + n += int32(o.size) + } + + return n +} + +// asmoutnacl assembles the instruction p. It replaces asmout for NaCl. +// It returns the total number of bytes put in out, and it can change +// p->pc if extra padding is necessary. +// In rare cases, asmoutnacl might split p into two instructions. +// origPC is the PC for this Prog (no padding is taken into account). +func asmoutnacl(ctxt *obj.Link, origPC int32, p *obj.Prog, o *Optab, out []uint32) int { + + var size int + var reg int + var q *obj.Prog + var a *obj.Addr + var a2 *obj.Addr + + size = int(o.size) + + // instruction specific + switch p.As { + + default: + if out != nil { + asmout(ctxt, p, o, out) + } + + case ADATABUNDLE, // align to 16-byte boundary + ADATABUNDLEEND: // zero width instruction, just to align next instruction to 16-byte boundary + p.Pc = (p.Pc + 15) &^ 15 + + if out != nil { + asmout(ctxt, p, o, out) + } + + case AUNDEF, + APLD: + size = 4 + if out != nil { + switch p.As { + case AUNDEF: + out[0] = 0xe7fedef0 // NACL_INSTR_ARM_ABORT_NOW (UDF #0xEDE0) + + case APLD: + out[0] = 0xe1a01001 // (MOVW R1, R1) + break + } + } + + case AB, + ABL: + if p.To.Type_ != D_OREG { + if out != nil { + asmout(ctxt, p, o, out) + } + } else { + + if p.To.Offset != 0 || size != 4 || p.To.Reg >= 16 || p.To.Reg < 0 { + ctxt.Diag("unsupported instruction: %v", p) + } + if p.Pc&15 == 12 { + p.Pc += 4 + } + if out != nil { + out[0] = (uint32(p.Scond)&C_SCOND)<<28 | 0x03c0013f | uint32(p.To.Reg)<<12 | uint32(p.To.Reg)<<16 // BIC $0xc000000f, Rx + if p.As == AB { + out[1] = (uint32(p.Scond)&C_SCOND)<<28 | 0x012fff10 | uint32(p.To.Reg) // BX Rx // ABL + } else { + out[1] = (uint32(p.Scond)&C_SCOND)<<28 | 0x012fff30 | uint32(p.To.Reg) // BLX Rx + } + } + + size = 8 + } + + // align the last instruction (the actual BL) to the last instruction in a bundle + if p.As == ABL { + + if deferreturn == nil { + deferreturn = obj.Linklookup(ctxt, "runtime.deferreturn", 0) + } + if p.To.Sym == deferreturn { + p.Pc = ((int64(origPC) + 15) &^ 15) + 16 - int64(size) + } else { + + p.Pc += (16 - ((p.Pc + int64(size)) & 15)) & 15 + } + } + + case ALDREX, + ALDREXD, + AMOVB, + AMOVBS, + AMOVBU, + AMOVD, + AMOVF, + AMOVH, + AMOVHS, + AMOVHU, + AMOVM, + AMOVW, + ASTREX, + ASTREXD: + if p.To.Type_ == D_REG && p.To.Reg == 15 && p.From.Reg == 13 { // MOVW.W x(R13), PC + if out != nil { + asmout(ctxt, p, o, out) + } + if size == 4 { + if out != nil { + // Note: 5c and 5g reg.c know that DIV/MOD smashes R12 + // so that this return instruction expansion is valid. + out[0] = out[0] &^ 0x3000 // change PC to R12 + out[1] = (uint32(p.Scond)&C_SCOND)<<28 | 0x03ccc13f // BIC $0xc000000f, R12 + out[2] = (uint32(p.Scond)&C_SCOND)<<28 | 0x012fff1c // BX R12 + } + + size += 8 + if (p.Pc+int64(size))&15 == 4 { + p.Pc += 4 + } + break + } else { + + // if the instruction used more than 4 bytes, then it must have used a very large + // offset to update R13, so we need to additionally mask R13. + if out != nil { + + out[size/4-1] &^= 0x3000 // change PC to R12 + out[size/4] = (uint32(p.Scond)&C_SCOND)<<28 | 0x03cdd103 // BIC $0xc0000000, R13 + out[size/4+1] = (uint32(p.Scond)&C_SCOND)<<28 | 0x03ccc13f // BIC $0xc000000f, R12 + out[size/4+2] = (uint32(p.Scond)&C_SCOND)<<28 | 0x012fff1c // BX R12 + } + + // p->pc+size is only ok at 4 or 12 mod 16. + if (p.Pc+int64(size))%8 == 0 { + + p.Pc += 4 + } + size += 12 + break + } + } + + if p.To.Type_ == D_REG && p.To.Reg == 15 { + ctxt.Diag("unsupported instruction (move to another register and use indirect jump instead): %v", p) + } + + if p.To.Type_ == D_OREG && p.To.Reg == 13 && (p.Scond&C_WBIT != 0) && size > 4 { + // function prolog with very large frame size: MOVW.W R14,-100004(R13) + // split it into two instructions: + // ADD $-100004, R13 + // MOVW R14, 0(R13) + q = ctxt.Arch.Prg() + + p.Scond &^= C_WBIT + *q = *p + a = &p.To + if p.To.Type_ == D_OREG { + a2 = &q.To + } else { + + a2 = &q.From + } + nocache(q) + nocache(p) + + // insert q after p + q.Link = p.Link + + p.Link = q + q.Pcond = nil + + // make p into ADD $X, R13 + p.As = AADD + + p.From = *a + p.From.Reg = NREG + p.From.Type_ = D_CONST + p.To = zprg.To + p.To.Type_ = D_REG + p.To.Reg = 13 + + // make q into p but load/store from 0(R13) + q.Spadj = 0 + + *a2 = zprg.From + a2.Type_ = D_OREG + a2.Reg = 13 + a2.Sym = nil + a2.Offset = 0 + size = int(oplook(ctxt, p).size) + break + } + + if (p.To.Type_ == D_OREG && p.To.Reg != 13 && p.To.Reg != 9) || (p.From.Type_ == D_OREG && p.From.Reg != 13 && p.From.Reg != 9) { // MOVW Rx, X(Ry), y != 13 && y != 9 // MOVW X(Rx), Ry, x != 13 && x != 9 + if p.To.Type_ == D_OREG { + a = &p.To + } else { + + a = &p.From + } + reg = int(a.Reg) + if size == 4 { + // if addr.reg == NREG, then it is probably load from x(FP) with small x, no need to modify. + if reg == NREG { + + if out != nil { + asmout(ctxt, p, o, out) + } + } else { + + if out != nil { + out[0] = (uint32(p.Scond)&C_SCOND)<<28 | 0x03c00103 | uint32(reg)<<16 | uint32(reg)<<12 // BIC $0xc0000000, Rx + } + if p.Pc&15 == 12 { + p.Pc += 4 + } + size += 4 + if out != nil { + asmout(ctxt, p, o, out[1:]) + } + } + + break + } else { + + // if a load/store instruction takes more than 1 word to implement, then + // we need to seperate the instruction into two: + // 1. explicitly load the address into R11. + // 2. load/store from R11. + // This won't handle .W/.P, so we should reject such code. + if p.Scond&(C_PBIT|C_WBIT) != 0 { + + ctxt.Diag("unsupported instruction (.P/.W): %v", p) + } + q = ctxt.Arch.Prg() + *q = *p + if p.To.Type_ == D_OREG { + a2 = &q.To + } else { + + a2 = &q.From + } + nocache(q) + nocache(p) + + // insert q after p + q.Link = p.Link + + p.Link = q + q.Pcond = nil + + // make p into MOVW $X(R), R11 + p.As = AMOVW + + p.From = *a + p.From.Type_ = D_CONST + p.To = zprg.To + p.To.Type_ = D_REG + p.To.Reg = 11 + + // make q into p but load/store from 0(R11) + *a2 = zprg.From + + a2.Type_ = D_OREG + a2.Reg = 11 + a2.Sym = nil + a2.Offset = 0 + size = int(oplook(ctxt, p).size) + break + } + } else if out != nil { + asmout(ctxt, p, o, out) + } + break + } + + // destination register specific + if p.To.Type_ == D_REG { + + switch p.To.Reg { + case 9: + ctxt.Diag("invalid instruction, cannot write to R9: %v", p) + + case 13: + if out != nil { + out[size/4] = 0xe3cdd103 // BIC $0xc0000000, R13 + } + if (p.Pc+int64(size))&15 == 0 { + p.Pc += 4 + } + size += 4 + break + } + } + + return size +} + +func span5(ctxt *obj.Link, cursym *obj.LSym) { + var p *obj.Prog + var op *obj.Prog + var o *Optab + var m int + var bflag int + var i int + var v int + var times int + var c int32 + var opc int32 + var out [6 + 3]uint32 + var bp []byte + + p = cursym.Text + if p == nil || p.Link == nil { // handle external functions and ELF section symbols + return + } + + if oprange[AAND].start == nil { + buildop(ctxt) + } + + ctxt.Cursym = cursym + + ctxt.Autosize = int32(p.To.Offset + 4) + c = 0 + + op = p + p = p.Link + for ; p != nil || ctxt.Blitrl != nil; (func() { op = p; p = p.Link })() { + if p == nil { + if checkpool(ctxt, op, 0) != 0 { + p = op + continue + } + + // can't happen: blitrl is not nil, but checkpool didn't flushpool + ctxt.Diag("internal inconsistency") + + break + } + + ctxt.Curp = p + p.Pc = int64(c) + o = oplook(ctxt, p) + if ctxt.Headtype != obj.Hnacl { + m = int(o.size) + } else { + + m = asmoutnacl(ctxt, c, p, o, nil) + c = int32(p.Pc) // asmoutnacl might change pc for alignment + o = oplook(ctxt, p) // asmoutnacl might change p in rare cases + } + + 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 ctxt.Blitrl != nil { + + i = m + if p.As == ACASE { + i = int(casesz(ctxt, p)) + } + if checkpool(ctxt, op, i) != 0 { + p = op + continue + } + } + + if m == 0 && (p.As != AFUNCDATA && p.As != APCDATA && p.As != ADATABUNDLEEND && p.As != ANOP) { + ctxt.Diag("zero-width instruction\n%v", p) + continue + } + + switch o.flag & (LFROM | LTO | LPOOL) { + case LFROM: + addpool(ctxt, p, &p.From) + + case LTO: + addpool(ctxt, p, &p.To) + + case LPOOL: + if p.Scond&C_SCOND == C_SCOND_NONE { + flushpool(ctxt, p, 0, 0) + } + break + } + + if p.As == AMOVW && p.To.Type_ == D_REG && p.To.Reg == REGPC && p.Scond&C_SCOND == C_SCOND_NONE { + flushpool(ctxt, p, 0, 0) + } + c += int32(m) + } + + cursym.Size = int64(c) + + /* + * 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 + + for { + if ctxt.Debugvlog != 0 { + fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime()) + } + bflag = 0 + c = 0 + times++ + cursym.Text.Pc = 0 // force re-layout the code. + for p = cursym.Text; p != nil; p = p.Link { + ctxt.Curp = p + o = oplook(ctxt, p) + if int64(c) > p.Pc { + p.Pc = int64(c) + } + + /* 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 = ctxt->arch->prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->pcond = p->pcond; + p->pcond = q; + q = ctxt->arch->prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->pcond = q->link->link; + bflag = 1; + } + } + */ + opc = int32(p.Pc) + + if ctxt.Headtype != obj.Hnacl { + m = int(o.size) + } else { + + m = asmoutnacl(ctxt, c, p, o, nil) + } + if p.Pc != int64(opc) { + bflag = 1 + } + + //print("%P pc changed %d to %d in iter. %d\n", p, opc, (int32)p->pc, times); + c = 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 != AFUNCDATA && p.As != APCDATA && p.As != ADATABUNDLEEND && p.As != ANOP) { + if p.As == ATEXT { + ctxt.Autosize = int32(p.To.Offset + 4) + continue + } + + ctxt.Diag("zero-width instruction\n%v", p) + continue + } + } + + cursym.Size = int64(c) + if !(bflag != 0) { + break + } + } + + if c%4 != 0 { + ctxt.Diag("sym->size=%d, invalid", c) + } + + /* + * 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. + */ + if ctxt.Tlsg == nil { + + ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0) + } + + p = cursym.Text + ctxt.Autosize = int32(p.To.Offset + 4) + obj.Symgrow(ctxt, cursym, cursym.Size) + + bp = cursym.P + c = int32(p.Pc) // even p->link might need extra padding + for p = p.Link; p != nil; p = p.Link { + ctxt.Pc = p.Pc + ctxt.Curp = p + o = oplook(ctxt, p) + opc = int32(p.Pc) + if ctxt.Headtype != obj.Hnacl { + asmout(ctxt, p, o, out[:]) + m = int(o.size) + } else { + + m = asmoutnacl(ctxt, c, p, o, out[:]) + if int64(opc) != p.Pc { + ctxt.Diag("asmoutnacl broken: pc changed (%d->%d) in last stage: %v", opc, int32(p.Pc), p) + } + } + + if m%4 != 0 || p.Pc%4 != 0 { + ctxt.Diag("final stage: pc invalid: %v size=%d", p, m) + } + + if int64(c) > p.Pc { + ctxt.Diag("PC padding invalid: want %#d, has %#d: %v", p.Pc, c, p) + } + for int64(c) != 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:] + c += 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:] + } + + c += int32(m) + } +} + +/* + * when the first reference to the literal pool threatens + * to go out of range of a 12-bit PC-relative offset, + * drop the pool now, and branch round it. + * this happens only in extended basic blocks that exceed 4k. + */ +func checkpool(ctxt *obj.Link, p *obj.Prog, sz int) int { + + if pool.size >= 0xff0 || immaddr(int32((p.Pc+int64(sz)+4)+4+int64(12+pool.size)-int64(pool.start+8))) == 0 { + return flushpool(ctxt, p, 1, 0) + } else if p.Link == nil { + return flushpool(ctxt, p, 2, 0) + } + return 0 +} + +func flushpool(ctxt *obj.Link, p *obj.Prog, skip int, force int) int { + var q *obj.Prog + + if ctxt.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), pool.size, pool.start) + } + q = ctxt.Arch.Prg() + q.As = AB + q.To.Type_ = D_BRANCH + q.Pcond = p.Link + q.Link = ctxt.Blitrl + q.Lineno = p.Lineno + ctxt.Blitrl = q + } else if !(force != 0) && (p.Pc+int64(12+pool.size)-int64(pool.start) < 2048) { // 12 take into account the maximum nacl literal pool alignment padding size + return 0 + } + if ctxt.Headtype == obj.Hnacl && pool.size%16 != 0 { + // if pool is not multiple of 16 bytes, add an alignment marker + q = ctxt.Arch.Prg() + + q.As = ADATABUNDLEEND + ctxt.Elitrl.Link = q + ctxt.Elitrl = q + } + + ctxt.Elitrl.Link = p.Link + p.Link = ctxt.Blitrl + + // BUG(minux): how to correctly handle line number for constant pool entries? + // for now, we set line number to the last instruction preceding them at least + // this won't bloat the .debug_line tables + for ctxt.Blitrl != nil { + + ctxt.Blitrl.Lineno = p.Lineno + ctxt.Blitrl = ctxt.Blitrl.Link + } + + ctxt.Blitrl = nil /* BUG: should refer back to values until out-of-range */ + ctxt.Elitrl = nil + pool.size = 0 + pool.start = 0 + pool.extra = 0 + return 1 + } + + return 0 +} + +func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { + var q *obj.Prog + var t obj.Prog + var c int + + c = aclass(ctxt, a) + + t = zprg + t.As = AWORD + + switch c { + default: + t.To.Offset = a.Offset + t.To.Sym = a.Sym + t.To.Type_ = a.Type_ + t.To.Name = a.Name + + if ctxt.Flag_shared != 0 && t.To.Sym != nil { + t.Pcrel = p + } + + case C_SROREG, + C_LOREG, + C_ROREG, + C_FOREG, + C_SOREG, + C_HOREG, + C_FAUTO, + C_SAUTO, + C_LAUTO, + C_LACON: + t.To.Type_ = D_CONST + t.To.Offset = ctxt.Instoffset + break + } + + if t.Pcrel == nil { + for q = ctxt.Blitrl; q != nil; q = q.Link { /* could hash on t.t0.offset */ + if q.Pcrel == nil && q.To == t.To { + p.Pcond = q + return + } + } + } + + if ctxt.Headtype == obj.Hnacl && pool.size%16 == 0 { + // start a new data bundle + q = ctxt.Arch.Prg() + + *q = zprg + q.As = ADATABUNDLE + q.Pc = int64(pool.size) + pool.size += 4 + if ctxt.Blitrl == nil { + ctxt.Blitrl = q + pool.start = uint32(p.Pc) + } else { + + ctxt.Elitrl.Link = q + } + + ctxt.Elitrl = q + } + + q = ctxt.Arch.Prg() + *q = t + q.Pc = int64(pool.size) + + if ctxt.Blitrl == nil { + ctxt.Blitrl = q + pool.start = uint32(p.Pc) + } else { + + ctxt.Elitrl.Link = q + } + ctxt.Elitrl = q + pool.size += 4 + + p.Pcond = q +} + +func regoff(ctxt *obj.Link, a *obj.Addr) int32 { + ctxt.Instoffset = 0 + aclass(ctxt, a) + return int32(ctxt.Instoffset) +} + +func immrot(v uint32) int32 { + var i int + + 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 +} + +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 aclass(ctxt *obj.Link, a *obj.Addr) int { + var s *obj.LSym + var t int + + switch a.Type_ { + case D_NONE: + return C_NONE + + case D_REG: + return C_REG + + case D_REGREG: + return C_REGREG + + case D_REGREG2: + return C_REGREG2 + + case D_SHIFT: + return C_SHIFT + + case D_FREG: + return C_FREG + + case D_FPCR: + return C_FCR + + case D_OREG: + switch a.Name { + case D_EXTERN, + D_STATIC: + if a.Sym == nil || a.Sym.Name == "" { + fmt.Printf("null sym external\n") + return C_GOK + } + + ctxt.Instoffset = 0 // s.b. unused but just in case + return C_ADDR + + case D_AUTO: + ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + t = int(immaddr(int32(ctxt.Instoffset))) + if t != 0 { + if immhalf(int32(ctxt.Instoffset)) { + if immfloat(int32(t)) { + return C_HFAUTO + } + return C_HAUTO + } + + if immfloat(int32(t)) { + return C_FAUTO + } + return C_SAUTO + } + + return C_LAUTO + + case D_PARAM: + ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 4 + t = int(immaddr(int32(ctxt.Instoffset))) + if t != 0 { + if immhalf(int32(ctxt.Instoffset)) { + if immfloat(int32(t)) { + return C_HFAUTO + } + return C_HAUTO + } + + if immfloat(int32(t)) { + return C_FAUTO + } + return C_SAUTO + } + + return C_LAUTO + + case D_NONE: + ctxt.Instoffset = a.Offset + t = int(immaddr(int32(ctxt.Instoffset))) + if t != 0 { + if immhalf(int32(ctxt.Instoffset)) { /* n.b. that it will also satisfy immrot */ + if immfloat(int32(t)) { + return C_HFOREG + } + return C_HOREG + } + + if immfloat(int32(t)) { + return C_FOREG /* n.b. that it will also satisfy immrot */ + } + t = int(immrot(uint32(ctxt.Instoffset))) + if t != 0 { + return C_SROREG + } + if immhalf(int32(ctxt.Instoffset)) { + return C_HOREG + } + return C_SOREG + } + + t = int(immrot(uint32(ctxt.Instoffset))) + if t != 0 { + return C_ROREG + } + return C_LOREG + } + + return C_GOK + + case D_PSR: + return C_PSR + + case D_OCONST: + switch a.Name { + case D_EXTERN, + D_STATIC: + ctxt.Instoffset = 0 // s.b. unused but just in case + return C_ADDR + } + + return C_GOK + + case D_FCONST: + if chipzero5(ctxt, a.U.Dval) >= 0 { + return C_ZFCON + } + if chipfloat5(ctxt, a.U.Dval) >= 0 { + return C_SFCON + } + return C_LFCON + + case D_CONST, + D_CONST2: + switch a.Name { + case D_NONE: + ctxt.Instoffset = a.Offset + if a.Reg != NREG { + return aconsize(ctxt) + } + + t = int(immrot(uint32(ctxt.Instoffset))) + if t != 0 { + return C_RCON + } + t = int(immrot(uint32(^ctxt.Instoffset))) + if t != 0 { + return C_NCON + } + return C_LCON + + case D_EXTERN, + D_STATIC: + s = a.Sym + if s == nil { + break + } + ctxt.Instoffset = 0 // s.b. unused but just in case + return C_LCONADDR + + case D_AUTO: + ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + return aconsize(ctxt) + + case D_PARAM: + ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 4 + return aconsize(ctxt) + } + + return C_GOK + + case D_BRANCH: + return C_SBRA + } + + return C_GOK +} + +func aconsize(ctxt *obj.Link) int { + var t int + + t = int(immrot(uint32(ctxt.Instoffset))) + if t != 0 { + return C_RACON + } + return C_LACON +} + +func prasm(p *obj.Prog) { + fmt.Printf("%v\n", p) +} + +func oplook(ctxt *obj.Link, p *obj.Prog) *Optab { + var a1 int + var a2 int + var a3 int + var r int + var c1 []byte + var c3 []byte + var o []Optab + var e []Optab + + a1 = int(p.Optab) + if a1 != 0 { + return &optab[a1-1:][0] + } + a1 = int(p.From.Class) + if a1 == 0 { + a1 = aclass(ctxt, &p.From) + 1 + p.From.Class = int8(a1) + } + + a1-- + a3 = int(p.To.Class) + if a3 == 0 { + a3 = aclass(ctxt, &p.To) + 1 + p.To.Class = int8(a3) + } + + a3-- + a2 = C_NONE + if p.Reg != NREG { + a2 = C_REG + } + r = int(p.As) + o = oprange[r].start + if o == nil { + o = oprange[r].stop /* just generate an error */ + } + + if false { /*debug['O']*/ + fmt.Printf("oplook %v %v %v %v\n", Aconv(int(p.As)), DRconv(a1), DRconv(a2), DRconv(a3)) + fmt.Printf("\t\t%d %d\n", p.From.Type_, p.To.Type_) + } + + e = oprange[r].stop + c1 = xcmp[a1][:] + c3 = xcmp[a3][:] + for ; -cap(o) < -cap(e); o = o[1:] { + if int(o[0].a2) == a2 { + if c1[o[0].a1] != 0 { + if c3[o[0].a3] != 0 { + p.Optab = uint16((-cap(o) + cap(optab)) + 1) + return &o[0] + } + } + } + } + + ctxt.Diag("illegal combination %v; %v %v %v, %d %d", p, DRconv(a1), DRconv(a2), DRconv(a3), p.From.Type_, p.To.Type_) + ctxt.Diag("from %d %d to %d %d\n", p.From.Type_, p.From.Name, p.To.Type_, p.To.Name) + prasm(p) + if o == nil { + o = optab + } + return &o[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 { + 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 { + var p1 *Optab + var p2 *Optab + var n int + + 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 buildop(ctxt *obj.Link) { + var i int + var n int + var r int + + for i = 0; i < C_GOK; i++ { + for n = 0; n < C_GOK; n++ { + if cmp(n, i) { + xcmp[i][n] = 1 + } + } + } + for n = 0; optab[n].as != AXXX; n++ { + if optab[n].flag&LPCREL != 0 { + if ctxt.Flag_shared != 0 { + optab[n].size += int8(optab[n].pcrelsiz) + } else { + + optab[n].flag &^= LPCREL + } + } + } + + sort.Sort(ocmp(optab[:n])) + for i = 0; i < n; i++ { + r = int(optab[i].as) + oprange[r].start = optab[i:] + for int(optab[i].as) == r { + i++ + } + oprange[r].stop = optab[i:] + i-- + + switch r { + default: + ctxt.Diag("unknown op in build: %v", Aconv(r)) + log.Fatalf("bad code") + + case AADD: + oprange[AAND] = oprange[r] + oprange[AEOR] = oprange[r] + oprange[ASUB] = oprange[r] + oprange[ARSB] = oprange[r] + oprange[AADC] = oprange[r] + oprange[ASBC] = oprange[r] + oprange[ARSC] = oprange[r] + oprange[AORR] = oprange[r] + oprange[ABIC] = oprange[r] + + case ACMP: + oprange[ATEQ] = oprange[r] + oprange[ACMN] = oprange[r] + + case AMVN: + break + + case ABEQ: + oprange[ABNE] = oprange[r] + oprange[ABCS] = oprange[r] + oprange[ABHS] = oprange[r] + oprange[ABCC] = oprange[r] + oprange[ABLO] = oprange[r] + oprange[ABMI] = oprange[r] + oprange[ABPL] = oprange[r] + oprange[ABVS] = oprange[r] + oprange[ABVC] = oprange[r] + oprange[ABHI] = oprange[r] + oprange[ABLS] = oprange[r] + oprange[ABGE] = oprange[r] + oprange[ABLT] = oprange[r] + oprange[ABGT] = oprange[r] + oprange[ABLE] = oprange[r] + + case ASLL: + oprange[ASRL] = oprange[r] + oprange[ASRA] = oprange[r] + + case AMUL: + oprange[AMULU] = oprange[r] + + case ADIV: + oprange[AMOD] = oprange[r] + oprange[AMODU] = oprange[r] + oprange[ADIVU] = oprange[r] + + case AMOVW, + AMOVB, + AMOVBS, + AMOVBU, + AMOVH, + AMOVHS, + AMOVHU: + break + + case ASWPW: + oprange[ASWPBU] = oprange[r] + + case AB, + ABL, + ABX, + ABXRET, + ADUFFZERO, + ADUFFCOPY, + ASWI, + AWORD, + AMOVM, + ARFE, + ATEXT, + AUSEFIELD, + ACASE, + ABCASE, + ATYPE: + break + + case AADDF: + oprange[AADDD] = oprange[r] + oprange[ASUBF] = oprange[r] + oprange[ASUBD] = oprange[r] + oprange[AMULF] = oprange[r] + oprange[AMULD] = oprange[r] + oprange[ADIVF] = oprange[r] + oprange[ADIVD] = oprange[r] + oprange[ASQRTF] = oprange[r] + oprange[ASQRTD] = oprange[r] + oprange[AMOVFD] = oprange[r] + oprange[AMOVDF] = oprange[r] + oprange[AABSF] = oprange[r] + oprange[AABSD] = oprange[r] + + case ACMPF: + oprange[ACMPD] = oprange[r] + + case AMOVF: + oprange[AMOVD] = oprange[r] + + case AMOVFW: + oprange[AMOVDW] = oprange[r] + + case AMOVWF: + oprange[AMOVWD] = oprange[r] + + case AMULL: + oprange[AMULAL] = oprange[r] + oprange[AMULLU] = oprange[r] + oprange[AMULALU] = oprange[r] + + case AMULWT: + oprange[AMULWB] = oprange[r] + + case AMULAWT: + oprange[AMULAWB] = oprange[r] + + case AMULA, + ALDREX, + ASTREX, + ALDREXD, + ASTREXD, + ATST, + APLD, + AUNDEF, + ACLZ, + AFUNCDATA, + APCDATA, + ANOP, + ADATABUNDLE, + ADATABUNDLEEND: + break + } + } +} + +func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { + var o1 uint32 + var o2 uint32 + var o3 uint32 + var o4 uint32 + var o5 uint32 + var o6 uint32 + var v int32 + var r int + var rf int + var rt int + var rt2 int + var rel *obj.Reloc + + ctxt.Printp = p + o1 = 0 + o2 = 0 + o3 = 0 + o4 = 0 + o5 = 0 + o6 = 0 + ctxt.Armsize += int32(o.size) + if false { /*debug['P']*/ + fmt.Printf("%x: %v\ttype %d\n", uint32(p.Pc), p, o.type_) + } + switch o.type_ { + default: + ctxt.Diag("unknown asm %d", o.type_) + prasm(p) + + case 0: /* pseudo ops */ + if false { /*debug['G']*/ + fmt.Printf("%x: %s: arm %d\n", uint32(p.Pc), p.From.Sym.Name, p.From.Sym.Fnptr) + } + + case 1: /* op R,[R],R */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + rf = int(p.From.Reg) + rt = int(p.To.Reg) + r = int(p.Reg) + if p.To.Type_ == D_NONE { + rt = 0 + } + if p.As == AMOVB || p.As == AMOVH || p.As == AMOVW || p.As == AMVN { + r = 0 + } else if r == NREG { + r = rt + } + o1 |= uint32(rf) | uint32(r)<<16 | uint32(rt)<<12 + + case 2: /* movbu $I,[R],R */ + aclass(ctxt, &p.From) + + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + o1 |= uint32(immrot(uint32(ctxt.Instoffset))) + rt = int(p.To.Reg) + r = int(p.Reg) + if p.To.Type_ == D_NONE { + rt = 0 + } + if p.As == AMOVW || p.As == AMVN { + r = 0 + } else if r == NREG { + r = rt + } + o1 |= uint32(r)<<16 | uint32(rt)<<12 + + case 3: /* add R<<[IR],[R],R */ + o1 = mov(ctxt, p) + + case 4: /* add $I,[R],R */ + aclass(ctxt, &p.From) + + o1 = oprrr(ctxt, AADD, int(p.Scond)) + o1 |= uint32(immrot(uint32(ctxt.Instoffset))) + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o1 |= uint32(r) << 16 + o1 |= uint32(p.To.Reg) << 12 + + case 5: /* bra s */ + o1 = opbra(ctxt, int(p.As), int(p.Scond)) + + v = -8 + if p.To.Sym != nil { + rel = obj.Addrel(ctxt.Cursym) + rel.Off = int32(ctxt.Pc) + rel.Siz = 4 + rel.Sym = p.To.Sym + v += int32(p.To.Offset) + rel.Add = int64(o1) | (int64(v)>>2)&0xffffff + rel.Type_ = obj.R_CALLARM + break + } + + if p.Pcond != nil { + v = int32((p.Pcond.Pc - ctxt.Pc) - 8) + } + o1 |= (uint32(v) >> 2) & 0xffffff + + case 6: /* b ,O(R) -> add $O,R,PC */ + aclass(ctxt, &p.To) + + o1 = oprrr(ctxt, AADD, int(p.Scond)) + o1 |= uint32(immrot(uint32(ctxt.Instoffset))) + o1 |= uint32(p.To.Reg) << 16 + o1 |= REGPC << 12 + + case 7: /* bl (R) -> blx R */ + aclass(ctxt, &p.To) + + if ctxt.Instoffset != 0 { + ctxt.Diag("%v: doesn't support BL offset(REG) where offset != 0", p) + } + o1 = oprrr(ctxt, ABL, int(p.Scond)) + o1 |= uint32(p.To.Reg) + rel = obj.Addrel(ctxt.Cursym) + rel.Off = int32(ctxt.Pc) + rel.Siz = 0 + rel.Type_ = obj.R_CALLIND + + case 8: /* sll $c,[R],R -> mov (R<<$c),R */ + aclass(ctxt, &p.From) + + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + r = int(p.Reg) + if r == NREG { + r = int(p.To.Reg) + } + o1 |= uint32(r) + o1 |= uint32((ctxt.Instoffset & 31) << 7) + o1 |= uint32(p.To.Reg) << 12 + + case 9: /* sll R,[R],R -> mov (R<<R),R */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + r = int(p.Reg) + if r == NREG { + r = int(p.To.Reg) + } + o1 |= uint32(r) + o1 |= uint32(p.From.Reg)<<8 | 1<<4 + o1 |= uint32(p.To.Reg) << 12 + + case 10: /* swi [$con] */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + if p.To.Type_ != D_NONE { + aclass(ctxt, &p.To) + o1 |= uint32(ctxt.Instoffset & 0xffffff) + } + + case 11: /* word */ + aclass(ctxt, &p.To) + + o1 = uint32(ctxt.Instoffset) + if p.To.Sym != nil { + // This case happens with words generated + // in the PC stream as part of the literal pool. + rel = obj.Addrel(ctxt.Cursym) + + rel.Off = int32(ctxt.Pc) + rel.Siz = 4 + rel.Sym = p.To.Sym + rel.Add = p.To.Offset + + // runtime.tlsg is special. + // Its "address" is the offset from the TLS thread pointer + // to the thread-local g and m pointers. + // Emit a TLS relocation instead of a standard one. + if rel.Sym == ctxt.Tlsg { + + rel.Type_ = obj.R_TLS + if ctxt.Flag_shared != 0 { + rel.Add += ctxt.Pc - p.Pcrel.Pc - 8 - int64(rel.Siz) + } + rel.Xadd = rel.Add + rel.Xsym = rel.Sym + } else if ctxt.Flag_shared != 0 { + rel.Type_ = obj.R_PCREL + rel.Add += ctxt.Pc - p.Pcrel.Pc - 8 + } else { + + rel.Type_ = obj.R_ADDR + } + o1 = 0 + } + + case 12: /* movw $lcon, reg */ + o1 = omvl(ctxt, p, &p.From, int(p.To.Reg)) + + if o.flag&LPCREL != 0 { + o2 = oprrr(ctxt, AADD, int(p.Scond)) | uint32(p.To.Reg) | REGPC<<16 | uint32(p.To.Reg)<<12 + } + + case 13: /* op $lcon, [R], R */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + o2 = oprrr(ctxt, int(p.As), int(p.Scond)) + o2 |= REGTMP + r = int(p.Reg) + if p.As == AMOVW || p.As == AMVN { + r = 0 + } else if r == NREG { + r = int(p.To.Reg) + } + o2 |= uint32(r) << 16 + if p.To.Type_ != D_NONE { + o2 |= uint32(p.To.Reg) << 12 + } + + case 14: /* movb/movbu/movh/movhu R,R */ + o1 = oprrr(ctxt, ASLL, int(p.Scond)) + + if p.As == AMOVBU || p.As == AMOVHU { + o2 = oprrr(ctxt, ASRL, int(p.Scond)) + } else { + + o2 = oprrr(ctxt, ASRA, int(p.Scond)) + } + + r = int(p.To.Reg) + o1 |= uint32(p.From.Reg) | uint32(r)<<12 + o2 |= uint32(r) | uint32(r)<<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 = oprrr(ctxt, int(p.As), int(p.Scond)) + + rf = int(p.From.Reg) + rt = int(p.To.Reg) + r = int(p.Reg) + if r == NREG { + r = rt + } + if rt == r { + r = rf + rf = rt + } + + if false { + if rt == r || rf == REGPC || r == REGPC || rt == REGPC { + ctxt.Diag("bad registers in MUL") + prasm(p) + } + } + + o1 |= uint32(rf)<<8 | uint32(r) | uint32(rt)<<16 + + case 16: /* div r,[r,]r */ + o1 = 0xf << 28 + + o2 = 0 + + case 17: + o1 = oprrr(ctxt, int(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)<<8 | uint32(r) | uint32(rt)<<16 | uint32(rt2)<<12 + + case 20: /* mov/movb/movbu R,O(R) */ + aclass(ctxt, &p.To) + + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o1 = osr(ctxt, int(p.As), int(p.From.Reg), int32(ctxt.Instoffset), r, int(p.Scond)) + + case 21: /* mov/movbu O(R),R -> lr */ + aclass(ctxt, &p.From) + + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o1 = olr(ctxt, int32(ctxt.Instoffset), r, int(p.To.Reg), int(p.Scond)) + if p.As != AMOVW { + o1 |= 1 << 22 + } + + case 30: /* mov/movb/movbu R,L(R) */ + o1 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o2 = osrr(ctxt, int(p.From.Reg), REGTMP, r, int(p.Scond)) + if p.As != AMOVW { + o2 |= 1 << 22 + } + + case 31: /* mov/movbu L(R),R -> lr[b] */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o2 = olrr(ctxt, REGTMP, 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 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + + o2 = oprrr(ctxt, AADD, int(p.Scond)) + o2 |= REGTMP + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o2 |= uint32(r) << 16 + if p.To.Type_ != D_NONE { + o2 |= uint32(p.To.Reg) << 12 + } + + case 35: /* mov PSR,R */ + o1 = 2<<23 | 0xf<<16 | 0<<0 + + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + o1 |= (uint32(p.From.Reg) & 1) << 22 + o1 |= uint32(p.To.Reg) << 12 + + case 36: /* mov R,PSR */ + o1 = 2<<23 | 0x29f<<12 | 0<<4 + + if p.Scond&C_FBIT != 0 { + o1 ^= 0x010 << 12 + } + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + o1 |= (uint32(p.To.Reg) & 1) << 22 + o1 |= uint32(p.From.Reg) << 0 + + case 37: /* mov $con,PSR */ + aclass(ctxt, &p.From) + + o1 = 2<<23 | 0x29f<<12 | 0<<4 + if p.Scond&C_FBIT != 0 { + o1 ^= 0x010 << 12 + } + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + o1 |= uint32(immrot(uint32(ctxt.Instoffset))) + o1 |= (uint32(p.To.Reg) & 1) << 22 + o1 |= uint32(p.From.Reg) << 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) << 16 + aclass(ctxt, &p.To) + + case 39: /* movm oreg,$con -> ldm */ + o1 = 0x4<<25 | 1<<20 + + o1 |= uint32(p.To.Offset & 0xffff) + o1 |= uint32(p.From.Reg) << 16 + aclass(ctxt, &p.From) + break + } + + if ctxt.Instoffset != 0 { + ctxt.Diag("offset must be zero in MOVM; %v", p) + } + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + if p.Scond&C_PBIT != 0 { + o1 |= 1 << 24 + } + if p.Scond&C_UBIT != 0 { + o1 |= 1 << 23 + } + if p.Scond&C_SBIT != 0 { + o1 |= 1 << 22 + } + if p.Scond&C_WBIT != 0 { + o1 |= 1 << 21 + } + + case 40: /* swp oreg,reg,reg */ + aclass(ctxt, &p.From) + + if ctxt.Instoffset != 0 { + 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) << 16 + o1 |= uint32(p.Reg) << 0 + o1 |= uint32(p.To.Reg) << 12 + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ + o1 = 0xe8fd8000 + + case 50: /* floating point store */ + v = regoff(ctxt, &p.To) + + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o1 = ofsr(ctxt, int(p.As), int(p.From.Reg), v, r, int(p.Scond), p) + + case 51: /* floating point load */ + v = regoff(ctxt, &p.From) + + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o1 = ofsr(ctxt, int(p.As), int(p.To.Reg), v, r, int(p.Scond), p) | 1<<20 + + case 52: /* floating point store, int32 offset UGLY */ + o1 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP<<12 | REGTMP<<16 | uint32(r) + o3 = ofsr(ctxt, int(p.As), int(p.From.Reg), 0, REGTMP, int(p.Scond), p) + + case 53: /* floating point load, int32 offset UGLY */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP<<12 | REGTMP<<16 | uint32(r) + o3 = ofsr(ctxt, int(p.As), int(p.To.Reg), 0, REGTMP, int(p.Scond), p) | 1<<20 + + case 54: /* floating point arith */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + rf = int(p.From.Reg) + rt = int(p.To.Reg) + r = int(p.Reg) + if r == NREG { + r = rt + if p.As == AMOVF || p.As == AMOVD || p.As == AMOVFD || p.As == AMOVDF || p.As == ASQRTF || p.As == ASQRTD || p.As == AABSF || p.As == AABSD { + r = 0 + } + } + + o1 |= uint32(rf) | uint32(r)<<16 | uint32(rt)<<12 + + case 56: /* move to FP[CS]R */ + o1 = (uint32(p.Scond)&C_SCOND)<<28 | 0xe<<24 | 1<<8 | 1<<4 + + o1 |= (uint32(p.To.Reg)+1)<<21 | uint32(p.From.Reg)<<12 + + case 57: /* move from FP[CS]R */ + o1 = (uint32(p.Scond)&C_SCOND)<<28 | 0xe<<24 | 1<<8 | 1<<4 + + o1 |= (uint32(p.From.Reg)+1)<<21 | uint32(p.To.Reg)<<12 | 1<<20 + + case 58: /* movbu R,R */ + o1 = oprrr(ctxt, AAND, int(p.Scond)) + + o1 |= uint32(immrot(0xff)) + rt = int(p.To.Reg) + r = int(p.From.Reg) + if p.To.Type_ == D_NONE { + rt = 0 + } + if r == NREG { + r = rt + } + o1 |= uint32(r)<<16 | uint32(rt)<<12 + + case 59: /* movw/bu R<<I(R),R -> ldr indexed */ + if p.From.Reg == NREG { + + if p.As != AMOVW { + ctxt.Diag("byte MOV from shifter operand") + } + o1 = mov(ctxt, p) + break + } + + if p.From.Offset&(1<<4) != 0 { + ctxt.Diag("bad shift in LDR") + } + o1 = olrr(ctxt, 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 == NREG { + + ctxt.Diag("byte MOV from shifter operand") + o1 = mov(ctxt, p) + break + } + + if p.From.Offset&(^0xf) != 0 { + ctxt.Diag("bad shift in LDRSB") + } + o1 = olhrr(ctxt, int(p.From.Offset), int(p.From.Reg), int(p.To.Reg), int(p.Scond)) + o1 ^= 1<<5 | 1<<6 + + case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */ + if p.To.Reg == NREG { + + ctxt.Diag("MOV to shifter operand") + } + o1 = osrr(ctxt, 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: /* case R -> movw R<<2(PC),PC */ + if o.flag&LPCREL != 0 { + + o1 = oprrr(ctxt, AADD, int(p.Scond)) | uint32(immrot(1)) | uint32(p.From.Reg)<<16 | REGTMP<<12 + o2 = olrr(ctxt, REGTMP, REGPC, REGTMP, int(p.Scond)) + o2 |= 2 << 7 + o3 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGPC<<12 + } else { + + o1 = olrr(ctxt, int(p.From.Reg), REGPC, REGPC, int(p.Scond)) + o1 |= 2 << 7 + } + + case 63: /* bcase */ + if p.Pcond != nil { + + rel = obj.Addrel(ctxt.Cursym) + rel.Off = int32(ctxt.Pc) + rel.Siz = 4 + if p.To.Sym != nil && p.To.Sym.Type_ != 0 { + rel.Sym = p.To.Sym + rel.Add = p.To.Offset + } else { + + rel.Sym = ctxt.Cursym + rel.Add = p.Pcond.Pc + } + + if o.flag&LPCREL != 0 { + rel.Type_ = obj.R_PCREL + rel.Add += ctxt.Pc - p.Pcrel.Pc - 16 + int64(rel.Siz) + } else { + + rel.Type_ = obj.R_ADDR + } + o1 = 0 + } + + /* reloc ops */ + case 64: /* mov/movb/movbu R,addr */ + o1 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + o2 = osr(ctxt, int(p.As), int(p.From.Reg), 0, REGTMP, int(p.Scond)) + if o.flag&LPCREL != 0 { + o3 = o2 + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + case 65: /* mov/movbu addr,R */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + o2 = olr(ctxt, 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 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + case 68: /* floating point store -> ADDR */ + o1 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + o2 = ofsr(ctxt, int(p.As), int(p.From.Reg), 0, REGTMP, int(p.Scond), p) + if o.flag&LPCREL != 0 { + o3 = o2 + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + case 69: /* floating point load <- ADDR */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + o2 = ofsr(ctxt, int(p.As), int(p.To.Reg), 0, REGTMP, int(p.Scond), p) | 1<<20 + if o.flag&LPCREL != 0 { + o3 = o2 + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + /* ArmV4 ops: */ + case 70: /* movh/movhu R,O(R) -> strh */ + aclass(ctxt, &p.To) + + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o1 = oshr(ctxt, int(p.From.Reg), int32(ctxt.Instoffset), r, int(p.Scond)) + + case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ + aclass(ctxt, &p.From) + + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o1 = olhr(ctxt, int32(ctxt.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 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.To.Reg) + if r == NREG { + r = int(o.param) + } + o2 = oshrr(ctxt, int(p.From.Reg), REGTMP, r, int(p.Scond)) + + case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + r = int(p.From.Reg) + if r == NREG { + r = int(o.param) + } + o2 = olhrr(ctxt, REGTMP, 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 */ + ctxt.Diag("ABX $I") + + case 75: /* bx O(R) */ + aclass(ctxt, &p.To) + + if ctxt.Instoffset != 0 { + ctxt.Diag("non-zero offset in ABX") + } + + /* + o1 = oprrr(ctxt, AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR + o2 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg; // BX R + */ + // p->to.reg may be REGLINK + o1 = oprrr(ctxt, AADD, int(p.Scond)) + + o1 |= uint32(immrot(uint32(ctxt.Instoffset))) + o1 |= uint32(p.To.Reg) << 16 + o1 |= REGTMP << 12 + o2 = oprrr(ctxt, AADD, int(p.Scond)) | uint32(immrot(0)) | REGPC<<16 | REGLINK<<12 // mov PC, LR + o3 = (uint32(p.Scond)&C_SCOND)<<28 | 0x12fff<<8 | 1<<4 | REGTMP // BX Rtmp + + case 76: /* bx O(R) when returning from fn*/ + ctxt.Diag("ABXRET") + + case 77: /* ldrex oreg,reg */ + aclass(ctxt, &p.From) + + if ctxt.Instoffset != 0 { + ctxt.Diag("offset must be zero in LDREX") + } + o1 = 0x19<<20 | 0xf9f + o1 |= uint32(p.From.Reg) << 16 + o1 |= uint32(p.To.Reg) << 12 + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 78: /* strex reg,oreg,reg */ + aclass(ctxt, &p.From) + + if ctxt.Instoffset != 0 { + ctxt.Diag("offset must be zero in STREX") + } + o1 = 0x18<<20 | 0xf90 + o1 |= uint32(p.From.Reg) << 16 + o1 |= uint32(p.Reg) << 0 + o1 |= uint32(p.To.Reg) << 12 + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 80: /* fmov zfcon,freg */ + if p.As == AMOVD { + + o1 = 0xeeb00b00 // VMOV imm 64 + o2 = oprrr(ctxt, ASUBD, int(p.Scond)) + } else { + + o1 = 0x0eb00a00 // VMOV imm 32 + o2 = oprrr(ctxt, ASUBF, int(p.Scond)) + } + + v = 0x70 // 1.0 + r = int(p.To.Reg) + + // movf $1.0, r + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + o1 |= uint32(r) << 12 + o1 |= (uint32(v) & 0xf) << 0 + o1 |= (uint32(v) & 0xf0) << 12 + + // subf r,r,r + o2 |= uint32(r) | uint32(r)<<16 | uint32(r)<<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) << 28 + o1 |= uint32(p.To.Reg) << 12 + v = int32(chipfloat5(ctxt, p.From.U.Dval)) + o1 |= (uint32(v) & 0xf) << 0 + o1 |= (uint32(v) & 0xf0) << 12 + + case 82: /* fcmp freg,freg, */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.Reg)<<12 | uint32(p.From.Reg)<<0 + o2 = 0x0ef1fa10 // VMRS R15 + o2 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 83: /* fcmp freg,, */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.From.Reg)<<12 | 1<<16 + o2 = 0x0ef1fa10 // VMRS R15 + o2 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 84: /* movfw freg,freg - truncate float-to-fix */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 0 + o1 |= uint32(p.To.Reg) << 12 + + case 85: /* movwf freg,freg - fix-to-float */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 0 + o1 |= uint32(p.To.Reg) << 12 + + // macro for movfw freg,FTMP; movw FTMP,reg + case 86: /* movfw freg,reg - truncate float-to-fix */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 0 + o1 |= FREGTMP << 12 + o2 = oprrr(ctxt, AMOVFW+AEND, int(p.Scond)) + o2 |= FREGTMP << 16 + o2 |= uint32(p.To.Reg) << 12 + + // macro for movw reg,FTMP; movwf FTMP,freg + case 87: /* movwf reg,freg - fix-to-float */ + o1 = oprrr(ctxt, AMOVWF+AEND, int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 12 + o1 |= FREGTMP << 16 + o2 = oprrr(ctxt, int(p.As), int(p.Scond)) + o2 |= FREGTMP << 0 + o2 |= uint32(p.To.Reg) << 12 + + case 88: /* movw reg,freg */ + o1 = oprrr(ctxt, AMOVWF+AEND, int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 12 + o1 |= uint32(p.To.Reg) << 16 + + case 89: /* movw freg,reg */ + o1 = oprrr(ctxt, AMOVFW+AEND, int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 16 + o1 |= uint32(p.To.Reg) << 12 + + case 90: /* tst reg */ + o1 = oprrr(ctxt, ACMP+AEND, int(p.Scond)) + + o1 |= uint32(p.From.Reg) << 16 + + case 91: /* ldrexd oreg,reg */ + aclass(ctxt, &p.From) + + if ctxt.Instoffset != 0 { + ctxt.Diag("offset must be zero in LDREX") + } + o1 = 0x1b<<20 | 0xf9f + o1 |= uint32(p.From.Reg) << 16 + o1 |= uint32(p.To.Reg) << 12 + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 92: /* strexd reg,oreg,reg */ + aclass(ctxt, &p.From) + + if ctxt.Instoffset != 0 { + ctxt.Diag("offset must be zero in STREX") + } + o1 = 0x1a<<20 | 0xf90 + o1 |= uint32(p.From.Reg) << 16 + o1 |= uint32(p.Reg) << 0 + o1 |= uint32(p.To.Reg) << 12 + o1 |= (uint32(p.Scond) & C_SCOND) << 28 + + case 93: /* movb/movh/movhu addr,R -> ldrsb/ldrsh/ldrh */ + o1 = omvl(ctxt, p, &p.From, REGTMP) + + if !(o1 != 0) { + break + } + o2 = olhr(ctxt, 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 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + case 94: /* movh/movhu R,addr -> strh */ + o1 = omvl(ctxt, p, &p.To, REGTMP) + + if !(o1 != 0) { + break + } + o2 = oshr(ctxt, int(p.From.Reg), 0, REGTMP, int(p.Scond)) + if o.flag&LPCREL != 0 { + o3 = o2 + o2 = oprrr(ctxt, AADD, int(p.Scond)) | REGTMP | REGPC<<16 | REGTMP<<12 + } + + case 95: /* PLD off(reg) */ + o1 = 0xf5d0f000 + + o1 |= uint32(p.From.Reg) << 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 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.To.Reg) << 12 + o1 |= uint32(p.From.Reg) + + case 98: /* MULW{T,B} Rs, Rm, Rd */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.To.Reg) << 16 + o1 |= uint32(p.From.Reg) << 8 + o1 |= uint32(p.Reg) + + case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + + o1 |= uint32(p.To.Reg) << 12 + o1 |= uint32(p.From.Reg) << 8 + o1 |= uint32(p.Reg) + o1 |= uint32(p.To.Offset << 16) + + // DATABUNDLE: BKPT $0x5be0, signify the start of NaCl data bundle; + // DATABUNDLEEND: zero width alignment marker + case 100: + if p.As == ADATABUNDLE { + + o1 = 0xe125be70 + } + break + } + + out[0] = o1 + out[1] = o2 + out[2] = o3 + out[3] = o4 + out[4] = o5 + out[5] = o6 + return +} + +func mov(ctxt *obj.Link, p *obj.Prog) uint32 { + var o1 uint32 + var rt int + var r int + + aclass(ctxt, &p.From) + o1 = oprrr(ctxt, int(p.As), int(p.Scond)) + o1 |= uint32(p.From.Offset) + rt = int(p.To.Reg) + r = int(p.Reg) + if p.To.Type_ == D_NONE { + rt = 0 + } + if p.As == AMOVW || p.As == AMVN { + r = 0 + } else if r == NREG { + r = rt + } + o1 |= uint32(r)<<16 | uint32(rt)<<12 + return o1 +} + +func oprrr(ctxt *obj.Link, a int, sc int) uint32 { + var o uint32 + + o = (uint32(sc) & C_SCOND) << 28 + if sc&C_SBIT != 0 { + o |= 1 << 20 + } + if sc&(C_PBIT|C_WBIT) != 0 { + ctxt.Diag(".nil/.W on dp instruction") + } + switch a { + 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: + 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 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 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 + AEND: // copy WtoF + return o | 0xe<<24 | 0x0<<20 | 0xb<<8 | 1<<4 + + case AMOVFW + AEND: // copy FtoW + return o | 0xe<<24 | 0x1<<20 | 0xb<<8 | 1<<4 + + case ACMP + AEND: // cmp imm + return o | 0x3<<24 | 0x5<<20 + + // CLZ doesn't support .nil + case ACLZ: + return o&(0xf<<28) | 0x16f<<16 | 0xf1<<4 + + case AMULWT: + return o&(0xf<<28) | 0x12<<20 | 0xe<<4 + + case AMULWB: + return o&(0xf<<28) | 0x12<<20 | 0xa<<4 + + case AMULAWT: + return o&(0xf<<28) | 0x12<<20 | 0xc<<4 + + case AMULAWB: + return o&(0xf<<28) | 0x12<<20 | 0x8<<4 + + case ABL: // BLX REG + return o&(0xf<<28) | 0x12fff3<<4 + } + + ctxt.Diag("bad rrr %d", a) + prasm(ctxt.Curp) + return 0 +} + +func opbra(ctxt *obj.Link, a int, sc int) uint32 { + if sc&(C_SBIT|C_PBIT|C_WBIT) != 0 { + ctxt.Diag(".nil/.nil/.W on bra instruction") + } + sc &= C_SCOND + if a == ABL || a == ADUFFZERO || a == ADUFFCOPY { + return uint32(sc)<<28 | 0x5<<25 | 0x1<<24 + } + if sc != 0xe { + ctxt.Diag(".COND on bcond instruction") + } + 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 + } + + ctxt.Diag("bad bra %v", Aconv(a)) + prasm(ctxt.Curp) + return 0 +} + +func olr(ctxt *obj.Link, v int32, b int, r int, sc int) uint32 { + var o uint32 + + if sc&C_SBIT != 0 { + ctxt.Diag(".nil on LDR/STR instruction") + } + o = (uint32(sc) & C_SCOND) << 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 { + ctxt.Diag(".U on neg offset") + } + v = -v + o ^= 1 << 23 + } + + if v >= 1<<12 || v < 0 { + ctxt.Diag("literal span too large: %d (R%d)\n%v", v, b, ctxt.Printp) + } + o |= uint32(v) + o |= uint32(b) << 16 + o |= uint32(r) << 12 + return o +} + +func olhr(ctxt *obj.Link, v int32, b int, r int, sc int) uint32 { + var o uint32 + + if sc&C_SBIT != 0 { + ctxt.Diag(".nil on LDRH/STRH instruction") + } + o = (uint32(sc) & C_SCOND) << 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 { + ctxt.Diag("literal span too large: %d (R%d)\n%v", v, b, ctxt.Printp) + } + o |= uint32(v)&0xf | (uint32(v)>>4)<<8 | 1<<22 + o |= uint32(b) << 16 + o |= uint32(r) << 12 + return o +} + +func osr(ctxt *obj.Link, a int, r int, v int32, b int, sc int) uint32 { + var o uint32 + + o = olr(ctxt, v, b, r, sc) ^ (1 << 20) + if a != AMOVW { + o |= 1 << 22 + } + return o +} + +func oshr(ctxt *obj.Link, r int, v int32, b int, sc int) uint32 { + var o uint32 + + o = olhr(ctxt, v, b, r, sc) ^ (1 << 20) + return o +} + +func osrr(ctxt *obj.Link, r int, i int, b int, sc int) uint32 { + return olr(ctxt, int32(i), b, r, sc) ^ (1<<25 | 1<<20) +} + +func oshrr(ctxt *obj.Link, r int, i int, b int, sc int) uint32 { + return olhr(ctxt, int32(i), b, r, sc) ^ (1<<22 | 1<<20) +} + +func olrr(ctxt *obj.Link, i int, b int, r int, sc int) uint32 { + return olr(ctxt, int32(i), b, r, sc) ^ (1 << 25) +} + +func olhrr(ctxt *obj.Link, i int, b int, r int, sc int) uint32 { + return olhr(ctxt, int32(i), b, r, sc) ^ (1 << 22) +} + +func ofsr(ctxt *obj.Link, a int, r int, v int32, b int, sc int, p *obj.Prog) uint32 { + var o uint32 + + if sc&C_SBIT != 0 { + ctxt.Diag(".nil on FLDR/FSTR instruction") + } + o = (uint32(sc) & C_SCOND) << 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 { + ctxt.Diag("odd offset for floating point op: %d\n%v", v, p) + } else if v >= 1<<10 || v < 0 { + ctxt.Diag("literal span too large: %d\n%v", v, p) + } + o |= (uint32(v) >> 2) & 0xFF + o |= uint32(b) << 16 + o |= uint32(r) << 12 + + switch a { + default: + ctxt.Diag("bad fst %v", Aconv(a)) + fallthrough + + case AMOVD: + o |= 1 << 8 + fallthrough + + case AMOVF: + break + } + + return o +} + +func omvl(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, dr int) uint32 { + var v int32 + var o1 uint32 + if !(p.Pcond != nil) { + aclass(ctxt, a) + v = immrot(uint32(^ctxt.Instoffset)) + if v == 0 { + ctxt.Diag("missing literal") + prasm(p) + return 0 + } + + o1 = oprrr(ctxt, AMVN, int(p.Scond)&C_SCOND) + o1 |= uint32(v) + o1 |= uint32(dr) << 12 + } else { + + v = int32(p.Pcond.Pc - p.Pc - 8) + o1 = olr(ctxt, v, REGPC, dr, int(p.Scond)&C_SCOND) + } + + return o1 +} + +func chipzero5(ctxt *obj.Link, e float64) int { + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if ctxt.Goarm < 7 || e != 0 { + + return -1 + } + return 0 +} + +func chipfloat5(ctxt *obj.Link, e float64) int { + var n int + var h1 uint32 + var l uint32 + var h uint32 + var ei uint64 + + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if ctxt.Goarm < 7 { + + goto no + } + + ei = math.Float64bits(e) + l = uint32(ei) + h = uint32(ei >> 32) + + if l != 0 || h&0xffff != 0 { + goto no + } + h1 = h & 0x7fc00000 + if h1 != 0x40000000 && h1 != 0x3fc00000 { + goto no + } + 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 + +no: + return -1 +} diff --git a/src/cmd/internal/obj/arm/list5.go b/src/cmd/internal/obj/arm/list5.go new file mode 100644 index 0000000000..1c817bb134 --- /dev/null +++ b/src/cmd/internal/obj/arm/list5.go @@ -0,0 +1,311 @@ +// Inferno utils/5c/list.c +// http://code.google.com/p/inferno-os/source/browse/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" +) + +const ( + STRINGSZ = 1000 +) + +var extra = []string{ + ".EQ", + ".NE", + ".CS", + ".CC", + ".MI", + ".PL", + ".VS", + ".VC", + ".HI", + ".LS", + ".GE", + ".LT", + ".GT", + ".LE", + "", + ".NV", +} + +var bigP *obj.Prog + +func Pconv(p *obj.Prog) string { + var str string + var sc string + var fp string + + var a int + var s int + + a = int(p.As) + s = int(p.Scond) + sc = extra[s&C_SCOND] + if s&C_SBIT != 0 { + sc += ".S" + } + if s&C_PBIT != 0 { + sc += ".P" + } + if s&C_WBIT != 0 { + sc += ".W" + } + if s&C_UBIT != 0 { /* ambiguous with FBIT */ + sc += ".U" + } + if a == AMOVM { + if p.From.Type_ == D_CONST { + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,%v", p.Pc, p.Line(), Aconv(a), sc, RAconv(&p.From), Dconv(p, 0, &p.To)) + } else if p.To.Type_ == D_CONST { + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,%v", p.Pc, p.Line(), Aconv(a), sc, Dconv(p, 0, &p.From), RAconv(&p.To)) + } else { + + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,%v", p.Pc, p.Line(), Aconv(a), sc, Dconv(p, 0, &p.From), Dconv(p, 0, &p.To)) + } + } else if a == ADATA { + str = fmt.Sprintf("%.5d (%v)\t%v\t%v/%d,%v", p.Pc, p.Line(), Aconv(a), Dconv(p, 0, &p.From), p.Reg, Dconv(p, 0, &p.To)) + } else if p.As == ATEXT { + str = fmt.Sprintf("%.5d (%v)\t%v\t%v,%d,%v", p.Pc, p.Line(), Aconv(a), Dconv(p, 0, &p.From), p.Reg, Dconv(p, 0, &p.To)) + } else if p.Reg == NREG { + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,%v", p.Pc, p.Line(), Aconv(a), sc, Dconv(p, 0, &p.From), Dconv(p, 0, &p.To)) + } else if p.From.Type_ != D_FREG { + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,R%d,%v", p.Pc, p.Line(), Aconv(a), sc, Dconv(p, 0, &p.From), p.Reg, Dconv(p, 0, &p.To)) + } else { + + str = fmt.Sprintf("%.5d (%v)\t%v%s\t%v,F%d,%v", p.Pc, p.Line(), Aconv(a), sc, Dconv(p, 0, &p.From), p.Reg, Dconv(p, 0, &p.To)) + } + + fp += str + return fp +} + +func Aconv(a int) string { + var s string + var fp string + + s = "???" + if a >= AXXX && a < ALAST { + s = anames5[a] + } + fp += s + return fp +} + +func Dconv(p *obj.Prog, flag int, a *obj.Addr) string { + var str string + var fp string + + var op string + var v int + + switch a.Type_ { + default: + str = fmt.Sprintf("GOK-type(%d)", a.Type_) + + case D_NONE: + str = "" + if a.Name != D_NONE || a.Reg != NREG || a.Sym != nil { + str = fmt.Sprintf("%v(R%d)(NONE)", Mconv(a), a.Reg) + } + + case D_CONST: + if a.Reg != NREG { + str = fmt.Sprintf("$%v(R%d)", Mconv(a), a.Reg) + } else { + + str = fmt.Sprintf("$%v", Mconv(a)) + } + + case D_CONST2: + str = fmt.Sprintf("$%d-%d", a.Offset, a.Offset2) + + case D_SHIFT: + v = int(a.Offset) + op = string("<<>>->@>"[((v>>5)&3)<<1:]) + if v&(1<<4) != 0 { + str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) + } else { + + str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) + } + if a.Reg != NREG { + str += fmt.Sprintf("(R%d)", a.Reg) + } + + case D_OREG: + if a.Reg != NREG { + str = fmt.Sprintf("%v(R%d)", Mconv(a), a.Reg) + } else { + + str = fmt.Sprintf("%v", Mconv(a)) + } + + case D_REG: + str = fmt.Sprintf("R%d", a.Reg) + if a.Name != D_NONE || a.Sym != nil { + str = fmt.Sprintf("%v(R%d)(REG)", Mconv(a), a.Reg) + } + + case D_FREG: + str = fmt.Sprintf("F%d", a.Reg) + if a.Name != D_NONE || a.Sym != nil { + str = fmt.Sprintf("%v(R%d)(REG)", Mconv(a), a.Reg) + } + + case D_PSR: + str = fmt.Sprintf("PSR") + if a.Name != D_NONE || a.Sym != nil { + str = fmt.Sprintf("%v(PSR)(REG)", Mconv(a)) + } + + case D_BRANCH: + if a.Sym != nil { + str = fmt.Sprintf("%s(SB)", a.Sym.Name) + } else if p != nil && p.Pcond != nil { + str = fmt.Sprintf("%d", p.Pcond.Pc) + } else if a.U.Branch != nil { + str = fmt.Sprintf("%d", a.U.Branch.Pc) + } else { + + str = fmt.Sprintf("%d(PC)", a.Offset) /*-pc*/ + } + + case D_FCONST: + str = fmt.Sprintf("$%.17g", a.U.Dval) + + case D_SCONST: + str = fmt.Sprintf("$\"%q\"", a.U.Sval) + break + } + + fp += str + return fp +} + +func RAconv(a *obj.Addr) string { + var str string + var fp string + + var i int + var v int + + str = fmt.Sprintf("GOK-reglist") + switch a.Type_ { + case D_CONST, + D_CONST2: + if a.Reg != NREG { + break + } + if a.Sym != nil { + break + } + v = int(a.Offset) + str = "" + for i = 0; i < NREG; i++ { + if v&(1<<uint(i)) != 0 { + if str[0] == 0 { + str += "[R" + } else { + + str += ",R" + } + str += fmt.Sprintf("%d", i) + } + } + + str += "]" + } + + fp += str + return fp +} + +func Rconv(r int) string { + var fp string + + var str string + + str = fmt.Sprintf("R%d", r) + fp += str + return fp +} + +func DRconv(a int) string { + var s string + var fp string + + s = "C_??" + if a >= C_NONE && a <= C_NCLASS { + s = cnames5[a] + } + fp += s + return fp +} + +func Mconv(a *obj.Addr) string { + var str string + var fp string + + var s *obj.LSym + + s = a.Sym + if s == nil { + str = fmt.Sprintf("%d", int(a.Offset)) + goto out + } + + switch a.Name { + default: + str = fmt.Sprintf("GOK-name(%d)", a.Name) + + case D_NONE: + str = fmt.Sprintf("%d", a.Offset) + + case D_EXTERN: + str = fmt.Sprintf("%s+%d(SB)", s.Name, int(a.Offset)) + + case D_STATIC: + str = fmt.Sprintf("%s<>+%d(SB)", s.Name, int(a.Offset)) + + case D_AUTO: + str = fmt.Sprintf("%s-%d(SP)", s.Name, int(-a.Offset)) + + case D_PARAM: + str = fmt.Sprintf("%s+%d(FP)", s.Name, int(a.Offset)) + break + } + +out: + fp += str + return fp +} diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go new file mode 100644 index 0000000000..6905376778 --- /dev/null +++ b/src/cmd/internal/obj/arm/obj5.go @@ -0,0 +1,1175 @@ +// Derived from Inferno utils/5c/swt.c +// http://code.google.com/p/inferno-os/source/browse/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" + "encoding/binary" + "fmt" + "log" + "math" +) + +var zprg5 = obj.Prog{ + As: AGOK, + Scond: C_SCOND_NONE, + Reg: NREG, + From: obj.Addr{ + Name: D_NONE, + Type_: D_NONE, + Reg: NREG, + }, + To: obj.Addr{ + Name: D_NONE, + Type_: D_NONE, + Reg: NREG, + }, +} + +func symtype(a *obj.Addr) int { + return int(a.Name) +} + +func isdata(p *obj.Prog) bool { + return p.As == ADATA || p.As == AGLOBL +} + +func iscall(p *obj.Prog) bool { + return p.As == ABL +} + +func datasize(p *obj.Prog) int { + return int(p.Reg) +} + +func textflag(p *obj.Prog) int { + return int(p.Reg) +} + +func settextflag(p *obj.Prog, f int) { + p.Reg = uint8(f) +} + +func progedit(ctxt *obj.Link, p *obj.Prog) { + var literal string + var s *obj.LSym + var tlsfallback *obj.LSym + + p.From.Class = 0 + p.To.Class = 0 + + // Rewrite B/BL to symbol as D_BRANCH. + switch p.As { + + case AB, + ABL, + ADUFFZERO, + ADUFFCOPY: + if p.To.Type_ == D_OREG && (p.To.Name == D_EXTERN || p.To.Name == D_STATIC) && p.To.Sym != nil { + p.To.Type_ = D_BRANCH + } + break + } + + // Replace TLS register fetches on older ARM procesors. + 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 rewriten 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 ctxt.Goarm < 7 { + // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. + if tlsfallback == nil { + + tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) + } + + // MOVW LR, R11 + p.As = AMOVW + + p.From.Type_ = D_REG + p.From.Reg = REGLINK + p.To.Type_ = D_REG + p.To.Reg = REGTMP + + // BL runtime.read_tls_fallback(SB) + p = obj.Appendp(ctxt, p) + + p.As = ABL + p.To.Type_ = D_BRANCH + p.To.Sym = tlsfallback + p.To.Offset = 0 + + // MOVW R11, LR + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_REG + p.From.Reg = REGTMP + p.To.Type_ = D_REG + p.To.Reg = REGLINK + break + } + } + + // Otherwise, MRC/MCR instructions need no further treatment. + p.As = AWORD + + break + } + + // Rewrite float constants to values stored in memory. + switch p.As { + + case AMOVF: + if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { + var i32 uint32 + var f32 float32 + f32 = float32(p.From.U.Dval) + i32 = math.Float32bits(f32) + literal = fmt.Sprintf("$f32.%08x", i32) + s = obj.Linklookup(ctxt, literal, 0) + if s.Type_ == 0 { + s.Type_ = obj.SRODATA + obj.Adduint32(ctxt, s, i32) + s.Reachable = 0 + } + + p.From.Type_ = D_OREG + p.From.Sym = s + p.From.Name = D_EXTERN + p.From.Offset = 0 + } + + case AMOVD: + if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { + var i64 uint64 + i64 = math.Float64bits(p.From.U.Dval) + literal = fmt.Sprintf("$f64.%016x", i64) + s = obj.Linklookup(ctxt, literal, 0) + if s.Type_ == 0 { + s.Type_ = obj.SRODATA + obj.Adduint64(ctxt, s, i64) + s.Reachable = 0 + } + + p.From.Type_ = D_OREG + p.From.Sym = s + p.From.Name = D_EXTERN + p.From.Offset = 0 + } + + break + } + + if ctxt.Flag_shared != 0 { + // Shared libraries use R_ARM_TLS_IE32 instead of + // R_ARM_TLS_LE32, replacing the link time constant TLS offset in + // runtime.tlsg with an address to a GOT entry containing the + // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to + // compensate. + if ctxt.Tlsg == nil { + + ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0) + } + + if p.From.Type_ == D_CONST && p.From.Name == D_EXTERN && p.From.Sym == ctxt.Tlsg { + p.From.Type_ = D_OREG + } + if p.To.Type_ == D_CONST && p.To.Name == D_EXTERN && p.To.Sym == ctxt.Tlsg { + p.To.Type_ = D_OREG + } + } +} + +func prg() *obj.Prog { + var p *obj.Prog + + p = new(obj.Prog) + *p = zprg5 + return p +} + +// Prog.mark +const ( + FOLL = 1 << 0 + LABEL = 1 << 1 + LEAF = 1 << 2 +) + +func linkcase(casep *obj.Prog) { + var p *obj.Prog + + for p = casep; p != nil; p = p.Link { + if p.As == ABCASE { + for ; p != nil && p.As == ABCASE; p = p.Link { + p.Pcrel = casep + } + break + } + } +} + +func nocache5(p *obj.Prog) { + p.Optab = 0 + p.From.Class = 0 + p.To.Class = 0 +} + +func addstacksplit(ctxt *obj.Link, cursym *obj.LSym) { + var p *obj.Prog + var pl *obj.Prog + var p1 *obj.Prog + var p2 *obj.Prog + var q *obj.Prog + var q1 *obj.Prog + var q2 *obj.Prog + var o int + var autosize int32 + var autoffset int32 + + autosize = 0 + + if ctxt.Symmorestack[0] == nil { + ctxt.Symmorestack[0] = obj.Linklookup(ctxt, "runtime.morestack", 0) + ctxt.Symmorestack[1] = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0) + } + + q = nil + + ctxt.Cursym = cursym + + if cursym.Text == nil || cursym.Text.Link == nil { + return + } + + softfloat(ctxt, cursym) + + p = cursym.Text + autoffset = int32(p.To.Offset) + if autoffset < 0 { + autoffset = 0 + } + cursym.Locals = autoffset + cursym.Args = p.To.Offset2 + + if ctxt.Debugzerostack != 0 { + if autoffset != 0 && !(p.Reg&obj.NOSPLIT != 0) { + // MOVW $4(R13), R1 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Reg = 13 + p.From.Offset = 4 + p.To.Type_ = D_REG + p.To.Reg = 1 + + // MOVW $n(R13), R2 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Reg = 13 + p.From.Offset = 4 + int64(autoffset) + p.To.Type_ = D_REG + p.To.Reg = 2 + + // MOVW $0, R3 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Offset = 0 + p.To.Type_ = D_REG + p.To.Reg = 3 + + // L: + // MOVW.nil R3, 0(R1) +4 + // CMP R1, R2 + // BNE L + pl = obj.Appendp(ctxt, p) + p = pl + + p.As = AMOVW + p.From.Type_ = D_REG + p.From.Reg = 3 + p.To.Type_ = D_OREG + p.To.Reg = 1 + p.To.Offset = 4 + p.Scond |= C_PBIT + + p = obj.Appendp(ctxt, p) + p.As = ACMP + p.From.Type_ = D_REG + p.From.Reg = 1 + p.Reg = 2 + + p = obj.Appendp(ctxt, p) + p.As = ABNE + p.To.Type_ = D_BRANCH + p.Pcond = pl + } + } + + /* + * find leaf subroutines + * strip NOPs + * expand RET + * expand BECOME pseudo + */ + for p = cursym.Text; p != nil; p = p.Link { + + switch p.As { + case ACASE: + if ctxt.Flag_shared != 0 { + linkcase(p) + } + + case ATEXT: + p.Mark |= LEAF + + case ARET: + break + + case ADIV, + ADIVU, + AMOD, + AMODU: + q = p + if ctxt.Sym_div == nil { + initdiv(ctxt) + } + cursym.Text.Mark &^= LEAF + continue + + case ANOP: + q1 = p.Link + q.Link = q1 /* q is non-nop */ + if q1 != nil { + q1.Mark |= p.Mark + } + continue + + case ABL, + ABX, + ADUFFZERO, + ADUFFCOPY: + cursym.Text.Mark &^= LEAF + fallthrough + + case ABCASE, + AB, + ABEQ, + ABNE, + ABCS, + ABHS, + ABCC, + ABLO, + ABMI, + ABPL, + ABVS, + ABVC, + ABHI, + ABLS, + ABGE, + ABLT, + ABGT, + ABLE: + q1 = p.Pcond + if q1 != nil { + for q1.As == ANOP { + q1 = q1.Link + p.Pcond = q1 + } + } + + break + } + + q = p + } + + for p = cursym.Text; p != nil; p = p.Link { + o = int(p.As) + switch o { + case ATEXT: + autosize = int32(p.To.Offset + 4) + if autosize <= 4 { + if cursym.Text.Mark&LEAF != 0 { + p.To.Offset = -4 + autosize = 0 + } + } + + if !(autosize != 0) && !(cursym.Text.Mark&LEAF != 0) { + if ctxt.Debugvlog != 0 { + fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) + obj.Bflush(ctxt.Bso) + } + + cursym.Text.Mark |= LEAF + } + + if cursym.Text.Mark&LEAF != 0 { + cursym.Leaf = 1 + if !(autosize != 0) { + break + } + } + + if !(p.Reg&obj.NOSPLIT != 0) { + p = stacksplit(ctxt, p, autosize, bool2int(!(cursym.Text.Reg&obj.NEEDCTXT != 0))) // emit split check + } + + // MOVW.W R14,$-autosize(SP) + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.Scond |= C_WBIT + p.From.Type_ = D_REG + p.From.Reg = REGLINK + p.To.Type_ = D_OREG + p.To.Offset = int64(-autosize) + p.To.Reg = REGSP + p.Spadj = autosize + + if cursym.Text.Reg&obj.WRAPPER != 0 { + // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame + // + // MOVW g_panic(g), R1 + // CMP $0, R1 + // B.EQ end + // 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) + // end: + // NOP + // + // 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(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_OREG + p.From.Reg = REGG + p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic + p.To.Type_ = D_REG + p.To.Reg = 1 + + p = obj.Appendp(ctxt, p) + p.As = ACMP + p.From.Type_ = D_CONST + p.From.Offset = 0 + p.Reg = 1 + + p = obj.Appendp(ctxt, p) + p.As = ABEQ + p.To.Type_ = D_BRANCH + p1 = p + + p = obj.Appendp(ctxt, p) + p.As = AMOVW + p.From.Type_ = D_OREG + p.From.Reg = 1 + p.From.Offset = 0 // Panic.argp + p.To.Type_ = D_REG + p.To.Reg = 2 + + p = obj.Appendp(ctxt, p) + p.As = AADD + p.From.Type_ = D_CONST + p.From.Offset = int64(autosize) + 4 + p.Reg = 13 + p.To.Type_ = D_REG + p.To.Reg = 3 + + p = obj.Appendp(ctxt, p) + p.As = ACMP + p.From.Type_ = D_REG + p.From.Reg = 2 + p.Reg = 3 + + p = obj.Appendp(ctxt, p) + p.As = ABNE + p.To.Type_ = D_BRANCH + p2 = p + + p = obj.Appendp(ctxt, p) + p.As = AADD + p.From.Type_ = D_CONST + p.From.Offset = 4 + p.Reg = 13 + p.To.Type_ = D_REG + p.To.Reg = 4 + + p = obj.Appendp(ctxt, p) + p.As = AMOVW + p.From.Type_ = D_REG + p.From.Reg = 4 + p.To.Type_ = D_OREG + p.To.Reg = 1 + p.To.Offset = 0 // Panic.argp + + p = obj.Appendp(ctxt, p) + + p.As = ANOP + p1.Pcond = p + p2.Pcond = p + } + + case ARET: + nocache5(p) + if cursym.Text.Mark&LEAF != 0 { + if !(autosize != 0) { + p.As = AB + p.From = zprg5.From + if p.To.Sym != nil { // retjmp + p.To.Type_ = D_BRANCH + } else { + + p.To.Type_ = D_OREG + p.To.Offset = 0 + p.To.Reg = REGLINK + } + + break + } + } + + p.As = AMOVW + p.Scond |= C_PBIT + p.From.Type_ = D_OREG + p.From.Offset = int64(autosize) + p.From.Reg = REGSP + p.To.Type_ = D_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(ctxt, p) + q2.As = AB + q2.To.Type_ = D_BRANCH + q2.To.Sym = p.To.Sym + p.To.Sym = nil + p = q2 + } + + case AADD: + if p.From.Type_ == D_CONST && p.From.Reg == NREG && p.To.Type_ == D_REG && p.To.Reg == REGSP { + p.Spadj = int32(-p.From.Offset) + } + + case ASUB: + if p.From.Type_ == D_CONST && p.From.Reg == NREG && p.To.Type_ == D_REG && p.To.Reg == REGSP { + p.Spadj = int32(p.From.Offset) + } + + case ADIV, + ADIVU, + AMOD, + AMODU: + if ctxt.Debugdivmod != 0 { + break + } + if p.From.Type_ != D_REG { + break + } + if p.To.Type_ != D_REG { + break + } + q1 = p + + /* MOV a,4(SP) */ + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.Lineno = q1.Lineno + p.From.Type_ = D_REG + p.From.Reg = q1.From.Reg + p.To.Type_ = D_OREG + p.To.Reg = REGSP + p.To.Offset = 4 + + /* MOV b,REGTMP */ + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.Lineno = q1.Lineno + p.From.Type_ = D_REG + p.From.Reg = int8(q1.Reg) + if q1.Reg == NREG { + p.From.Reg = q1.To.Reg + } + p.To.Type_ = D_REG + p.To.Reg = REGTMP + p.To.Offset = 0 + + /* CALL appropriate */ + p = obj.Appendp(ctxt, p) + + p.As = ABL + p.Lineno = q1.Lineno + p.To.Type_ = D_BRANCH + switch o { + case ADIV: + p.To.Sym = ctxt.Sym_div + + case ADIVU: + p.To.Sym = ctxt.Sym_divu + + case AMOD: + p.To.Sym = ctxt.Sym_mod + + case AMODU: + p.To.Sym = ctxt.Sym_modu + break + } + + /* MOV REGTMP, b */ + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.Lineno = q1.Lineno + p.From.Type_ = D_REG + p.From.Reg = REGTMP + p.From.Offset = 0 + p.To.Type_ = D_REG + p.To.Reg = q1.To.Reg + + /* ADD $8,SP */ + p = obj.Appendp(ctxt, p) + + p.As = AADD + p.Lineno = q1.Lineno + p.From.Type_ = D_CONST + p.From.Reg = NREG + p.From.Offset = 8 + p.Reg = NREG + p.To.Type_ = D_REG + p.To.Reg = REGSP + p.Spadj = -8 + + /* Keep saved LR at 0(SP) after SP change. */ + /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ + /* TODO: Remove SP adjustments; see issue 6699. */ + q1.As = AMOVW + + q1.From.Type_ = D_OREG + q1.From.Reg = REGSP + q1.From.Offset = 0 + q1.Reg = NREG + q1.To.Type_ = D_REG + q1.To.Reg = REGTMP + + /* SUB $8,SP */ + q1 = obj.Appendp(ctxt, q1) + + q1.As = AMOVW + q1.From.Type_ = D_REG + q1.From.Reg = REGTMP + q1.Reg = NREG + q1.To.Type_ = D_OREG + q1.To.Reg = REGSP + q1.To.Offset = -8 + q1.Scond |= C_WBIT + q1.Spadj = 8 + + case AMOVW: + if (p.Scond&C_WBIT != 0) && p.To.Type_ == D_OREG && p.To.Reg == REGSP { + p.Spadj = int32(-p.To.Offset) + } + if (p.Scond&C_PBIT != 0) && p.From.Type_ == D_OREG && p.From.Reg == REGSP && p.To.Reg != REGPC { + p.Spadj = int32(-p.From.Offset) + } + if p.From.Type_ == D_CONST && p.From.Reg == REGSP && p.To.Type_ == D_REG && p.To.Reg == REGSP { + p.Spadj = int32(-p.From.Offset) + } + break + } + } +} + +func softfloat(ctxt *obj.Link, cursym *obj.LSym) { + var p *obj.Prog + var next *obj.Prog + var symsfloat *obj.LSym + var wasfloat int + + if ctxt.Goarm > 5 { + return + } + + symsfloat = obj.Linklookup(ctxt, "_sfloat", 0) + + wasfloat = 0 + for p = cursym.Text; p != nil; p = p.Link { + if p.Pcond != nil { + p.Pcond.Mark |= LABEL + } + } + for p = cursym.Text; p != nil; p = p.Link { + switch p.As { + case AMOVW: + if p.To.Type_ == D_FREG || p.From.Type_ == D_FREG { + goto soft + } + goto notsoft + + case AMOVWD, + AMOVWF, + AMOVDW, + AMOVFW, + AMOVFD, + AMOVDF, + AMOVF, + AMOVD, + ACMPF, + ACMPD, + AADDF, + AADDD, + ASUBF, + ASUBD, + AMULF, + AMULD, + ADIVF, + ADIVD, + ASQRTF, + ASQRTD, + AABSF, + AABSD: + goto soft + + default: + goto notsoft + } + + soft: + if !(wasfloat != 0) || (p.Mark&LABEL != 0) { + next = ctxt.Arch.Prg() + *next = *p + + // BL _sfloat(SB) + *p = zprg5 + + p.Link = next + p.As = ABL + p.To.Type_ = D_BRANCH + p.To.Sym = symsfloat + p.Lineno = next.Lineno + + p = next + wasfloat = 1 + } + + continue + + notsoft: + wasfloat = 0 + } +} + +func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, noctxt int) *obj.Prog { + // MOVW g_stackguard(g), R1 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_OREG + p.From.Reg = REGG + p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 + if ctxt.Cursym.Cfunc != 0 { + p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 + } + p.To.Type_ = D_REG + p.To.Reg = 1 + + if framesize <= obj.StackSmall { + // small stack: SP < stackguard + // CMP stackguard, SP + p = obj.Appendp(ctxt, p) + + p.As = ACMP + p.From.Type_ = D_REG + p.From.Reg = 1 + p.Reg = REGSP + } else if framesize <= obj.StackBig { + // large stack: SP-framesize < stackguard-StackSmall + // MOVW $-framesize(SP), R2 + // CMP stackguard, R2 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Reg = REGSP + p.From.Offset = int64(-framesize) + p.To.Type_ = D_REG + p.To.Reg = 2 + + p = obj.Appendp(ctxt, p) + p.As = ACMP + p.From.Type_ = D_REG + p.From.Reg = 1 + p.Reg = 2 + } else { + + // Such a large stack we need to protect against wraparound + // if SP is close to zero. + // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // CMP $StackPreempt, R1 + // MOVW.NE $StackGuard(SP), R2 + // SUB.NE R1, R2 + // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 + // CMP.NE R3, R2 + p = obj.Appendp(ctxt, p) + + p.As = ACMP + p.From.Type_ = D_CONST + p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) + p.Reg = 1 + + p = obj.Appendp(ctxt, p) + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Reg = REGSP + p.From.Offset = obj.StackGuard + p.To.Type_ = D_REG + p.To.Reg = 2 + p.Scond = C_SCOND_NE + + p = obj.Appendp(ctxt, p) + p.As = ASUB + p.From.Type_ = D_REG + p.From.Reg = 1 + p.To.Type_ = D_REG + p.To.Reg = 2 + p.Scond = C_SCOND_NE + + p = obj.Appendp(ctxt, p) + p.As = AMOVW + p.From.Type_ = D_CONST + p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) + p.To.Type_ = D_REG + p.To.Reg = 3 + p.Scond = C_SCOND_NE + + p = obj.Appendp(ctxt, p) + p.As = ACMP + p.From.Type_ = D_REG + p.From.Reg = 3 + p.Reg = 2 + p.Scond = C_SCOND_NE + } + + // MOVW.LS R14, R3 + p = obj.Appendp(ctxt, p) + + p.As = AMOVW + p.Scond = C_SCOND_LS + p.From.Type_ = D_REG + p.From.Reg = REGLINK + p.To.Type_ = D_REG + p.To.Reg = 3 + + // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted + p = obj.Appendp(ctxt, p) + + p.As = ABL + p.Scond = C_SCOND_LS + p.To.Type_ = D_BRANCH + if ctxt.Cursym.Cfunc != 0 { + p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0) + } else { + + p.To.Sym = ctxt.Symmorestack[noctxt] + } + + // BLS start + p = obj.Appendp(ctxt, p) + + p.As = ABLS + p.To.Type_ = D_BRANCH + p.Pcond = ctxt.Cursym.Text.Link + + return p +} + +func initdiv(ctxt *obj.Link) { + if ctxt.Sym_div != nil { + return + } + ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) + ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) + ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) + ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) +} + +func follow(ctxt *obj.Link, s *obj.LSym) { + var firstp *obj.Prog + var lastp *obj.Prog + + ctxt.Cursym = s + + firstp = ctxt.Arch.Prg() + lastp = firstp + xfol(ctxt, s.Text, &lastp) + lastp.Link = nil + s.Text = firstp.Link +} + +func relinv(a int) int { + switch a { + case ABEQ: + return ABNE + case ABNE: + return ABEQ + case ABCS: + return ABCC + case ABHS: + return ABLO + case ABCC: + return ABCS + case ABLO: + return ABHS + case ABMI: + return ABPL + case ABPL: + return ABMI + case ABVS: + return ABVC + case ABVC: + return ABVS + case ABHI: + return ABLS + case ABLS: + return ABHI + case ABGE: + return ABLT + case ABLT: + return ABGE + case ABGT: + return ABLE + case ABLE: + return ABGT + } + + log.Fatalf("unknown relation: %s", anames5[a]) + return 0 +} + +func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { + var q *obj.Prog + var r *obj.Prog + var a int + var i int + +loop: + if p == nil { + return + } + a = int(p.As) + if a == AB { + q = p.Pcond + if q != nil && q.As != ATEXT { + p.Mark |= FOLL + p = q + if !(p.Mark&FOLL != 0) { + goto loop + } + } + } + + if p.Mark&FOLL != 0 { + i = 0 + q = p + for ; i < 4; (func() { i++; q = q.Link })() { + if q == *last || q == nil { + break + } + a = int(q.As) + if a == ANOP { + i-- + continue + } + + if a == AB || (a == ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF { + goto copy + } + if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { + continue + } + if a != ABEQ && a != ABNE { + continue + } + + copy: + for { + r = ctxt.Arch.Prg() + *r = *p + if !(r.Mark&FOLL != 0) { + fmt.Printf("can't happen 1\n") + } + r.Mark |= FOLL + if p != q { + p = p.Link + (*last).Link = r + *last = r + continue + } + + (*last).Link = r + *last = r + if a == AB || (a == ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF { + return + } + r.As = ABNE + if a == ABNE { + r.As = ABEQ + } + r.Pcond = p.Link + r.Link = p.Pcond + if !(r.Link.Mark&FOLL != 0) { + xfol(ctxt, r.Link, last) + } + if !(r.Pcond.Mark&FOLL != 0) { + fmt.Printf("can't happen 2\n") + } + return + } + } + + a = AB + q = ctxt.Arch.Prg() + q.As = int16(a) + q.Lineno = p.Lineno + q.To.Type_ = D_BRANCH + q.To.Offset = p.Pc + q.Pcond = p + p = q + } + + p.Mark |= FOLL + (*last).Link = p + *last = p + if a == AB || (a == ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF { + return + } + + if p.Pcond != nil { + if a != ABL && a != ABX && p.Link != nil { + q = obj.Brchain(ctxt, p.Link) + if a != ATEXT && a != ABCASE { + if q != nil && (q.Mark&FOLL != 0) { + p.As = int16(relinv(a)) + p.Link = p.Pcond + p.Pcond = q + } + } + + xfol(ctxt, p.Link, last) + q = obj.Brchain(ctxt, p.Pcond) + if q == nil { + q = p.Pcond + } + if q.Mark&FOLL != 0 { + p.Pcond = q + return + } + + p = q + goto loop + } + } + + p = p.Link + goto loop +} + +var Linkarm = obj.LinkArch{ + ByteOrder: binary.LittleEndian, + Pconv: Pconv, + Name: "arm", + Thechar: '5', + Endian: obj.LittleEndian, + Addstacksplit: addstacksplit, + Assemble: span5, + Datasize: datasize, + Follow: follow, + Iscall: iscall, + Isdata: isdata, + Prg: prg, + Progedit: progedit, + Settextflag: settextflag, + Symtype: symtype, + Textflag: textflag, + Minlc: 4, + Ptrsize: 4, + Regsize: 4, + D_ADDR: D_ADDR, + D_AUTO: D_AUTO, + D_BRANCH: D_BRANCH, + D_CONST: D_CONST, + D_EXTERN: D_EXTERN, + D_FCONST: D_FCONST, + D_NONE: D_NONE, + D_PARAM: D_PARAM, + D_SCONST: D_SCONST, + D_STATIC: D_STATIC, + D_OREG: D_OREG, + ACALL: ABL, + ADATA: ADATA, + AEND: AEND, + AFUNCDATA: AFUNCDATA, + AGLOBL: AGLOBL, + AJMP: AB, + ANOP: ANOP, + APCDATA: APCDATA, + ARET: ARET, + ATEXT: ATEXT, + ATYPE: ATYPE, + AUSEFIELD: AUSEFIELD, +} diff --git a/src/cmd/internal/obj/arm/util.go b/src/cmd/internal/obj/arm/util.go new file mode 100644 index 0000000000..f036c5ffea --- /dev/null +++ b/src/cmd/internal/obj/arm/util.go @@ -0,0 +1,12 @@ +// 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 + +func bool2int(b bool) int { + if b { + return 1 + } + return 0 +} |
