diff options
Diffstat (limited to 'src/liblink/asm5.c')
-rw-r--r-- | src/liblink/asm5.c | 2458 |
1 files changed, 2458 insertions, 0 deletions
diff --git a/src/liblink/asm5.c b/src/liblink/asm5.c new file mode 100644 index 000000000..465b645b2 --- /dev/null +++ b/src/liblink/asm5.c @@ -0,0 +1,2458 @@ +// 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. + +// Instruction layout. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/5l/5.out.h" +#include "../pkg/runtime/stack.h" + +typedef struct Optab Optab; +typedef struct Oprang Oprang; +typedef uchar Opcross[32][2][32]; + +struct Optab +{ + char as; + uchar a1; + char a2; + uchar a3; + uchar type; + char size; + char param; + char flag; + uchar pcrelsiz; +}; +struct Oprang +{ + Optab* start; + Optab* stop; +}; + +enum +{ + LFROM = 1<<0, + LTO = 1<<1, + LPOOL = 1<<2, + LPCREL = 1<<3, + + C_NONE = 0, + C_REG, + C_REGREG, + C_REGREG2, + C_SHIFT, + C_FREG, + C_PSR, + C_FCR, + + C_RCON, /* 0xff rotated */ + C_NCON, /* ~RCON */ + C_SCON, /* 0xffff */ + C_LCON, + C_LCONADDR, + C_ZFCON, + C_SFCON, + C_LFCON, + + C_RACON, + C_LACON, + + C_SBRA, + C_LBRA, + + C_HAUTO, /* halfword insn offset (-0xff to 0xff) */ + C_FAUTO, /* float insn offset (0 to 0x3fc, word aligned) */ + C_HFAUTO, /* both H and F */ + C_SAUTO, /* -0xfff to 0xfff */ + C_LAUTO, + + C_HOREG, + C_FOREG, + C_HFOREG, + C_SOREG, + C_ROREG, + C_SROREG, /* both nil and R */ + C_LOREG, + + C_PC, + C_SP, + C_HREG, + + C_ADDR, /* reference to relocatable address */ + + C_GOK, +}; + +static Optab optab[] = +{ + /* struct Optab: + OPCODE, from, prog->reg, to, type,size,param,flag */ + { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, + { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, + + { AADD, C_REG, C_REG, C_REG, 1, 4, 0 }, + { AADD, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMVN, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { ACMP, C_REG, C_REG, C_NONE, 1, 4, 0 }, + + { AADD, C_RCON, C_REG, C_REG, 2, 4, 0 }, + { AADD, C_RCON, C_NONE, C_REG, 2, 4, 0 }, + { AMOVW, C_RCON, C_NONE, C_REG, 2, 4, 0 }, + { AMVN, C_RCON, C_NONE, C_REG, 2, 4, 0 }, + { ACMP, C_RCON, C_REG, C_NONE, 2, 4, 0 }, + + { AADD, C_SHIFT,C_REG, C_REG, 3, 4, 0 }, + { AADD, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, + { AMVN, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, + { ACMP, C_SHIFT,C_REG, C_NONE, 3, 4, 0 }, + + { AMOVW, C_RACON,C_NONE, C_REG, 4, 4, REGSP }, + + { AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL }, + { ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, + { ABX, C_NONE, C_NONE, C_SBRA, 74, 20, 0 }, + { ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, + + { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, + { ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0 }, + { ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0 }, + { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, + { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, + + { ASLL, C_RCON, C_REG, C_REG, 8, 4, 0 }, + { ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0 }, + + { ASLL, C_REG, C_NONE, C_REG, 9, 4, 0 }, + { ASLL, C_REG, C_REG, C_REG, 9, 4, 0 }, + + { ASWI, C_NONE, C_NONE, C_NONE, 10, 4, 0 }, + { ASWI, C_NONE, C_NONE, C_LOREG, 10, 4, 0 }, + { ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0 }, + + { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, + + { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, + { AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM }, + { AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4}, + + { AADD, C_NCON, C_REG, C_REG, 13, 8, 0 }, + { AADD, C_NCON, C_NONE, C_REG, 13, 8, 0 }, + { AMVN, C_NCON, C_NONE, C_REG, 13, 8, 0 }, + { ACMP, C_NCON, C_REG, C_NONE, 13, 8, 0 }, + { AADD, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM }, + { AADD, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM }, + { AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM }, + { ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM }, + + { AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0 }, + { AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0 }, + { AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0 }, + { AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0 }, + + { AMUL, C_REG, C_REG, C_REG, 15, 4, 0 }, + { AMUL, C_REG, C_NONE, C_REG, 15, 4, 0 }, + + { ADIV, C_REG, C_REG, C_REG, 16, 4, 0 }, + { ADIV, C_REG, C_NONE, C_REG, 16, 4, 0 }, + + { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, + { AMULA, C_REG, C_REG, C_REGREG2, 17, 4, 0 }, + + { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, + { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, + { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, + { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, + { AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, + { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, + { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, + + { AMOVW, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, + { AMOVW, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, + { AMOVBU, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, + { AMOVBU, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, + + { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, + { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, + { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, + { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, + { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, + { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, + { AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, + { AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, + { AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, + { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, + { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, + { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, + + { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, + { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, + { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, + { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, + { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, + { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, + + { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, + + { AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0 }, + { AMOVW, C_REG, C_NONE, C_PSR, 36, 4, 0 }, + { AMOVW, C_RCON, C_NONE, C_PSR, 37, 4, 0 }, + + { AMOVM, C_LCON, C_NONE, C_SOREG, 38, 4, 0 }, + { AMOVM, C_SOREG,C_NONE, C_LCON, 39, 4, 0 }, + + { ASWPW, C_SOREG,C_REG, C_REG, 40, 4, 0 }, + + { ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0 }, + + { AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP }, + { AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0 }, + + { AMOVF, C_FAUTO,C_NONE, C_FREG, 51, 4, REGSP }, + { AMOVF, C_FOREG,C_NONE, C_FREG, 51, 4, 0 }, + + { AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO }, + { AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO }, + + { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, + { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, + + { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4 }, + { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4}, + + { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, + { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, + { AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, + + { AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0 }, + { AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0 }, + + { AMOVW, C_SHIFT,C_NONE, C_REG, 59, 4, 0 }, + { AMOVBU, C_SHIFT,C_NONE, C_REG, 59, 4, 0 }, + + { AMOVB, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, + { AMOVBS, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, + + { AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, + { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, + { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, + + { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 }, + { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0, LPCREL, 0 }, + + { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + { AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + + { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVBS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVBS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVHS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVHS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + + { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, + { AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, + { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, + + { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVBS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVBS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVHS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVHS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + + { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, + { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, + + { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 8, 0 }, + { AMOVF, C_SFCON,C_NONE, C_FREG, 81, 4, 0 }, + + { ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0 }, + { ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0 }, + { AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0 }, + { AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0 }, + + { AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0 }, + { AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0 }, + + { ATST, C_REG, C_NONE, C_NONE, 90, 4, 0 }, + + { ALDREXD, C_SOREG,C_NONE, C_REG, 91, 4, 0 }, + { ASTREXD, C_SOREG,C_REG, C_REG, 92, 4, 0 }, + + { APLD, C_SOREG,C_NONE, C_NONE, 95, 4, 0 }, + + { AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0 }, + + { ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0 }, + + { AMULWT, C_REG, C_REG, C_REG, 98, 4, 0 }, + { AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 }, + + { AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 }, + { APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0 }, + { AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0 }, + + { ADUFFZERO, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, // same as ABL + { ADUFFCOPY, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, // same as ABL + + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, +}; + +static struct { + uint32 start; + uint32 size; + uint32 extra; +} pool; + +static int checkpool(Link*, Prog*, int); +static int flushpool(Link*, Prog*, int, int); +static void addpool(Link*, Prog*, Addr*); +static void asmout(Link*, Prog*, Optab*, int32*); +static Optab* oplook(Link*, Prog*); +static int32 oprrr(Link*, int, int); +static int32 olr(Link*, int32, int, int, int); +static int32 olhr(Link*, int32, int, int, int); +static int32 olrr(Link*, int, int, int, int); +static int32 olhrr(Link*, int, int, int, int); +static int32 osr(Link*, int, int, int32, int, int); +static int32 oshr(Link*, int, int32, int, int); +static int32 ofsr(Link*, int, int, int32, int, int, Prog*); +static int32 osrr(Link*, int, int, int, int); +static int32 oshrr(Link*, int, int, int, int); +static int32 omvl(Link*, Prog*, Addr*, int); +static int32 immaddr(int32); +static int aclass(Link*, Addr*); +static int32 immrot(uint32); +static int32 immaddr(int32); +static int32 opbra(Link*, int, int); + +static Opcross opcross[8]; +static Oprang oprange[ALAST]; +static char xcmp[C_GOK+1][C_GOK+1]; +static uchar repop[ALAST]; + +static Prog zprg = { + .as = AGOK, + .scond = C_SCOND_NONE, + .reg = NREG, + .from = { + .name = D_NONE, + .type = D_NONE, + .reg = NREG, + }, + .to = { + .name = D_NONE, + .type = D_NONE, + .reg = NREG, + }, +}; + +static void +nocache(Prog *p) +{ + p->optab = 0; + p->from.class = 0; + p->to.class = 0; +} + +static int +scan(Link *ctxt, Prog *op, Prog *p, int c) +{ + Prog *q; + + for(q = op->link; q != p && q != nil; q = q->link){ + q->pc = c; + c += oplook(ctxt, q)->size; + nocache(q); + } + return c; +} + +/* size of a case statement including jump table */ +static int32 +casesz(Link *ctxt, Prog *p) +{ + int jt = 0; + int32 n = 0; + Optab *o; + + for( ; p != nil; p = p->link){ + if(p->as == ABCASE) + jt = 1; + else if(jt) + break; + o = oplook(ctxt, p); + n += o->size; + } + return n; +} + +static void buildop(Link*); + +void +span5(Link *ctxt, LSym *cursym) +{ + Prog *p, *op; + Optab *o; + int m, bflag, i, v; + int32 c, out[6]; + uchar *bp; + + 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 = p->to.offset + 4; + c = 0; + + for(op = p, p = p->link; p != nil; op = p, p = p->link) { + ctxt->curp = p; + p->pc = c; + o = oplook(ctxt, p); + m = o->size; + // must check literal pool here in case p generates many instructions + if(ctxt->blitrl){ + if(checkpool(ctxt, op, p->as == ACASE ? casesz(ctxt, p) : m)) { + p->pc = scan(ctxt, op, p, c); + c = p->pc; + } + } + if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { + ctxt->diag("zero-width instruction\n%P", p); + continue; + } + switch(o->flag & (LFROM|LTO|LPOOL)) { + case LFROM: + addpool(ctxt, p, &p->from); + break; + case LTO: + addpool(ctxt, p, &p->to); + break; + 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 += m; + } + if(ctxt->blitrl){ + if(checkpool(ctxt, op, 0)) + c = scan(ctxt, op, nil, c); + } + cursym->size = 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. + */ + do { + if(ctxt->debugvlog) + Bprint(ctxt->bso, "%5.2f span1\n", cputime()); + bflag = 0; + c = 0; + for(p = cursym->text; p != nil; p = p->link) { + ctxt->curp = p; + p->pc = c; + o = oplook(ctxt,p); +/* 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; + } + } + */ + m = o->size; + if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { + if(p->as == ATEXT) { + ctxt->autosize = p->to.offset + 4; + continue; + } + ctxt->diag("zero-width instruction\n%P", p); + continue; + } + c += m; + } + cursym->size = c; + } while(bflag); + + /* + * 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->gmsym == nil) + ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); + + p = cursym->text; + ctxt->autosize = p->to.offset + 4; + symgrow(ctxt, cursym, cursym->size); + + bp = cursym->p; + for(p = p->link; p != nil; p = p->link) { + ctxt->pc = p->pc; + ctxt->curp = p; + o = oplook(ctxt, p); + asmout(ctxt, p, o, out); + for(i=0; i<o->size/4; i++) { + v = out[i]; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; + } + } +} + +/* + * 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. + */ +static int +checkpool(Link *ctxt, Prog *p, int sz) +{ + if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) + return flushpool(ctxt, p, 1, 0); + else if(p->link == nil) + return flushpool(ctxt, p, 2, 0); + return 0; +} + +static int +flushpool(Link *ctxt, Prog *p, int skip, int force) +{ + Prog *q; + + if(ctxt->blitrl) { + if(skip){ + if(0 && skip==1)print("note: flush literal pool at %llux: len=%ud ref=%ux\n", 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 && (p->pc+pool.size-pool.start < 2048)) + return 0; + 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 + while(ctxt->blitrl) { + ctxt->blitrl->lineno = p->lineno; + ctxt->blitrl = ctxt->blitrl->link; + } + ctxt->blitrl = 0; /* BUG: should refer back to values until out-of-range */ + ctxt->elitrl = 0; + pool.size = 0; + pool.start = 0; + pool.extra = 0; + return 1; + } + return 0; +} + +static void +addpool(Link *ctxt, Prog *p, Addr *a) +{ + Prog *q, t; + int c; + + c = aclass(ctxt, a); + + t = zprg; + t.as = AWORD; + + switch(c) { + default: + t.to = *a; + if(ctxt->flag_shared && t.to.sym != nil) + t.pcrel = p; + break; + + case C_SROREG: + case C_LOREG: + case C_ROREG: + case C_FOREG: + case C_SOREG: + case C_HOREG: + case C_FAUTO: + case C_SAUTO: + case C_LAUTO: + case 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 && memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { + p->pcond = q; + return; + } + } + + q = ctxt->arch->prg(); + *q = t; + q->pc = pool.size; + + if(ctxt->blitrl == nil) { + ctxt->blitrl = q; + pool.start = p->pc; + } else + ctxt->elitrl->link = q; + ctxt->elitrl = q; + pool.size += 4; + + p->pcond = q; +} + +static int32 +regoff(Link *ctxt, Addr *a) +{ + + ctxt->instoffset = 0; + aclass(ctxt, a); + return ctxt->instoffset; +} + +static int32 +immrot(uint32 v) +{ + int i; + + for(i=0; i<16; i++) { + if((v & ~0xff) == 0) + return (i<<8) | v | (1<<25); + v = (v<<2) | (v>>30); + } + return 0; +} + +static int32 +immaddr(int32 v) +{ + if(v >= 0 && v <= 0xfff) + return (v & 0xfff) | + (1<<24) | /* pre indexing */ + (1<<23); /* pre indexing, up */ + if(v >= -0xfff && v < 0) + return (-v & 0xfff) | + (1<<24); /* pre indexing */ + return 0; +} + +static int +immfloat(int32 v) +{ + return (v & 0xC03) == 0; /* offset will fit in floating-point load/store */ +} + +static int +immhalf(int32 v) +{ + if(v >= 0 && v <= 0xff) + return v| + (1<<24)| /* pre indexing */ + (1<<23); /* pre indexing, up */ + if(v >= -0xff && v < 0) + return (-v & 0xff)| + (1<<24); /* pre indexing */ + return 0; +} + +static int +aclass(Link *ctxt, Addr *a) +{ + LSym *s; + int t; + + 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: + case D_STATIC: + if(a->sym == 0 || a->sym->name == 0) { + print("null sym external\n"); + print("%D\n", a); + return C_GOK; + } + ctxt->instoffset = 0; // s.b. unused but just in case + return C_ADDR; + + case D_AUTO: + ctxt->instoffset = ctxt->autosize + a->offset; + t = immaddr(ctxt->instoffset); + if(t){ + if(immhalf(ctxt->instoffset)) + return immfloat(t) ? C_HFAUTO : C_HAUTO; + if(immfloat(t)) + return C_FAUTO; + return C_SAUTO; + } + return C_LAUTO; + + case D_PARAM: + ctxt->instoffset = ctxt->autosize + a->offset + 4L; + t = immaddr(ctxt->instoffset); + if(t){ + if(immhalf(ctxt->instoffset)) + return immfloat(t) ? C_HFAUTO : C_HAUTO; + if(immfloat(t)) + return C_FAUTO; + return C_SAUTO; + } + return C_LAUTO; + case D_NONE: + ctxt->instoffset = a->offset; + t = immaddr(ctxt->instoffset); + if(t) { + if(immhalf(ctxt->instoffset)) /* n.b. that it will also satisfy immrot */ + return immfloat(t) ? C_HFOREG : C_HOREG; + if(immfloat(t)) + return C_FOREG; /* n.b. that it will also satisfy immrot */ + t = immrot(ctxt->instoffset); + if(t) + return C_SROREG; + if(immhalf(ctxt->instoffset)) + return C_HOREG; + return C_SOREG; + } + t = immrot(ctxt->instoffset); + if(t) + return C_ROREG; + return C_LOREG; + } + return C_GOK; + + case D_PSR: + return C_PSR; + + case D_OCONST: + switch(a->name) { + case D_EXTERN: + case 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: + case D_CONST2: + switch(a->name) { + + case D_NONE: + ctxt->instoffset = a->offset; + if(a->reg != NREG) + goto aconsize; + + t = immrot(ctxt->instoffset); + if(t) + return C_RCON; + t = immrot(~ctxt->instoffset); + if(t) + return C_NCON; + return C_LCON; + + case D_EXTERN: + case 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 = ctxt->autosize + a->offset; + goto aconsize; + + case D_PARAM: + ctxt->instoffset = ctxt->autosize + a->offset + 4L; + aconsize: + t = immrot(ctxt->instoffset); + if(t) + return C_RACON; + return C_LACON; + } + return C_GOK; + + case D_BRANCH: + return C_SBRA; + } + return C_GOK; +} + +static void +prasm(Prog *p) +{ + print("%P\n", p); +} + +static Optab* +oplook(Link *ctxt, Prog *p) +{ + int a1, a2, a3, r; + char *c1, *c3; + Optab *o, *e; + + a1 = p->optab; + if(a1) + return optab+(a1-1); + a1 = p->from.class; + if(a1 == 0) { + a1 = aclass(ctxt, &p->from) + 1; + p->from.class = a1; + } + a1--; + a3 = p->to.class; + if(a3 == 0) { + a3 = aclass(ctxt, &p->to) + 1; + p->to.class = a3; + } + a3--; + a2 = C_NONE; + if(p->reg != NREG) + a2 = C_REG; + r = p->as; + o = oprange[r].start; + if(o == 0) { + a1 = opcross[repop[r]][a1][a2][a3]; + if(a1) { + p->optab = a1+1; + return optab+a1; + } + o = oprange[r].stop; /* just generate an error */ + } + if(0 /*debug['O']*/) { + print("oplook %A %d %d %d\n", + (int)p->as, a1, a2, a3); + print(" %d %d\n", p->from.type, p->to.type); + } + e = oprange[r].stop; + c1 = xcmp[a1]; + c3 = xcmp[a3]; + for(; o<e; o++) + if(o->a2 == a2) + if(c1[o->a1]) + if(c3[o->a3]) { + p->optab = (o-optab)+1; + return o; + } + ctxt->diag("illegal combination %P; %d %d %d, %d %d", + p, a1, a2, 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 == 0) + o = optab; + return o; +} + +static int +cmp(int a, int b) +{ + + if(a == b) + return 1; + switch(a) { + case C_LCON: + if(b == C_RCON || b == C_NCON) + return 1; + break; + case C_LACON: + if(b == C_RACON) + return 1; + break; + case C_LFCON: + if(b == C_ZFCON || b == C_SFCON) + return 1; + break; + + case C_HFAUTO: + return b == C_HAUTO || b == C_FAUTO; + case C_FAUTO: + case 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: + case C_HOREG: + return b == C_HFOREG; + case C_SROREG: + return cmp(C_SOREG, b) || cmp(C_ROREG, b); + case C_SOREG: + case 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 1; + break; + + case C_HREG: + return cmp(C_SP, b) || cmp(C_PC, b); + + } + return 0; +} + +static int +ocmp(const void *a1, const void *a2) +{ + Optab *p1, *p2; + int n; + + p1 = (Optab*)a1; + p2 = (Optab*)a2; + n = p1->as - p2->as; + if(n) + return n; + n = p1->a1 - p2->a1; + if(n) + return n; + n = p1->a2 - p2->a2; + if(n) + return n; + n = p1->a3 - p2->a3; + if(n) + return n; + return 0; +} + +static void +buildop(Link *ctxt) +{ + int i, n, r; + + for(i=0; i<C_GOK; i++) + for(n=0; n<C_GOK; n++) + xcmp[i][n] = cmp(n, i); + for(n=0; optab[n].as != AXXX; n++) { + if((optab[n].flag & LPCREL) != 0) { + if(ctxt->flag_shared) + optab[n].size += optab[n].pcrelsiz; + else + optab[n].flag &= ~LPCREL; + } + } + qsort(optab, n, sizeof(optab[0]), ocmp); + for(i=0; i<n; i++) { + r = optab[i].as; + oprange[r].start = optab+i; + while(optab[i].as == r) + i++; + oprange[r].stop = optab+i; + i--; + + switch(r) + { + default: + ctxt->diag("unknown op in build: %A", r); + sysfatal("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]; + break; + case ACMP: + oprange[ATEQ] = oprange[r]; + oprange[ACMN] = oprange[r]; + break; + 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]; + break; + case ASLL: + oprange[ASRL] = oprange[r]; + oprange[ASRA] = oprange[r]; + break; + case AMUL: + oprange[AMULU] = oprange[r]; + break; + case ADIV: + oprange[AMOD] = oprange[r]; + oprange[AMODU] = oprange[r]; + oprange[ADIVU] = oprange[r]; + break; + case AMOVW: + case AMOVB: + case AMOVBS: + case AMOVBU: + case AMOVH: + case AMOVHS: + case AMOVHU: + break; + case ASWPW: + oprange[ASWPBU] = oprange[r]; + break; + case AB: + case ABL: + case ABX: + case ABXRET: + case ADUFFZERO: + case ADUFFCOPY: + case ASWI: + case AWORD: + case AMOVM: + case ARFE: + case ATEXT: + case AUSEFIELD: + case ACASE: + case ABCASE: + case 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]; + break; + + case ACMPF: + oprange[ACMPD] = oprange[r]; + break; + + case AMOVF: + oprange[AMOVD] = oprange[r]; + break; + + case AMOVFW: + oprange[AMOVDW] = oprange[r]; + break; + + case AMOVWF: + oprange[AMOVWD] = oprange[r]; + break; + + case AMULL: + oprange[AMULAL] = oprange[r]; + oprange[AMULLU] = oprange[r]; + oprange[AMULALU] = oprange[r]; + break; + + case AMULWT: + oprange[AMULWB] = oprange[r]; + break; + + case AMULAWT: + oprange[AMULAWB] = oprange[r]; + break; + + case AMULA: + case ALDREX: + case ASTREX: + case ALDREXD: + case ASTREXD: + case ATST: + case APLD: + case AUNDEF: + case ACLZ: + case AFUNCDATA: + case APCDATA: + break; + } + } +} + +static void +asmout(Link *ctxt, Prog *p, Optab *o, int32 *out) +{ + int32 o1, o2, o3, o4, o5, o6, v; + int r, rf, rt, rt2; + Reloc *rel; + +ctxt->printp = p; + o1 = 0; + o2 = 0; + o3 = 0; + o4 = 0; + o5 = 0; + o6 = 0; + ctxt->armsize += o->size; +if(0 /*debug['P']*/) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); + switch(o->type) { + default: + ctxt->diag("unknown asm %d", o->type); + prasm(p); + break; + + case 0: /* pseudo ops */ +if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->fnptr); + break; + + case 1: /* op R,[R],R */ + o1 = oprrr(ctxt, p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + r = 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 |= rf | (r<<16) | (rt<<12); + break; + + case 2: /* movbu $I,[R],R */ + aclass(ctxt, &p->from); + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= immrot(ctxt->instoffset); + rt = p->to.reg; + r = 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 |= (r<<16) | (rt<<12); + break; + + case 3: /* add R<<[IR],[R],R */ + mov: + aclass(ctxt, &p->from); + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= p->from.offset; + rt = p->to.reg; + r = 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 |= (r<<16) | (rt<<12); + break; + + case 4: /* add $I,[R],R */ + aclass(ctxt, &p->from); + o1 = oprrr(ctxt, AADD, p->scond); + o1 |= immrot(ctxt->instoffset); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 |= r << 16; + o1 |= p->to.reg << 12; + break; + + case 5: /* bra s */ + o1 = opbra(ctxt, p->as, p->scond); + v = -8; + if(p->to.sym != nil) { + rel = addrel(ctxt->cursym); + rel->off = ctxt->pc; + rel->siz = 4; + rel->sym = p->to.sym; + v += p->to.offset; + rel->add = o1 | ((v >> 2) & 0xffffff); + rel->type = R_CALLARM; + break; + } + if(p->pcond != nil) + v = (p->pcond->pc - ctxt->pc) - 8; + o1 |= (v >> 2) & 0xffffff; + break; + + case 6: /* b ,O(R) -> add $O,R,PC */ + aclass(ctxt, &p->to); + o1 = oprrr(ctxt, AADD, p->scond); + o1 |= immrot(ctxt->instoffset); + o1 |= p->to.reg << 16; + o1 |= REGPC << 12; + break; + + case 7: /* bl (R) -> blx R */ + aclass(ctxt, &p->to); + if(ctxt->instoffset != 0) + ctxt->diag("%P: doesn't support BL offset(REG) where offset != 0", p); + o1 = oprrr(ctxt, ABL, p->scond); + o1 |= p->to.reg; + rel = addrel(ctxt->cursym); + rel->off = ctxt->pc; + rel->siz = 0; + rel->type = R_CALLIND; + break; + + case 8: /* sll $c,[R],R -> mov (R<<$c),R */ + aclass(ctxt, &p->from); + o1 = oprrr(ctxt, p->as, p->scond); + r = p->reg; + if(r == NREG) + r = p->to.reg; + o1 |= r; + o1 |= (ctxt->instoffset&31) << 7; + o1 |= p->to.reg << 12; + break; + + case 9: /* sll R,[R],R -> mov (R<<R),R */ + o1 = oprrr(ctxt, p->as, p->scond); + r = p->reg; + if(r == NREG) + r = p->to.reg; + o1 |= r; + o1 |= (p->from.reg << 8) | (1<<4); + o1 |= p->to.reg << 12; + break; + + case 10: /* swi [$con] */ + o1 = oprrr(ctxt, p->as, p->scond); + if(p->to.type != D_NONE) { + aclass(ctxt, &p->to); + o1 |= ctxt->instoffset & 0xffffff; + } + break; + + case 11: /* word */ + aclass(ctxt, &p->to); + o1 = ctxt->instoffset; + if(p->to.sym != nil) { + // This case happens with words generated + // in the PC stream as part of the literal pool. + rel = addrel(ctxt->cursym); + rel->off = ctxt->pc; + rel->siz = 4; + rel->sym = p->to.sym; + rel->add = p->to.offset; + + // runtime.tlsgm (aka gmsym) 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->gmsym) { + rel->type = R_TLS; + if(ctxt->flag_shared) + rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz; + rel->xadd = rel->add; + rel->xsym = rel->sym; + } else if(ctxt->flag_shared) { + rel->type = R_PCREL; + rel->add += ctxt->pc - p->pcrel->pc - 8; + } else + rel->type = R_ADDR; + o1 = 0; + } + break; + + case 12: /* movw $lcon, reg */ + o1 = omvl(ctxt, p, &p->from, p->to.reg); + if(o->flag & LPCREL) { + o2 = oprrr(ctxt, AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; + } + break; + + case 13: /* op $lcon, [R], R */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + o2 = oprrr(ctxt, p->as, p->scond); + o2 |= REGTMP; + r = p->reg; + if(p->as == AMOVW || p->as == AMVN) + r = 0; + else if(r == NREG) + r = p->to.reg; + o2 |= r << 16; + if(p->to.type != D_NONE) + o2 |= p->to.reg << 12; + break; + + case 14: /* movb/movbu/movh/movhu R,R */ + o1 = oprrr(ctxt, ASLL, p->scond); + + if(p->as == AMOVBU || p->as == AMOVHU) + o2 = oprrr(ctxt, ASRL, p->scond); + else + o2 = oprrr(ctxt, ASRA, p->scond); + + r = p->to.reg; + o1 |= (p->from.reg)|(r<<12); + o2 |= (r)|(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); + } + break; + + case 15: /* mul r,[r,]r */ + o1 = oprrr(ctxt, p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + r = p->reg; + if(r == NREG) + r = rt; + if(rt == r) { + r = rf; + rf = rt; + } + if(0) + if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) { + ctxt->diag("bad registers in MUL"); + prasm(p); + } + o1 |= (rf<<8) | r | (rt<<16); + break; + + + case 16: /* div r,[r,]r */ + o1 = 0xf << 28; + o2 = 0; + break; + + case 17: + o1 = oprrr(ctxt, p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + rt2 = p->to.offset; + r = p->reg; + o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12); + break; + + case 20: /* mov/movb/movbu R,O(R) */ + aclass(ctxt, &p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = osr(ctxt, p->as, p->from.reg, ctxt->instoffset, r, p->scond); + break; + + case 21: /* mov/movbu O(R),R -> lr */ + aclass(ctxt, &p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = olr(ctxt, ctxt->instoffset, r, p->to.reg, p->scond); + if(p->as != AMOVW) + o1 |= 1<<22; + break; + + case 30: /* mov/movb/movbu R,L(R) */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = osrr(ctxt, p->from.reg, REGTMP,r, p->scond); + if(p->as != AMOVW) + o2 |= 1<<22; + break; + + case 31: /* mov/movbu L(R),R -> lr[b] */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = olrr(ctxt, REGTMP,r, p->to.reg, p->scond); + if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) + o2 |= 1<<22; + break; + + case 34: /* mov $lacon,R */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + + o2 = oprrr(ctxt, AADD, p->scond); + o2 |= REGTMP; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 |= r << 16; + if(p->to.type != D_NONE) + o2 |= p->to.reg << 12; + break; + + case 35: /* mov PSR,R */ + o1 = (2<<23) | (0xf<<16) | (0<<0); + o1 |= (p->scond & C_SCOND) << 28; + o1 |= (p->from.reg & 1) << 22; + o1 |= p->to.reg << 12; + break; + + case 36: /* mov R,PSR */ + o1 = (2<<23) | (0x29f<<12) | (0<<4); + if(p->scond & C_FBIT) + o1 ^= 0x010 << 12; + o1 |= (p->scond & C_SCOND) << 28; + o1 |= (p->to.reg & 1) << 22; + o1 |= p->from.reg << 0; + break; + + case 37: /* mov $con,PSR */ + aclass(ctxt, &p->from); + o1 = (2<<23) | (0x29f<<12) | (0<<4); + if(p->scond & C_FBIT) + o1 ^= 0x010 << 12; + o1 |= (p->scond & C_SCOND) << 28; + o1 |= immrot(ctxt->instoffset); + o1 |= (p->to.reg & 1) << 22; + o1 |= p->from.reg << 0; + break; + + case 38: /* movm $con,oreg -> stm */ + o1 = (0x4 << 25); + o1 |= p->from.offset & 0xffff; + o1 |= p->to.reg << 16; + aclass(ctxt, &p->to); + goto movm; + + case 39: /* movm oreg,$con -> ldm */ + o1 = (0x4 << 25) | (1 << 20); + o1 |= p->to.offset & 0xffff; + o1 |= p->from.reg << 16; + aclass(ctxt, &p->from); + movm: + if(ctxt->instoffset != 0) + ctxt->diag("offset must be zero in MOVM; %P", p); + o1 |= (p->scond & C_SCOND) << 28; + if(p->scond & C_PBIT) + o1 |= 1 << 24; + if(p->scond & C_UBIT) + o1 |= 1 << 23; + if(p->scond & C_SBIT) + o1 |= 1 << 22; + if(p->scond & C_WBIT) + o1 |= 1 << 21; + break; + + 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 |= p->from.reg << 16; + o1 |= p->reg << 0; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + + case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ + o1 = 0xe8fd8000; + break; + + case 50: /* floating point store */ + v = regoff(ctxt, &p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = ofsr(ctxt, p->as, p->from.reg, v, r, p->scond, p); + break; + + case 51: /* floating point load */ + v = regoff(ctxt, &p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = ofsr(ctxt, p->as, p->to.reg, v, r, p->scond, p) | (1<<20); + break; + + case 52: /* floating point store, int32 offset UGLY */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = oprrr(ctxt, AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; + o3 = ofsr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond, p); + break; + + case 53: /* floating point load, int32 offset UGLY */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = oprrr(ctxt, AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; + o3 = ofsr(ctxt, p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + break; + + case 54: /* floating point arith */ + o1 = oprrr(ctxt, p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + r = 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 |= rf | (r<<16) | (rt<<12); + break; + + case 56: /* move to FP[CS]R */ + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12); + break; + + case 57: /* move from FP[CS]R */ + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20); + break; + case 58: /* movbu R,R */ + o1 = oprrr(ctxt, AAND, p->scond); + o1 |= immrot(0xff); + rt = p->to.reg; + r = p->from.reg; + if(p->to.type == D_NONE) + rt = 0; + if(r == NREG) + r = rt; + o1 |= (r<<16) | (rt<<12); + break; + + 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"); + goto mov; + } + if(p->from.offset&(1<<4)) + ctxt->diag("bad shift in LDR"); + o1 = olrr(ctxt, p->from.offset, p->from.reg, p->to.reg, p->scond); + if(p->as == AMOVBU) + o1 |= 1<<22; + break; + + case 60: /* movb R(R),R -> ldrsb indexed */ + if(p->from.reg == NREG) { + ctxt->diag("byte MOV from shifter operand"); + goto mov; + } + if(p->from.offset&(~0xf)) + ctxt->diag("bad shift in LDRSB"); + o1 = olhrr(ctxt, p->from.offset, p->from.reg, p->to.reg, p->scond); + o1 ^= (1<<5)|(1<<6); + break; + + 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, p->from.reg, p->to.offset, p->to.reg, p->scond); + if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) + o1 |= 1<<22; + break; + + case 62: /* case R -> movw R<<2(PC),PC */ + if(o->flag & LPCREL) { + o1 = oprrr(ctxt, AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; + o2 = olrr(ctxt, REGTMP, REGPC, REGTMP, p->scond); + o2 |= 2<<7; + o3 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; + } else { + o1 = olrr(ctxt, p->from.reg, REGPC, REGPC, p->scond); + o1 |= 2<<7; + } + break; + + case 63: /* bcase */ + if(p->pcond != nil) { + rel = addrel(ctxt->cursym); + rel->off = 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) { + rel->type = R_PCREL; + rel->add += ctxt->pc - p->pcrel->pc - 16 + rel->siz; + } else + rel->type = R_ADDR; + o1 = 0; + } + break; + + /* reloc ops */ + case 64: /* mov/movb/movbu R,addr */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + o2 = osr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + + case 65: /* mov/movbu addr,R */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + o2 = olr(ctxt, 0, REGTMP, p->to.reg, p->scond); + if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) + o2 |= 1<<22; + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + + case 68: /* floating point store -> ADDR */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + o2 = ofsr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond, p); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + + case 69: /* floating point load <- ADDR */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + o2 = ofsr(ctxt, p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + + /* ArmV4 ops: */ + case 70: /* movh/movhu R,O(R) -> strh */ + aclass(ctxt, &p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = oshr(ctxt, p->from.reg, ctxt->instoffset, r, p->scond); + break; + case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ + aclass(ctxt, &p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = olhr(ctxt, ctxt->instoffset, r, p->to.reg, 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); + break; + case 72: /* movh/movhu R,L(R) -> strh */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = oshrr(ctxt, p->from.reg, REGTMP,r, p->scond); + break; + case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = olhrr(ctxt, REGTMP, r, p->to.reg, 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); + break; + case 74: /* bx $I */ + ctxt->diag("ABX $I"); + break; + 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, p->scond); + o1 |= immrot(ctxt->instoffset); + o1 |= p->to.reg << 16; + o1 |= REGTMP << 12; + o2 = oprrr(ctxt, AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR + o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp + break; + case 76: /* bx O(R) when returning from fn*/ + ctxt->diag("ABXRET"); + break; + 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 |= p->from.reg << 16; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + 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 |= p->from.reg << 16; + o1 |= p->reg << 0; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + case 80: /* fmov zfcon,freg */ + if(p->as == AMOVD) { + o1 = 0xeeb00b00; // VMOV imm 64 + o2 = oprrr(ctxt, ASUBD, p->scond); + } else { + o1 = 0x0eb00a00; // VMOV imm 32 + o2 = oprrr(ctxt, ASUBF, p->scond); + } + v = 0x70; // 1.0 + r = p->to.reg; + + // movf $1.0, r + o1 |= (p->scond & C_SCOND) << 28; + o1 |= r << 12; + o1 |= (v&0xf) << 0; + o1 |= (v&0xf0) << 12; + + // subf r,r,r + o2 |= r | (r<<16) | (r<<12); + break; + case 81: /* fmov sfcon,freg */ + o1 = 0x0eb00a00; // VMOV imm 32 + if(p->as == AMOVD) + o1 = 0xeeb00b00; // VMOV imm 64 + o1 |= (p->scond & C_SCOND) << 28; + o1 |= p->to.reg << 12; + v = chipfloat5(ctxt, p->from.u.dval); + o1 |= (v&0xf) << 0; + o1 |= (v&0xf0) << 12; + break; + case 82: /* fcmp freg,freg, */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= (p->reg<<12) | (p->from.reg<<0); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 83: /* fcmp freg,, */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= (p->from.reg<<12) | (1<<16); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 84: /* movfw freg,freg - truncate float-to-fix */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 85: /* movwf freg,freg - fix-to-float */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 86: /* movfw freg,reg - truncate float-to-fix */ + // macro for movfw freg,FTMP; movw FTMP,reg + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (FREGTMP<<12); + o2 = oprrr(ctxt, AMOVFW+AEND, p->scond); + o2 |= (FREGTMP<<16); + o2 |= (p->to.reg<<12); + break; + case 87: /* movwf reg,freg - fix-to-float */ + // macro for movw reg,FTMP; movwf FTMP,freg + o1 = oprrr(ctxt, AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (FREGTMP<<16); + o2 = oprrr(ctxt, p->as, p->scond); + o2 |= (FREGTMP<<0); + o2 |= (p->to.reg<<12); + break; + case 88: /* movw reg,freg */ + o1 = oprrr(ctxt, AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (p->to.reg<<16); + break; + case 89: /* movw freg,reg */ + o1 = oprrr(ctxt, AMOVFW+AEND, p->scond); + o1 |= (p->from.reg<<16); + o1 |= (p->to.reg<<12); + break; + case 90: /* tst reg */ + o1 = oprrr(ctxt, ACMP+AEND, p->scond); + o1 |= p->from.reg<<16; + break; + 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 |= p->from.reg << 16; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + 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 |= p->from.reg << 16; + o1 |= p->reg << 0; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + case 93: /* movb/movh/movhu addr,R -> ldrsb/ldrsh/ldrh */ + o1 = omvl(ctxt, p, &p->from, REGTMP); + if(!o1) + break; + o2 = olhr(ctxt, 0, REGTMP, p->to.reg, 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) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + case 94: /* movh/movhu R,addr -> strh */ + o1 = omvl(ctxt, p, &p->to, REGTMP); + if(!o1) + break; + o2 = oshr(ctxt, p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + case 95: /* PLD off(reg) */ + o1 = 0xf5d0f000; + o1 |= p->from.reg << 16; + if(p->from.offset < 0) { + o1 &= ~(1 << 23); + o1 |= (-p->from.offset) & 0xfff; + } else + o1 |= p->from.offset & 0xfff; + break; + case 96: /* UNDEF */ + // 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. + o1 = 0xf7fabcfd; + break; + case 97: /* CLZ Rm, Rd */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg; + break; + case 98: /* MULW{T,B} Rs, Rm, Rd */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= p->to.reg << 16; + o1 |= p->from.reg << 8; + o1 |= p->reg; + break; + case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ + o1 = oprrr(ctxt, p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg << 8; + o1 |= p->reg; + o1 |= p->to.offset << 16; + break; + } + + out[0] = o1; + out[1] = o2; + out[2] = o3; + out[3] = o4; + out[4] = o5; + out[5] = o6; + return; + +#ifdef NOTDEF + v = p->pc; + switch(o->size) { + default: + if(debug['a']) + Bprint(&bso, " %.8ux:\t\t%P\n", v, p); + break; + case 4: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); + lputl(o1); + break; + case 8: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux %.8ux%P\n", v, o1, o2, p); + lputl(o1); + lputl(o2); + break; + case 12: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, p); + lputl(o1); + lputl(o2); + lputl(o3); + break; + case 16: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux%P\n", + v, o1, o2, o3, o4, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + break; + case 20: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", + v, o1, o2, o3, o4, o5, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + lputl(o5); + break; + case 24: + if(debug['a']) + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", + v, o1, o2, o3, o4, o5, o6, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + lputl(o5); + lputl(o6); + break; + } +#endif +} + +static int32 +oprrr(Link *ctxt, int a, int sc) +{ + int32 o; + + o = (sc & C_SCOND) << 28; + if(sc & C_SBIT) + o |= 1 << 20; + if(sc & (C_PBIT|C_WBIT)) + ctxt->diag(".nil/.W on dp instruction"); + switch(a) { + case AMULU: + case 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: + case AMOVH: + case 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); + + case ACLZ: + // CLZ doesn't support .nil + 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; +} + +static int32 +opbra(Link *ctxt, int a, int sc) +{ + + if(sc & (C_SBIT|C_PBIT|C_WBIT)) + ctxt->diag(".nil/.nil/.W on bra instruction"); + sc &= C_SCOND; + if(a == ABL || a == ADUFFZERO || a == ADUFFCOPY) + return (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 %A", a); + prasm(ctxt->curp); + return 0; +} + +static int32 +olr(Link *ctxt, int32 v, int b, int r, int sc) +{ + int32 o; + + if(sc & C_SBIT) + ctxt->diag(".nil on LDR/STR instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(!(sc & C_UBIT)) + o |= 1 << 23; + if(sc & C_WBIT) + o |= 1 << 21; + o |= (1<<26) | (1<<20); + if(v < 0) { + if(sc & C_UBIT) + 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%P", v, b, ctxt->printp); + o |= v; + o |= b << 16; + o |= r << 12; + return o; +} + +static int32 +olhr(Link *ctxt, int32 v, int b, int r, int sc) +{ + int32 o; + + if(sc & C_SBIT) + ctxt->diag(".nil on LDRH/STRH instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(sc & C_WBIT) + 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%P", v, b, ctxt->printp); + o |= (v&0xf)|((v>>4)<<8)|(1<<22); + o |= b << 16; + o |= r << 12; + return o; +} + +static int32 +osr(Link *ctxt, int a, int r, int32 v, int b, int sc) +{ + int32 o; + + o = olr(ctxt, v, b, r, sc) ^ (1<<20); + if(a != AMOVW) + o |= 1<<22; + return o; +} + +static int32 +oshr(Link *ctxt, int r, int32 v, int b, int sc) +{ + int32 o; + + o = olhr(ctxt, v, b, r, sc) ^ (1<<20); + return o; +} + + +static int32 +osrr(Link *ctxt, int r, int i, int b, int sc) +{ + + return olr(ctxt, i, b, r, sc) ^ ((1<<25) | (1<<20)); +} + +static int32 +oshrr(Link *ctxt, int r, int i, int b, int sc) +{ + return olhr(ctxt, i, b, r, sc) ^ ((1<<22) | (1<<20)); +} + +static int32 +olrr(Link *ctxt, int i, int b, int r, int sc) +{ + + return olr(ctxt, i, b, r, sc) ^ (1<<25); +} + +static int32 +olhrr(Link *ctxt, int i, int b, int r, int sc) +{ + return olhr(ctxt, i, b, r, sc) ^ (1<<22); +} + +static int32 +ofsr(Link *ctxt, int a, int r, int32 v, int b, int sc, Prog *p) +{ + int32 o; + + if(sc & C_SBIT) + ctxt->diag(".nil on FLDR/FSTR instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(sc & C_WBIT) + o |= 1 << 21; + o |= (6<<25) | (1<<24) | (1<<23) | (10<<8); + if(v < 0) { + v = -v; + o ^= 1 << 23; + } + if(v & 3) + ctxt->diag("odd offset for floating point op: %d\n%P", v, p); + else + if(v >= (1<<10) || v < 0) + ctxt->diag("literal span too large: %d\n%P", v, p); + o |= (v>>2) & 0xFF; + o |= b << 16; + o |= r << 12; + + switch(a) { + default: + ctxt->diag("bad fst %A", a); + case AMOVD: + o |= 1 << 8; + case AMOVF: + break; + } + return o; +} + +static int32 +omvl(Link *ctxt, Prog *p, Addr *a, int dr) +{ + int32 v, o1; + if(!p->pcond) { + aclass(ctxt, a); + v = immrot(~ctxt->instoffset); + if(v == 0) { + ctxt->diag("missing literal"); + prasm(p); + return 0; + } + o1 = oprrr(ctxt, AMVN, p->scond&C_SCOND); + o1 |= v; + o1 |= dr << 12; + } else { + v = p->pcond->pc - p->pc - 8; + o1 = olr(ctxt, v, REGPC, dr, p->scond&C_SCOND); + } + return o1; +} + +int +chipzero5(Link *ctxt, float64 e) +{ + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(ctxt->goarm < 7 || e != 0) + return -1; + return 0; +} + +int +chipfloat5(Link *ctxt, float64 e) +{ + int n; + ulong h1; + int32 l, h; + uint64 ei; + + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(ctxt->goarm < 7) + goto no; + + memmove(&ei, &e, 8); + l = (int32)ei; + h = (int32)(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) + n |= 1<<7; + + // exp sign bit (b) + if(h1 == 0x3fc00000) + n |= 1<<6; + + // rest of exp and mantissa (cd-efgh) + n |= (h >> 16) & 0x3f; + +//print("match %.8lux %.8lux %d\n", l, h, n); + return n; + +no: + return -1; +} |