diff options
Diffstat (limited to 'src/liblink')
-rw-r--r-- | src/liblink/Makefile | 5 | ||||
-rw-r--r-- | src/liblink/asm5.c | 2458 | ||||
-rw-r--r-- | src/liblink/asm6.c | 3585 | ||||
-rw-r--r-- | src/liblink/asm8.c | 2785 | ||||
-rw-r--r-- | src/liblink/data.c | 370 | ||||
-rw-r--r-- | src/liblink/go.c | 74 | ||||
-rw-r--r-- | src/liblink/ld.c | 258 | ||||
-rw-r--r-- | src/liblink/list5.c | 356 | ||||
-rw-r--r-- | src/liblink/list6.c | 406 | ||||
-rw-r--r-- | src/liblink/list8.c | 354 | ||||
-rw-r--r-- | src/liblink/obj.c | 296 | ||||
-rw-r--r-- | src/liblink/obj5.c | 1068 | ||||
-rw-r--r-- | src/liblink/obj6.c | 1171 | ||||
-rw-r--r-- | src/liblink/obj8.c | 859 | ||||
-rw-r--r-- | src/liblink/objfile.c | 746 | ||||
-rw-r--r-- | src/liblink/pass.c | 115 | ||||
-rw-r--r-- | src/liblink/pcln.c | 365 | ||||
-rw-r--r-- | src/liblink/sym.c | 271 |
18 files changed, 15542 insertions, 0 deletions
diff --git a/src/liblink/Makefile b/src/liblink/Makefile new file mode 100644 index 000000000..2a317462b --- /dev/null +++ b/src/liblink/Makefile @@ -0,0 +1,5 @@ +# Copyright 2013 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. + +include ../Make.dist 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; +} diff --git a/src/liblink/asm6.c b/src/liblink/asm6.c new file mode 100644 index 000000000..66afc7a12 --- /dev/null +++ b/src/liblink/asm6.c @@ -0,0 +1,3585 @@ +// Inferno utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/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/6l/6.out.h" +#include "../pkg/runtime/stack.h" + +enum +{ + MaxAlign = 32, // max data alignment + + // Loop alignment constants: + // want to align loop entry to LoopAlign-byte boundary, + // and willing to insert at most MaxLoopPad bytes of NOP to do so. + // We define a loop entry as the target of a backward jump. + // + // gcc uses MaxLoopPad = 10 for its 'generic x86-64' config, + // and it aligns all jump targets, not just backward jump targets. + // + // As of 6/1/2012, the effect of setting MaxLoopPad = 10 here + // is very slight but negative, so the alignment is disabled by + // setting MaxLoopPad = 0. The code is here for reference and + // for future experiments. + // + LoopAlign = 16, + MaxLoopPad = 0, + + FuncAlign = 16 +}; + +extern char *anames6[]; + +typedef struct Optab Optab; +typedef struct Movtab Movtab; + +struct Optab +{ + short as; + uchar* ytab; + uchar prefix; + uchar op[23]; +}; +struct Movtab +{ + short as; + uchar ft; + uchar tt; + uchar code; + uchar op[4]; +}; + +enum +{ + Yxxx = 0, + Ynone, + Yi0, + Yi1, + Yi8, + Ys32, + Yi32, + Yi64, + Yiauto, + Yal, + Ycl, + Yax, + Ycx, + Yrb, + Yrl, + Yrf, + Yf0, + Yrx, + Ymb, + Yml, + Ym, + Ybr, + Ycol, + + Ycs, Yss, Yds, Yes, Yfs, Ygs, + Ygdtr, Yidtr, Yldtr, Ymsw, Ytask, + Ycr0, Ycr1, Ycr2, Ycr3, Ycr4, Ycr5, Ycr6, Ycr7, Ycr8, + Ydr0, Ydr1, Ydr2, Ydr3, Ydr4, Ydr5, Ydr6, Ydr7, + Ytr0, Ytr1, Ytr2, Ytr3, Ytr4, Ytr5, Ytr6, Ytr7, Yrl32, Yrl64, + Ymr, Ymm, + Yxr, Yxm, + Ytls, + Ymax, + + Zxxx = 0, + + Zlit, + Zlitm_r, + Z_rp, + Zbr, + Zcall, + Zcallindreg, + Zib_, + Zib_rp, + Zibo_m, + Zibo_m_xm, + Zil_, + Zil_rp, + Ziq_rp, + Zilo_m, + Ziqo_m, + Zjmp, + Zloop, + Zo_iw, + Zm_o, + Zm_r, + Zm2_r, + Zm_r_xm, + Zm_r_i_xm, + Zm_r_3d, + Zm_r_xm_nr, + Zr_m_xm_nr, + Zibm_r, /* mmx1,mmx2/mem64,imm8 */ + Zmb_r, + Zaut_r, + Zo_m, + Zo_m64, + Zpseudo, + Zr_m, + Zr_m_xm, + Zr_m_i_xm, + Zrp_, + Z_ib, + Z_il, + Zm_ibo, + Zm_ilo, + Zib_rr, + Zil_rr, + Zclr, + Zbyte, + Zmax, + + Px = 0, + P32 = 0x32, /* 32-bit only */ + Pe = 0x66, /* operand escape */ + Pm = 0x0f, /* 2byte opcode escape */ + Pq = 0xff, /* both escapes: 66 0f */ + Pb = 0xfe, /* byte operands */ + Pf2 = 0xf2, /* xmm escape 1: f2 0f */ + Pf3 = 0xf3, /* xmm escape 2: f3 0f */ + Pq3 = 0x67, /* xmm escape 3: 66 48 0f */ + Pw = 0x48, /* Rex.w */ + Py = 0x80, /* defaults to 64-bit mode */ + + Rxf = 1<<9, /* internal flag for Rxr on from */ + Rxt = 1<<8, /* internal flag for Rxr on to */ + Rxw = 1<<3, /* =1, 64-bit operand size */ + Rxr = 1<<2, /* extend modrm reg */ + Rxx = 1<<1, /* extend sib index */ + Rxb = 1<<0, /* extend modrm r/m, sib base, or opcode reg */ + + Maxand = 10, /* in -a output width of the byte codes */ +}; + +static char ycover[Ymax*Ymax]; +static int reg[D_NONE]; +static int regrex[D_NONE+1]; +static void asmins(Link *ctxt, Prog *p); + +static uchar ynone[] = +{ + Ynone, Ynone, Zlit, 1, + 0 +}; +static uchar ytext[] = +{ + Ymb, Yi64, Zpseudo,1, + 0 +}; +static uchar ynop[] = +{ + Ynone, Ynone, Zpseudo,0, + Ynone, Yiauto, Zpseudo,0, + Ynone, Yml, Zpseudo,0, + Ynone, Yrf, Zpseudo,0, + Ynone, Yxr, Zpseudo,0, + Yiauto, Ynone, Zpseudo,0, + Yml, Ynone, Zpseudo,0, + Yrf, Ynone, Zpseudo,0, + Yxr, Ynone, Zpseudo,1, + 0 +}; +static uchar yfuncdata[] = +{ + Yi32, Ym, Zpseudo, 0, + 0 +}; +static uchar ypcdata[] = +{ + Yi32, Yi32, Zpseudo, 0, + 0 +}; +static uchar yxorb[] = +{ + Yi32, Yal, Zib_, 1, + Yi32, Ymb, Zibo_m, 2, + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar yxorl[] = +{ + Yi8, Yml, Zibo_m, 2, + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yaddl[] = +{ + Yi8, Yml, Zibo_m, 2, + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yincb[] = +{ + Ynone, Ymb, Zo_m, 2, + 0 +}; +static uchar yincw[] = +{ + Ynone, Yml, Zo_m, 2, + 0 +}; +static uchar yincl[] = +{ + Ynone, Yml, Zo_m, 2, + 0 +}; +static uchar ycmpb[] = +{ + Yal, Yi32, Z_ib, 1, + Ymb, Yi32, Zm_ibo, 2, + Ymb, Yrb, Zm_r, 1, + Yrb, Ymb, Zr_m, 1, + 0 +}; +static uchar ycmpl[] = +{ + Yml, Yi8, Zm_ibo, 2, + Yax, Yi32, Z_il, 1, + Yml, Yi32, Zm_ilo, 2, + Yml, Yrl, Zm_r, 1, + Yrl, Yml, Zr_m, 1, + 0 +}; +static uchar yshb[] = +{ + Yi1, Ymb, Zo_m, 2, + Yi32, Ymb, Zibo_m, 2, + Ycx, Ymb, Zo_m, 2, + 0 +}; +static uchar yshl[] = +{ + Yi1, Yml, Zo_m, 2, + Yi32, Yml, Zibo_m, 2, + Ycl, Yml, Zo_m, 2, + Ycx, Yml, Zo_m, 2, + 0 +}; +static uchar ytestb[] = +{ + Yi32, Yal, Zib_, 1, + Yi32, Ymb, Zibo_m, 2, + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar ytestl[] = +{ + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar ymovb[] = +{ + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + Yi32, Yrb, Zib_rp, 1, + Yi32, Ymb, Zibo_m, 2, + 0 +}; +static uchar ymbs[] = +{ + Ymb, Ynone, Zm_o, 2, + 0 +}; +static uchar ybtl[] = +{ + Yi8, Yml, Zibo_m, 2, + Yrl, Yml, Zr_m, 1, + 0 +}; +static uchar ymovw[] = +{ + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + Yi0, Yrl, Zclr, 1, + Yi32, Yrl, Zil_rp, 1, + Yi32, Yml, Zilo_m, 2, + Yiauto, Yrl, Zaut_r, 2, + 0 +}; +static uchar ymovl[] = +{ + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + Yi0, Yrl, Zclr, 1, + Yi32, Yrl, Zil_rp, 1, + Yi32, Yml, Zilo_m, 2, + Yml, Ymr, Zm_r_xm, 1, // MMX MOVD + Ymr, Yml, Zr_m_xm, 1, // MMX MOVD + Yml, Yxr, Zm_r_xm, 2, // XMM MOVD (32 bit) + Yxr, Yml, Zr_m_xm, 2, // XMM MOVD (32 bit) + Yiauto, Yrl, Zaut_r, 2, + 0 +}; +static uchar yret[] = +{ + Ynone, Ynone, Zo_iw, 1, + Yi32, Ynone, Zo_iw, 1, + 0 +}; +static uchar ymovq[] = +{ + Yrl, Yml, Zr_m, 1, // 0x89 + Yml, Yrl, Zm_r, 1, // 0x8b + Yi0, Yrl, Zclr, 1, // 0x31 + Ys32, Yrl, Zilo_m, 2, // 32 bit signed 0xc7,(0) + Yi64, Yrl, Ziq_rp, 1, // 0xb8 -- 32/64 bit immediate + Yi32, Yml, Zilo_m, 2, // 0xc7,(0) + Ym, Ymr, Zm_r_xm_nr, 1, // MMX MOVQ (shorter encoding) + Ymr, Ym, Zr_m_xm_nr, 1, // MMX MOVQ + Ymm, Ymr, Zm_r_xm, 1, // MMX MOVD + Ymr, Ymm, Zr_m_xm, 1, // MMX MOVD + Yxr, Ymr, Zm_r_xm_nr, 2, // MOVDQ2Q + Yxm, Yxr, Zm_r_xm_nr, 2, // MOVQ xmm1/m64 -> xmm2 + Yxr, Yxm, Zr_m_xm_nr, 2, // MOVQ xmm1 -> xmm2/m64 + Yml, Yxr, Zm_r_xm, 2, // MOVD xmm load + Yxr, Yml, Zr_m_xm, 2, // MOVD xmm store + Yiauto, Yrl, Zaut_r, 2, // built-in LEAQ + 0 +}; +static uchar ym_rl[] = +{ + Ym, Yrl, Zm_r, 1, + 0 +}; +static uchar yrl_m[] = +{ + Yrl, Ym, Zr_m, 1, + 0 +}; +static uchar ymb_rl[] = +{ + Ymb, Yrl, Zmb_r, 1, + 0 +}; +static uchar yml_rl[] = +{ + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yrl_ml[] = +{ + Yrl, Yml, Zr_m, 1, + 0 +}; +static uchar yml_mb[] = +{ + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar yrb_mb[] = +{ + Yrb, Ymb, Zr_m, 1, + 0 +}; +static uchar yxchg[] = +{ + Yax, Yrl, Z_rp, 1, + Yrl, Yax, Zrp_, 1, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar ydivl[] = +{ + Yml, Ynone, Zm_o, 2, + 0 +}; +static uchar ydivb[] = +{ + Ymb, Ynone, Zm_o, 2, + 0 +}; +static uchar yimul[] = +{ + Yml, Ynone, Zm_o, 2, + Yi8, Yrl, Zib_rr, 1, + Yi32, Yrl, Zil_rr, 1, + Yml, Yrl, Zm_r, 2, + 0 +}; +static uchar yimul3[] = +{ + Yml, Yrl, Zibm_r, 2, + 0 +}; +static uchar ybyte[] = +{ + Yi64, Ynone, Zbyte, 1, + 0 +}; +static uchar yin[] = +{ + Yi32, Ynone, Zib_, 1, + Ynone, Ynone, Zlit, 1, + 0 +}; +static uchar yint[] = +{ + Yi32, Ynone, Zib_, 1, + 0 +}; +static uchar ypushl[] = +{ + Yrl, Ynone, Zrp_, 1, + Ym, Ynone, Zm_o, 2, + Yi8, Ynone, Zib_, 1, + Yi32, Ynone, Zil_, 1, + 0 +}; +static uchar ypopl[] = +{ + Ynone, Yrl, Z_rp, 1, + Ynone, Ym, Zo_m, 2, + 0 +}; +static uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 2, + 0, +}; +static uchar yscond[] = +{ + Ynone, Ymb, Zo_m, 2, + 0 +}; +static uchar yjcond[] = +{ + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, + 0 +}; +static uchar yloop[] = +{ + Ynone, Ybr, Zloop, 1, + 0 +}; +static uchar ycall[] = +{ + Ynone, Yml, Zcallindreg, 0, + Yrx, Yrx, Zcallindreg, 2, + Ynone, Ybr, Zcall, 1, + 0 +}; +static uchar yduff[] = +{ + Ynone, Yi32, Zcall, 1, + 0 +}; +static uchar yjmp[] = +{ + Ynone, Yml, Zo_m64, 2, + Ynone, Ybr, Zjmp, 1, + 0 +}; + +static uchar yfmvd[] = +{ + Ym, Yf0, Zm_o, 2, + Yf0, Ym, Zo_m, 2, + Yrf, Yf0, Zm_o, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfmvdp[] = +{ + Yf0, Ym, Zo_m, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfmvf[] = +{ + Ym, Yf0, Zm_o, 2, + Yf0, Ym, Zo_m, 2, + 0 +}; +static uchar yfmvx[] = +{ + Ym, Yf0, Zm_o, 2, + 0 +}; +static uchar yfmvp[] = +{ + Yf0, Ym, Zo_m, 2, + 0 +}; +static uchar yfadd[] = +{ + Ym, Yf0, Zm_o, 2, + Yrf, Yf0, Zm_o, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfaddp[] = +{ + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfxch[] = +{ + Yf0, Yrf, Zo_m, 2, + Yrf, Yf0, Zm_o, 2, + 0 +}; +static uchar ycompp[] = +{ + Yf0, Yrf, Zo_m, 2, /* botch is really f0,f1 */ + 0 +}; +static uchar ystsw[] = +{ + Ynone, Ym, Zo_m, 2, + Ynone, Yax, Zlit, 1, + 0 +}; +static uchar ystcw[] = +{ + Ynone, Ym, Zo_m, 2, + Ym, Ynone, Zm_o, 2, + 0 +}; +static uchar ysvrs[] = +{ + Ynone, Ym, Zo_m, 2, + Ym, Ynone, Zm_o, 2, + 0 +}; +static uchar ymm[] = +{ + Ymm, Ymr, Zm_r_xm, 1, + Yxm, Yxr, Zm_r_xm, 2, + 0 +}; +static uchar yxm[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxcvm1[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Yxm, Ymr, Zm_r_xm, 2, + 0 +}; +static uchar yxcvm2[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Ymm, Yxr, Zm_r_xm, 2, + 0 +}; +/* +static uchar yxmq[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + 0 +}; +*/ +static uchar yxr[] = +{ + Yxr, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxr_ml[] = +{ + Yxr, Yml, Zr_m_xm, 1, + 0 +}; +static uchar ymr[] = +{ + Ymr, Ymr, Zm_r, 1, + 0 +}; +static uchar ymr_ml[] = +{ + Ymr, Yml, Zr_m_xm, 1, + 0 +}; +static uchar yxcmp[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxcmpi[] = +{ + Yxm, Yxr, Zm_r_i_xm, 2, + 0 +}; +static uchar yxmov[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + Yxr, Yxm, Zr_m_xm, 1, + 0 +}; +static uchar yxcvfl[] = +{ + Yxm, Yrl, Zm_r_xm, 1, + 0 +}; +static uchar yxcvlf[] = +{ + Yml, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxcvfq[] = +{ + Yxm, Yrl, Zm_r_xm, 2, + 0 +}; +static uchar yxcvqf[] = +{ + Yml, Yxr, Zm_r_xm, 2, + 0 +}; +static uchar yps[] = +{ + Ymm, Ymr, Zm_r_xm, 1, + Yi8, Ymr, Zibo_m_xm, 2, + Yxm, Yxr, Zm_r_xm, 2, + Yi8, Yxr, Zibo_m_xm, 3, + 0 +}; +static uchar yxrrl[] = +{ + Yxr, Yrl, Zm_r, 1, + 0 +}; +static uchar ymfp[] = +{ + Ymm, Ymr, Zm_r_3d, 1, + 0, +}; +static uchar ymrxr[] = +{ + Ymr, Yxr, Zm_r, 1, + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar ymshuf[] = +{ + Ymm, Ymr, Zibm_r, 2, + 0 +}; +static uchar ymshufb[] = +{ + Yxm, Yxr, Zm2_r, 2, + 0 +}; +static uchar yxshuf[] = +{ + Yxm, Yxr, Zibm_r, 2, + 0 +}; +static uchar yextrw[] = +{ + Yxr, Yrl, Zibm_r, 2, + 0 +}; +static uchar yinsrw[] = +{ + Yml, Yxr, Zibm_r, 2, + 0 +}; +static uchar yinsr[] = +{ + Ymm, Yxr, Zibm_r, 3, + 0 +}; +static uchar ypsdq[] = +{ + Yi8, Yxr, Zibo_m, 2, + 0 +}; +static uchar ymskb[] = +{ + Yxr, Yrl, Zm_r_xm, 2, + Ymr, Yrl, Zm_r_xm, 1, + 0 +}; +static uchar ycrc32l[] = +{ + Yml, Yrl, Zlitm_r, 0, +}; +static uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; +static uchar yaes[] = +{ + Yxm, Yxr, Zlitm_r, 2, + 0 +}; +static uchar yaes2[] = +{ + Yxm, Yxr, Zibm_r, 2, + 0 +}; + +/* + * You are doasm, holding in your hand a Prog* with p->as set to, say, ACRC32, + * and p->from and p->to as operands (Addr*). The linker scans optab to find + * the entry with the given p->as and then looks through the ytable for that + * instruction (the second field in the optab struct) for a line whose first + * two values match the Ytypes of the p->from and p->to operands. The function + * oclass in span.c computes the specific Ytype of an operand and then the set + * of more general Ytypes that it satisfies is implied by the ycover table, set + * up in instinit. For example, oclass distinguishes the constants 0 and 1 + * from the more general 8-bit constants, but instinit says + * + * ycover[Yi0*Ymax + Ys32] = 1; + * ycover[Yi1*Ymax + Ys32] = 1; + * ycover[Yi8*Ymax + Ys32] = 1; + * + * which means that Yi0, Yi1, and Yi8 all count as Ys32 (signed 32) + * if that's what an instruction can handle. + * + * In parallel with the scan through the ytable for the appropriate line, there + * is a z pointer that starts out pointing at the strange magic byte list in + * the Optab struct. With each step past a non-matching ytable line, z + * advances by the 4th entry in the line. When a matching line is found, that + * z pointer has the extra data to use in laying down the instruction bytes. + * The actual bytes laid down are a function of the 3rd entry in the line (that + * is, the Ztype) and the z bytes. + * + * For example, let's look at AADDL. The optab line says: + * { AADDL, yaddl, Px, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + * + * and yaddl says + * uchar yaddl[] = + * { + * Yi8, Yml, Zibo_m, 2, + * Yi32, Yax, Zil_, 1, + * Yi32, Yml, Zilo_m, 2, + * Yrl, Yml, Zr_m, 1, + * Yml, Yrl, Zm_r, 1, + * 0 + * }; + * + * so there are 5 possible types of ADDL instruction that can be laid down, and + * possible states used to lay them down (Ztype and z pointer, assuming z + * points at {0x83,(00),0x05,0x81,(00),0x01,0x03}) are: + * + * Yi8, Yml -> Zibo_m, z (0x83, 00) + * Yi32, Yax -> Zil_, z+2 (0x05) + * Yi32, Yml -> Zilo_m, z+2+1 (0x81, 0x00) + * Yrl, Yml -> Zr_m, z+2+1+2 (0x01) + * Yml, Yrl -> Zm_r, z+2+1+2+1 (0x03) + * + * The Pconstant in the optab line controls the prefix bytes to emit. That's + * relatively straightforward as this program goes. + * + * The switch on t[2] in doasm implements the various Z cases. Zibo_m, for + * example, is an opcode byte (z[0]) then an asmando (which is some kind of + * encoded addressing mode for the Yml arg), and then a single immediate byte. + * Zilo_m is the same but a long (32-bit) immediate. + */ +Optab optab[] = +/* as, ytab, andproto, opcode */ +{ + { AXXX }, + { AAAA, ynone, P32, 0x37 }, + { AAAD, ynone, P32, 0xd5,0x0a }, + { AAAM, ynone, P32, 0xd4,0x0a }, + { AAAS, ynone, P32, 0x3f }, + { AADCB, yxorb, Pb, 0x14,0x80,(02),0x10,0x10 }, + { AADCL, yxorl, Px, 0x83,(02),0x15,0x81,(02),0x11,0x13 }, + { AADCQ, yxorl, Pw, 0x83,(02),0x15,0x81,(02),0x11,0x13 }, + { AADCW, yxorl, Pe, 0x83,(02),0x15,0x81,(02),0x11,0x13 }, + { AADDB, yxorb, Pb, 0x04,0x80,(00),0x00,0x02 }, + { AADDL, yaddl, Px, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + { AADDPD, yxm, Pq, 0x58 }, + { AADDPS, yxm, Pm, 0x58 }, + { AADDQ, yaddl, Pw, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + { AADDSD, yxm, Pf2, 0x58 }, + { AADDSS, yxm, Pf3, 0x58 }, + { AADDW, yaddl, Pe, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + { AADJSP }, + { AANDB, yxorb, Pb, 0x24,0x80,(04),0x20,0x22 }, + { AANDL, yxorl, Px, 0x83,(04),0x25,0x81,(04),0x21,0x23 }, + { AANDNPD, yxm, Pq, 0x55 }, + { AANDNPS, yxm, Pm, 0x55 }, + { AANDPD, yxm, Pq, 0x54 }, + { AANDPS, yxm, Pq, 0x54 }, + { AANDQ, yxorl, Pw, 0x83,(04),0x25,0x81,(04),0x21,0x23 }, + { AANDW, yxorl, Pe, 0x83,(04),0x25,0x81,(04),0x21,0x23 }, + { AARPL, yrl_ml, P32, 0x63 }, + { ABOUNDL, yrl_m, P32, 0x62 }, + { ABOUNDW, yrl_m, Pe, 0x62 }, + { ABSFL, yml_rl, Pm, 0xbc }, + { ABSFQ, yml_rl, Pw, 0x0f,0xbc }, + { ABSFW, yml_rl, Pq, 0xbc }, + { ABSRL, yml_rl, Pm, 0xbd }, + { ABSRQ, yml_rl, Pw, 0x0f,0xbd }, + { ABSRW, yml_rl, Pq, 0xbd }, + { ABSWAPL, ybswap, Px, 0x0f,0xc8 }, + { ABSWAPQ, ybswap, Pw, 0x0f,0xc8 }, + { ABTCL, ybtl, Pm, 0xba,(07),0xbb }, + { ABTCQ, ybtl, Pw, 0x0f,0xba,(07),0x0f,0xbb }, + { ABTCW, ybtl, Pq, 0xba,(07),0xbb }, + { ABTL, ybtl, Pm, 0xba,(04),0xa3 }, + { ABTQ, ybtl, Pw, 0x0f,0xba,(04),0x0f,0xa3}, + { ABTRL, ybtl, Pm, 0xba,(06),0xb3 }, + { ABTRQ, ybtl, Pw, 0x0f,0xba,(06),0x0f,0xb3 }, + { ABTRW, ybtl, Pq, 0xba,(06),0xb3 }, + { ABTSL, ybtl, Pm, 0xba,(05),0xab }, + { ABTSQ, ybtl, Pw, 0x0f,0xba,(05),0x0f,0xab }, + { ABTSW, ybtl, Pq, 0xba,(05),0xab }, + { ABTW, ybtl, Pq, 0xba,(04),0xa3 }, + { ABYTE, ybyte, Px, 1 }, + { ACALL, ycall, Px, 0xff,(02),0xe8 }, + { ACDQ, ynone, Px, 0x99 }, + { ACLC, ynone, Px, 0xf8 }, + { ACLD, ynone, Px, 0xfc }, + { ACLI, ynone, Px, 0xfa }, + { ACLTS, ynone, Pm, 0x06 }, + { ACMC, ynone, Px, 0xf5 }, + { ACMOVLCC, yml_rl, Pm, 0x43 }, + { ACMOVLCS, yml_rl, Pm, 0x42 }, + { ACMOVLEQ, yml_rl, Pm, 0x44 }, + { ACMOVLGE, yml_rl, Pm, 0x4d }, + { ACMOVLGT, yml_rl, Pm, 0x4f }, + { ACMOVLHI, yml_rl, Pm, 0x47 }, + { ACMOVLLE, yml_rl, Pm, 0x4e }, + { ACMOVLLS, yml_rl, Pm, 0x46 }, + { ACMOVLLT, yml_rl, Pm, 0x4c }, + { ACMOVLMI, yml_rl, Pm, 0x48 }, + { ACMOVLNE, yml_rl, Pm, 0x45 }, + { ACMOVLOC, yml_rl, Pm, 0x41 }, + { ACMOVLOS, yml_rl, Pm, 0x40 }, + { ACMOVLPC, yml_rl, Pm, 0x4b }, + { ACMOVLPL, yml_rl, Pm, 0x49 }, + { ACMOVLPS, yml_rl, Pm, 0x4a }, + { ACMOVQCC, yml_rl, Pw, 0x0f,0x43 }, + { ACMOVQCS, yml_rl, Pw, 0x0f,0x42 }, + { ACMOVQEQ, yml_rl, Pw, 0x0f,0x44 }, + { ACMOVQGE, yml_rl, Pw, 0x0f,0x4d }, + { ACMOVQGT, yml_rl, Pw, 0x0f,0x4f }, + { ACMOVQHI, yml_rl, Pw, 0x0f,0x47 }, + { ACMOVQLE, yml_rl, Pw, 0x0f,0x4e }, + { ACMOVQLS, yml_rl, Pw, 0x0f,0x46 }, + { ACMOVQLT, yml_rl, Pw, 0x0f,0x4c }, + { ACMOVQMI, yml_rl, Pw, 0x0f,0x48 }, + { ACMOVQNE, yml_rl, Pw, 0x0f,0x45 }, + { ACMOVQOC, yml_rl, Pw, 0x0f,0x41 }, + { ACMOVQOS, yml_rl, Pw, 0x0f,0x40 }, + { ACMOVQPC, yml_rl, Pw, 0x0f,0x4b }, + { ACMOVQPL, yml_rl, Pw, 0x0f,0x49 }, + { ACMOVQPS, yml_rl, Pw, 0x0f,0x4a }, + { ACMOVWCC, yml_rl, Pq, 0x43 }, + { ACMOVWCS, yml_rl, Pq, 0x42 }, + { ACMOVWEQ, yml_rl, Pq, 0x44 }, + { ACMOVWGE, yml_rl, Pq, 0x4d }, + { ACMOVWGT, yml_rl, Pq, 0x4f }, + { ACMOVWHI, yml_rl, Pq, 0x47 }, + { ACMOVWLE, yml_rl, Pq, 0x4e }, + { ACMOVWLS, yml_rl, Pq, 0x46 }, + { ACMOVWLT, yml_rl, Pq, 0x4c }, + { ACMOVWMI, yml_rl, Pq, 0x48 }, + { ACMOVWNE, yml_rl, Pq, 0x45 }, + { ACMOVWOC, yml_rl, Pq, 0x41 }, + { ACMOVWOS, yml_rl, Pq, 0x40 }, + { ACMOVWPC, yml_rl, Pq, 0x4b }, + { ACMOVWPL, yml_rl, Pq, 0x49 }, + { ACMOVWPS, yml_rl, Pq, 0x4a }, + { ACMPB, ycmpb, Pb, 0x3c,0x80,(07),0x38,0x3a }, + { ACMPL, ycmpl, Px, 0x83,(07),0x3d,0x81,(07),0x39,0x3b }, + { ACMPPD, yxcmpi, Px, Pe,0xc2 }, + { ACMPPS, yxcmpi, Pm, 0xc2,0 }, + { ACMPQ, ycmpl, Pw, 0x83,(07),0x3d,0x81,(07),0x39,0x3b }, + { ACMPSB, ynone, Pb, 0xa6 }, + { ACMPSD, yxcmpi, Px, Pf2,0xc2 }, + { ACMPSL, ynone, Px, 0xa7 }, + { ACMPSQ, ynone, Pw, 0xa7 }, + { ACMPSS, yxcmpi, Px, Pf3,0xc2 }, + { ACMPSW, ynone, Pe, 0xa7 }, + { ACMPW, ycmpl, Pe, 0x83,(07),0x3d,0x81,(07),0x39,0x3b }, + { ACOMISD, yxcmp, Pe, 0x2f }, + { ACOMISS, yxcmp, Pm, 0x2f }, + { ACPUID, ynone, Pm, 0xa2 }, + { ACVTPL2PD, yxcvm2, Px, Pf3,0xe6,Pe,0x2a }, + { ACVTPL2PS, yxcvm2, Pm, 0x5b,0,0x2a,0, }, + { ACVTPD2PL, yxcvm1, Px, Pf2,0xe6,Pe,0x2d }, + { ACVTPD2PS, yxm, Pe, 0x5a }, + { ACVTPS2PL, yxcvm1, Px, Pe,0x5b,Pm,0x2d }, + { ACVTPS2PD, yxm, Pm, 0x5a }, + { API2FW, ymfp, Px, 0x0c }, + { ACVTSD2SL, yxcvfl, Pf2, 0x2d }, + { ACVTSD2SQ, yxcvfq, Pw, Pf2,0x2d }, + { ACVTSD2SS, yxm, Pf2, 0x5a }, + { ACVTSL2SD, yxcvlf, Pf2, 0x2a }, + { ACVTSQ2SD, yxcvqf, Pw, Pf2,0x2a }, + { ACVTSL2SS, yxcvlf, Pf3, 0x2a }, + { ACVTSQ2SS, yxcvqf, Pw, Pf3,0x2a }, + { ACVTSS2SD, yxm, Pf3, 0x5a }, + { ACVTSS2SL, yxcvfl, Pf3, 0x2d }, + { ACVTSS2SQ, yxcvfq, Pw, Pf3,0x2d }, + { ACVTTPD2PL, yxcvm1, Px, Pe,0xe6,Pe,0x2c }, + { ACVTTPS2PL, yxcvm1, Px, Pf3,0x5b,Pm,0x2c }, + { ACVTTSD2SL, yxcvfl, Pf2, 0x2c }, + { ACVTTSD2SQ, yxcvfq, Pw, Pf2,0x2c }, + { ACVTTSS2SL, yxcvfl, Pf3, 0x2c }, + { ACVTTSS2SQ, yxcvfq, Pw, Pf3,0x2c }, + { ACWD, ynone, Pe, 0x99 }, + { ACQO, ynone, Pw, 0x99 }, + { ADAA, ynone, P32, 0x27 }, + { ADAS, ynone, P32, 0x2f }, + { ADATA }, + { ADECB, yincb, Pb, 0xfe,(01) }, + { ADECL, yincl, Px, 0xff,(01) }, + { ADECQ, yincl, Pw, 0xff,(01) }, + { ADECW, yincw, Pe, 0xff,(01) }, + { ADIVB, ydivb, Pb, 0xf6,(06) }, + { ADIVL, ydivl, Px, 0xf7,(06) }, + { ADIVPD, yxm, Pe, 0x5e }, + { ADIVPS, yxm, Pm, 0x5e }, + { ADIVQ, ydivl, Pw, 0xf7,(06) }, + { ADIVSD, yxm, Pf2, 0x5e }, + { ADIVSS, yxm, Pf3, 0x5e }, + { ADIVW, ydivl, Pe, 0xf7,(06) }, + { AEMMS, ynone, Pm, 0x77 }, + { AENTER }, /* botch */ + { AFXRSTOR, ysvrs, Pm, 0xae,(01),0xae,(01) }, + { AFXSAVE, ysvrs, Pm, 0xae,(00),0xae,(00) }, + { AFXRSTOR64, ysvrs, Pw, 0x0f,0xae,(01),0x0f,0xae,(01) }, + { AFXSAVE64, ysvrs, Pw, 0x0f,0xae,(00),0x0f,0xae,(00) }, + { AGLOBL }, + { AGOK }, + { AHISTORY }, + { AHLT, ynone, Px, 0xf4 }, + { AIDIVB, ydivb, Pb, 0xf6,(07) }, + { AIDIVL, ydivl, Px, 0xf7,(07) }, + { AIDIVQ, ydivl, Pw, 0xf7,(07) }, + { AIDIVW, ydivl, Pe, 0xf7,(07) }, + { AIMULB, ydivb, Pb, 0xf6,(05) }, + { AIMULL, yimul, Px, 0xf7,(05),0x6b,0x69,Pm,0xaf }, + { AIMULQ, yimul, Pw, 0xf7,(05),0x6b,0x69,Pm,0xaf }, + { AIMULW, yimul, Pe, 0xf7,(05),0x6b,0x69,Pm,0xaf }, + { AIMUL3Q, yimul3, Pw, 0x6b,(00) }, + { AINB, yin, Pb, 0xe4,0xec }, + { AINCB, yincb, Pb, 0xfe,(00) }, + { AINCL, yincl, Px, 0xff,(00) }, + { AINCQ, yincl, Pw, 0xff,(00) }, + { AINCW, yincw, Pe, 0xff,(00) }, + { AINL, yin, Px, 0xe5,0xed }, + { AINSB, ynone, Pb, 0x6c }, + { AINSL, ynone, Px, 0x6d }, + { AINSW, ynone, Pe, 0x6d }, + { AINT, yint, Px, 0xcd }, + { AINTO, ynone, P32, 0xce }, + { AINW, yin, Pe, 0xe5,0xed }, + { AIRETL, ynone, Px, 0xcf }, + { AIRETQ, ynone, Pw, 0xcf }, + { AIRETW, ynone, Pe, 0xcf }, + { AJCC, yjcond, Px, 0x73,0x83,(00) }, + { AJCS, yjcond, Px, 0x72,0x82 }, + { AJCXZL, yloop, Px, 0xe3 }, + { AJCXZQ, yloop, Px, 0xe3 }, + { AJEQ, yjcond, Px, 0x74,0x84 }, + { AJGE, yjcond, Px, 0x7d,0x8d }, + { AJGT, yjcond, Px, 0x7f,0x8f }, + { AJHI, yjcond, Px, 0x77,0x87 }, + { AJLE, yjcond, Px, 0x7e,0x8e }, + { AJLS, yjcond, Px, 0x76,0x86 }, + { AJLT, yjcond, Px, 0x7c,0x8c }, + { AJMI, yjcond, Px, 0x78,0x88 }, + { AJMP, yjmp, Px, 0xff,(04),0xeb,0xe9 }, + { AJNE, yjcond, Px, 0x75,0x85 }, + { AJOC, yjcond, Px, 0x71,0x81,(00) }, + { AJOS, yjcond, Px, 0x70,0x80,(00) }, + { AJPC, yjcond, Px, 0x7b,0x8b }, + { AJPL, yjcond, Px, 0x79,0x89 }, + { AJPS, yjcond, Px, 0x7a,0x8a }, + { ALAHF, ynone, Px, 0x9f }, + { ALARL, yml_rl, Pm, 0x02 }, + { ALARW, yml_rl, Pq, 0x02 }, + { ALDMXCSR, ysvrs, Pm, 0xae,(02),0xae,(02) }, + { ALEAL, ym_rl, Px, 0x8d }, + { ALEAQ, ym_rl, Pw, 0x8d }, + { ALEAVEL, ynone, P32, 0xc9 }, + { ALEAVEQ, ynone, Py, 0xc9 }, + { ALEAVEW, ynone, Pe, 0xc9 }, + { ALEAW, ym_rl, Pe, 0x8d }, + { ALOCK, ynone, Px, 0xf0 }, + { ALODSB, ynone, Pb, 0xac }, + { ALODSL, ynone, Px, 0xad }, + { ALODSQ, ynone, Pw, 0xad }, + { ALODSW, ynone, Pe, 0xad }, + { ALONG, ybyte, Px, 4 }, + { ALOOP, yloop, Px, 0xe2 }, + { ALOOPEQ, yloop, Px, 0xe1 }, + { ALOOPNE, yloop, Px, 0xe0 }, + { ALSLL, yml_rl, Pm, 0x03 }, + { ALSLW, yml_rl, Pq, 0x03 }, + { AMASKMOVOU, yxr, Pe, 0xf7 }, + { AMASKMOVQ, ymr, Pm, 0xf7 }, + { AMAXPD, yxm, Pe, 0x5f }, + { AMAXPS, yxm, Pm, 0x5f }, + { AMAXSD, yxm, Pf2, 0x5f }, + { AMAXSS, yxm, Pf3, 0x5f }, + { AMINPD, yxm, Pe, 0x5d }, + { AMINPS, yxm, Pm, 0x5d }, + { AMINSD, yxm, Pf2, 0x5d }, + { AMINSS, yxm, Pf3, 0x5d }, + { AMOVAPD, yxmov, Pe, 0x28,0x29 }, + { AMOVAPS, yxmov, Pm, 0x28,0x29 }, + { AMOVB, ymovb, Pb, 0x88,0x8a,0xb0,0xc6,(00) }, + { AMOVBLSX, ymb_rl, Pm, 0xbe }, + { AMOVBLZX, ymb_rl, Pm, 0xb6 }, + { AMOVBQSX, ymb_rl, Pw, 0x0f,0xbe }, + { AMOVBQZX, ymb_rl, Pw, 0x0f,0xb6 }, + { AMOVBWSX, ymb_rl, Pq, 0xbe }, + { AMOVBWZX, ymb_rl, Pq, 0xb6 }, + { AMOVO, yxmov, Pe, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, + { AMOVHLPS, yxr, Pm, 0x12 }, + { AMOVHPD, yxmov, Pe, 0x16,0x17 }, + { AMOVHPS, yxmov, Pm, 0x16,0x17 }, + { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e,0 }, + { AMOVLHPS, yxr, Pm, 0x16 }, + { AMOVLPD, yxmov, Pe, 0x12,0x13 }, + { AMOVLPS, yxmov, Pm, 0x12,0x13 }, + { AMOVLQSX, yml_rl, Pw, 0x63 }, + { AMOVLQZX, yml_rl, Px, 0x8b }, + { AMOVMSKPD, yxrrl, Pq, 0x50 }, + { AMOVMSKPS, yxrrl, Pm, 0x50 }, + { AMOVNTO, yxr_ml, Pe, 0xe7 }, + { AMOVNTPD, yxr_ml, Pe, 0x2b }, + { AMOVNTPS, yxr_ml, Pm, 0x2b }, + { AMOVNTQ, ymr_ml, Pm, 0xe7 }, + { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e,0 }, + { AMOVQOZX, ymrxr, Pf3, 0xd6,0x7e }, + { AMOVSB, ynone, Pb, 0xa4 }, + { AMOVSD, yxmov, Pf2, 0x10,0x11 }, + { AMOVSL, ynone, Px, 0xa5 }, + { AMOVSQ, ynone, Pw, 0xa5 }, + { AMOVSS, yxmov, Pf3, 0x10,0x11 }, + { AMOVSW, ynone, Pe, 0xa5 }, + { AMOVUPD, yxmov, Pe, 0x10,0x11 }, + { AMOVUPS, yxmov, Pm, 0x10,0x11 }, + { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00),0 }, + { AMOVWLSX, yml_rl, Pm, 0xbf }, + { AMOVWLZX, yml_rl, Pm, 0xb7 }, + { AMOVWQSX, yml_rl, Pw, 0x0f,0xbf }, + { AMOVWQZX, yml_rl, Pw, 0x0f,0xb7 }, + { AMULB, ydivb, Pb, 0xf6,(04) }, + { AMULL, ydivl, Px, 0xf7,(04) }, + { AMULPD, yxm, Pe, 0x59 }, + { AMULPS, yxm, Ym, 0x59 }, + { AMULQ, ydivl, Pw, 0xf7,(04) }, + { AMULSD, yxm, Pf2, 0x59 }, + { AMULSS, yxm, Pf3, 0x59 }, + { AMULW, ydivl, Pe, 0xf7,(04) }, + { ANAME }, + { ANEGB, yscond, Pb, 0xf6,(03) }, + { ANEGL, yscond, Px, 0xf7,(03) }, + { ANEGQ, yscond, Pw, 0xf7,(03) }, + { ANEGW, yscond, Pe, 0xf7,(03) }, + { ANOP, ynop, Px, 0,0 }, + { ANOTB, yscond, Pb, 0xf6,(02) }, + { ANOTL, yscond, Px, 0xf7,(02) }, + { ANOTQ, yscond, Pw, 0xf7,(02) }, + { ANOTW, yscond, Pe, 0xf7,(02) }, + { AORB, yxorb, Pb, 0x0c,0x80,(01),0x08,0x0a }, + { AORL, yxorl, Px, 0x83,(01),0x0d,0x81,(01),0x09,0x0b }, + { AORPD, yxm, Pq, 0x56 }, + { AORPS, yxm, Pm, 0x56 }, + { AORQ, yxorl, Pw, 0x83,(01),0x0d,0x81,(01),0x09,0x0b }, + { AORW, yxorl, Pe, 0x83,(01),0x0d,0x81,(01),0x09,0x0b }, + { AOUTB, yin, Pb, 0xe6,0xee }, + { AOUTL, yin, Px, 0xe7,0xef }, + { AOUTSB, ynone, Pb, 0x6e }, + { AOUTSL, ynone, Px, 0x6f }, + { AOUTSW, ynone, Pe, 0x6f }, + { AOUTW, yin, Pe, 0xe7,0xef }, + { APACKSSLW, ymm, Py, 0x6b,Pe,0x6b }, + { APACKSSWB, ymm, Py, 0x63,Pe,0x63 }, + { APACKUSWB, ymm, Py, 0x67,Pe,0x67 }, + { APADDB, ymm, Py, 0xfc,Pe,0xfc }, + { APADDL, ymm, Py, 0xfe,Pe,0xfe }, + { APADDQ, yxm, Pe, 0xd4 }, + { APADDSB, ymm, Py, 0xec,Pe,0xec }, + { APADDSW, ymm, Py, 0xed,Pe,0xed }, + { APADDUSB, ymm, Py, 0xdc,Pe,0xdc }, + { APADDUSW, ymm, Py, 0xdd,Pe,0xdd }, + { APADDW, ymm, Py, 0xfd,Pe,0xfd }, + { APAND, ymm, Py, 0xdb,Pe,0xdb }, + { APANDN, ymm, Py, 0xdf,Pe,0xdf }, + { APAUSE, ynone, Px, 0xf3,0x90 }, + { APAVGB, ymm, Py, 0xe0,Pe,0xe0 }, + { APAVGW, ymm, Py, 0xe3,Pe,0xe3 }, + { APCMPEQB, ymm, Py, 0x74,Pe,0x74 }, + { APCMPEQL, ymm, Py, 0x76,Pe,0x76 }, + { APCMPEQW, ymm, Py, 0x75,Pe,0x75 }, + { APCMPGTB, ymm, Py, 0x64,Pe,0x64 }, + { APCMPGTL, ymm, Py, 0x66,Pe,0x66 }, + { APCMPGTW, ymm, Py, 0x65,Pe,0x65 }, + { APEXTRW, yextrw, Pq, 0xc5,(00) }, + { APF2IL, ymfp, Px, 0x1d }, + { APF2IW, ymfp, Px, 0x1c }, + { API2FL, ymfp, Px, 0x0d }, + { APFACC, ymfp, Px, 0xae }, + { APFADD, ymfp, Px, 0x9e }, + { APFCMPEQ, ymfp, Px, 0xb0 }, + { APFCMPGE, ymfp, Px, 0x90 }, + { APFCMPGT, ymfp, Px, 0xa0 }, + { APFMAX, ymfp, Px, 0xa4 }, + { APFMIN, ymfp, Px, 0x94 }, + { APFMUL, ymfp, Px, 0xb4 }, + { APFNACC, ymfp, Px, 0x8a }, + { APFPNACC, ymfp, Px, 0x8e }, + { APFRCP, ymfp, Px, 0x96 }, + { APFRCPIT1, ymfp, Px, 0xa6 }, + { APFRCPI2T, ymfp, Px, 0xb6 }, + { APFRSQIT1, ymfp, Px, 0xa7 }, + { APFRSQRT, ymfp, Px, 0x97 }, + { APFSUB, ymfp, Px, 0x9a }, + { APFSUBR, ymfp, Px, 0xaa }, + { APINSRW, yinsrw, Pq, 0xc4,(00) }, + { APINSRD, yinsr, Pq, 0x3a, 0x22, (00) }, + { APINSRQ, yinsr, Pq3, 0x3a, 0x22, (00) }, + { APMADDWL, ymm, Py, 0xf5,Pe,0xf5 }, + { APMAXSW, yxm, Pe, 0xee }, + { APMAXUB, yxm, Pe, 0xde }, + { APMINSW, yxm, Pe, 0xea }, + { APMINUB, yxm, Pe, 0xda }, + { APMOVMSKB, ymskb, Px, Pe,0xd7,0xd7 }, + { APMULHRW, ymfp, Px, 0xb7 }, + { APMULHUW, ymm, Py, 0xe4,Pe,0xe4 }, + { APMULHW, ymm, Py, 0xe5,Pe,0xe5 }, + { APMULLW, ymm, Py, 0xd5,Pe,0xd5 }, + { APMULULQ, ymm, Py, 0xf4,Pe,0xf4 }, + { APOPAL, ynone, P32, 0x61 }, + { APOPAW, ynone, Pe, 0x61 }, + { APOPFL, ynone, P32, 0x9d }, + { APOPFQ, ynone, Py, 0x9d }, + { APOPFW, ynone, Pe, 0x9d }, + { APOPL, ypopl, P32, 0x58,0x8f,(00) }, + { APOPQ, ypopl, Py, 0x58,0x8f,(00) }, + { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, + { APOR, ymm, Py, 0xeb,Pe,0xeb }, + { APSADBW, yxm, Pq, 0xf6 }, + { APSHUFHW, yxshuf, Pf3, 0x70,(00) }, + { APSHUFL, yxshuf, Pq, 0x70,(00) }, + { APSHUFLW, yxshuf, Pf2, 0x70,(00) }, + { APSHUFW, ymshuf, Pm, 0x70,(00) }, + { APSHUFB, ymshufb,Pq, 0x38, 0x00 }, + { APSLLO, ypsdq, Pq, 0x73,(07) }, + { APSLLL, yps, Py, 0xf2, 0x72,(06), Pe,0xf2, Pe,0x72,(06) }, + { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x73,(06) }, + { APSLLW, yps, Py, 0xf1, 0x71,(06), Pe,0xf1, Pe,0x71,(06) }, + { APSRAL, yps, Py, 0xe2, 0x72,(04), Pe,0xe2, Pe,0x72,(04) }, + { APSRAW, yps, Py, 0xe1, 0x71,(04), Pe,0xe1, Pe,0x71,(04) }, + { APSRLO, ypsdq, Pq, 0x73,(03) }, + { APSRLL, yps, Py, 0xd2, 0x72,(02), Pe,0xd2, Pe,0x72,(02) }, + { APSRLQ, yps, Py, 0xd3, 0x73,(02), Pe,0xd3, Pe,0x73,(02) }, + { APSRLW, yps, Py, 0xd1, 0x71,(02), Pe,0xe1, Pe,0x71,(02) }, + { APSUBB, yxm, Pe, 0xf8 }, + { APSUBL, yxm, Pe, 0xfa }, + { APSUBQ, yxm, Pe, 0xfb }, + { APSUBSB, yxm, Pe, 0xe8 }, + { APSUBSW, yxm, Pe, 0xe9 }, + { APSUBUSB, yxm, Pe, 0xd8 }, + { APSUBUSW, yxm, Pe, 0xd9 }, + { APSUBW, yxm, Pe, 0xf9 }, + { APSWAPL, ymfp, Px, 0xbb }, + { APUNPCKHBW, ymm, Py, 0x68,Pe,0x68 }, + { APUNPCKHLQ, ymm, Py, 0x6a,Pe,0x6a }, + { APUNPCKHQDQ, yxm, Pe, 0x6d }, + { APUNPCKHWL, ymm, Py, 0x69,Pe,0x69 }, + { APUNPCKLBW, ymm, Py, 0x60,Pe,0x60 }, + { APUNPCKLLQ, ymm, Py, 0x62,Pe,0x62 }, + { APUNPCKLQDQ, yxm, Pe, 0x6c }, + { APUNPCKLWL, ymm, Py, 0x61,Pe,0x61 }, + { APUSHAL, ynone, P32, 0x60 }, + { APUSHAW, ynone, Pe, 0x60 }, + { APUSHFL, ynone, P32, 0x9c }, + { APUSHFQ, ynone, Py, 0x9c }, + { APUSHFW, ynone, Pe, 0x9c }, + { APUSHL, ypushl, P32, 0x50,0xff,(06),0x6a,0x68 }, + { APUSHQ, ypushl, Py, 0x50,0xff,(06),0x6a,0x68 }, + { APUSHW, ypushl, Pe, 0x50,0xff,(06),0x6a,0x68 }, + { APXOR, ymm, Py, 0xef,Pe,0xef }, + { AQUAD, ybyte, Px, 8 }, + { ARCLB, yshb, Pb, 0xd0,(02),0xc0,(02),0xd2,(02) }, + { ARCLL, yshl, Px, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) }, + { ARCLQ, yshl, Pw, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) }, + { ARCLW, yshl, Pe, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) }, + { ARCPPS, yxm, Pm, 0x53 }, + { ARCPSS, yxm, Pf3, 0x53 }, + { ARCRB, yshb, Pb, 0xd0,(03),0xc0,(03),0xd2,(03) }, + { ARCRL, yshl, Px, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) }, + { ARCRQ, yshl, Pw, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) }, + { ARCRW, yshl, Pe, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) }, + { AREP, ynone, Px, 0xf3 }, + { AREPN, ynone, Px, 0xf2 }, + { ARET, ynone, Px, 0xc3 }, + { ARETFW, yret, Pe, 0xcb,0xca }, + { ARETFL, yret, Px, 0xcb,0xca }, + { ARETFQ, yret, Pw, 0xcb,0xca }, + { AROLB, yshb, Pb, 0xd0,(00),0xc0,(00),0xd2,(00) }, + { AROLL, yshl, Px, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) }, + { AROLQ, yshl, Pw, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) }, + { AROLW, yshl, Pe, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) }, + { ARORB, yshb, Pb, 0xd0,(01),0xc0,(01),0xd2,(01) }, + { ARORL, yshl, Px, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) }, + { ARORQ, yshl, Pw, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) }, + { ARORW, yshl, Pe, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) }, + { ARSQRTPS, yxm, Pm, 0x52 }, + { ARSQRTSS, yxm, Pf3, 0x52 }, + { ASAHF, ynone, Px, 0x86,0xe0,0x50,0x9d }, /* XCHGB AH,AL; PUSH AX; POPFL */ + { ASALB, yshb, Pb, 0xd0,(04),0xc0,(04),0xd2,(04) }, + { ASALL, yshl, Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASALQ, yshl, Pw, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASALW, yshl, Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASARB, yshb, Pb, 0xd0,(07),0xc0,(07),0xd2,(07) }, + { ASARL, yshl, Px, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) }, + { ASARQ, yshl, Pw, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) }, + { ASARW, yshl, Pe, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) }, + { ASBBB, yxorb, Pb, 0x1c,0x80,(03),0x18,0x1a }, + { ASBBL, yxorl, Px, 0x83,(03),0x1d,0x81,(03),0x19,0x1b }, + { ASBBQ, yxorl, Pw, 0x83,(03),0x1d,0x81,(03),0x19,0x1b }, + { ASBBW, yxorl, Pe, 0x83,(03),0x1d,0x81,(03),0x19,0x1b }, + { ASCASB, ynone, Pb, 0xae }, + { ASCASL, ynone, Px, 0xaf }, + { ASCASQ, ynone, Pw, 0xaf }, + { ASCASW, ynone, Pe, 0xaf }, + { ASETCC, yscond, Pm, 0x93,(00) }, + { ASETCS, yscond, Pm, 0x92,(00) }, + { ASETEQ, yscond, Pm, 0x94,(00) }, + { ASETGE, yscond, Pm, 0x9d,(00) }, + { ASETGT, yscond, Pm, 0x9f,(00) }, + { ASETHI, yscond, Pm, 0x97,(00) }, + { ASETLE, yscond, Pm, 0x9e,(00) }, + { ASETLS, yscond, Pm, 0x96,(00) }, + { ASETLT, yscond, Pm, 0x9c,(00) }, + { ASETMI, yscond, Pm, 0x98,(00) }, + { ASETNE, yscond, Pm, 0x95,(00) }, + { ASETOC, yscond, Pm, 0x91,(00) }, + { ASETOS, yscond, Pm, 0x90,(00) }, + { ASETPC, yscond, Pm, 0x96,(00) }, + { ASETPL, yscond, Pm, 0x99,(00) }, + { ASETPS, yscond, Pm, 0x9a,(00) }, + { ASHLB, yshb, Pb, 0xd0,(04),0xc0,(04),0xd2,(04) }, + { ASHLL, yshl, Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASHLQ, yshl, Pw, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASHLW, yshl, Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASHRB, yshb, Pb, 0xd0,(05),0xc0,(05),0xd2,(05) }, + { ASHRL, yshl, Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, + { ASHRQ, yshl, Pw, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, + { ASHRW, yshl, Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, + { ASHUFPD, yxshuf, Pq, 0xc6,(00) }, + { ASHUFPS, yxshuf, Pm, 0xc6,(00) }, + { ASQRTPD, yxm, Pe, 0x51 }, + { ASQRTPS, yxm, Pm, 0x51 }, + { ASQRTSD, yxm, Pf2, 0x51 }, + { ASQRTSS, yxm, Pf3, 0x51 }, + { ASTC, ynone, Px, 0xf9 }, + { ASTD, ynone, Px, 0xfd }, + { ASTI, ynone, Px, 0xfb }, + { ASTMXCSR, ysvrs, Pm, 0xae,(03),0xae,(03) }, + { ASTOSB, ynone, Pb, 0xaa }, + { ASTOSL, ynone, Px, 0xab }, + { ASTOSQ, ynone, Pw, 0xab }, + { ASTOSW, ynone, Pe, 0xab }, + { ASUBB, yxorb, Pb, 0x2c,0x80,(05),0x28,0x2a }, + { ASUBL, yaddl, Px, 0x83,(05),0x2d,0x81,(05),0x29,0x2b }, + { ASUBPD, yxm, Pe, 0x5c }, + { ASUBPS, yxm, Pm, 0x5c }, + { ASUBQ, yaddl, Pw, 0x83,(05),0x2d,0x81,(05),0x29,0x2b }, + { ASUBSD, yxm, Pf2, 0x5c }, + { ASUBSS, yxm, Pf3, 0x5c }, + { ASUBW, yaddl, Pe, 0x83,(05),0x2d,0x81,(05),0x29,0x2b }, + { ASWAPGS, ynone, Pm, 0x01,0xf8 }, + { ASYSCALL, ynone, Px, 0x0f,0x05 }, /* fast syscall */ + { ATESTB, ytestb, Pb, 0xa8,0xf6,(00),0x84,0x84 }, + { ATESTL, ytestl, Px, 0xa9,0xf7,(00),0x85,0x85 }, + { ATESTQ, ytestl, Pw, 0xa9,0xf7,(00),0x85,0x85 }, + { ATESTW, ytestl, Pe, 0xa9,0xf7,(00),0x85,0x85 }, + { ATEXT, ytext, Px }, + { AUCOMISD, yxcmp, Pe, 0x2e }, + { AUCOMISS, yxcmp, Pm, 0x2e }, + { AUNPCKHPD, yxm, Pe, 0x15 }, + { AUNPCKHPS, yxm, Pm, 0x15 }, + { AUNPCKLPD, yxm, Pe, 0x14 }, + { AUNPCKLPS, yxm, Pm, 0x14 }, + { AVERR, ydivl, Pm, 0x00,(04) }, + { AVERW, ydivl, Pm, 0x00,(05) }, + { AWAIT, ynone, Px, 0x9b }, + { AWORD, ybyte, Px, 2 }, + { AXCHGB, yml_mb, Pb, 0x86,0x86 }, + { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 }, + { AXCHGQ, yxchg, Pw, 0x90,0x90,0x87,0x87 }, + { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 }, + { AXLAT, ynone, Px, 0xd7 }, + { AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 }, + { AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, + { AXORPD, yxm, Pe, 0x57 }, + { AXORPS, yxm, Pm, 0x57 }, + { AXORQ, yxorl, Pw, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, + { AXORW, yxorl, Pe, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, + + { AFMOVB, yfmvx, Px, 0xdf,(04) }, + { AFMOVBP, yfmvp, Px, 0xdf,(06) }, + { AFMOVD, yfmvd, Px, 0xdd,(00),0xdd,(02),0xd9,(00),0xdd,(02) }, + { AFMOVDP, yfmvdp, Px, 0xdd,(03),0xdd,(03) }, + { AFMOVF, yfmvf, Px, 0xd9,(00),0xd9,(02) }, + { AFMOVFP, yfmvp, Px, 0xd9,(03) }, + { AFMOVL, yfmvf, Px, 0xdb,(00),0xdb,(02) }, + { AFMOVLP, yfmvp, Px, 0xdb,(03) }, + { AFMOVV, yfmvx, Px, 0xdf,(05) }, + { AFMOVVP, yfmvp, Px, 0xdf,(07) }, + { AFMOVW, yfmvf, Px, 0xdf,(00),0xdf,(02) }, + { AFMOVWP, yfmvp, Px, 0xdf,(03) }, + { AFMOVX, yfmvx, Px, 0xdb,(05) }, + { AFMOVXP, yfmvp, Px, 0xdb,(07) }, + + { AFCOMB }, + { AFCOMBP }, + { AFCOMD, yfadd, Px, 0xdc,(02),0xd8,(02),0xdc,(02) }, /* botch */ + { AFCOMDP, yfadd, Px, 0xdc,(03),0xd8,(03),0xdc,(03) }, /* botch */ + { AFCOMDPP, ycompp, Px, 0xde,(03) }, + { AFCOMF, yfmvx, Px, 0xd8,(02) }, + { AFCOMFP, yfmvx, Px, 0xd8,(03) }, + { AFCOML, yfmvx, Px, 0xda,(02) }, + { AFCOMLP, yfmvx, Px, 0xda,(03) }, + { AFCOMW, yfmvx, Px, 0xde,(02) }, + { AFCOMWP, yfmvx, Px, 0xde,(03) }, + + { AFUCOM, ycompp, Px, 0xdd,(04) }, + { AFUCOMP, ycompp, Px, 0xdd,(05) }, + { AFUCOMPP, ycompp, Px, 0xda,(13) }, + + { AFADDDP, yfaddp, Px, 0xde,(00) }, + { AFADDW, yfmvx, Px, 0xde,(00) }, + { AFADDL, yfmvx, Px, 0xda,(00) }, + { AFADDF, yfmvx, Px, 0xd8,(00) }, + { AFADDD, yfadd, Px, 0xdc,(00),0xd8,(00),0xdc,(00) }, + + { AFMULDP, yfaddp, Px, 0xde,(01) }, + { AFMULW, yfmvx, Px, 0xde,(01) }, + { AFMULL, yfmvx, Px, 0xda,(01) }, + { AFMULF, yfmvx, Px, 0xd8,(01) }, + { AFMULD, yfadd, Px, 0xdc,(01),0xd8,(01),0xdc,(01) }, + + { AFSUBDP, yfaddp, Px, 0xde,(05) }, + { AFSUBW, yfmvx, Px, 0xde,(04) }, + { AFSUBL, yfmvx, Px, 0xda,(04) }, + { AFSUBF, yfmvx, Px, 0xd8,(04) }, + { AFSUBD, yfadd, Px, 0xdc,(04),0xd8,(04),0xdc,(05) }, + + { AFSUBRDP, yfaddp, Px, 0xde,(04) }, + { AFSUBRW, yfmvx, Px, 0xde,(05) }, + { AFSUBRL, yfmvx, Px, 0xda,(05) }, + { AFSUBRF, yfmvx, Px, 0xd8,(05) }, + { AFSUBRD, yfadd, Px, 0xdc,(05),0xd8,(05),0xdc,(04) }, + + { AFDIVDP, yfaddp, Px, 0xde,(07) }, + { AFDIVW, yfmvx, Px, 0xde,(06) }, + { AFDIVL, yfmvx, Px, 0xda,(06) }, + { AFDIVF, yfmvx, Px, 0xd8,(06) }, + { AFDIVD, yfadd, Px, 0xdc,(06),0xd8,(06),0xdc,(07) }, + + { AFDIVRDP, yfaddp, Px, 0xde,(06) }, + { AFDIVRW, yfmvx, Px, 0xde,(07) }, + { AFDIVRL, yfmvx, Px, 0xda,(07) }, + { AFDIVRF, yfmvx, Px, 0xd8,(07) }, + { AFDIVRD, yfadd, Px, 0xdc,(07),0xd8,(07),0xdc,(06) }, + + { AFXCHD, yfxch, Px, 0xd9,(01),0xd9,(01) }, + { AFFREE }, + { AFLDCW, ystcw, Px, 0xd9,(05),0xd9,(05) }, + { AFLDENV, ystcw, Px, 0xd9,(04),0xd9,(04) }, + { AFRSTOR, ysvrs, Px, 0xdd,(04),0xdd,(04) }, + { AFSAVE, ysvrs, Px, 0xdd,(06),0xdd,(06) }, + { AFSTCW, ystcw, Px, 0xd9,(07),0xd9,(07) }, + { AFSTENV, ystcw, Px, 0xd9,(06),0xd9,(06) }, + { AFSTSW, ystsw, Px, 0xdd,(07),0xdf,0xe0 }, + { AF2XM1, ynone, Px, 0xd9, 0xf0 }, + { AFABS, ynone, Px, 0xd9, 0xe1 }, + { AFCHS, ynone, Px, 0xd9, 0xe0 }, + { AFCLEX, ynone, Px, 0xdb, 0xe2 }, + { AFCOS, ynone, Px, 0xd9, 0xff }, + { AFDECSTP, ynone, Px, 0xd9, 0xf6 }, + { AFINCSTP, ynone, Px, 0xd9, 0xf7 }, + { AFINIT, ynone, Px, 0xdb, 0xe3 }, + { AFLD1, ynone, Px, 0xd9, 0xe8 }, + { AFLDL2E, ynone, Px, 0xd9, 0xea }, + { AFLDL2T, ynone, Px, 0xd9, 0xe9 }, + { AFLDLG2, ynone, Px, 0xd9, 0xec }, + { AFLDLN2, ynone, Px, 0xd9, 0xed }, + { AFLDPI, ynone, Px, 0xd9, 0xeb }, + { AFLDZ, ynone, Px, 0xd9, 0xee }, + { AFNOP, ynone, Px, 0xd9, 0xd0 }, + { AFPATAN, ynone, Px, 0xd9, 0xf3 }, + { AFPREM, ynone, Px, 0xd9, 0xf8 }, + { AFPREM1, ynone, Px, 0xd9, 0xf5 }, + { AFPTAN, ynone, Px, 0xd9, 0xf2 }, + { AFRNDINT, ynone, Px, 0xd9, 0xfc }, + { AFSCALE, ynone, Px, 0xd9, 0xfd }, + { AFSIN, ynone, Px, 0xd9, 0xfe }, + { AFSINCOS, ynone, Px, 0xd9, 0xfb }, + { AFSQRT, ynone, Px, 0xd9, 0xfa }, + { AFTST, ynone, Px, 0xd9, 0xe4 }, + { AFXAM, ynone, Px, 0xd9, 0xe5 }, + { AFXTRACT, ynone, Px, 0xd9, 0xf4 }, + { AFYL2X, ynone, Px, 0xd9, 0xf1 }, + { AFYL2XP1, ynone, Px, 0xd9, 0xf9 }, + + { ACMPXCHGB, yrb_mb, Pb, 0x0f,0xb0 }, + { ACMPXCHGL, yrl_ml, Px, 0x0f,0xb1 }, + { ACMPXCHGW, yrl_ml, Pe, 0x0f,0xb1 }, + { ACMPXCHGQ, yrl_ml, Pw, 0x0f,0xb1 }, + { ACMPXCHG8B, yscond, Pm, 0xc7,(01) }, + { AINVD, ynone, Pm, 0x08 }, + { AINVLPG, ymbs, Pm, 0x01,(07) }, + { ALFENCE, ynone, Pm, 0xae,0xe8 }, + { AMFENCE, ynone, Pm, 0xae,0xf0 }, + { AMOVNTIL, yrl_ml, Pm, 0xc3 }, + { AMOVNTIQ, yrl_ml, Pw, 0x0f,0xc3 }, + { ARDMSR, ynone, Pm, 0x32 }, + { ARDPMC, ynone, Pm, 0x33 }, + { ARDTSC, ynone, Pm, 0x31 }, + { ARSM, ynone, Pm, 0xaa }, + { ASFENCE, ynone, Pm, 0xae,0xf8 }, + { ASYSRET, ynone, Pm, 0x07 }, + { AWBINVD, ynone, Pm, 0x09 }, + { AWRMSR, ynone, Pm, 0x30 }, + + { AXADDB, yrb_mb, Pb, 0x0f,0xc0 }, + { AXADDL, yrl_ml, Px, 0x0f,0xc1 }, + { AXADDQ, yrl_ml, Pw, 0x0f,0xc1 }, + { AXADDW, yrl_ml, Pe, 0x0f,0xc1 }, + + { ACRC32B, ycrc32l,Px, 0xf2,0x0f,0x38,0xf0,0 }, + { ACRC32Q, ycrc32l,Pw, 0xf2,0x0f,0x38,0xf1,0 }, + + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { AMOVQL, yrl_ml, Px, 0x89 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AAESENC, yaes, Pq, 0x38,0xdc,(0) }, + { AAESENCLAST, yaes, Pq, 0x38,0xdd,(0) }, + { AAESDEC, yaes, Pq, 0x38,0xde,(0) }, + { AAESDECLAST, yaes, Pq, 0x38,0xdf,(0) }, + { AAESIMC, yaes, Pq, 0x38,0xdb,(0) }, + { AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) }, + + { APSHUFD, yaes2, Pq, 0x70,(0) }, + { APCLMULQDQ, yxshuf, Pq, 0x3a,0x44,0 }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ATYPE }, + { AFUNCDATA, yfuncdata, Px, 0,0 }, + { APCDATA, ypcdata, Px, 0,0 }, + { ACHECKNIL }, + { AVARDEF }, + { AVARKILL }, + { ADUFFCOPY, yduff, Px, 0xe8 }, + { ADUFFZERO, yduff, Px, 0xe8 }, + + { AEND }, + 0 +}; + +static Optab* opindex[ALAST+1]; +static vlong vaddr(Link*, Addr*, Reloc*); + +// single-instruction no-ops of various lengths. +// constructed by hand and disassembled with gdb to verify. +// see http://www.agner.org/optimize/optimizing_assembly.pdf for discussion. +static uchar nop[][16] = { + {0x90}, + {0x66, 0x90}, + {0x0F, 0x1F, 0x00}, + {0x0F, 0x1F, 0x40, 0x00}, + {0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + // Native Client rejects the repeated 0x66 prefix. + // {0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void +fillnop(uchar *p, int n) +{ + int m; + + while(n > 0) { + m = n; + if(m > nelem(nop)) + m = nelem(nop); + memmove(p, nop[m-1], m); + p += m; + n -= m; + } +} + +static void instinit(void); + +static int32 +naclpad(Link *ctxt, LSym *s, int32 c, int32 pad) +{ + symgrow(ctxt, s, c+pad); + fillnop(s->p+c, pad); + return c+pad; +} + +static int +spadjop(Link *ctxt, Prog *p, int l, int q) +{ + if(p->mode != 64 || ctxt->arch->ptrsize == 4) + return l; + return q; +} + +void +span6(Link *ctxt, LSym *s) +{ + Prog *p, *q; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + ctxt->cursym = s; + + if(s->p != nil) + return; + + if(ycover[0] == 0) + instinit(); + + for(p = ctxt->cursym->text; p != nil; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == nil) + p->pcond = p; + if((q = p->pcond) != nil) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = spadjop(ctxt, p, AADDL, AADDQ); + if(v < 0) { + p->as = spadjop(ctxt, p, ASUBL, ASUBQ); + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; + } + } + + for(p = s->text; p != nil; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != nil && (q->back & 2)) { + p->back |= 1; // backward jump + q->back |= 4; // loop head + } + + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = spadjop(ctxt, p, AADDL, AADDQ); + if(v < 0) { + p->as = spadjop(ctxt, p, ASUBL, ASUBQ); + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; + } + } + + n = 0; + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != nil; p = p->link) { + if(ctxt->headtype == Hnacl && p->isize > 0) { + static LSym *deferreturn; + + if(deferreturn == nil) + deferreturn = linklookup(ctxt, "runtime.deferreturn", 0); + + // pad everything to avoid crossing 32-byte boundary + if((c>>5) != ((c+p->isize-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + // pad call deferreturn to start at 32-byte boundary + // so that subtracting 5 in jmpdefer will jump back + // to that boundary and rerun the call. + if(p->as == ACALL && p->to.sym == deferreturn) + c = naclpad(ctxt, s, c, -c&31); + // pad call to end at 32-byte boundary + if(p->as == ACALL) + c = naclpad(ctxt, s, c, -(c+p->isize)&31); + + // the linker treats REP and STOSQ as different instructions + // but in fact the REP is a prefix on the STOSQ. + // make sure REP has room for 2 more bytes, so that + // padding will not be inserted before the next instruction. + if((p->as == AREP || p->as == AREPN) && (c>>5) != ((c+3-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + + // same for LOCK. + // various instructions follow; the longest is 4 bytes. + // give ourselves 8 bytes so as to avoid surprises. + if(p->as == ALOCK && (c>>5) != ((c+8-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + } + + if((p->back & 4) && (c&(LoopAlign-1)) != 0) { + // pad with NOPs + v = -c&(LoopAlign-1); + if(v <= MaxLoopPad) { + symgrow(ctxt, s, c+v); + fillnop(s->p+c, v); + c += v; + } + } + + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != nil; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + if(q->as == AJCXZL) + s->p[q->pc+2] = v; + else + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp = v>>24; + } + } + p->comefrom = nil; + + p->pc = c; + asmins(ctxt, p); + m = ctxt->andptr-ctxt->and; + if(p->isize != m) { + p->isize = m; + loop++; + } + symgrow(ctxt, s, p->pc+m); + memmove(s->p+p->pc, ctxt->and, m); + p->mark = m; + c += m; + } + if(++n > 20) { + ctxt->diag("span must be looping"); + sysfatal("loop"); + } + } while(loop); + + if(ctxt->headtype == Hnacl) + c = naclpad(ctxt, s, c, -c&31); + + c += -c&(FuncAlign-1); + s->size = c; + + if(0 /* debug['a'] > 1 */) { + print("span1 %s %lld (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; i<s->np; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; i<s->nr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+lld\n", r->off, r->siz, r->sym->name, r->add); + } + } +} + +static void +instinit(void) +{ + int c, i; + + for(i=1; optab[i].as; i++) { + c = optab[i].as; + if(opindex[c] != nil) + sysfatal("phase error in optab: %d (%A)", i, c); + opindex[c] = &optab[i]; + } + + for(i=0; i<Ymax; i++) + ycover[i*Ymax + i] = 1; + + ycover[Yi0*Ymax + Yi8] = 1; + ycover[Yi1*Ymax + Yi8] = 1; + + ycover[Yi0*Ymax + Ys32] = 1; + ycover[Yi1*Ymax + Ys32] = 1; + ycover[Yi8*Ymax + Ys32] = 1; + + ycover[Yi0*Ymax + Yi32] = 1; + ycover[Yi1*Ymax + Yi32] = 1; + ycover[Yi8*Ymax + Yi32] = 1; + ycover[Ys32*Ymax + Yi32] = 1; + + ycover[Yi0*Ymax + Yi64] = 1; + ycover[Yi1*Ymax + Yi64] = 1; + ycover[Yi8*Ymax + Yi64] = 1; + ycover[Ys32*Ymax + Yi64] = 1; + ycover[Yi32*Ymax + Yi64] = 1; + + ycover[Yal*Ymax + Yrb] = 1; + ycover[Ycl*Ymax + Yrb] = 1; + ycover[Yax*Ymax + Yrb] = 1; + ycover[Ycx*Ymax + Yrb] = 1; + ycover[Yrx*Ymax + Yrb] = 1; + ycover[Yrl*Ymax + Yrb] = 1; + + ycover[Ycl*Ymax + Ycx] = 1; + + ycover[Yax*Ymax + Yrx] = 1; + ycover[Ycx*Ymax + Yrx] = 1; + + ycover[Yax*Ymax + Yrl] = 1; + ycover[Ycx*Ymax + Yrl] = 1; + ycover[Yrx*Ymax + Yrl] = 1; + + ycover[Yf0*Ymax + Yrf] = 1; + + ycover[Yal*Ymax + Ymb] = 1; + ycover[Ycl*Ymax + Ymb] = 1; + ycover[Yax*Ymax + Ymb] = 1; + ycover[Ycx*Ymax + Ymb] = 1; + ycover[Yrx*Ymax + Ymb] = 1; + ycover[Yrb*Ymax + Ymb] = 1; + ycover[Yrl*Ymax + Ymb] = 1; + ycover[Ym*Ymax + Ymb] = 1; + + ycover[Yax*Ymax + Yml] = 1; + ycover[Ycx*Ymax + Yml] = 1; + ycover[Yrx*Ymax + Yml] = 1; + ycover[Yrl*Ymax + Yml] = 1; + ycover[Ym*Ymax + Yml] = 1; + + ycover[Yax*Ymax + Ymm] = 1; + ycover[Ycx*Ymax + Ymm] = 1; + ycover[Yrx*Ymax + Ymm] = 1; + ycover[Yrl*Ymax + Ymm] = 1; + ycover[Ym*Ymax + Ymm] = 1; + ycover[Ymr*Ymax + Ymm] = 1; + + ycover[Ym*Ymax + Yxm] = 1; + ycover[Yxr*Ymax + Yxm] = 1; + + for(i=0; i<D_NONE; i++) { + reg[i] = -1; + if(i >= D_AL && i <= D_R15B) { + reg[i] = (i-D_AL) & 7; + if(i >= D_SPB && i <= D_DIB) + regrex[i] = 0x40; + if(i >= D_R8B && i <= D_R15B) + regrex[i] = Rxr | Rxx | Rxb; + } + if(i >= D_AH && i<= D_BH) + reg[i] = 4 + ((i-D_AH) & 7); + if(i >= D_AX && i <= D_R15) { + reg[i] = (i-D_AX) & 7; + if(i >= D_R8) + regrex[i] = Rxr | Rxx | Rxb; + } + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; + if(i >= D_M0 && i <= D_M0+7) + reg[i] = (i-D_M0) & 7; + if(i >= D_X0 && i <= D_X0+15) { + reg[i] = (i-D_X0) & 7; + if(i >= D_X0+8) + regrex[i] = Rxr | Rxx | Rxb; + } + if(i >= D_CR+8 && i <= D_CR+15) + regrex[i] = Rxr; + } +} + +static int +prefixof(Link *ctxt, Addr *a) +{ + switch(a->type) { + case D_INDIR+D_CS: + return 0x2e; + case D_INDIR+D_DS: + return 0x3e; + case D_INDIR+D_ES: + return 0x26; + case D_INDIR+D_FS: + return 0x64; + case D_INDIR+D_GS: + return 0x65; + case D_INDIR+D_TLS: + // NOTE: Systems listed here should be only systems that + // support direct TLS references like 8(TLS) implemented as + // direct references from FS or GS. Systems that require + // the initial-exec model, where you load the TLS base into + // a register and then index from that register, do not reach + // this code and should not be listed. + switch(ctxt->headtype) { + default: + sysfatal("unknown TLS base register for %s", headstr(ctxt->headtype)); + case Hdragonfly: + case Hfreebsd: + case Hlinux: + case Hnetbsd: + case Hopenbsd: + case Hplan9: + case Hsolaris: + return 0x64; // FS + case Hdarwin: + return 0x65; // GS + } + } + switch(a->index) { + case D_CS: + return 0x2e; + case D_DS: + return 0x3e; + case D_ES: + return 0x26; + case D_FS: + return 0x64; + case D_GS: + return 0x65; + } + return 0; +} + +static int +oclass(Link *ctxt, Addr *a) +{ + vlong v; + int32 l; + + if(a->type >= D_INDIR || a->index != D_NONE) { + if(a->index != D_NONE && a->scale == 0) { + if(a->type == D_ADDR) { + switch(a->index) { + case D_EXTERN: + case D_STATIC: + if(ctxt->flag_shared || ctxt->headtype == Hnacl) + return Yiauto; + else + return Yi32; /* TO DO: Yi64 */ + case D_AUTO: + case D_PARAM: + return Yiauto; + } + return Yxxx; + } + return Ycol; + } + return Ym; + } + switch(a->type) + { + case D_AL: + return Yal; + + case D_AX: + return Yax; + +/* + case D_SPB: +*/ + case D_BPB: + case D_SIB: + case D_DIB: + case D_R8B: + case D_R9B: + case D_R10B: + case D_R11B: + case D_R12B: + case D_R13B: + case D_R14B: + case D_R15B: + if(ctxt->asmode != 64) + return Yxxx; + case D_DL: + case D_BL: + case D_AH: + case D_CH: + case D_DH: + case D_BH: + return Yrb; + + case D_CL: + return Ycl; + + case D_CX: + return Ycx; + + case D_DX: + case D_BX: + return Yrx; + + case D_R8: /* not really Yrl */ + case D_R9: + case D_R10: + case D_R11: + case D_R12: + case D_R13: + case D_R14: + case D_R15: + if(ctxt->asmode != 64) + return Yxxx; + case D_SP: + case D_BP: + case D_SI: + case D_DI: + return Yrl; + + case D_F0+0: + return Yf0; + + case D_F0+1: + case D_F0+2: + case D_F0+3: + case D_F0+4: + case D_F0+5: + case D_F0+6: + case D_F0+7: + return Yrf; + + case D_M0+0: + case D_M0+1: + case D_M0+2: + case D_M0+3: + case D_M0+4: + case D_M0+5: + case D_M0+6: + case D_M0+7: + return Ymr; + + case D_X0+0: + case D_X0+1: + case D_X0+2: + case D_X0+3: + case D_X0+4: + case D_X0+5: + case D_X0+6: + case D_X0+7: + case D_X0+8: + case D_X0+9: + case D_X0+10: + case D_X0+11: + case D_X0+12: + case D_X0+13: + case D_X0+14: + case D_X0+15: + return Yxr; + + case D_NONE: + return Ynone; + + case D_CS: return Ycs; + case D_SS: return Yss; + case D_DS: return Yds; + case D_ES: return Yes; + case D_FS: return Yfs; + case D_GS: return Ygs; + case D_TLS: return Ytls; + + case D_GDTR: return Ygdtr; + case D_IDTR: return Yidtr; + case D_LDTR: return Yldtr; + case D_MSW: return Ymsw; + case D_TASK: return Ytask; + + case D_CR+0: return Ycr0; + case D_CR+1: return Ycr1; + case D_CR+2: return Ycr2; + case D_CR+3: return Ycr3; + case D_CR+4: return Ycr4; + case D_CR+5: return Ycr5; + case D_CR+6: return Ycr6; + case D_CR+7: return Ycr7; + case D_CR+8: return Ycr8; + + case D_DR+0: return Ydr0; + case D_DR+1: return Ydr1; + case D_DR+2: return Ydr2; + case D_DR+3: return Ydr3; + case D_DR+4: return Ydr4; + case D_DR+5: return Ydr5; + case D_DR+6: return Ydr6; + case D_DR+7: return Ydr7; + + case D_TR+0: return Ytr0; + case D_TR+1: return Ytr1; + case D_TR+2: return Ytr2; + case D_TR+3: return Ytr3; + case D_TR+4: return Ytr4; + case D_TR+5: return Ytr5; + case D_TR+6: return Ytr6; + case D_TR+7: return Ytr7; + + case D_EXTERN: + case D_STATIC: + case D_AUTO: + case D_PARAM: + return Ym; + + case D_CONST: + case D_ADDR: + if(a->sym == nil) { + v = a->offset; + if(v == 0) + return Yi0; + if(v == 1) + return Yi1; + if(v >= -128 && v <= 127) + return Yi8; + l = v; + if((vlong)l == v) + return Ys32; /* can sign extend */ + if((v>>32) == 0) + return Yi32; /* unsigned */ + return Yi64; + } + return Yi32; /* TO DO: D_ADDR as Yi64 */ + + case D_BRANCH: + return Ybr; + } + return Yxxx; +} + +static void +asmidx(Link *ctxt, int scale, int index, int base) +{ + int i; + + switch(index) { + default: + goto bad; + + case D_NONE: + i = 4 << 3; + goto bas; + + case D_R8: + case D_R9: + case D_R10: + case D_R11: + case D_R12: + case D_R13: + case D_R14: + case D_R15: + if(ctxt->asmode != 64) + goto bad; + case D_AX: + case D_CX: + case D_DX: + case D_BX: + case D_BP: + case D_SI: + case D_DI: + i = reg[index] << 3; + break; + } + switch(scale) { + default: + goto bad; + case 1: + break; + case 2: + i |= (1<<6); + break; + case 4: + i |= (2<<6); + break; + case 8: + i |= (3<<6); + break; + } +bas: + switch(base) { + default: + goto bad; + case D_NONE: /* must be mod=00 */ + i |= 5; + break; + case D_R8: + case D_R9: + case D_R10: + case D_R11: + case D_R12: + case D_R13: + case D_R14: + case D_R15: + if(ctxt->asmode != 64) + goto bad; + case D_AX: + case D_CX: + case D_DX: + case D_BX: + case D_SP: + case D_BP: + case D_SI: + case D_DI: + i |= reg[base]; + break; + } + *ctxt->andptr++ = i; + return; +bad: + ctxt->diag("asmidx: bad address %d/%d/%d", scale, index, base); + *ctxt->andptr++ = 0; + return; +} + +static void +put4(Link *ctxt, int32 v) +{ + ctxt->andptr[0] = v; + ctxt->andptr[1] = v>>8; + ctxt->andptr[2] = v>>16; + ctxt->andptr[3] = v>>24; + ctxt->andptr += 4; +} + +static void +relput4(Link *ctxt, Prog *p, Addr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(ctxt, a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + ctxt->diag("bad reloc"); + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + put4(ctxt, v); +} + +static void +put8(Link *ctxt, vlong v) +{ + ctxt->andptr[0] = v; + ctxt->andptr[1] = v>>8; + ctxt->andptr[2] = v>>16; + ctxt->andptr[3] = v>>24; + ctxt->andptr[4] = v>>32; + ctxt->andptr[5] = v>>40; + ctxt->andptr[6] = v>>48; + ctxt->andptr[7] = v>>56; + ctxt->andptr += 8; +} + +/* +static void +relput8(Prog *p, Addr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(ctxt, a, &rel); + if(rel.siz != 0) { + r = addrel(ctxt->cursym); + *r = rel; + r->siz = 8; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + put8(ctxt, v); +} +*/ + +static vlong +vaddr(Link *ctxt, Addr *a, Reloc *r) +{ + int t; + vlong v; + LSym *s; + + if(r != nil) + memset(r, 0, sizeof *r); + + t = a->type; + v = a->offset; + if(t == D_ADDR) + t = a->index; + switch(t) { + case D_STATIC: + case D_EXTERN: + s = a->sym; + if(r == nil) { + ctxt->diag("need reloc for %D", a); + sysfatal("reloc"); + } + r->siz = 4; // TODO: 8 for external symbols + r->off = -1; // caller must fill in + r->sym = s; + r->add = v; + v = 0; + if(ctxt->flag_shared || ctxt->headtype == Hnacl) { + if(s->type == STLSBSS) { + r->xadd = r->add - r->siz; + r->type = R_TLS; + r->xsym = s; + } else + r->type = R_PCREL; + } else + r->type = R_ADDR; + break; + + case D_INDIR+D_TLS: + if(r == nil) { + ctxt->diag("need reloc for %D", a); + sysfatal("reloc"); + } + r->type = R_TLS_LE; + r->siz = 4; + r->off = -1; // caller must fill in + r->add = v; + v = 0; + break; + } + return v; +} + +static void +asmandsz(Link *ctxt, Addr *a, int r, int rex, int m64) +{ + int32 v; + int t, scale; + Reloc rel; + + USED(m64); + rex &= (0x40 | Rxr); + v = a->offset; + t = a->type; + rel.siz = 0; + if(a->index != D_NONE && a->index != D_TLS) { + if(t < D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + if(ctxt->flag_shared || ctxt->headtype == Hnacl) + goto bad; + t = D_NONE; + v = vaddr(ctxt, a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + } else + t -= D_INDIR; + ctxt->rexflag |= (regrex[(int)a->index] & Rxx) | (regrex[t] & Rxb) | rex; + if(t == D_NONE) { + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + goto putrelv; + } + if(v == 0 && rel.siz == 0 && t != D_BP && t != D_R13) { + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + *ctxt->andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + *ctxt->andptr++ = v; + return; + } + *ctxt->andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + goto putrelv; + } + if(t >= D_AL && t <= D_X0+15) { + if(v) + goto bad; + *ctxt->andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); + ctxt->rexflag |= (regrex[t] & (0x40 | Rxb)) | rex; + return; + } + + scale = a->scale; + if(t < D_INDIR) { + switch(a->type) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(ctxt, a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + scale = 1; + } else + t -= D_INDIR; + if(t == D_TLS) + v = vaddr(ctxt, a, &rel); + + ctxt->rexflag |= (regrex[t] & Rxb) | rex; + if(t == D_NONE || (D_CS <= t && t <= D_GS) || t == D_TLS) { + if((ctxt->flag_shared || ctxt->headtype == Hnacl) && t == D_NONE && (a->type == D_STATIC || a->type == D_EXTERN) || ctxt->asmode != 64) { + *ctxt->andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + /* temporary */ + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); /* sib present */ + *ctxt->andptr++ = (0 << 6) | (4 << 3) | (5 << 0); /* DS:d32 */ + goto putrelv; + } + if(t == D_SP || t == D_R12) { + if(v == 0) { + *ctxt->andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + return; + } + if(v >= -128 && v < 128) { + *ctxt->andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + *ctxt->andptr++ = v; + return; + } + *ctxt->andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_R15) { + if(a->index == D_TLS) { + memset(&rel, 0, sizeof rel); + rel.type = R_TLS_IE; + rel.siz = 4; + rel.sym = nil; + rel.add = v; + v = 0; + } + if(v == 0 && rel.siz == 0 && t != D_BP && t != D_R13) { + *ctxt->andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + ctxt->andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + ctxt->andptr[1] = v; + ctxt->andptr += 2; + return; + } + *ctxt->andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; + } + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + ctxt->diag("bad rel"); + goto bad; + } + r = addrel(ctxt->cursym); + *r = rel; + r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and; + } + + put4(ctxt, v); + return; + +bad: + ctxt->diag("asmand: bad address %D", a); + return; +} + +static void +asmand(Link *ctxt, Addr *a, Addr *ra) +{ + asmandsz(ctxt, a, reg[ra->type], regrex[ra->type], 0); +} + +static void +asmando(Link *ctxt, Addr *a, int o) +{ + asmandsz(ctxt, a, o, 0, 0); +} + +static void +bytereg(Addr *a, char *t) +{ + if(a->index == D_NONE && (a->type >= D_AX && a->type <= D_R15)) { + a->type = D_AL + (a->type-D_AX); + *t = 0; + } +} + +#define E 0xff +static Movtab ymovtab[] = +{ +/* push */ + {APUSHL, Ycs, Ynone, 0, 0x0e,E,0,0}, + {APUSHL, Yss, Ynone, 0, 0x16,E,0,0}, + {APUSHL, Yds, Ynone, 0, 0x1e,E,0,0}, + {APUSHL, Yes, Ynone, 0, 0x06,E,0,0}, + {APUSHL, Yfs, Ynone, 0, 0x0f,0xa0,E,0}, + {APUSHL, Ygs, Ynone, 0, 0x0f,0xa8,E,0}, + {APUSHQ, Yfs, Ynone, 0, 0x0f,0xa0,E,0}, + {APUSHQ, Ygs, Ynone, 0, 0x0f,0xa8,E,0}, + + {APUSHW, Ycs, Ynone, 0, Pe,0x0e,E,0}, + {APUSHW, Yss, Ynone, 0, Pe,0x16,E,0}, + {APUSHW, Yds, Ynone, 0, Pe,0x1e,E,0}, + {APUSHW, Yes, Ynone, 0, Pe,0x06,E,0}, + {APUSHW, Yfs, Ynone, 0, Pe,0x0f,0xa0,E}, + {APUSHW, Ygs, Ynone, 0, Pe,0x0f,0xa8,E}, + +/* pop */ + {APOPL, Ynone, Yds, 0, 0x1f,E,0,0}, + {APOPL, Ynone, Yes, 0, 0x07,E,0,0}, + {APOPL, Ynone, Yss, 0, 0x17,E,0,0}, + {APOPL, Ynone, Yfs, 0, 0x0f,0xa1,E,0}, + {APOPL, Ynone, Ygs, 0, 0x0f,0xa9,E,0}, + {APOPQ, Ynone, Yfs, 0, 0x0f,0xa1,E,0}, + {APOPQ, Ynone, Ygs, 0, 0x0f,0xa9,E,0}, + + {APOPW, Ynone, Yds, 0, Pe,0x1f,E,0}, + {APOPW, Ynone, Yes, 0, Pe,0x07,E,0}, + {APOPW, Ynone, Yss, 0, Pe,0x17,E,0}, + {APOPW, Ynone, Yfs, 0, Pe,0x0f,0xa1,E}, + {APOPW, Ynone, Ygs, 0, Pe,0x0f,0xa9,E}, + +/* mov seg */ + {AMOVW, Yes, Yml, 1, 0x8c,0,0,0}, + {AMOVW, Ycs, Yml, 1, 0x8c,1,0,0}, + {AMOVW, Yss, Yml, 1, 0x8c,2,0,0}, + {AMOVW, Yds, Yml, 1, 0x8c,3,0,0}, + {AMOVW, Yfs, Yml, 1, 0x8c,4,0,0}, + {AMOVW, Ygs, Yml, 1, 0x8c,5,0,0}, + + {AMOVW, Yml, Yes, 2, 0x8e,0,0,0}, + {AMOVW, Yml, Ycs, 2, 0x8e,1,0,0}, + {AMOVW, Yml, Yss, 2, 0x8e,2,0,0}, + {AMOVW, Yml, Yds, 2, 0x8e,3,0,0}, + {AMOVW, Yml, Yfs, 2, 0x8e,4,0,0}, + {AMOVW, Yml, Ygs, 2, 0x8e,5,0,0}, + +/* mov cr */ + {AMOVL, Ycr0, Yml, 3, 0x0f,0x20,0,0}, + {AMOVL, Ycr2, Yml, 3, 0x0f,0x20,2,0}, + {AMOVL, Ycr3, Yml, 3, 0x0f,0x20,3,0}, + {AMOVL, Ycr4, Yml, 3, 0x0f,0x20,4,0}, + {AMOVL, Ycr8, Yml, 3, 0x0f,0x20,8,0}, + {AMOVQ, Ycr0, Yml, 3, 0x0f,0x20,0,0}, + {AMOVQ, Ycr2, Yml, 3, 0x0f,0x20,2,0}, + {AMOVQ, Ycr3, Yml, 3, 0x0f,0x20,3,0}, + {AMOVQ, Ycr4, Yml, 3, 0x0f,0x20,4,0}, + {AMOVQ, Ycr8, Yml, 3, 0x0f,0x20,8,0}, + + {AMOVL, Yml, Ycr0, 4, 0x0f,0x22,0,0}, + {AMOVL, Yml, Ycr2, 4, 0x0f,0x22,2,0}, + {AMOVL, Yml, Ycr3, 4, 0x0f,0x22,3,0}, + {AMOVL, Yml, Ycr4, 4, 0x0f,0x22,4,0}, + {AMOVL, Yml, Ycr8, 4, 0x0f,0x22,8,0}, + {AMOVQ, Yml, Ycr0, 4, 0x0f,0x22,0,0}, + {AMOVQ, Yml, Ycr2, 4, 0x0f,0x22,2,0}, + {AMOVQ, Yml, Ycr3, 4, 0x0f,0x22,3,0}, + {AMOVQ, Yml, Ycr4, 4, 0x0f,0x22,4,0}, + {AMOVQ, Yml, Ycr8, 4, 0x0f,0x22,8,0}, + +/* mov dr */ + {AMOVL, Ydr0, Yml, 3, 0x0f,0x21,0,0}, + {AMOVL, Ydr6, Yml, 3, 0x0f,0x21,6,0}, + {AMOVL, Ydr7, Yml, 3, 0x0f,0x21,7,0}, + {AMOVQ, Ydr0, Yml, 3, 0x0f,0x21,0,0}, + {AMOVQ, Ydr6, Yml, 3, 0x0f,0x21,6,0}, + {AMOVQ, Ydr7, Yml, 3, 0x0f,0x21,7,0}, + + {AMOVL, Yml, Ydr0, 4, 0x0f,0x23,0,0}, + {AMOVL, Yml, Ydr6, 4, 0x0f,0x23,6,0}, + {AMOVL, Yml, Ydr7, 4, 0x0f,0x23,7,0}, + {AMOVQ, Yml, Ydr0, 4, 0x0f,0x23,0,0}, + {AMOVQ, Yml, Ydr6, 4, 0x0f,0x23,6,0}, + {AMOVQ, Yml, Ydr7, 4, 0x0f,0x23,7,0}, + +/* mov tr */ + {AMOVL, Ytr6, Yml, 3, 0x0f,0x24,6,0}, + {AMOVL, Ytr7, Yml, 3, 0x0f,0x24,7,0}, + + {AMOVL, Yml, Ytr6, 4, 0x0f,0x26,6,E}, + {AMOVL, Yml, Ytr7, 4, 0x0f,0x26,7,E}, + +/* lgdt, sgdt, lidt, sidt */ + {AMOVL, Ym, Ygdtr, 4, 0x0f,0x01,2,0}, + {AMOVL, Ygdtr, Ym, 3, 0x0f,0x01,0,0}, + {AMOVL, Ym, Yidtr, 4, 0x0f,0x01,3,0}, + {AMOVL, Yidtr, Ym, 3, 0x0f,0x01,1,0}, + {AMOVQ, Ym, Ygdtr, 4, 0x0f,0x01,2,0}, + {AMOVQ, Ygdtr, Ym, 3, 0x0f,0x01,0,0}, + {AMOVQ, Ym, Yidtr, 4, 0x0f,0x01,3,0}, + {AMOVQ, Yidtr, Ym, 3, 0x0f,0x01,1,0}, + +/* lldt, sldt */ + {AMOVW, Yml, Yldtr, 4, 0x0f,0x00,2,0}, + {AMOVW, Yldtr, Yml, 3, 0x0f,0x00,0,0}, + +/* lmsw, smsw */ + {AMOVW, Yml, Ymsw, 4, 0x0f,0x01,6,0}, + {AMOVW, Ymsw, Yml, 3, 0x0f,0x01,4,0}, + +/* ltr, str */ + {AMOVW, Yml, Ytask, 4, 0x0f,0x00,3,0}, + {AMOVW, Ytask, Yml, 3, 0x0f,0x00,1,0}, + +/* load full pointer */ + {AMOVL, Yml, Ycol, 5, 0,0,0,0}, + {AMOVW, Yml, Ycol, 5, Pe,0,0,0}, + +/* double shift */ + {ASHLL, Ycol, Yml, 6, 0xa4,0xa5,0,0}, + {ASHRL, Ycol, Yml, 6, 0xac,0xad,0,0}, + {ASHLQ, Ycol, Yml, 6, Pw,0xa4,0xa5,0}, + {ASHRQ, Ycol, Yml, 6, Pw,0xac,0xad,0}, + {ASHLW, Ycol, Yml, 6, Pe,0xa4,0xa5,0}, + {ASHRW, Ycol, Yml, 6, Pe,0xac,0xad,0}, + +/* load TLS base */ + {AMOVQ, Ytls, Yrl, 7, 0,0,0,0}, + + 0 +}; + +static int +isax(Addr *a) +{ + + switch(a->type) { + case D_AX: + case D_AL: + case D_AH: + case D_INDIR+D_AX: + return 1; + } + if(a->index == D_AX) + return 1; + return 0; +} + +static void +subreg(Prog *p, int from, int to) +{ + + if(0 /*debug['Q']*/) + print("\n%P s/%R/%R/\n", p, from, to); + + if(p->from.type == from) + p->from.type = to; + if(p->to.type == from) + p->to.type = to; + + if(p->from.index == from) + p->from.index = to; + if(p->to.index == from) + p->to.index = to; + + from += D_INDIR; + if(p->from.type == from) + p->from.type = to+D_INDIR; + if(p->to.type == from) + p->to.type = to+D_INDIR; + + if(0 /*debug['Q']*/) + print("%P\n", p); +} + +static int +mediaop(Link *ctxt, Optab *o, int op, int osize, int z) +{ + switch(op){ + case Pm: + case Pe: + case Pf2: + case Pf3: + if(osize != 1){ + if(op != Pm) + *ctxt->andptr++ = op; + *ctxt->andptr++ = Pm; + op = o->op[++z]; + break; + } + default: + if(ctxt->andptr == ctxt->and || ctxt->andptr[-1] != Pm) + *ctxt->andptr++ = Pm; + break; + } + *ctxt->andptr++ = op; + return z; +} + +static void +doasm(Link *ctxt, Prog *p) +{ + Optab *o; + Prog *q, pp; + uchar *t; + Movtab *mo; + int z, op, ft, tt, xo, l, pre; + vlong v; + Reloc rel, *r; + Addr *a; + + ctxt->curp = p; // TODO + + o = opindex[p->as]; + if(o == nil) { + ctxt->diag("asmins: missing op %P", p); + return; + } + + pre = prefixof(ctxt, &p->from); + if(pre) + *ctxt->andptr++ = pre; + pre = prefixof(ctxt, &p->to); + if(pre) + *ctxt->andptr++ = pre; + + if(p->ft == 0) + p->ft = oclass(ctxt, &p->from); + if(p->tt == 0) + p->tt = oclass(ctxt, &p->to); + + ft = p->ft * Ymax; + tt = p->tt * Ymax; + + t = o->ytab; + if(t == 0) { + ctxt->diag("asmins: noproto %P", p); + return; + } + xo = o->op[0] == 0x0f; + for(z=0; *t; z+=t[3]+xo,t+=4) + if(ycover[ft+t[0]]) + if(ycover[tt+t[1]]) + goto found; + goto domov; + +found: + switch(o->prefix) { + case Pq: /* 16 bit escape and opcode escape */ + *ctxt->andptr++ = Pe; + *ctxt->andptr++ = Pm; + break; + case Pq3: /* 16 bit escape, Rex.w, and opcode escape */ + *ctxt->andptr++ = Pe; + *ctxt->andptr++ = Pw; + *ctxt->andptr++ = Pm; + break; + + case Pf2: /* xmm opcode escape */ + case Pf3: + *ctxt->andptr++ = o->prefix; + *ctxt->andptr++ = Pm; + break; + + case Pm: /* opcode escape */ + *ctxt->andptr++ = Pm; + break; + + case Pe: /* 16 bit escape */ + *ctxt->andptr++ = Pe; + break; + + case Pw: /* 64-bit escape */ + if(p->mode != 64) + ctxt->diag("asmins: illegal 64: %P", p); + ctxt->rexflag |= Pw; + break; + + case Pb: /* botch */ + bytereg(&p->from, &p->ft); + bytereg(&p->to, &p->tt); + break; + + case P32: /* 32 bit but illegal if 64-bit mode */ + if(p->mode == 64) + ctxt->diag("asmins: illegal in 64-bit mode: %P", p); + break; + + case Py: /* 64-bit only, no prefix */ + if(p->mode != 64) + ctxt->diag("asmins: illegal in %d-bit mode: %P", p->mode, p); + break; + } + + if(z >= nelem(o->op)) + sysfatal("asmins bad table %P", p); + op = o->op[z]; + if(op == 0x0f) { + *ctxt->andptr++ = op; + op = o->op[++z]; + } + switch(t[2]) { + default: + ctxt->diag("asmins: unknown z %d %P", t[2], p); + return; + + case Zpseudo: + break; + + case Zlit: + for(; op = o->op[z]; z++) + *ctxt->andptr++ = op; + break; + + case Zlitm_r: + for(; op = o->op[z]; z++) + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, &p->to); + break; + + case Zmb_r: + bytereg(&p->from, &p->ft); + /* fall through */ + case Zm_r: + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, &p->to); + break; + case Zm2_r: + *ctxt->andptr++ = op; + *ctxt->andptr++ = o->op[z+1]; + asmand(ctxt, &p->from, &p->to); + break; + + case Zm_r_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->from, &p->to); + break; + + case Zm_r_xm_nr: + ctxt->rexflag = 0; + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->from, &p->to); + break; + + case Zm_r_i_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->from, &p->to); + *ctxt->andptr++ = p->to.offset; + break; + + case Zm_r_3d: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0x0f; + asmand(ctxt, &p->from, &p->to); + *ctxt->andptr++ = op; + break; + + case Zibm_r: + while ((op = o->op[z++]) != 0) + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, &p->to); + *ctxt->andptr++ = p->to.offset; + break; + + case Zaut_r: + *ctxt->andptr++ = 0x8d; /* leal */ + if(p->from.type != D_ADDR) + ctxt->diag("asmins: Zaut sb type ADDR"); + p->from.type = p->from.index; + p->from.index = D_NONE; + asmand(ctxt, &p->from, &p->to); + p->from.index = p->from.type; + p->from.type = D_ADDR; + break; + + case Zm_o: + *ctxt->andptr++ = op; + asmando(ctxt, &p->from, o->op[z+1]); + break; + + case Zr_m: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, &p->from); + break; + + case Zr_m_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->to, &p->from); + break; + + case Zr_m_xm_nr: + ctxt->rexflag = 0; + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->to, &p->from); + break; + + case Zr_m_i_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->to, &p->from); + *ctxt->andptr++ = p->from.offset; + break; + + case Zo_m: + *ctxt->andptr++ = op; + asmando(ctxt, &p->to, o->op[z+1]); + break; + + case Zo_m64: + case_Zo_m64: + *ctxt->andptr++ = op; + asmandsz(ctxt, &p->to, o->op[z+1], 0, 1); + break; + + case Zm_ibo: + *ctxt->andptr++ = op; + asmando(ctxt, &p->from, o->op[z+1]); + *ctxt->andptr++ = vaddr(ctxt, &p->to, nil); + break; + + case Zibo_m: + *ctxt->andptr++ = op; + asmando(ctxt, &p->to, o->op[z+1]); + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Zibo_m_xm: + z = mediaop(ctxt, o, op, t[3], z); + asmando(ctxt, &p->to, o->op[z+1]); + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Z_ib: + case Zib_: + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; + *ctxt->andptr++ = op; + *ctxt->andptr++ = vaddr(ctxt, a, nil); + break; + + case Zib_rp: + ctxt->rexflag |= regrex[p->to.type] & (Rxb|0x40); + *ctxt->andptr++ = op + reg[p->to.type]; + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Zil_rp: + ctxt->rexflag |= regrex[p->to.type] & Rxb; + *ctxt->andptr++ = op + reg[p->to.type]; + if(o->prefix == Pe) { + v = vaddr(ctxt, &p->from, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, &p->from); + break; + + case Zo_iw: + *ctxt->andptr++ = op; + if(p->from.type != D_NONE){ + v = vaddr(ctxt, &p->from, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + break; + + case Ziq_rp: + v = vaddr(ctxt, &p->from, &rel); + l = v>>32; + if(l == 0 && rel.siz != 8){ + //p->mark |= 0100; + //print("zero: %llux %P\n", v, p); + ctxt->rexflag &= ~(0x40|Rxw); + ctxt->rexflag |= regrex[p->to.type] & Rxb; + *ctxt->andptr++ = 0xb8 + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + put4(ctxt, v); + }else if(l == -1 && (v&((uvlong)1<<31))!=0){ /* sign extend */ + //p->mark |= 0100; + //print("sign: %llux %P\n", v, p); + *ctxt->andptr ++ = 0xc7; + asmando(ctxt, &p->to, 0); + put4(ctxt, v); + }else{ /* need all 8 */ + //print("all: %llux %P\n", v, p); + ctxt->rexflag |= regrex[p->to.type] & Rxb; + *ctxt->andptr++ = op + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + put8(ctxt, v); + } + break; + + case Zib_rr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, &p->to); + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Z_il: + case Zil_: + if(t[2] == Zil_) + a = &p->from; + else + a = &p->to; + *ctxt->andptr++ = op; + if(o->prefix == Pe) { + v = vaddr(ctxt, a, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, a); + break; + + case Zm_ilo: + case Zilo_m: + *ctxt->andptr++ = op; + if(t[2] == Zilo_m) { + a = &p->from; + asmando(ctxt, &p->to, o->op[z+1]); + } else { + a = &p->to; + asmando(ctxt, &p->from, o->op[z+1]); + } + if(o->prefix == Pe) { + v = vaddr(ctxt, a, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, a); + break; + + case Zil_rr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, &p->to); + if(o->prefix == Pe) { + v = vaddr(ctxt, &p->from, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, &p->from); + break; + + case Z_rp: + ctxt->rexflag |= regrex[p->to.type] & (Rxb|0x40); + *ctxt->andptr++ = op + reg[p->to.type]; + break; + + case Zrp_: + ctxt->rexflag |= regrex[p->from.type] & (Rxb|0x40); + *ctxt->andptr++ = op + reg[p->from.type]; + break; + + case Zclr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, &p->to); + break; + + case Zcall: + if(p->to.sym == nil) { + ctxt->diag("call without target"); + sysfatal("bad code"); + } + *ctxt->andptr++ = op; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->sym = p->to.sym; + r->add = p->to.offset; + r->type = R_CALL; + r->siz = 4; + put4(ctxt, 0); + break; + + case Zcallindreg: + r = addrel(ctxt->cursym); + r->off = p->pc; + r->type = R_CALLIND; + r->siz = 0; + goto case_Zo_m64; + + case Zbr: + case Zjmp: + case Zloop: + // TODO: jump across functions needs reloc + if(p->to.sym != nil) { + if(t[2] != Zjmp) { + ctxt->diag("branch to ATEXT"); + sysfatal("bad code"); + } + *ctxt->andptr++ = o->op[z+1]; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->sym = p->to.sym; + r->type = R_PCREL; + r->siz = 4; + put4(ctxt, 0); + break; + } + // Assumes q is in this function. + // TODO: Check in input, preserve in brchain. + + // Fill in backward jump now. + q = p->pcond; + if(q == nil) { + ctxt->diag("jmp/branch/loop without target"); + sysfatal("bad code"); + } + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { + if(p->as == AJCXZL) + *ctxt->andptr++ = 0x67; + *ctxt->andptr++ = op; + *ctxt->andptr++ = v; + } else if(t[2] == Zloop) { + ctxt->diag("loop too far: %P", p); + } else { + v -= 5-2; + if(t[2] == Zbr) { + *ctxt->andptr++ = 0x0f; + v--; + } + *ctxt->andptr++ = o->op[z+1]; + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + *ctxt->andptr++ = v>>16; + *ctxt->andptr++ = v>>24; + } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + if(p->as == AJCXZL) + *ctxt->andptr++ = 0x67; + *ctxt->andptr++ = op; + *ctxt->andptr++ = 0; + } else if(t[2] == Zloop) { + ctxt->diag("loop too far: %P", p); + } else { + if(t[2] == Zbr) + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = o->op[z+1]; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + } + break; + +/* + v = q->pc - p->pc - 2; + if((v >= -128 && v <= 127) || p->pc == -1 || q->pc == -1) { + *ctxt->andptr++ = op; + *ctxt->andptr++ = v; + } else { + v -= 5-2; + if(t[2] == Zbr) { + *ctxt->andptr++ = 0x0f; + v--; + } + *ctxt->andptr++ = o->op[z+1]; + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + *ctxt->andptr++ = v>>16; + *ctxt->andptr++ = v>>24; + } +*/ + break; + + case Zbyte: + v = vaddr(ctxt, &p->from, &rel); + if(rel.siz != 0) { + rel.siz = op; + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + *ctxt->andptr++ = v; + if(op > 1) { + *ctxt->andptr++ = v>>8; + if(op > 2) { + *ctxt->andptr++ = v>>16; + *ctxt->andptr++ = v>>24; + if(op > 4) { + *ctxt->andptr++ = v>>32; + *ctxt->andptr++ = v>>40; + *ctxt->andptr++ = v>>48; + *ctxt->andptr++ = v>>56; + } + } + } + break; + } + return; + +domov: + for(mo=ymovtab; mo->as; mo++) + if(p->as == mo->as) + if(ycover[ft+mo->ft]) + if(ycover[tt+mo->tt]){ + t = mo->op; + goto mfound; + } +bad: + if(p->mode != 64){ + /* + * here, the assembly has failed. + * if its a byte instruction that has + * unaddressable registers, try to + * exchange registers and reissue the + * instruction with the operands renamed. + */ + pp = *p; + z = p->from.type; + if(z >= D_BP && z <= D_DI) { + if(isax(&p->to) || p->to.type == D_NONE) { + // We certainly don't want to exchange + // with AX if the op is MUL or DIV. + *ctxt->andptr++ = 0x87; /* xchg lhs,bx */ + asmando(ctxt, &p->from, reg[D_BX]); + subreg(&pp, z, D_BX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x87; /* xchg lhs,bx */ + asmando(ctxt, &p->from, reg[D_BX]); + } else { + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ + subreg(&pp, z, D_AX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ + } + return; + } + z = p->to.type; + if(z >= D_BP && z <= D_DI) { + if(isax(&p->from)) { + *ctxt->andptr++ = 0x87; /* xchg rhs,bx */ + asmando(ctxt, &p->to, reg[D_BX]); + subreg(&pp, z, D_BX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x87; /* xchg rhs,bx */ + asmando(ctxt, &p->to, reg[D_BX]); + } else { + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ + subreg(&pp, z, D_AX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ + } + return; + } + } + ctxt->diag("doasm: notfound from=%ux to=%ux %P", p->from.type, p->to.type, p); + return; + +mfound: + switch(mo->code) { + default: + ctxt->diag("asmins: unknown mov %d %P", mo->code, p); + break; + + case 0: /* lit */ + for(z=0; t[z]!=E; z++) + *ctxt->andptr++ = t[z]; + break; + + case 1: /* r,m */ + *ctxt->andptr++ = t[0]; + asmando(ctxt, &p->to, t[1]); + break; + + case 2: /* m,r */ + *ctxt->andptr++ = t[0]; + asmando(ctxt, &p->from, t[1]); + break; + + case 3: /* r,m - 2op */ + *ctxt->andptr++ = t[0]; + *ctxt->andptr++ = t[1]; + asmando(ctxt, &p->to, t[2]); + ctxt->rexflag |= regrex[p->from.type] & (Rxr|0x40); + break; + + case 4: /* m,r - 2op */ + *ctxt->andptr++ = t[0]; + *ctxt->andptr++ = t[1]; + asmando(ctxt, &p->from, t[2]); + ctxt->rexflag |= regrex[p->to.type] & (Rxr|0x40); + break; + + case 5: /* load full pointer, trash heap */ + if(t[0]) + *ctxt->andptr++ = t[0]; + switch(p->to.index) { + default: + goto bad; + case D_DS: + *ctxt->andptr++ = 0xc5; + break; + case D_SS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb2; + break; + case D_ES: + *ctxt->andptr++ = 0xc4; + break; + case D_FS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb4; + break; + case D_GS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb5; + break; + } + asmand(ctxt, &p->from, &p->to); + break; + + case 6: /* double shift */ + if(t[0] == Pw){ + if(p->mode != 64) + ctxt->diag("asmins: illegal 64: %P", p); + ctxt->rexflag |= Pw; + t++; + }else if(t[0] == Pe){ + *ctxt->andptr++ = Pe; + t++; + } + z = p->from.type; + switch(z) { + default: + goto bad; + case D_CONST: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = t[0]; + asmandsz(ctxt, &p->to, reg[(int)p->from.index], regrex[(int)p->from.index], 0); + *ctxt->andptr++ = p->from.offset; + break; + case D_CL: + case D_CX: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = t[1]; + asmandsz(ctxt, &p->to, reg[(int)p->from.index], regrex[(int)p->from.index], 0); + break; + } + break; + + case 7: /* mov tls, r */ + // NOTE: The systems listed here are the ones that use the "TLS initial exec" model, + // where you load the TLS base register into a register and then index off that + // register to access the actual TLS variables. Systems that allow direct TLS access + // are handled in prefixof above and should not be listed here. + switch(ctxt->headtype) { + default: + sysfatal("unknown TLS base location for %s", headstr(ctxt->headtype)); + + case Hsolaris: // TODO(rsc): Delete Hsolaris from list. Should not use this code. See progedit in obj6.c. + // TLS base is 0(FS). + pp.from = p->from; + pp.from.type = D_INDIR+D_NONE; + pp.from.offset = 0; + pp.from.index = D_NONE; + pp.from.scale = 0; + ctxt->rexflag |= Pw; + *ctxt->andptr++ = 0x64; // FS + *ctxt->andptr++ = 0x8B; + asmand(ctxt, &pp.from, &p->to); + break; + + case Hwindows: + // Windows TLS base is always 0x28(GS). + pp.from = p->from; + pp.from.type = D_INDIR+D_GS; + pp.from.offset = 0x28; + pp.from.index = D_NONE; + pp.from.scale = 0; + ctxt->rexflag |= Pw; + *ctxt->andptr++ = 0x65; // GS + *ctxt->andptr++ = 0x8B; + asmand(ctxt, &pp.from, &p->to); + break; + } + break; + } +} + +static uchar naclret[] = { + 0x5e, // POPL SI + // 0x8b, 0x7d, 0x00, // MOVL (BP), DI - catch return to invalid address, for debugging + 0x83, 0xe6, 0xe0, // ANDL $~31, SI + 0x4c, 0x01, 0xfe, // ADDQ R15, SI + 0xff, 0xe6, // JMP SI +}; + +static uchar naclspfix[] = { + 0x4c, 0x01, 0xfc, // ADDQ R15, SP +}; + +static uchar naclbpfix[] = { + 0x4c, 0x01, 0xfd, // ADDQ R15, BP +}; + +static uchar naclmovs[] = { + 0x89, 0xf6, // MOVL SI, SI + 0x49, 0x8d, 0x34, 0x37, // LEAQ (R15)(SI*1), SI + 0x89, 0xff, // MOVL DI, DI + 0x49, 0x8d, 0x3c, 0x3f, // LEAQ (R15)(DI*1), DI +}; + +static uchar naclstos[] = { + 0x89, 0xff, // MOVL DI, DI + 0x49, 0x8d, 0x3c, 0x3f, // LEAQ (R15)(DI*1), DI +}; + +static void +nacltrunc(Link *ctxt, int reg) +{ + if(reg >= D_R8) + *ctxt->andptr++ = 0x45; + reg = (reg - D_AX) & 7; + *ctxt->andptr++ = 0x89; + *ctxt->andptr++ = (3<<6) | (reg<<3) | reg; +} + +static void +asmins(Link *ctxt, Prog *p) +{ + int n, np, c; + uchar *and0; + Reloc *r; + + ctxt->andptr = ctxt->and; + ctxt->asmode = p->mode; + + if(p->as == AUSEFIELD) { + r = addrel(ctxt->cursym); + r->off = 0; + r->siz = 0; + r->sym = p->from.sym; + r->type = R_USEFIELD; + return; + } + + if(ctxt->headtype == Hnacl) { + if(p->as == AREP) { + ctxt->rep++; + return; + } + if(p->as == AREPN) { + ctxt->repn++; + return; + } + if(p->as == ALOCK) { + ctxt->lock++; + return; + } + if(p->as != ALEAQ && p->as != ALEAL) { + if(p->from.index != D_NONE && p->from.scale > 0) + nacltrunc(ctxt, p->from.index); + if(p->to.index != D_NONE && p->to.scale > 0) + nacltrunc(ctxt, p->to.index); + } + switch(p->as) { + case ARET: + memmove(ctxt->andptr, naclret, sizeof naclret); + ctxt->andptr += sizeof naclret; + return; + case ACALL: + case AJMP: + if(D_AX <= p->to.type && p->to.type <= D_DI) { + // ANDL $~31, reg + *ctxt->andptr++ = 0x83; + *ctxt->andptr++ = 0xe0 | (p->to.type - D_AX); + *ctxt->andptr++ = 0xe0; + // ADDQ R15, reg + *ctxt->andptr++ = 0x4c; + *ctxt->andptr++ = 0x01; + *ctxt->andptr++ = 0xf8 | (p->to.type - D_AX); + } + if(D_R8 <= p->to.type && p->to.type <= D_R15) { + // ANDL $~31, reg + *ctxt->andptr++ = 0x41; + *ctxt->andptr++ = 0x83; + *ctxt->andptr++ = 0xe0 | (p->to.type - D_R8); + *ctxt->andptr++ = 0xe0; + // ADDQ R15, reg + *ctxt->andptr++ = 0x4d; + *ctxt->andptr++ = 0x01; + *ctxt->andptr++ = 0xf8 | (p->to.type - D_R8); + } + break; + case AINT: + *ctxt->andptr++ = 0xf4; + return; + case ASCASB: + case ASCASW: + case ASCASL: + case ASCASQ: + case ASTOSB: + case ASTOSW: + case ASTOSL: + case ASTOSQ: + memmove(ctxt->andptr, naclstos, sizeof naclstos); + ctxt->andptr += sizeof naclstos; + break; + case AMOVSB: + case AMOVSW: + case AMOVSL: + case AMOVSQ: + memmove(ctxt->andptr, naclmovs, sizeof naclmovs); + ctxt->andptr += sizeof naclmovs; + break; + } + if(ctxt->rep) { + *ctxt->andptr++ = 0xf3; + ctxt->rep = 0; + } + if(ctxt->repn) { + *ctxt->andptr++ = 0xf2; + ctxt->repn = 0; + } + if(ctxt->lock) { + *ctxt->andptr++ = 0xf0; + ctxt->lock = 0; + } + } + + ctxt->rexflag = 0; + and0 = ctxt->andptr; + ctxt->asmode = p->mode; + doasm(ctxt, p); + if(ctxt->rexflag){ + /* + * as befits the whole approach of the architecture, + * the rex prefix must appear before the first opcode byte + * (and thus after any 66/67/f2/f3/26/2e/3e prefix bytes, but + * before the 0f opcode escape!), or it might be ignored. + * note that the handbook often misleadingly shows 66/f2/f3 in `opcode'. + */ + if(p->mode != 64) + ctxt->diag("asmins: illegal in mode %d: %P", p->mode, p); + n = ctxt->andptr - and0; + for(np = 0; np < n; np++) { + c = and0[np]; + if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) + break; + } + memmove(and0+np+1, and0+np, n-np); + and0[np] = 0x40 | ctxt->rexflag; + ctxt->andptr++; + } + n = ctxt->andptr - ctxt->and; + for(r=ctxt->cursym->r+ctxt->cursym->nr; r-- > ctxt->cursym->r; ) { + if(r->off < p->pc) + break; + if(ctxt->rexflag) + r->off++; + if(r->type == R_PCREL || r->type == R_CALL) + r->add -= p->pc + n - (r->off + r->siz); + } + + if(ctxt->headtype == Hnacl && p->as != ACMPL && p->as != ACMPQ) { + switch(p->to.type) { + case D_SP: + memmove(ctxt->andptr, naclspfix, sizeof naclspfix); + ctxt->andptr += sizeof naclspfix; + break; + case D_BP: + memmove(ctxt->andptr, naclbpfix, sizeof naclbpfix); + ctxt->andptr += sizeof naclbpfix; + break; + } + } +} diff --git a/src/liblink/asm8.c b/src/liblink/asm8.c new file mode 100644 index 000000000..3ab527ce8 --- /dev/null +++ b/src/liblink/asm8.c @@ -0,0 +1,2785 @@ +// Inferno utils/8l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/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/8l/8.out.h" +#include "../pkg/runtime/stack.h" + +enum +{ + MaxAlign = 32, // max data alignment + FuncAlign = 16 +}; + +extern char *anames6[]; + +typedef struct Optab Optab; + +struct Optab +{ + short as; + uchar* ytab; + uchar prefix; + uchar op[13]; +}; + +enum +{ + Yxxx = 0, + Ynone, + Yi0, + Yi1, + Yi8, + Yi32, + Yiauto, + Yal, + Ycl, + Yax, + Ycx, + Yrb, + Yrl, + Yrf, + Yf0, + Yrx, + Ymb, + Yml, + Ym, + Ybr, + Ycol, + Ytls, + + Ycs, Yss, Yds, Yes, Yfs, Ygs, + Ygdtr, Yidtr, Yldtr, Ymsw, Ytask, + Ycr0, Ycr1, Ycr2, Ycr3, Ycr4, Ycr5, Ycr6, Ycr7, + Ydr0, Ydr1, Ydr2, Ydr3, Ydr4, Ydr5, Ydr6, Ydr7, + Ytr0, Ytr1, Ytr2, Ytr3, Ytr4, Ytr5, Ytr6, Ytr7, + Ymr, Ymm, + Yxr, Yxm, + Ymax, + + Zxxx = 0, + + Zlit, + Zlitm_r, + Z_rp, + Zbr, + Zcall, + Zcallcon, + Zcallind, + Zcallindreg, + Zib_, + Zib_rp, + Zibo_m, + Zil_, + Zil_rp, + Zilo_m, + Zjmp, + Zjmpcon, + Zloop, + Zm_o, + Zm_r, + Zm2_r, + Zm_r_xm, + Zm_r_i_xm, + Zaut_r, + Zo_m, + Zpseudo, + Zr_m, + Zr_m_xm, + Zr_m_i_xm, + Zrp_, + Z_ib, + Z_il, + Zm_ibo, + Zm_ilo, + Zib_rr, + Zil_rr, + Zclr, + Zibm_r, /* mmx1,mmx2/mem64,imm8 */ + Zbyte, + Zmov, + Zmax, + + Px = 0, + Pe = 0x66, /* operand escape */ + Pm = 0x0f, /* 2byte opcode escape */ + Pq = 0xff, /* both escape */ + Pb = 0xfe, /* byte operands */ + Pf2 = 0xf2, /* xmm escape 1 */ + Pf3 = 0xf3, /* xmm escape 2 */ +}; + +static uchar ycover[Ymax*Ymax]; +static char reg[D_NONE]; +static void asmins(Link *ctxt, Prog *p); + +static uchar ynone[] = +{ + Ynone, Ynone, Zlit, 1, + 0 +}; +static uchar ytext[] = +{ + Ymb, Yi32, Zpseudo,1, + 0 +}; +static uchar ynop[] = +{ + Ynone, Ynone, Zpseudo,0, + Ynone, Yiauto, Zpseudo,0, + Ynone, Yml, Zpseudo,0, + Ynone, Yrf, Zpseudo,0, + Yiauto, Ynone, Zpseudo,0, + Ynone, Yxr, Zpseudo,0, + Yml, Ynone, Zpseudo,0, + Yrf, Ynone, Zpseudo,0, + Yxr, Ynone, Zpseudo,1, + 0 +}; +static uchar yfuncdata[] = +{ + Yi32, Ym, Zpseudo, 0, + 0 +}; +static uchar ypcdata[] = +{ + Yi32, Yi32, Zpseudo, 0, + 0, +}; +static uchar yxorb[] = +{ + Yi32, Yal, Zib_, 1, + Yi32, Ymb, Zibo_m, 2, + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar yxorl[] = +{ + Yi8, Yml, Zibo_m, 2, + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yaddl[] = +{ + Yi8, Yml, Zibo_m, 2, + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yincb[] = +{ + Ynone, Ymb, Zo_m, 2, + 0 +}; +static uchar yincl[] = +{ + Ynone, Yrl, Z_rp, 1, + Ynone, Yml, Zo_m, 2, + 0 +}; +static uchar ycmpb[] = +{ + Yal, Yi32, Z_ib, 1, + Ymb, Yi32, Zm_ibo, 2, + Ymb, Yrb, Zm_r, 1, + Yrb, Ymb, Zr_m, 1, + 0 +}; +static uchar ycmpl[] = +{ + Yml, Yi8, Zm_ibo, 2, + Yax, Yi32, Z_il, 1, + Yml, Yi32, Zm_ilo, 2, + Yml, Yrl, Zm_r, 1, + Yrl, Yml, Zr_m, 1, + 0 +}; +static uchar yshb[] = +{ + Yi1, Ymb, Zo_m, 2, + Yi32, Ymb, Zibo_m, 2, + Ycx, Ymb, Zo_m, 2, + 0 +}; +static uchar yshl[] = +{ + Yi1, Yml, Zo_m, 2, + Yi32, Yml, Zibo_m, 2, + Ycl, Yml, Zo_m, 2, + Ycx, Yml, Zo_m, 2, + 0 +}; +static uchar ytestb[] = +{ + Yi32, Yal, Zib_, 1, + Yi32, Ymb, Zibo_m, 2, + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar ytestl[] = +{ + Yi32, Yax, Zil_, 1, + Yi32, Yml, Zilo_m, 2, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar ymovb[] = +{ + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + Yi32, Yrb, Zib_rp, 1, + Yi32, Ymb, Zibo_m, 2, + 0 +}; +static uchar ymovw[] = +{ + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + Yi0, Yrl, Zclr, 1+2, +// Yi0, Yml, Zibo_m, 2, // shorter but slower AND $0,dst + Yi32, Yrl, Zil_rp, 1, + Yi32, Yml, Zilo_m, 2, + Yiauto, Yrl, Zaut_r, 1, + 0 +}; +static uchar ymovl[] = +{ + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + Yi0, Yrl, Zclr, 1+2, +// Yi0, Yml, Zibo_m, 2, // shorter but slower AND $0,dst + Yi32, Yrl, Zil_rp, 1, + Yi32, Yml, Zilo_m, 2, + Yml, Yxr, Zm_r_xm, 2, // XMM MOVD (32 bit) + Yxr, Yml, Zr_m_xm, 2, // XMM MOVD (32 bit) + Yiauto, Yrl, Zaut_r, 1, + 0 +}; +static uchar ymovq[] = +{ + Yml, Yxr, Zm_r_xm, 2, + 0 +}; +static uchar ym_rl[] = +{ + Ym, Yrl, Zm_r, 1, + 0 +}; +static uchar yrl_m[] = +{ + Yrl, Ym, Zr_m, 1, + 0 +}; +static uchar ymb_rl[] = +{ + Ymb, Yrl, Zm_r, 1, + 0 +}; +static uchar yml_rl[] = +{ + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar yrb_mb[] = +{ + Yrb, Ymb, Zr_m, 1, + 0 +}; +static uchar yrl_ml[] = +{ + Yrl, Yml, Zr_m, 1, + 0 +}; +static uchar yml_mb[] = +{ + Yrb, Ymb, Zr_m, 1, + Ymb, Yrb, Zm_r, 1, + 0 +}; +static uchar yxchg[] = +{ + Yax, Yrl, Z_rp, 1, + Yrl, Yax, Zrp_, 1, + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + 0 +}; +static uchar ydivl[] = +{ + Yml, Ynone, Zm_o, 2, + 0 +}; +static uchar ydivb[] = +{ + Ymb, Ynone, Zm_o, 2, + 0 +}; +static uchar yimul[] = +{ + Yml, Ynone, Zm_o, 2, + Yi8, Yrl, Zib_rr, 1, + Yi32, Yrl, Zil_rr, 1, + 0 +}; +static uchar ybyte[] = +{ + Yi32, Ynone, Zbyte, 1, + 0 +}; +static uchar yin[] = +{ + Yi32, Ynone, Zib_, 1, + Ynone, Ynone, Zlit, 1, + 0 +}; +static uchar yint[] = +{ + Yi32, Ynone, Zib_, 1, + 0 +}; +static uchar ypushl[] = +{ + Yrl, Ynone, Zrp_, 1, + Ym, Ynone, Zm_o, 2, + Yi8, Ynone, Zib_, 1, + Yi32, Ynone, Zil_, 1, + 0 +}; +static uchar ypopl[] = +{ + Ynone, Yrl, Z_rp, 1, + Ynone, Ym, Zo_m, 2, + 0 +}; +static uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 1, + 0, +}; +static uchar yscond[] = +{ + Ynone, Ymb, Zo_m, 2, + 0 +}; +static uchar yjcond[] = +{ + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, + 0 +}; +static uchar yloop[] = +{ + Ynone, Ybr, Zloop, 1, + 0 +}; +static uchar ycall[] = +{ + Ynone, Yml, Zcallindreg, 0, + Yrx, Yrx, Zcallindreg, 2, + Ynone, Ycol, Zcallind, 2, + Ynone, Ybr, Zcall, 0, + Ynone, Yi32, Zcallcon, 1, + 0 +}; +static uchar yduff[] = +{ + Ynone, Yi32, Zcall, 1, + 0 +}; +static uchar yjmp[] = +{ + Ynone, Yml, Zo_m, 2, + Ynone, Ybr, Zjmp, 0, + Ynone, Yi32, Zjmpcon, 1, + 0 +}; + +static uchar yfmvd[] = +{ + Ym, Yf0, Zm_o, 2, + Yf0, Ym, Zo_m, 2, + Yrf, Yf0, Zm_o, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfmvdp[] = +{ + Yf0, Ym, Zo_m, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfmvf[] = +{ + Ym, Yf0, Zm_o, 2, + Yf0, Ym, Zo_m, 2, + 0 +}; +static uchar yfmvx[] = +{ + Ym, Yf0, Zm_o, 2, + 0 +}; +static uchar yfmvp[] = +{ + Yf0, Ym, Zo_m, 2, + 0 +}; +static uchar yfcmv[] = +{ + Yrf, Yf0, Zm_o, 2, + 0 +}; +static uchar yfadd[] = +{ + Ym, Yf0, Zm_o, 2, + Yrf, Yf0, Zm_o, 2, + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfaddp[] = +{ + Yf0, Yrf, Zo_m, 2, + 0 +}; +static uchar yfxch[] = +{ + Yf0, Yrf, Zo_m, 2, + Yrf, Yf0, Zm_o, 2, + 0 +}; +static uchar ycompp[] = +{ + Yf0, Yrf, Zo_m, 2, /* botch is really f0,f1 */ + 0 +}; +static uchar ystsw[] = +{ + Ynone, Ym, Zo_m, 2, + Ynone, Yax, Zlit, 1, + 0 +}; +static uchar ystcw[] = +{ + Ynone, Ym, Zo_m, 2, + Ym, Ynone, Zm_o, 2, + 0 +}; +static uchar ysvrs[] = +{ + Ynone, Ym, Zo_m, 2, + Ym, Ynone, Zm_o, 2, + 0 +}; +static uchar ymskb[] = +{ + Yxr, Yrl, Zm_r_xm, 2, + Ymr, Yrl, Zm_r_xm, 1, + 0 +}; +static uchar yxm[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxcvm1[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Yxm, Ymr, Zm_r_xm, 2, + 0 +}; +static uchar yxcvm2[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Ymm, Yxr, Zm_r_xm, 2, + 0 +}; +static uchar yxmq[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + 0 +}; +static uchar yxr[] = +{ + Yxr, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxr_ml[] = +{ + Yxr, Yml, Zr_m_xm, 1, + 0 +}; +static uchar yxcmp[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +static uchar yxcmpi[] = +{ + Yxm, Yxr, Zm_r_i_xm, 2, + 0 +}; +static uchar yxmov[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + Yxr, Yxm, Zr_m_xm, 1, + 0 +}; +static uchar yxcvfl[] = +{ + Yxm, Yrl, Zm_r_xm, 1, + 0 +}; +static uchar yxcvlf[] = +{ + Yml, Yxr, Zm_r_xm, 1, + 0 +}; +/* +static uchar yxcvfq[] = +{ + Yxm, Yrl, Zm_r_xm, 2, + 0 +}; +static uchar yxcvqf[] = +{ + Yml, Yxr, Zm_r_xm, 2, + 0 +}; +*/ +static uchar yxrrl[] = +{ + Yxr, Yrl, Zm_r, 1, + 0 +}; +static uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; +static uchar yaes[] = +{ + Yxm, Yxr, Zlitm_r, 2, + 0 +}; +static uchar yinsrd[] = +{ + Yml, Yxr, Zibm_r, 2, + 0 +}; +static uchar ymshufb[] = +{ + Yxm, Yxr, Zm2_r, 2, + 0 +}; + +static Optab optab[] = +/* as, ytab, andproto, opcode */ +{ + { AXXX }, + { AAAA, ynone, Px, 0x37 }, + { AAAD, ynone, Px, 0xd5,0x0a }, + { AAAM, ynone, Px, 0xd4,0x0a }, + { AAAS, ynone, Px, 0x3f }, + { AADCB, yxorb, Pb, 0x14,0x80,(02),0x10,0x10 }, + { AADCL, yxorl, Px, 0x83,(02),0x15,0x81,(02),0x11,0x13 }, + { AADCW, yxorl, Pe, 0x83,(02),0x15,0x81,(02),0x11,0x13 }, + { AADDB, yxorb, Px, 0x04,0x80,(00),0x00,0x02 }, + { AADDL, yaddl, Px, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + { AADDW, yaddl, Pe, 0x83,(00),0x05,0x81,(00),0x01,0x03 }, + { AADJSP }, + { AANDB, yxorb, Pb, 0x24,0x80,(04),0x20,0x22 }, + { AANDL, yxorl, Px, 0x83,(04),0x25,0x81,(04),0x21,0x23 }, + { AANDW, yxorl, Pe, 0x83,(04),0x25,0x81,(04),0x21,0x23 }, + { AARPL, yrl_ml, Px, 0x63 }, + { ABOUNDL, yrl_m, Px, 0x62 }, + { ABOUNDW, yrl_m, Pe, 0x62 }, + { ABSFL, yml_rl, Pm, 0xbc }, + { ABSFW, yml_rl, Pq, 0xbc }, + { ABSRL, yml_rl, Pm, 0xbd }, + { ABSRW, yml_rl, Pq, 0xbd }, + { ABTL, yml_rl, Pm, 0xa3 }, + { ABTW, yml_rl, Pq, 0xa3 }, + { ABTCL, yml_rl, Pm, 0xbb }, + { ABTCW, yml_rl, Pq, 0xbb }, + { ABTRL, yml_rl, Pm, 0xb3 }, + { ABTRW, yml_rl, Pq, 0xb3 }, + { ABTSL, yml_rl, Pm, 0xab }, + { ABTSW, yml_rl, Pq, 0xab }, + { ABYTE, ybyte, Px, 1 }, + { ACALL, ycall, Px, 0xff,(02),0xff,(0x15),0xe8 }, + { ACLC, ynone, Px, 0xf8 }, + { ACLD, ynone, Px, 0xfc }, + { ACLI, ynone, Px, 0xfa }, + { ACLTS, ynone, Pm, 0x06 }, + { ACMC, ynone, Px, 0xf5 }, + { ACMPB, ycmpb, Pb, 0x3c,0x80,(07),0x38,0x3a }, + { ACMPL, ycmpl, Px, 0x83,(07),0x3d,0x81,(07),0x39,0x3b }, + { ACMPW, ycmpl, Pe, 0x83,(07),0x3d,0x81,(07),0x39,0x3b }, + { ACMPSB, ynone, Pb, 0xa6 }, + { ACMPSL, ynone, Px, 0xa7 }, + { ACMPSW, ynone, Pe, 0xa7 }, + { ADAA, ynone, Px, 0x27 }, + { ADAS, ynone, Px, 0x2f }, + { ADATA }, + { ADECB, yincb, Pb, 0xfe,(01) }, + { ADECL, yincl, Px, 0x48,0xff,(01) }, + { ADECW, yincl, Pe, 0x48,0xff,(01) }, + { ADIVB, ydivb, Pb, 0xf6,(06) }, + { ADIVL, ydivl, Px, 0xf7,(06) }, + { ADIVW, ydivl, Pe, 0xf7,(06) }, + { AENTER }, /* botch */ + { AGLOBL }, + { AGOK }, + { AHISTORY }, + { AHLT, ynone, Px, 0xf4 }, + { AIDIVB, ydivb, Pb, 0xf6,(07) }, + { AIDIVL, ydivl, Px, 0xf7,(07) }, + { AIDIVW, ydivl, Pe, 0xf7,(07) }, + { AIMULB, ydivb, Pb, 0xf6,(05) }, + { AIMULL, yimul, Px, 0xf7,(05),0x6b,0x69 }, + { AIMULW, yimul, Pe, 0xf7,(05),0x6b,0x69 }, + { AINB, yin, Pb, 0xe4,0xec }, + { AINL, yin, Px, 0xe5,0xed }, + { AINW, yin, Pe, 0xe5,0xed }, + { AINCB, yincb, Pb, 0xfe,(00) }, + { AINCL, yincl, Px, 0x40,0xff,(00) }, + { AINCW, yincl, Pe, 0x40,0xff,(00) }, + { AINSB, ynone, Pb, 0x6c }, + { AINSL, ynone, Px, 0x6d }, + { AINSW, ynone, Pe, 0x6d }, + { AINT, yint, Px, 0xcd }, + { AINTO, ynone, Px, 0xce }, + { AIRETL, ynone, Px, 0xcf }, + { AIRETW, ynone, Pe, 0xcf }, + { AJCC, yjcond, Px, 0x73,0x83,(00) }, + { AJCS, yjcond, Px, 0x72,0x82 }, + { AJCXZL, yloop, Px, 0xe3 }, + { AJCXZW, yloop, Px, 0xe3 }, + { AJEQ, yjcond, Px, 0x74,0x84 }, + { AJGE, yjcond, Px, 0x7d,0x8d }, + { AJGT, yjcond, Px, 0x7f,0x8f }, + { AJHI, yjcond, Px, 0x77,0x87 }, + { AJLE, yjcond, Px, 0x7e,0x8e }, + { AJLS, yjcond, Px, 0x76,0x86 }, + { AJLT, yjcond, Px, 0x7c,0x8c }, + { AJMI, yjcond, Px, 0x78,0x88 }, + { AJMP, yjmp, Px, 0xff,(04),0xeb,0xe9 }, + { AJNE, yjcond, Px, 0x75,0x85 }, + { AJOC, yjcond, Px, 0x71,0x81,(00) }, + { AJOS, yjcond, Px, 0x70,0x80,(00) }, + { AJPC, yjcond, Px, 0x7b,0x8b }, + { AJPL, yjcond, Px, 0x79,0x89 }, + { AJPS, yjcond, Px, 0x7a,0x8a }, + { ALAHF, ynone, Px, 0x9f }, + { ALARL, yml_rl, Pm, 0x02 }, + { ALARW, yml_rl, Pq, 0x02 }, + { ALEAL, ym_rl, Px, 0x8d }, + { ALEAW, ym_rl, Pe, 0x8d }, + { ALEAVEL, ynone, Px, 0xc9 }, + { ALEAVEW, ynone, Pe, 0xc9 }, + { ALOCK, ynone, Px, 0xf0 }, + { ALODSB, ynone, Pb, 0xac }, + { ALODSL, ynone, Px, 0xad }, + { ALODSW, ynone, Pe, 0xad }, + { ALONG, ybyte, Px, 4 }, + { ALOOP, yloop, Px, 0xe2 }, + { ALOOPEQ, yloop, Px, 0xe1 }, + { ALOOPNE, yloop, Px, 0xe0 }, + { ALSLL, yml_rl, Pm, 0x03 }, + { ALSLW, yml_rl, Pq, 0x03 }, + { AMOVB, ymovb, Pb, 0x88,0x8a,0xb0,0xc6,(00) }, + { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),Pe,0x6e,Pe,0x7e,0 }, + { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),0 }, + { AMOVQ, ymovq, Pf3, 0x7e }, + { AMOVBLSX, ymb_rl, Pm, 0xbe }, + { AMOVBLZX, ymb_rl, Pm, 0xb6 }, + { AMOVBWSX, ymb_rl, Pq, 0xbe }, + { AMOVBWZX, ymb_rl, Pq, 0xb6 }, + { AMOVWLSX, yml_rl, Pm, 0xbf }, + { AMOVWLZX, yml_rl, Pm, 0xb7 }, + { AMOVSB, ynone, Pb, 0xa4 }, + { AMOVSL, ynone, Px, 0xa5 }, + { AMOVSW, ynone, Pe, 0xa5 }, + { AMULB, ydivb, Pb, 0xf6,(04) }, + { AMULL, ydivl, Px, 0xf7,(04) }, + { AMULW, ydivl, Pe, 0xf7,(04) }, + { ANAME }, + { ANEGB, yscond, Px, 0xf6,(03) }, + { ANEGL, yscond, Px, 0xf7,(03) }, + { ANEGW, yscond, Pe, 0xf7,(03) }, + { ANOP, ynop, Px,0,0 }, + { ANOTB, yscond, Px, 0xf6,(02) }, + { ANOTL, yscond, Px, 0xf7,(02) }, + { ANOTW, yscond, Pe, 0xf7,(02) }, + { AORB, yxorb, Pb, 0x0c,0x80,(01),0x08,0x0a }, + { AORL, yxorl, Px, 0x83,(01),0x0d,0x81,(01),0x09,0x0b }, + { AORW, yxorl, Pe, 0x83,(01),0x0d,0x81,(01),0x09,0x0b }, + { AOUTB, yin, Pb, 0xe6,0xee }, + { AOUTL, yin, Px, 0xe7,0xef }, + { AOUTW, yin, Pe, 0xe7,0xef }, + { AOUTSB, ynone, Pb, 0x6e }, + { AOUTSL, ynone, Px, 0x6f }, + { AOUTSW, ynone, Pe, 0x6f }, + { APAUSE, ynone, Px, 0xf3,0x90 }, + { APOPAL, ynone, Px, 0x61 }, + { APOPAW, ynone, Pe, 0x61 }, + { APOPFL, ynone, Px, 0x9d }, + { APOPFW, ynone, Pe, 0x9d }, + { APOPL, ypopl, Px, 0x58,0x8f,(00) }, + { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, + { APUSHAL, ynone, Px, 0x60 }, + { APUSHAW, ynone, Pe, 0x60 }, + { APUSHFL, ynone, Px, 0x9c }, + { APUSHFW, ynone, Pe, 0x9c }, + { APUSHL, ypushl, Px, 0x50,0xff,(06),0x6a,0x68 }, + { APUSHW, ypushl, Pe, 0x50,0xff,(06),0x6a,0x68 }, + { ARCLB, yshb, Pb, 0xd0,(02),0xc0,(02),0xd2,(02) }, + { ARCLL, yshl, Px, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) }, + { ARCLW, yshl, Pe, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) }, + { ARCRB, yshb, Pb, 0xd0,(03),0xc0,(03),0xd2,(03) }, + { ARCRL, yshl, Px, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) }, + { ARCRW, yshl, Pe, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) }, + { AREP, ynone, Px, 0xf3 }, + { AREPN, ynone, Px, 0xf2 }, + { ARET, ynone, Px, 0xc3 }, + { AROLB, yshb, Pb, 0xd0,(00),0xc0,(00),0xd2,(00) }, + { AROLL, yshl, Px, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) }, + { AROLW, yshl, Pe, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) }, + { ARORB, yshb, Pb, 0xd0,(01),0xc0,(01),0xd2,(01) }, + { ARORL, yshl, Px, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) }, + { ARORW, yshl, Pe, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) }, + { ASAHF, ynone, Px, 0x9e }, + { ASALB, yshb, Pb, 0xd0,(04),0xc0,(04),0xd2,(04) }, + { ASALL, yshl, Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASALW, yshl, Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASARB, yshb, Pb, 0xd0,(07),0xc0,(07),0xd2,(07) }, + { ASARL, yshl, Px, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) }, + { ASARW, yshl, Pe, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) }, + { ASBBB, yxorb, Pb, 0x1c,0x80,(03),0x18,0x1a }, + { ASBBL, yxorl, Px, 0x83,(03),0x1d,0x81,(03),0x19,0x1b }, + { ASBBW, yxorl, Pe, 0x83,(03),0x1d,0x81,(03),0x19,0x1b }, + { ASCASB, ynone, Pb, 0xae }, + { ASCASL, ynone, Px, 0xaf }, + { ASCASW, ynone, Pe, 0xaf }, + { ASETCC, yscond, Pm, 0x93,(00) }, + { ASETCS, yscond, Pm, 0x92,(00) }, + { ASETEQ, yscond, Pm, 0x94,(00) }, + { ASETGE, yscond, Pm, 0x9d,(00) }, + { ASETGT, yscond, Pm, 0x9f,(00) }, + { ASETHI, yscond, Pm, 0x97,(00) }, + { ASETLE, yscond, Pm, 0x9e,(00) }, + { ASETLS, yscond, Pm, 0x96,(00) }, + { ASETLT, yscond, Pm, 0x9c,(00) }, + { ASETMI, yscond, Pm, 0x98,(00) }, + { ASETNE, yscond, Pm, 0x95,(00) }, + { ASETOC, yscond, Pm, 0x91,(00) }, + { ASETOS, yscond, Pm, 0x90,(00) }, + { ASETPC, yscond, Pm, 0x96,(00) }, + { ASETPL, yscond, Pm, 0x99,(00) }, + { ASETPS, yscond, Pm, 0x9a,(00) }, + { ACDQ, ynone, Px, 0x99 }, + { ACWD, ynone, Pe, 0x99 }, + { ASHLB, yshb, Pb, 0xd0,(04),0xc0,(04),0xd2,(04) }, + { ASHLL, yshl, Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASHLW, yshl, Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) }, + { ASHRB, yshb, Pb, 0xd0,(05),0xc0,(05),0xd2,(05) }, + { ASHRL, yshl, Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, + { ASHRW, yshl, Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, + { ASTC, ynone, Px, 0xf9 }, + { ASTD, ynone, Px, 0xfd }, + { ASTI, ynone, Px, 0xfb }, + { ASTOSB, ynone, Pb, 0xaa }, + { ASTOSL, ynone, Px, 0xab }, + { ASTOSW, ynone, Pe, 0xab }, + { ASUBB, yxorb, Pb, 0x2c,0x80,(05),0x28,0x2a }, + { ASUBL, yaddl, Px, 0x83,(05),0x2d,0x81,(05),0x29,0x2b }, + { ASUBW, yaddl, Pe, 0x83,(05),0x2d,0x81,(05),0x29,0x2b }, + { ASYSCALL, ynone, Px, 0xcd,100 }, + { ATESTB, ytestb, Pb, 0xa8,0xf6,(00),0x84,0x84 }, + { ATESTL, ytestl, Px, 0xa9,0xf7,(00),0x85,0x85 }, + { ATESTW, ytestl, Pe, 0xa9,0xf7,(00),0x85,0x85 }, + { ATEXT, ytext, Px }, + { AVERR, ydivl, Pm, 0x00,(04) }, + { AVERW, ydivl, Pm, 0x00,(05) }, + { AWAIT, ynone, Px, 0x9b }, + { AWORD, ybyte, Px, 2 }, + { AXCHGB, yml_mb, Pb, 0x86,0x86 }, + { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 }, + { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 }, + { AXLAT, ynone, Px, 0xd7 }, + { AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 }, + { AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, + { AXORW, yxorl, Pe, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, + + { AFMOVB, yfmvx, Px, 0xdf,(04) }, + { AFMOVBP, yfmvp, Px, 0xdf,(06) }, + { AFMOVD, yfmvd, Px, 0xdd,(00),0xdd,(02),0xd9,(00),0xdd,(02) }, + { AFMOVDP, yfmvdp, Px, 0xdd,(03),0xdd,(03) }, + { AFMOVF, yfmvf, Px, 0xd9,(00),0xd9,(02) }, + { AFMOVFP, yfmvp, Px, 0xd9,(03) }, + { AFMOVL, yfmvf, Px, 0xdb,(00),0xdb,(02) }, + { AFMOVLP, yfmvp, Px, 0xdb,(03) }, + { AFMOVV, yfmvx, Px, 0xdf,(05) }, + { AFMOVVP, yfmvp, Px, 0xdf,(07) }, + { AFMOVW, yfmvf, Px, 0xdf,(00),0xdf,(02) }, + { AFMOVWP, yfmvp, Px, 0xdf,(03) }, + { AFMOVX, yfmvx, Px, 0xdb,(05) }, + { AFMOVXP, yfmvp, Px, 0xdb,(07) }, + + { AFCOMB }, + { AFCOMBP }, + { AFCOMD, yfadd, Px, 0xdc,(02),0xd8,(02),0xdc,(02) }, /* botch */ + { AFCOMDP, yfadd, Px, 0xdc,(03),0xd8,(03),0xdc,(03) }, /* botch */ + { AFCOMDPP, ycompp, Px, 0xde,(03) }, + { AFCOMF, yfmvx, Px, 0xd8,(02) }, + { AFCOMFP, yfmvx, Px, 0xd8,(03) }, + { AFCOMI, yfmvx, Px, 0xdb,(06) }, + { AFCOMIP, yfmvx, Px, 0xdf,(06) }, + { AFCOML, yfmvx, Px, 0xda,(02) }, + { AFCOMLP, yfmvx, Px, 0xda,(03) }, + { AFCOMW, yfmvx, Px, 0xde,(02) }, + { AFCOMWP, yfmvx, Px, 0xde,(03) }, + + { AFUCOM, ycompp, Px, 0xdd,(04) }, + { AFUCOMI, ycompp, Px, 0xdb,(05) }, + { AFUCOMIP, ycompp, Px, 0xdf,(05) }, + { AFUCOMP, ycompp, Px, 0xdd,(05) }, + { AFUCOMPP, ycompp, Px, 0xda,(13) }, + + { AFADDDP, yfaddp, Px, 0xde,(00) }, + { AFADDW, yfmvx, Px, 0xde,(00) }, + { AFADDL, yfmvx, Px, 0xda,(00) }, + { AFADDF, yfmvx, Px, 0xd8,(00) }, + { AFADDD, yfadd, Px, 0xdc,(00),0xd8,(00),0xdc,(00) }, + + { AFMULDP, yfaddp, Px, 0xde,(01) }, + { AFMULW, yfmvx, Px, 0xde,(01) }, + { AFMULL, yfmvx, Px, 0xda,(01) }, + { AFMULF, yfmvx, Px, 0xd8,(01) }, + { AFMULD, yfadd, Px, 0xdc,(01),0xd8,(01),0xdc,(01) }, + + { AFSUBDP, yfaddp, Px, 0xde,(05) }, + { AFSUBW, yfmvx, Px, 0xde,(04) }, + { AFSUBL, yfmvx, Px, 0xda,(04) }, + { AFSUBF, yfmvx, Px, 0xd8,(04) }, + { AFSUBD, yfadd, Px, 0xdc,(04),0xd8,(04),0xdc,(05) }, + + { AFSUBRDP, yfaddp, Px, 0xde,(04) }, + { AFSUBRW, yfmvx, Px, 0xde,(05) }, + { AFSUBRL, yfmvx, Px, 0xda,(05) }, + { AFSUBRF, yfmvx, Px, 0xd8,(05) }, + { AFSUBRD, yfadd, Px, 0xdc,(05),0xd8,(05),0xdc,(04) }, + + { AFDIVDP, yfaddp, Px, 0xde,(07) }, + { AFDIVW, yfmvx, Px, 0xde,(06) }, + { AFDIVL, yfmvx, Px, 0xda,(06) }, + { AFDIVF, yfmvx, Px, 0xd8,(06) }, + { AFDIVD, yfadd, Px, 0xdc,(06),0xd8,(06),0xdc,(07) }, + + { AFDIVRDP, yfaddp, Px, 0xde,(06) }, + { AFDIVRW, yfmvx, Px, 0xde,(07) }, + { AFDIVRL, yfmvx, Px, 0xda,(07) }, + { AFDIVRF, yfmvx, Px, 0xd8,(07) }, + { AFDIVRD, yfadd, Px, 0xdc,(07),0xd8,(07),0xdc,(06) }, + + { AFXCHD, yfxch, Px, 0xd9,(01),0xd9,(01) }, + { AFFREE }, + { AFLDCW, ystcw, Px, 0xd9,(05),0xd9,(05) }, + { AFLDENV, ystcw, Px, 0xd9,(04),0xd9,(04) }, + { AFRSTOR, ysvrs, Px, 0xdd,(04),0xdd,(04) }, + { AFSAVE, ysvrs, Px, 0xdd,(06),0xdd,(06) }, + { AFSTCW, ystcw, Px, 0xd9,(07),0xd9,(07) }, + { AFSTENV, ystcw, Px, 0xd9,(06),0xd9,(06) }, + { AFSTSW, ystsw, Px, 0xdd,(07),0xdf,0xe0 }, + { AF2XM1, ynone, Px, 0xd9, 0xf0 }, + { AFABS, ynone, Px, 0xd9, 0xe1 }, + { AFCHS, ynone, Px, 0xd9, 0xe0 }, + { AFCLEX, ynone, Px, 0xdb, 0xe2 }, + { AFCOS, ynone, Px, 0xd9, 0xff }, + { AFDECSTP, ynone, Px, 0xd9, 0xf6 }, + { AFINCSTP, ynone, Px, 0xd9, 0xf7 }, + { AFINIT, ynone, Px, 0xdb, 0xe3 }, + { AFLD1, ynone, Px, 0xd9, 0xe8 }, + { AFLDL2E, ynone, Px, 0xd9, 0xea }, + { AFLDL2T, ynone, Px, 0xd9, 0xe9 }, + { AFLDLG2, ynone, Px, 0xd9, 0xec }, + { AFLDLN2, ynone, Px, 0xd9, 0xed }, + { AFLDPI, ynone, Px, 0xd9, 0xeb }, + { AFLDZ, ynone, Px, 0xd9, 0xee }, + { AFNOP, ynone, Px, 0xd9, 0xd0 }, + { AFPATAN, ynone, Px, 0xd9, 0xf3 }, + { AFPREM, ynone, Px, 0xd9, 0xf8 }, + { AFPREM1, ynone, Px, 0xd9, 0xf5 }, + { AFPTAN, ynone, Px, 0xd9, 0xf2 }, + { AFRNDINT, ynone, Px, 0xd9, 0xfc }, + { AFSCALE, ynone, Px, 0xd9, 0xfd }, + { AFSIN, ynone, Px, 0xd9, 0xfe }, + { AFSINCOS, ynone, Px, 0xd9, 0xfb }, + { AFSQRT, ynone, Px, 0xd9, 0xfa }, + { AFTST, ynone, Px, 0xd9, 0xe4 }, + { AFXAM, ynone, Px, 0xd9, 0xe5 }, + { AFXTRACT, ynone, Px, 0xd9, 0xf4 }, + { AFYL2X, ynone, Px, 0xd9, 0xf1 }, + { AFYL2XP1, ynone, Px, 0xd9, 0xf9 }, + { AEND }, + { ADYNT_ }, + { AINIT_ }, + { ASIGNAME }, + { ACMPXCHGB, yrb_mb, Pm, 0xb0 }, + { ACMPXCHGL, yrl_ml, Pm, 0xb1 }, + { ACMPXCHGW, yrl_ml, Pm, 0xb1 }, + { ACMPXCHG8B, yscond, Pm, 0xc7,(01) }, + + { ACPUID, ynone, Pm, 0xa2 }, + { ARDTSC, ynone, Pm, 0x31 }, + + { AXADDB, yrb_mb, Pb, 0x0f,0xc0 }, + { AXADDL, yrl_ml, Pm, 0xc1 }, + { AXADDW, yrl_ml, Pe, 0x0f,0xc1 }, + + { ACMOVLCC, yml_rl, Pm, 0x43 }, + { ACMOVLCS, yml_rl, Pm, 0x42 }, + { ACMOVLEQ, yml_rl, Pm, 0x44 }, + { ACMOVLGE, yml_rl, Pm, 0x4d }, + { ACMOVLGT, yml_rl, Pm, 0x4f }, + { ACMOVLHI, yml_rl, Pm, 0x47 }, + { ACMOVLLE, yml_rl, Pm, 0x4e }, + { ACMOVLLS, yml_rl, Pm, 0x46 }, + { ACMOVLLT, yml_rl, Pm, 0x4c }, + { ACMOVLMI, yml_rl, Pm, 0x48 }, + { ACMOVLNE, yml_rl, Pm, 0x45 }, + { ACMOVLOC, yml_rl, Pm, 0x41 }, + { ACMOVLOS, yml_rl, Pm, 0x40 }, + { ACMOVLPC, yml_rl, Pm, 0x4b }, + { ACMOVLPL, yml_rl, Pm, 0x49 }, + { ACMOVLPS, yml_rl, Pm, 0x4a }, + { ACMOVWCC, yml_rl, Pq, 0x43 }, + { ACMOVWCS, yml_rl, Pq, 0x42 }, + { ACMOVWEQ, yml_rl, Pq, 0x44 }, + { ACMOVWGE, yml_rl, Pq, 0x4d }, + { ACMOVWGT, yml_rl, Pq, 0x4f }, + { ACMOVWHI, yml_rl, Pq, 0x47 }, + { ACMOVWLE, yml_rl, Pq, 0x4e }, + { ACMOVWLS, yml_rl, Pq, 0x46 }, + { ACMOVWLT, yml_rl, Pq, 0x4c }, + { ACMOVWMI, yml_rl, Pq, 0x48 }, + { ACMOVWNE, yml_rl, Pq, 0x45 }, + { ACMOVWOC, yml_rl, Pq, 0x41 }, + { ACMOVWOS, yml_rl, Pq, 0x40 }, + { ACMOVWPC, yml_rl, Pq, 0x4b }, + { ACMOVWPL, yml_rl, Pq, 0x49 }, + { ACMOVWPS, yml_rl, Pq, 0x4a }, + + { AFCMOVCC, yfcmv, Px, 0xdb,(00) }, + { AFCMOVCS, yfcmv, Px, 0xda,(00) }, + { AFCMOVEQ, yfcmv, Px, 0xda,(01) }, + { AFCMOVHI, yfcmv, Px, 0xdb,(02) }, + { AFCMOVLS, yfcmv, Px, 0xda,(02) }, + { AFCMOVNE, yfcmv, Px, 0xdb,(01) }, + { AFCMOVNU, yfcmv, Px, 0xdb,(03) }, + { AFCMOVUN, yfcmv, Px, 0xda,(03) }, + + { ALFENCE, ynone, Pm, 0xae,0xe8 }, + { AMFENCE, ynone, Pm, 0xae,0xf0 }, + { ASFENCE, ynone, Pm, 0xae,0xf8 }, + + { AEMMS, ynone, Pm, 0x77 }, + + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { ABSWAPL, ybswap, Pm, 0xc8 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AADDPD, yxm, Pq, 0x58 }, + { AADDPS, yxm, Pm, 0x58 }, + { AADDSD, yxm, Pf2, 0x58 }, + { AADDSS, yxm, Pf3, 0x58 }, + { AANDNPD, yxm, Pq, 0x55 }, + { AANDNPS, yxm, Pm, 0x55 }, + { AANDPD, yxm, Pq, 0x54 }, + { AANDPS, yxm, Pq, 0x54 }, + { ACMPPD, yxcmpi, Px, Pe,0xc2 }, + { ACMPPS, yxcmpi, Pm, 0xc2,0 }, + { ACMPSD, yxcmpi, Px, Pf2,0xc2 }, + { ACMPSS, yxcmpi, Px, Pf3,0xc2 }, + { ACOMISD, yxcmp, Pe, 0x2f }, + { ACOMISS, yxcmp, Pm, 0x2f }, + { ACVTPL2PD, yxcvm2, Px, Pf3,0xe6,Pe,0x2a }, + { ACVTPL2PS, yxcvm2, Pm, 0x5b,0,0x2a,0, }, + { ACVTPD2PL, yxcvm1, Px, Pf2,0xe6,Pe,0x2d }, + { ACVTPD2PS, yxm, Pe, 0x5a }, + { ACVTPS2PL, yxcvm1, Px, Pe,0x5b,Pm,0x2d }, + { ACVTPS2PD, yxm, Pm, 0x5a }, + { ACVTSD2SL, yxcvfl, Pf2, 0x2d }, + { ACVTSD2SS, yxm, Pf2, 0x5a }, + { ACVTSL2SD, yxcvlf, Pf2, 0x2a }, + { ACVTSL2SS, yxcvlf, Pf3, 0x2a }, + { ACVTSS2SD, yxm, Pf3, 0x5a }, + { ACVTSS2SL, yxcvfl, Pf3, 0x2d }, + { ACVTTPD2PL, yxcvm1, Px, Pe,0xe6,Pe,0x2c }, + { ACVTTPS2PL, yxcvm1, Px, Pf3,0x5b,Pm,0x2c }, + { ACVTTSD2SL, yxcvfl, Pf2, 0x2c }, + { ACVTTSS2SL, yxcvfl, Pf3, 0x2c }, + { ADIVPD, yxm, Pe, 0x5e }, + { ADIVPS, yxm, Pm, 0x5e }, + { ADIVSD, yxm, Pf2, 0x5e }, + { ADIVSS, yxm, Pf3, 0x5e }, + { AMASKMOVOU, yxr, Pe, 0xf7 }, + { AMAXPD, yxm, Pe, 0x5f }, + { AMAXPS, yxm, Pm, 0x5f }, + { AMAXSD, yxm, Pf2, 0x5f }, + { AMAXSS, yxm, Pf3, 0x5f }, + { AMINPD, yxm, Pe, 0x5d }, + { AMINPS, yxm, Pm, 0x5d }, + { AMINSD, yxm, Pf2, 0x5d }, + { AMINSS, yxm, Pf3, 0x5d }, + { AMOVAPD, yxmov, Pe, 0x28,0x29 }, + { AMOVAPS, yxmov, Pm, 0x28,0x29 }, + { AMOVO, yxmov, Pe, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, + { AMOVHLPS, yxr, Pm, 0x12 }, + { AMOVHPD, yxmov, Pe, 0x16,0x17 }, + { AMOVHPS, yxmov, Pm, 0x16,0x17 }, + { AMOVLHPS, yxr, Pm, 0x16 }, + { AMOVLPD, yxmov, Pe, 0x12,0x13 }, + { AMOVLPS, yxmov, Pm, 0x12,0x13 }, + { AMOVMSKPD, yxrrl, Pq, 0x50 }, + { AMOVMSKPS, yxrrl, Pm, 0x50 }, + { AMOVNTO, yxr_ml, Pe, 0xe7 }, + { AMOVNTPD, yxr_ml, Pe, 0x2b }, + { AMOVNTPS, yxr_ml, Pm, 0x2b }, + { AMOVSD, yxmov, Pf2, 0x10,0x11 }, + { AMOVSS, yxmov, Pf3, 0x10,0x11 }, + { AMOVUPD, yxmov, Pe, 0x10,0x11 }, + { AMOVUPS, yxmov, Pm, 0x10,0x11 }, + { AMULPD, yxm, Pe, 0x59 }, + { AMULPS, yxm, Ym, 0x59 }, + { AMULSD, yxm, Pf2, 0x59 }, + { AMULSS, yxm, Pf3, 0x59 }, + { AORPD, yxm, Pq, 0x56 }, + { AORPS, yxm, Pm, 0x56 }, + { APADDQ, yxm, Pe, 0xd4 }, + { APAND, yxm, Pe, 0xdb }, + { APCMPEQB, yxmq, Pe ,0x74 }, + { APMAXSW, yxm, Pe, 0xee }, + { APMAXUB, yxm, Pe, 0xde }, + { APMINSW, yxm, Pe, 0xea }, + { APMINUB, yxm, Pe, 0xda }, + { APMOVMSKB, ymskb, Px, Pe,0xd7,0xd7 }, + { APSADBW, yxm, Pq, 0xf6 }, + { APSUBB, yxm, Pe, 0xf8 }, + { APSUBL, yxm, Pe, 0xfa }, + { APSUBQ, yxm, Pe, 0xfb }, + { APSUBSB, yxm, Pe, 0xe8 }, + { APSUBSW, yxm, Pe, 0xe9 }, + { APSUBUSB, yxm, Pe, 0xd8 }, + { APSUBUSW, yxm, Pe, 0xd9 }, + { APSUBW, yxm, Pe, 0xf9 }, + { APUNPCKHQDQ, yxm, Pe, 0x6d }, + { APUNPCKLQDQ, yxm, Pe, 0x6c }, + { APXOR, yxm, Pe, 0xef }, + { ARCPPS, yxm, Pm, 0x53 }, + { ARCPSS, yxm, Pf3, 0x53 }, + { ARSQRTPS, yxm, Pm, 0x52 }, + { ARSQRTSS, yxm, Pf3, 0x52 }, + { ASQRTPD, yxm, Pe, 0x51 }, + { ASQRTPS, yxm, Pm, 0x51 }, + { ASQRTSD, yxm, Pf2, 0x51 }, + { ASQRTSS, yxm, Pf3, 0x51 }, + { ASUBPD, yxm, Pe, 0x5c }, + { ASUBPS, yxm, Pm, 0x5c }, + { ASUBSD, yxm, Pf2, 0x5c }, + { ASUBSS, yxm, Pf3, 0x5c }, + { AUCOMISD, yxcmp, Pe, 0x2e }, + { AUCOMISS, yxcmp, Pm, 0x2e }, + { AUNPCKHPD, yxm, Pe, 0x15 }, + { AUNPCKHPS, yxm, Pm, 0x15 }, + { AUNPCKLPD, yxm, Pe, 0x14 }, + { AUNPCKLPS, yxm, Pm, 0x14 }, + { AXORPD, yxm, Pe, 0x57 }, + { AXORPS, yxm, Pm, 0x57 }, + + { AAESENC, yaes, Pq, 0x38,0xdc,(0) }, + { APINSRD, yinsrd, Pq, 0x3a, 0x22, (00) }, + { APSHUFB, ymshufb,Pq, 0x38, 0x00 }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ATYPE }, + { AFUNCDATA, yfuncdata, Px, 0,0 }, + { APCDATA, ypcdata, Px, 0,0 }, + { ACHECKNIL }, + { AVARDEF }, + { AVARKILL }, + { ADUFFCOPY, yduff, Px, 0xe8 }, + { ADUFFZERO, yduff, Px, 0xe8 }, + + 0 +}; + +static int32 vaddr(Link*, Addr*, Reloc*); + +// single-instruction no-ops of various lengths. +// constructed by hand and disassembled with gdb to verify. +// see http://www.agner.org/optimize/optimizing_assembly.pdf for discussion. +static uchar nop[][16] = { + {0x90}, + {0x66, 0x90}, + {0x0F, 0x1F, 0x00}, + {0x0F, 0x1F, 0x40, 0x00}, + {0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + // Native Client rejects the repeated 0x66 prefix. + // {0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void +fillnop(uchar *p, int n) +{ + int m; + + while(n > 0) { + m = n; + if(m > nelem(nop)) + m = nelem(nop); + memmove(p, nop[m-1], m); + p += m; + n -= m; + } +} + +static int32 +naclpad(Link *ctxt, LSym *s, int32 c, int32 pad) +{ + symgrow(ctxt, s, c+pad); + fillnop(s->p+c, pad); + return c+pad; +} + +static void instinit(void); + +void +span8(Link *ctxt, LSym *s) +{ + Prog *p, *q; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + ctxt->cursym = s; + + if(s->text == nil || s->text->link == nil) + return; + + if(ycover[0] == 0) + instinit(); + + for(p = s->text; p != nil; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == nil) + p->pcond = p; + if((q = p->pcond) != nil) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = AADDL; + if(v < 0) { + p->as = ASUBL; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; + } + } + + for(p = s->text; p != nil; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != nil && (q->back & 2)) + p->back |= 1; // backward jump + + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = AADDL; + if(v < 0) { + p->as = ASUBL; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; + } + } + + n = 0; + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != nil; p = p->link) { + if(ctxt->headtype == Hnacl && p->isize > 0) { + static LSym *deferreturn; + + if(deferreturn == nil) + deferreturn = linklookup(ctxt, "runtime.deferreturn", 0); + + // pad everything to avoid crossing 32-byte boundary + if((c>>5) != ((c+p->isize-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + // pad call deferreturn to start at 32-byte boundary + // so that subtracting 5 in jmpdefer will jump back + // to that boundary and rerun the call. + if(p->as == ACALL && p->to.sym == deferreturn) + c = naclpad(ctxt, s, c, -c&31); + // pad call to end at 32-byte boundary + if(p->as == ACALL) + c = naclpad(ctxt, s, c, -(c+p->isize)&31); + + // the linker treats REP and STOSQ as different instructions + // but in fact the REP is a prefix on the STOSQ. + // make sure REP has room for 2 more bytes, so that + // padding will not be inserted before the next instruction. + if(p->as == AREP && (c>>5) != ((c+3-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + + // same for LOCK. + // various instructions follow; the longest is 4 bytes. + // give ourselves 8 bytes so as to avoid surprises. + if(p->as == ALOCK && (c>>5) != ((c+8-1)>>5)) + c = naclpad(ctxt, s, c, -c&31); + } + + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != nil; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + if(q->as == AJCXZW) + s->p[q->pc+2] = v; + else + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp = v>>24; + } + } + p->comefrom = nil; + + p->pc = c; + asmins(ctxt, p); + m = ctxt->andptr-ctxt->and; + if(p->isize != m) { + p->isize = m; + loop++; + } + symgrow(ctxt, s, p->pc+m); + memmove(s->p+p->pc, ctxt->and, m); + p->mark = m; + c += m; + } + if(++n > 20) { + ctxt->diag("span must be looping"); + sysfatal("bad code"); + } + } while(loop); + + if(ctxt->headtype == Hnacl) + c = naclpad(ctxt, s, c, -c&31); + c += -c&(FuncAlign-1); + s->size = c; + + if(0 /* debug['a'] > 1 */) { + print("span1 %s %lld (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; i<s->np; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; i<s->nr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+lld\n", r->off, r->siz, r->sym->name, r->add); + } + } +} + +static void +instinit(void) +{ + int i; + + for(i=1; optab[i].as; i++) + if(i != optab[i].as) + sysfatal("phase error in optab: at %A found %A", i, optab[i].as); + + for(i=0; i<Ymax; i++) + ycover[i*Ymax + i] = 1; + + ycover[Yi0*Ymax + Yi8] = 1; + ycover[Yi1*Ymax + Yi8] = 1; + + ycover[Yi0*Ymax + Yi32] = 1; + ycover[Yi1*Ymax + Yi32] = 1; + ycover[Yi8*Ymax + Yi32] = 1; + + ycover[Yal*Ymax + Yrb] = 1; + ycover[Ycl*Ymax + Yrb] = 1; + ycover[Yax*Ymax + Yrb] = 1; + ycover[Ycx*Ymax + Yrb] = 1; + ycover[Yrx*Ymax + Yrb] = 1; + + ycover[Yax*Ymax + Yrx] = 1; + ycover[Ycx*Ymax + Yrx] = 1; + + ycover[Yax*Ymax + Yrl] = 1; + ycover[Ycx*Ymax + Yrl] = 1; + ycover[Yrx*Ymax + Yrl] = 1; + + ycover[Yf0*Ymax + Yrf] = 1; + + ycover[Yal*Ymax + Ymb] = 1; + ycover[Ycl*Ymax + Ymb] = 1; + ycover[Yax*Ymax + Ymb] = 1; + ycover[Ycx*Ymax + Ymb] = 1; + ycover[Yrx*Ymax + Ymb] = 1; + ycover[Yrb*Ymax + Ymb] = 1; + ycover[Ym*Ymax + Ymb] = 1; + + ycover[Yax*Ymax + Yml] = 1; + ycover[Ycx*Ymax + Yml] = 1; + ycover[Yrx*Ymax + Yml] = 1; + ycover[Yrl*Ymax + Yml] = 1; + ycover[Ym*Ymax + Yml] = 1; + + ycover[Yax*Ymax + Ymm] = 1; + ycover[Ycx*Ymax + Ymm] = 1; + ycover[Yrx*Ymax + Ymm] = 1; + ycover[Yrl*Ymax + Ymm] = 1; + ycover[Ym*Ymax + Ymm] = 1; + ycover[Ymr*Ymax + Ymm] = 1; + + ycover[Ym*Ymax + Yxm] = 1; + ycover[Yxr*Ymax + Yxm] = 1; + + for(i=0; i<D_NONE; i++) { + reg[i] = -1; + if(i >= D_AL && i <= D_BH) + reg[i] = (i-D_AL) & 7; + if(i >= D_AX && i <= D_DI) + reg[i] = (i-D_AX) & 7; + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; + if(i >= D_X0 && i <= D_X0+7) + reg[i] = (i-D_X0) & 7; + } +} + +static int +prefixof(Link *ctxt, Addr *a) +{ + switch(a->type) { + case D_INDIR+D_CS: + return 0x2e; + case D_INDIR+D_DS: + return 0x3e; + case D_INDIR+D_ES: + return 0x26; + case D_INDIR+D_FS: + return 0x64; + case D_INDIR+D_GS: + return 0x65; + case D_INDIR+D_TLS: + // NOTE: Systems listed here should be only systems that + // support direct TLS references like 8(TLS) implemented as + // direct references from FS or GS. Systems that require + // the initial-exec model, where you load the TLS base into + // a register and then index from that register, do not reach + // this code and should not be listed. + switch(ctxt->headtype) { + default: + sysfatal("unknown TLS base register for %s", headstr(ctxt->headtype)); + case Hdarwin: + case Hdragonfly: + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + return 0x65; // GS + } + } + return 0; +} + +static int +oclass(Addr *a) +{ + int32 v; + + if((a->type >= D_INDIR && a->type < 2*D_INDIR) || a->index != D_NONE) { + if(a->index != D_NONE && a->scale == 0) { + if(a->type == D_ADDR) { + switch(a->index) { + case D_EXTERN: + case D_STATIC: + return Yi32; + case D_AUTO: + case D_PARAM: + return Yiauto; + } + return Yxxx; + } + //if(a->type == D_INDIR+D_ADDR) + // print("*Ycol\n"); + return Ycol; + } + return Ym; + } + switch(a->type) + { + case D_AL: + return Yal; + + case D_AX: + return Yax; + + case D_CL: + case D_DL: + case D_BL: + case D_AH: + case D_CH: + case D_DH: + case D_BH: + return Yrb; + + case D_CX: + return Ycx; + + case D_DX: + case D_BX: + return Yrx; + + case D_SP: + case D_BP: + case D_SI: + case D_DI: + return Yrl; + + case D_F0+0: + return Yf0; + + case D_F0+1: + case D_F0+2: + case D_F0+3: + case D_F0+4: + case D_F0+5: + case D_F0+6: + case D_F0+7: + return Yrf; + + case D_X0+0: + case D_X0+1: + case D_X0+2: + case D_X0+3: + case D_X0+4: + case D_X0+5: + case D_X0+6: + case D_X0+7: + return Yxr; + + case D_NONE: + return Ynone; + + case D_CS: return Ycs; + case D_SS: return Yss; + case D_DS: return Yds; + case D_ES: return Yes; + case D_FS: return Yfs; + case D_GS: return Ygs; + case D_TLS: return Ytls; + + case D_GDTR: return Ygdtr; + case D_IDTR: return Yidtr; + case D_LDTR: return Yldtr; + case D_MSW: return Ymsw; + case D_TASK: return Ytask; + + case D_CR+0: return Ycr0; + case D_CR+1: return Ycr1; + case D_CR+2: return Ycr2; + case D_CR+3: return Ycr3; + case D_CR+4: return Ycr4; + case D_CR+5: return Ycr5; + case D_CR+6: return Ycr6; + case D_CR+7: return Ycr7; + + case D_DR+0: return Ydr0; + case D_DR+1: return Ydr1; + case D_DR+2: return Ydr2; + case D_DR+3: return Ydr3; + case D_DR+4: return Ydr4; + case D_DR+5: return Ydr5; + case D_DR+6: return Ydr6; + case D_DR+7: return Ydr7; + + case D_TR+0: return Ytr0; + case D_TR+1: return Ytr1; + case D_TR+2: return Ytr2; + case D_TR+3: return Ytr3; + case D_TR+4: return Ytr4; + case D_TR+5: return Ytr5; + case D_TR+6: return Ytr6; + case D_TR+7: return Ytr7; + + case D_EXTERN: + case D_STATIC: + case D_AUTO: + case D_PARAM: + return Ym; + + case D_CONST: + case D_CONST2: + case D_ADDR: + if(a->sym == nil) { + v = a->offset; + if(v == 0) + return Yi0; + if(v == 1) + return Yi1; + if(v >= -128 && v <= 127) + return Yi8; + } + return Yi32; + + case D_BRANCH: + return Ybr; + } + return Yxxx; +} + +static void +asmidx(Link *ctxt, int scale, int index, int base) +{ + int i; + + switch(index) { + default: + goto bad; + + case D_NONE: + i = 4 << 3; + goto bas; + + case D_AX: + case D_CX: + case D_DX: + case D_BX: + case D_BP: + case D_SI: + case D_DI: + i = reg[index] << 3; + break; + } + switch(scale) { + default: + goto bad; + case 1: + break; + case 2: + i |= (1<<6); + break; + case 4: + i |= (2<<6); + break; + case 8: + i |= (3<<6); + break; + } +bas: + switch(base) { + default: + goto bad; + case D_NONE: /* must be mod=00 */ + i |= 5; + break; + case D_AX: + case D_CX: + case D_DX: + case D_BX: + case D_SP: + case D_BP: + case D_SI: + case D_DI: + i |= reg[base]; + break; + } + *ctxt->andptr++ = i; + return; +bad: + ctxt->diag("asmidx: bad address %d,%d,%d", scale, index, base); + *ctxt->andptr++ = 0; + return; +} + +static void +put4(Link *ctxt, int32 v) +{ + ctxt->andptr[0] = v; + ctxt->andptr[1] = v>>8; + ctxt->andptr[2] = v>>16; + ctxt->andptr[3] = v>>24; + ctxt->andptr += 4; +} + +static void +relput4(Link *ctxt, Prog *p, Addr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(ctxt, a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + ctxt->diag("bad reloc"); + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + put4(ctxt, v); +} + +static int32 +vaddr(Link *ctxt, Addr *a, Reloc *r) +{ + int t; + int32 v; + LSym *s; + + if(r != nil) + memset(r, 0, sizeof *r); + + t = a->type; + v = a->offset; + if(t == D_ADDR) + t = a->index; + switch(t) { + case D_STATIC: + case D_EXTERN: + s = a->sym; + if(s != nil) { + if(r == nil) { + ctxt->diag("need reloc for %D", a); + sysfatal("bad code"); + } + r->type = R_ADDR; + r->siz = 4; + r->off = -1; + r->sym = s; + r->add = v; + v = 0; + } + break; + + case D_INDIR+D_TLS: + if(r == nil) { + ctxt->diag("need reloc for %D", a); + sysfatal("bad code"); + } + r->type = R_TLS_LE; + r->siz = 4; + r->off = -1; // caller must fill in + r->add = v; + v = 0; + break; + } + return v; +} + +static void +asmand(Link *ctxt, Addr *a, int r) +{ + int32 v; + int t, scale; + Reloc rel; + + v = a->offset; + t = a->type; + rel.siz = 0; + if(a->index != D_NONE && a->index != D_TLS) { + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(ctxt, a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + } else + t -= D_INDIR; + + if(t == D_NONE) { + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + goto putrelv; + } + if(v == 0 && rel.siz == 0 && t != D_BP) { + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + *ctxt->andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + *ctxt->andptr++ = v; + return; + } + *ctxt->andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, a->scale, a->index, t); + goto putrelv; + } + if(t >= D_AL && t <= D_F7 || t >= D_X0 && t <= D_X7) { + if(v) + goto bad; + *ctxt->andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); + return; + } + + scale = a->scale; + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(a->type) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(ctxt, a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + scale = 1; + } else + t -= D_INDIR; + if(t == D_TLS) + v = vaddr(ctxt, a, &rel); + + if(t == D_NONE || (D_CS <= t && t <= D_GS) || t == D_TLS) { + *ctxt->andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + if(t == D_SP) { + if(v == 0 && rel.siz == 0) { + *ctxt->andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + *ctxt->andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + *ctxt->andptr++ = v; + return; + } + *ctxt->andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(ctxt, scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_DI) { + if(a->index == D_TLS) { + memset(&rel, 0, sizeof rel); + rel.type = R_TLS_IE; + rel.siz = 4; + rel.sym = nil; + rel.add = v; + v = 0; + } + if(v == 0 && rel.siz == 0 && t != D_BP) { + *ctxt->andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + ctxt->andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + ctxt->andptr[1] = v; + ctxt->andptr += 2; + return; + } + *ctxt->andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; + } + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + ctxt->diag("bad rel"); + goto bad; + } + r = addrel(ctxt->cursym); + *r = rel; + r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and; + } + + put4(ctxt, v); + return; + +bad: + ctxt->diag("asmand: bad address %D", a); + return; +} + +#define E 0xff +static uchar ymovtab[] = +{ +/* push */ + APUSHL, Ycs, Ynone, 0, 0x0e,E,0,0, + APUSHL, Yss, Ynone, 0, 0x16,E,0,0, + APUSHL, Yds, Ynone, 0, 0x1e,E,0,0, + APUSHL, Yes, Ynone, 0, 0x06,E,0,0, + APUSHL, Yfs, Ynone, 0, 0x0f,0xa0,E,0, + APUSHL, Ygs, Ynone, 0, 0x0f,0xa8,E,0, + + APUSHW, Ycs, Ynone, 0, Pe,0x0e,E,0, + APUSHW, Yss, Ynone, 0, Pe,0x16,E,0, + APUSHW, Yds, Ynone, 0, Pe,0x1e,E,0, + APUSHW, Yes, Ynone, 0, Pe,0x06,E,0, + APUSHW, Yfs, Ynone, 0, Pe,0x0f,0xa0,E, + APUSHW, Ygs, Ynone, 0, Pe,0x0f,0xa8,E, + +/* pop */ + APOPL, Ynone, Yds, 0, 0x1f,E,0,0, + APOPL, Ynone, Yes, 0, 0x07,E,0,0, + APOPL, Ynone, Yss, 0, 0x17,E,0,0, + APOPL, Ynone, Yfs, 0, 0x0f,0xa1,E,0, + APOPL, Ynone, Ygs, 0, 0x0f,0xa9,E,0, + + APOPW, Ynone, Yds, 0, Pe,0x1f,E,0, + APOPW, Ynone, Yes, 0, Pe,0x07,E,0, + APOPW, Ynone, Yss, 0, Pe,0x17,E,0, + APOPW, Ynone, Yfs, 0, Pe,0x0f,0xa1,E, + APOPW, Ynone, Ygs, 0, Pe,0x0f,0xa9,E, + +/* mov seg */ + AMOVW, Yes, Yml, 1, 0x8c,0,0,0, + AMOVW, Ycs, Yml, 1, 0x8c,1,0,0, + AMOVW, Yss, Yml, 1, 0x8c,2,0,0, + AMOVW, Yds, Yml, 1, 0x8c,3,0,0, + AMOVW, Yfs, Yml, 1, 0x8c,4,0,0, + AMOVW, Ygs, Yml, 1, 0x8c,5,0,0, + + AMOVW, Yml, Yes, 2, 0x8e,0,0,0, + AMOVW, Yml, Ycs, 2, 0x8e,1,0,0, + AMOVW, Yml, Yss, 2, 0x8e,2,0,0, + AMOVW, Yml, Yds, 2, 0x8e,3,0,0, + AMOVW, Yml, Yfs, 2, 0x8e,4,0,0, + AMOVW, Yml, Ygs, 2, 0x8e,5,0,0, + +/* mov cr */ + AMOVL, Ycr0, Yml, 3, 0x0f,0x20,0,0, + AMOVL, Ycr2, Yml, 3, 0x0f,0x20,2,0, + AMOVL, Ycr3, Yml, 3, 0x0f,0x20,3,0, + AMOVL, Ycr4, Yml, 3, 0x0f,0x20,4,0, + + AMOVL, Yml, Ycr0, 4, 0x0f,0x22,0,0, + AMOVL, Yml, Ycr2, 4, 0x0f,0x22,2,0, + AMOVL, Yml, Ycr3, 4, 0x0f,0x22,3,0, + AMOVL, Yml, Ycr4, 4, 0x0f,0x22,4,0, + +/* mov dr */ + AMOVL, Ydr0, Yml, 3, 0x0f,0x21,0,0, + AMOVL, Ydr6, Yml, 3, 0x0f,0x21,6,0, + AMOVL, Ydr7, Yml, 3, 0x0f,0x21,7,0, + + AMOVL, Yml, Ydr0, 4, 0x0f,0x23,0,0, + AMOVL, Yml, Ydr6, 4, 0x0f,0x23,6,0, + AMOVL, Yml, Ydr7, 4, 0x0f,0x23,7,0, + +/* mov tr */ + AMOVL, Ytr6, Yml, 3, 0x0f,0x24,6,0, + AMOVL, Ytr7, Yml, 3, 0x0f,0x24,7,0, + + AMOVL, Yml, Ytr6, 4, 0x0f,0x26,6,E, + AMOVL, Yml, Ytr7, 4, 0x0f,0x26,7,E, + +/* lgdt, sgdt, lidt, sidt */ + AMOVL, Ym, Ygdtr, 4, 0x0f,0x01,2,0, + AMOVL, Ygdtr, Ym, 3, 0x0f,0x01,0,0, + AMOVL, Ym, Yidtr, 4, 0x0f,0x01,3,0, + AMOVL, Yidtr, Ym, 3, 0x0f,0x01,1,0, + +/* lldt, sldt */ + AMOVW, Yml, Yldtr, 4, 0x0f,0x00,2,0, + AMOVW, Yldtr, Yml, 3, 0x0f,0x00,0,0, + +/* lmsw, smsw */ + AMOVW, Yml, Ymsw, 4, 0x0f,0x01,6,0, + AMOVW, Ymsw, Yml, 3, 0x0f,0x01,4,0, + +/* ltr, str */ + AMOVW, Yml, Ytask, 4, 0x0f,0x00,3,0, + AMOVW, Ytask, Yml, 3, 0x0f,0x00,1,0, + +/* load full pointer */ + AMOVL, Yml, Ycol, 5, 0,0,0,0, + AMOVW, Yml, Ycol, 5, Pe,0,0,0, + +/* double shift */ + ASHLL, Ycol, Yml, 6, 0xa4,0xa5,0,0, + ASHRL, Ycol, Yml, 6, 0xac,0xad,0,0, + +/* extra imul */ + AIMULW, Yml, Yrl, 7, Pq,0xaf,0,0, + AIMULL, Yml, Yrl, 7, Pm,0xaf,0,0, + +/* load TLS base pointer */ + AMOVL, Ytls, Yrl, 8, 0,0,0,0, + + 0 +}; + +// byteswapreg returns a byte-addressable register (AX, BX, CX, DX) +// which is not referenced in a->type. +// If a is empty, it returns BX to account for MULB-like instructions +// that might use DX and AX. +static int +byteswapreg(Link *ctxt, Addr *a) +{ + int cana, canb, canc, cand; + + cana = canb = canc = cand = 1; + + switch(a->type) { + case D_NONE: + cana = cand = 0; + break; + case D_AX: + case D_AL: + case D_AH: + case D_INDIR+D_AX: + cana = 0; + break; + case D_BX: + case D_BL: + case D_BH: + case D_INDIR+D_BX: + canb = 0; + break; + case D_CX: + case D_CL: + case D_CH: + case D_INDIR+D_CX: + canc = 0; + break; + case D_DX: + case D_DL: + case D_DH: + case D_INDIR+D_DX: + cand = 0; + break; + } + switch(a->index) { + case D_AX: + cana = 0; + break; + case D_BX: + canb = 0; + break; + case D_CX: + canc = 0; + break; + case D_DX: + cand = 0; + break; + } + if(cana) + return D_AX; + if(canb) + return D_BX; + if(canc) + return D_CX; + if(cand) + return D_DX; + + ctxt->diag("impossible byte register"); + sysfatal("bad code"); + return 0; +} + +static void +subreg(Prog *p, int from, int to) +{ + + if(0 /* debug['Q'] */) + print("\n%P s/%R/%R/\n", p, from, to); + + if(p->from.type == from) { + p->from.type = to; + p->ft = 0; + } + if(p->to.type == from) { + p->to.type = to; + p->tt = 0; + } + + if(p->from.index == from) { + p->from.index = to; + p->ft = 0; + } + if(p->to.index == from) { + p->to.index = to; + p->tt = 0; + } + + from += D_INDIR; + if(p->from.type == from) { + p->from.type = to+D_INDIR; + p->ft = 0; + } + if(p->to.type == from) { + p->to.type = to+D_INDIR; + p->tt = 0; + } + + if(0 /* debug['Q'] */) + print("%P\n", p); +} + +static int +mediaop(Link *ctxt, Optab *o, int op, int osize, int z) +{ + switch(op){ + case Pm: + case Pe: + case Pf2: + case Pf3: + if(osize != 1){ + if(op != Pm) + *ctxt->andptr++ = op; + *ctxt->andptr++ = Pm; + op = o->op[++z]; + break; + } + default: + if(ctxt->andptr == ctxt->and || ctxt->andptr[-1] != Pm) + *ctxt->andptr++ = Pm; + break; + } + *ctxt->andptr++ = op; + return z; +} + +static void +doasm(Link *ctxt, Prog *p) +{ + Optab *o; + Prog *q, pp; + uchar *t; + int z, op, ft, tt, breg; + int32 v, pre; + Reloc rel, *r; + Addr *a; + + ctxt->curp = p; // TODO + + pre = prefixof(ctxt, &p->from); + if(pre) + *ctxt->andptr++ = pre; + pre = prefixof(ctxt, &p->to); + if(pre) + *ctxt->andptr++ = pre; + + if(p->ft == 0) + p->ft = oclass(&p->from); + if(p->tt == 0) + p->tt = oclass(&p->to); + + ft = p->ft * Ymax; + tt = p->tt * Ymax; + o = &optab[p->as]; + t = o->ytab; + if(t == 0) { + ctxt->diag("asmins: noproto %P", p); + return; + } + for(z=0; *t; z+=t[3],t+=4) + if(ycover[ft+t[0]]) + if(ycover[tt+t[1]]) + goto found; + goto domov; + +found: + switch(o->prefix) { + case Pq: /* 16 bit escape and opcode escape */ + *ctxt->andptr++ = Pe; + *ctxt->andptr++ = Pm; + break; + + case Pf2: /* xmm opcode escape */ + case Pf3: + *ctxt->andptr++ = o->prefix; + *ctxt->andptr++ = Pm; + break; + + case Pm: /* opcode escape */ + *ctxt->andptr++ = Pm; + break; + + case Pe: /* 16 bit escape */ + *ctxt->andptr++ = Pe; + break; + + case Pb: /* botch */ + break; + } + + op = o->op[z]; + switch(t[2]) { + default: + ctxt->diag("asmins: unknown z %d %P", t[2], p); + return; + + case Zpseudo: + break; + + case Zlit: + for(; op = o->op[z]; z++) + *ctxt->andptr++ = op; + break; + + case Zlitm_r: + for(; op = o->op[z]; z++) + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case Zm_r: + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case Zm2_r: + *ctxt->andptr++ = op; + *ctxt->andptr++ = o->op[z+1]; + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case Zm_r_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case Zm_r_i_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->from, reg[p->to.type]); + *ctxt->andptr++ = p->to.offset; + break; + + case Zibm_r: + while ((op = o->op[z++]) != 0) + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, reg[p->to.type]); + *ctxt->andptr++ = p->to.offset; + break; + + case Zaut_r: + *ctxt->andptr++ = 0x8d; /* leal */ + if(p->from.type != D_ADDR) + ctxt->diag("asmins: Zaut sb type ADDR"); + p->from.type = p->from.index; + p->from.index = D_NONE; + p->ft = 0; + asmand(ctxt, &p->from, reg[p->to.type]); + p->from.index = p->from.type; + p->from.type = D_ADDR; + p->ft = 0; + break; + + case Zm_o: + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, o->op[z+1]); + break; + + case Zr_m: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, reg[p->from.type]); + break; + + case Zr_m_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->to, reg[p->from.type]); + break; + + case Zr_m_i_xm: + mediaop(ctxt, o, op, t[3], z); + asmand(ctxt, &p->to, reg[p->from.type]); + *ctxt->andptr++ = p->from.offset; + break; + + case Zo_m: + case_Zo_m: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, o->op[z+1]); + break; + + case Zm_ibo: + *ctxt->andptr++ = op; + asmand(ctxt, &p->from, o->op[z+1]); + *ctxt->andptr++ = vaddr(ctxt, &p->to, nil); + break; + + case Zibo_m: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, o->op[z+1]); + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Z_ib: + case Zib_: + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; + v = vaddr(ctxt, a, nil); + *ctxt->andptr++ = op; + *ctxt->andptr++ = v; + break; + + case Zib_rp: + *ctxt->andptr++ = op + reg[p->to.type]; + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Zil_rp: + *ctxt->andptr++ = op + reg[p->to.type]; + if(o->prefix == Pe) { + v = vaddr(ctxt, &p->from, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, &p->from); + break; + + case Zib_rr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, reg[p->to.type]); + *ctxt->andptr++ = vaddr(ctxt, &p->from, nil); + break; + + case Z_il: + case Zil_: + if(t[2] == Zil_) + a = &p->from; + else + a = &p->to; + *ctxt->andptr++ = op; + if(o->prefix == Pe) { + v = vaddr(ctxt, a, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, a); + break; + + case Zm_ilo: + case Zilo_m: + *ctxt->andptr++ = op; + if(t[2] == Zilo_m) { + a = &p->from; + asmand(ctxt, &p->to, o->op[z+1]); + } else { + a = &p->to; + asmand(ctxt, &p->from, o->op[z+1]); + } + if(o->prefix == Pe) { + v = vaddr(ctxt, a, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, a); + break; + + case Zil_rr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, reg[p->to.type]); + if(o->prefix == Pe) { + v = vaddr(ctxt, &p->from, nil); + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + } + else + relput4(ctxt, p, &p->from); + break; + + case Z_rp: + *ctxt->andptr++ = op + reg[p->to.type]; + break; + + case Zrp_: + *ctxt->andptr++ = op + reg[p->from.type]; + break; + + case Zclr: + *ctxt->andptr++ = op; + asmand(ctxt, &p->to, reg[p->to.type]); + break; + + case Zcall: + if(p->to.sym == nil) { + ctxt->diag("call without target"); + sysfatal("bad code"); + } + *ctxt->andptr++ = op; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->type = R_CALL; + r->siz = 4; + r->sym = p->to.sym; + r->add = p->to.offset; + put4(ctxt, 0); + break; + + case Zbr: + case Zjmp: + case Zloop: + if(p->to.sym != nil) { + if(t[2] != Zjmp) { + ctxt->diag("branch to ATEXT"); + sysfatal("bad code"); + } + *ctxt->andptr++ = o->op[z+1]; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->sym = p->to.sym; + r->type = R_PCREL; + r->siz = 4; + put4(ctxt, 0); + break; + } + + // Assumes q is in this function. + // Fill in backward jump now. + q = p->pcond; + if(q == nil) { + ctxt->diag("jmp/branch/loop without target"); + sysfatal("bad code"); + } + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { + if(p->as == AJCXZW) + *ctxt->andptr++ = 0x67; + *ctxt->andptr++ = op; + *ctxt->andptr++ = v; + } else if(t[2] == Zloop) { + ctxt->diag("loop too far: %P", p); + } else { + v -= 5-2; + if(t[2] == Zbr) { + *ctxt->andptr++ = 0x0f; + v--; + } + *ctxt->andptr++ = o->op[z+1]; + *ctxt->andptr++ = v; + *ctxt->andptr++ = v>>8; + *ctxt->andptr++ = v>>16; + *ctxt->andptr++ = v>>24; + } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + if(p->as == AJCXZW) + *ctxt->andptr++ = 0x67; + *ctxt->andptr++ = op; + *ctxt->andptr++ = 0; + } else if(t[2] == Zloop) { + ctxt->diag("loop too far: %P", p); + } else { + if(t[2] == Zbr) + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = o->op[z+1]; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + *ctxt->andptr++ = 0; + } + break; + + case Zcallcon: + case Zjmpcon: + if(t[2] == Zcallcon) + *ctxt->andptr++ = op; + else + *ctxt->andptr++ = o->op[z+1]; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->type = R_PCREL; + r->siz = 4; + r->add = p->to.offset; + put4(ctxt, 0); + break; + + case Zcallind: + *ctxt->andptr++ = op; + *ctxt->andptr++ = o->op[z+1]; + r = addrel(ctxt->cursym); + r->off = p->pc + ctxt->andptr - ctxt->and; + r->type = R_ADDR; + r->siz = 4; + r->add = p->to.offset; + r->sym = p->to.sym; + put4(ctxt, 0); + break; + + case Zcallindreg: + r = addrel(ctxt->cursym); + r->off = p->pc; + r->type = R_CALLIND; + r->siz = 0; + goto case_Zo_m; + + case Zbyte: + v = vaddr(ctxt, &p->from, &rel); + if(rel.siz != 0) { + rel.siz = op; + r = addrel(ctxt->cursym); + *r = rel; + r->off = p->pc + ctxt->andptr - ctxt->and; + } + *ctxt->andptr++ = v; + if(op > 1) { + *ctxt->andptr++ = v>>8; + if(op > 2) { + *ctxt->andptr++ = v>>16; + *ctxt->andptr++ = v>>24; + } + } + break; + + case Zmov: + goto domov; + } + return; + +domov: + for(t=ymovtab; *t; t+=8) + if(p->as == t[0]) + if(ycover[ft+t[1]]) + if(ycover[tt+t[2]]) + goto mfound; +bad: + /* + * here, the assembly has failed. + * if its a byte instruction that has + * unaddressable registers, try to + * exchange registers and reissue the + * instruction with the operands renamed. + */ + pp = *p; + z = p->from.type; + if(z >= D_BP && z <= D_DI) { + if((breg = byteswapreg(ctxt, &p->to)) != D_AX) { + *ctxt->andptr++ = 0x87; /* xchg lhs,bx */ + asmand(ctxt, &p->from, reg[breg]); + subreg(&pp, z, breg); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x87; /* xchg lhs,bx */ + asmand(ctxt, &p->from, reg[breg]); + } else { + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ + subreg(&pp, z, D_AX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ + } + return; + } + z = p->to.type; + if(z >= D_BP && z <= D_DI) { + if((breg = byteswapreg(ctxt, &p->from)) != D_AX) { + *ctxt->andptr++ = 0x87; /* xchg rhs,bx */ + asmand(ctxt, &p->to, reg[breg]); + subreg(&pp, z, breg); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x87; /* xchg rhs,bx */ + asmand(ctxt, &p->to, reg[breg]); + } else { + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ + subreg(&pp, z, D_AX); + doasm(ctxt, &pp); + *ctxt->andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ + } + return; + } + ctxt->diag("doasm: notfound t2=%ux from=%ux to=%ux %P", t[2], p->from.type, p->to.type, p); + return; + +mfound: + switch(t[3]) { + default: + ctxt->diag("asmins: unknown mov %d %P", t[3], p); + break; + + case 0: /* lit */ + for(z=4; t[z]!=E; z++) + *ctxt->andptr++ = t[z]; + break; + + case 1: /* r,m */ + *ctxt->andptr++ = t[4]; + asmand(ctxt, &p->to, t[5]); + break; + + case 2: /* m,r */ + *ctxt->andptr++ = t[4]; + asmand(ctxt, &p->from, t[5]); + break; + + case 3: /* r,m - 2op */ + *ctxt->andptr++ = t[4]; + *ctxt->andptr++ = t[5]; + asmand(ctxt, &p->to, t[6]); + break; + + case 4: /* m,r - 2op */ + *ctxt->andptr++ = t[4]; + *ctxt->andptr++ = t[5]; + asmand(ctxt, &p->from, t[6]); + break; + + case 5: /* load full pointer, trash heap */ + if(t[4]) + *ctxt->andptr++ = t[4]; + switch(p->to.index) { + default: + goto bad; + case D_DS: + *ctxt->andptr++ = 0xc5; + break; + case D_SS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb2; + break; + case D_ES: + *ctxt->andptr++ = 0xc4; + break; + case D_FS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb4; + break; + case D_GS: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = 0xb5; + break; + } + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case 6: /* double shift */ + z = p->from.type; + switch(z) { + default: + goto bad; + case D_CONST: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = t[4]; + asmand(ctxt, &p->to, reg[p->from.index]); + *ctxt->andptr++ = p->from.offset; + break; + case D_CL: + case D_CX: + *ctxt->andptr++ = 0x0f; + *ctxt->andptr++ = t[5]; + asmand(ctxt, &p->to, reg[p->from.index]); + break; + } + break; + + case 7: /* imul rm,r */ + if(t[4] == Pq) { + *ctxt->andptr++ = Pe; + *ctxt->andptr++ = Pm; + } else + *ctxt->andptr++ = t[4]; + *ctxt->andptr++ = t[5]; + asmand(ctxt, &p->from, reg[p->to.type]); + break; + + case 8: /* mov tls, r */ + // NOTE: The systems listed here are the ones that use the "TLS initial exec" model, + // where you load the TLS base register into a register and then index off that + // register to access the actual TLS variables. Systems that allow direct TLS access + // are handled in prefixof above and should not be listed here. + switch(ctxt->headtype) { + default: + sysfatal("unknown TLS base location for %s", headstr(ctxt->headtype)); + + case Hlinux: + case Hnacl: + // ELF TLS base is 0(GS). + pp.from = p->from; + pp.from.type = D_INDIR+D_GS; + pp.from.offset = 0; + pp.from.index = D_NONE; + pp.from.scale = 0; + *ctxt->andptr++ = 0x65; // GS + *ctxt->andptr++ = 0x8B; + asmand(ctxt, &pp.from, reg[p->to.type]); + break; + + case Hplan9: + if(ctxt->plan9tos == nil) + ctxt->plan9tos = linklookup(ctxt, "_tos", 0); + memset(&pp.from, 0, sizeof pp.from); + pp.from.type = D_EXTERN; + pp.from.sym = ctxt->plan9tos; + pp.from.offset = 0; + pp.from.index = D_NONE; + *ctxt->andptr++ = 0x8B; + asmand(ctxt, &pp.from, reg[p->to.type]); + break; + + case Hwindows: + // Windows TLS base is always 0x14(FS). + pp.from = p->from; + pp.from.type = D_INDIR+D_FS; + pp.from.offset = 0x14; + pp.from.index = D_NONE; + pp.from.scale = 0; + *ctxt->andptr++ = 0x64; // FS + *ctxt->andptr++ = 0x8B; + asmand(ctxt, &pp.from, reg[p->to.type]); + break; + } + break; + } +} + +static uchar naclret[] = { + 0x5d, // POPL BP + // 0x8b, 0x7d, 0x00, // MOVL (BP), DI - catch return to invalid address, for debugging + 0x83, 0xe5, 0xe0, // ANDL $~31, BP + 0xff, 0xe5, // JMP BP +}; + +static void +asmins(Link *ctxt, Prog *p) +{ + Reloc *r; + + ctxt->andptr = ctxt->and; + + if(p->as == AUSEFIELD) { + r = addrel(ctxt->cursym); + r->off = 0; + r->sym = p->from.sym; + r->type = R_USEFIELD; + r->siz = 0; + return; + } + + if(ctxt->headtype == Hnacl) { + switch(p->as) { + case ARET: + memmove(ctxt->andptr, naclret, sizeof naclret); + ctxt->andptr += sizeof naclret; + return; + case ACALL: + case AJMP: + if(D_AX <= p->to.type && p->to.type <= D_DI) { + *ctxt->andptr++ = 0x83; + *ctxt->andptr++ = 0xe0 | (p->to.type - D_AX); + *ctxt->andptr++ = 0xe0; + } + break; + case AINT: + *ctxt->andptr++ = 0xf4; + return; + } + } + + doasm(ctxt, p); + if(ctxt->andptr > ctxt->and+sizeof ctxt->and) { + print("and[] is too short - %ld byte instruction\n", ctxt->andptr - ctxt->and); + sysfatal("bad code"); + } +} diff --git a/src/liblink/data.c b/src/liblink/data.c new file mode 100644 index 000000000..4504f4171 --- /dev/null +++ b/src/liblink/data.c @@ -0,0 +1,370 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +void +mangle(char *file) +{ + sysfatal("%s: mangled input file", file); +} + +void +symgrow(Link *ctxt, LSym *s, vlong lsiz) +{ + int32 siz; + + USED(ctxt); + + siz = (int32)lsiz; + if((vlong)siz != lsiz) + sysfatal("symgrow size %lld too long", lsiz); + + if(s->np >= siz) + return; + + if(s->np > s->maxp) { + ctxt->cursym = s; + sysfatal("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp); + } + + if(s->maxp < siz) { + if(s->maxp == 0) + s->maxp = 8; + while(s->maxp < siz) + s->maxp <<= 1; + s->p = erealloc(s->p, s->maxp); + memset(s->p+s->np, 0, s->maxp-s->np); + } + s->np = siz; +} + +void +savedata(Link *ctxt, LSym *s, Prog *p, char *pn) +{ + int32 off, siz, i, fl; + float32 flt; + uchar *cast; + vlong o; + Reloc *r; + + off = p->from.offset; + siz = ctxt->arch->datasize(p); + if(off < 0 || siz < 0 || off >= 1<<30 || siz >= 100) + mangle(pn); + symgrow(ctxt, s, off+siz); + + if(p->to.type == ctxt->arch->D_FCONST) { + switch(siz) { + default: + case 4: + flt = p->to.u.dval; + cast = (uchar*)&flt; + for(i=0; i<4; i++) + s->p[off+i] = cast[fnuxi4[i]]; + break; + case 8: + cast = (uchar*)&p->to.u.dval; + for(i=0; i<8; i++) + s->p[off+i] = cast[fnuxi8[i]]; + break; + } + } else if(p->to.type == ctxt->arch->D_SCONST) { + for(i=0; i<siz; i++) + s->p[off+i] = p->to.u.sval[i]; + } else if(p->to.type == ctxt->arch->D_CONST) { + if(p->to.sym) + goto addr; + o = p->to.offset; + fl = o; + cast = (uchar*)&fl; + switch(siz) { + default: + ctxt->diag("bad nuxi %d\n%P", siz, p); + break; + case 1: + s->p[off] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[off+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + } else if(p->to.type == ctxt->arch->D_ADDR) { + addr: + r = addrel(s); + r->off = off; + r->siz = siz; + r->sym = p->to.sym; + r->type = R_ADDR; + r->add = p->to.offset; + } else { + ctxt->diag("bad data: %P", p); + } +} + +Reloc* +addrel(LSym *s) +{ + if(s->nr >= s->maxr) { + if(s->maxr == 0) + s->maxr = 4; + else + s->maxr <<= 1; + s->r = erealloc(s->r, s->maxr*sizeof s->r[0]); + memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); + } + return &s->r[s->nr++]; +} + +vlong +setuintxx(Link *ctxt, LSym *s, vlong off, uint64 v, vlong wid) +{ + int32 i, fl; + vlong o; + uchar *cast; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + if(s->size < off+wid) { + s->size = off+wid; + symgrow(ctxt, s, s->size); + } + fl = v; + cast = (uchar*)&fl; + switch(wid) { + case 1: + s->p[off] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[off+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + o = v; + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + return off+wid; +} + +vlong +adduintxx(Link *ctxt, LSym *s, uint64 v, int wid) +{ + vlong off; + + off = s->size; + setuintxx(ctxt, s, off, v, wid); + return off; +} + +vlong +adduint8(Link *ctxt, LSym *s, uint8 v) +{ + return adduintxx(ctxt, s, v, 1); +} + +vlong +adduint16(Link *ctxt, LSym *s, uint16 v) +{ + return adduintxx(ctxt, s, v, 2); +} + +vlong +adduint32(Link *ctxt, LSym *s, uint32 v) +{ + return adduintxx(ctxt, s, v, 4); +} + +vlong +adduint64(Link *ctxt, LSym *s, uint64 v) +{ + return adduintxx(ctxt, s, v, 8); +} + +vlong +setuint8(Link *ctxt, LSym *s, vlong r, uint8 v) +{ + return setuintxx(ctxt, s, r, v, 1); +} + +vlong +setuint16(Link *ctxt, LSym *s, vlong r, uint16 v) +{ + return setuintxx(ctxt, s, r, v, 2); +} + +vlong +setuint32(Link *ctxt, LSym *s, vlong r, uint32 v) +{ + return setuintxx(ctxt, s, r, v, 4); +} + +vlong +setuint64(Link *ctxt, LSym *s, vlong r, uint64 v) +{ + return setuintxx(ctxt, s, r, v, 8); +} + +vlong +addaddrplus(Link *ctxt, LSym *s, LSym *t, vlong add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += ctxt->arch->ptrsize; + symgrow(ctxt, s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = ctxt->arch->ptrsize; + r->type = R_ADDR; + r->add = add; + return i + r->siz; +} + +vlong +addpcrelplus(Link *ctxt, LSym *s, LSym *t, vlong add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(ctxt, s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->add = add; + r->type = R_PCREL; + r->siz = 4; + return i + r->siz; +} + +vlong +addaddr(Link *ctxt, LSym *s, LSym *t) +{ + return addaddrplus(ctxt, s, t, 0); +} + +vlong +setaddrplus(Link *ctxt, LSym *s, vlong off, LSym *t, vlong add) +{ + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + if(off+ctxt->arch->ptrsize > s->size) { + s->size = off + ctxt->arch->ptrsize; + symgrow(ctxt, s, s->size); + } + r = addrel(s); + r->sym = t; + r->off = off; + r->siz = ctxt->arch->ptrsize; + r->type = R_ADDR; + r->add = add; + return off + r->siz; +} + +vlong +setaddr(Link *ctxt, LSym *s, vlong off, LSym *t) +{ + return setaddrplus(ctxt, s, off, t, 0); +} + +vlong +addsize(Link *ctxt, LSym *s, LSym *t) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += ctxt->arch->ptrsize; + symgrow(ctxt, s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = ctxt->arch->ptrsize; + r->type = R_SIZE; + return i + r->siz; +} + +vlong +addaddrplus4(Link *ctxt, LSym *s, LSym *t, vlong add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(ctxt, s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = 4; + r->type = R_ADDR; + r->add = add; + return i + r->siz; +} diff --git a/src/liblink/go.c b/src/liblink/go.c new file mode 100644 index 000000000..9f5a423d3 --- /dev/null +++ b/src/liblink/go.c @@ -0,0 +1,74 @@ +// Copyright 2009 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. + +// go-specific code shared across loaders (5l, 6l, 8l). + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +// replace all "". with pkg. +char* +expandpkg(char *t0, char *pkg) +{ + int n; + char *p; + char *w, *w0, *t; + + n = 0; + for(p=t0; (p=strstr(p, "\"\".")) != nil; p+=3) + n++; + + if(n == 0) + return estrdup(t0); + + w0 = emallocz(strlen(t0) + strlen(pkg)*n); + w = w0; + for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) { + memmove(w, t, p - t); + w += p-t; + strcpy(w, pkg); + w += strlen(pkg); + t = p+2; + } + strcpy(w, t); + return w0; +} + +void* +emallocz(long n) +{ + void *p; + + p = malloc(n); + if(p == nil) + sysfatal("out of memory"); + memset(p, 0, n); + return p; +} + +char* +estrdup(char *p) +{ + p = strdup(p); + if(p == nil) + sysfatal("out of memory"); + return p; +} + +void* +erealloc(void *p, long n) +{ + p = realloc(p, n); + if(p == nil) + sysfatal("out of memory"); + return p; +} + +void +double2ieee(uint64 *ieee, float64 f) +{ + memmove(ieee, &f, 8); +} diff --git a/src/liblink/ld.c b/src/liblink/ld.c new file mode 100644 index 000000000..9ea0e9a73 --- /dev/null +++ b/src/liblink/ld.c @@ -0,0 +1,258 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +void +addlib(Link *ctxt, char *src, char *obj, char *pathname) +{ + char name[1024], pname[1024], *p; + int i; + + if(strlen(pathname) >= sizeof name) + sysfatal("addlib pathname too long"); + strcpy(name, pathname); + cleanname(name); + + // runtime.a -> runtime + p = nil; + if(strlen(name) > 2 && name[strlen(name)-2] == '.') { + p = name+strlen(name)-2; + *p = '\0'; + } + + // already loaded? + for(i=0; i<ctxt->libraryp; i++) + if(strcmp(ctxt->library[i].pkg, name) == 0) + return; + + // runtime -> runtime.a for search + if(p != nil) + *p = '.'; + + if((!ctxt->windows && name[0] == '/') || (ctxt->windows && name[1] == ':')) + snprint(pname, sizeof pname, "%s", name); + else { + // try dot, -L "libdir", and then goroot. + for(i=0; i<ctxt->nlibdir; i++) { + snprint(pname, sizeof pname, "%s/%s", ctxt->libdir[i], name); + if(access(pname, AEXIST) >= 0) + break; + } + } + cleanname(pname); + + /* runtime.a -> runtime */ + if(p != nil) + *p = '\0'; + + if(ctxt->debugvlog > 1 && ctxt->bso) + Bprint(ctxt->bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); + + addlibpath(ctxt, src, obj, pname, name); +} + +/* + * add library to library list. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + */ +void +addlibpath(Link *ctxt, char *srcref, char *objref, char *file, char *pkg) +{ + int i; + Library *l; + + for(i=0; i<ctxt->libraryp; i++) + if(strcmp(file, ctxt->library[i].file) == 0) + return; + + if(ctxt->debugvlog > 1 && ctxt->bso) + Bprint(ctxt->bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", + cputime(), srcref, objref, file, pkg); + + if(ctxt->libraryp == ctxt->nlibrary){ + ctxt->nlibrary = 50 + 2*ctxt->libraryp; + ctxt->library = erealloc(ctxt->library, sizeof ctxt->library[0] * ctxt->nlibrary); + } + + l = &ctxt->library[ctxt->libraryp++]; + l->objref = estrdup(objref); + l->srcref = estrdup(srcref); + l->file = estrdup(file); + l->pkg = estrdup(pkg); +} + +int +find1(int32 l, int c) +{ + char *p; + int i; + + p = (char*)&l; + for(i=0; i<4; i++) + if(*p++ == c) + return i; + return 0; +} + +void +nuxiinit(void) +{ + int i, c; + + for(i=0; i<4; i++) { + c = find1(0x04030201L, i+1); + if(i < 2) + inuxi2[i] = c; + if(i < 1) + inuxi1[i] = c; + inuxi4[i] = c; + if(c == i) { + inuxi8[i] = c; + inuxi8[i+4] = c+4; + } else { + inuxi8[i] = c+4; + inuxi8[i+4] = c; + } + fnuxi4[i] = c; + fnuxi8[i] = c; + fnuxi8[i+4] = c+4; + } +} + +uchar fnuxi8[8]; +uchar fnuxi4[4]; +uchar inuxi1[1]; +uchar inuxi2[2]; +uchar inuxi4[4]; +uchar inuxi8[8]; + +#define LOG 5 +void +mkfwd(LSym *sym) +{ + Prog *p; + int i; + int32 dwn[LOG], cnt[LOG]; + Prog *lst[LOG]; + + for(i=0; i<LOG; i++) { + if(i == 0) + cnt[i] = 1; + else + cnt[i] = LOG * cnt[i-1]; + dwn[i] = 1; + lst[i] = nil; + } + i = 0; + for(p = sym->text; p != nil && p->link != nil; p = p->link) { + i--; + if(i < 0) + i = LOG-1; + p->forwd = nil; + dwn[i]--; + if(dwn[i] <= 0) { + dwn[i] = cnt[i]; + if(lst[i] != nil) + lst[i]->forwd = p; + lst[i] = p; + } + } +} + +Prog* +copyp(Link *ctxt, Prog *q) +{ + Prog *p; + + p = ctxt->arch->prg(); + *p = *q; + return p; +} + +Prog* +appendp(Link *ctxt, Prog *q) +{ + Prog *p; + + p = ctxt->arch->prg(); + p->link = q->link; + q->link = p; + p->lineno = q->lineno; + p->mode = q->mode; + return p; +} + +vlong +atolwhex(char *s) +{ + vlong n; + int f; + + n = 0; + f = 0; + while(*s == ' ' || *s == '\t') + s++; + if(*s == '-' || *s == '+') { + if(*s++ == '-') + f = 1; + while(*s == ' ' || *s == '\t') + s++; + } + if(s[0]=='0' && s[1]){ + if(s[1]=='x' || s[1]=='X'){ + s += 2; + for(;;){ + if(*s >= '0' && *s <= '9') + n = n*16 + *s++ - '0'; + else if(*s >= 'a' && *s <= 'f') + n = n*16 + *s++ - 'a' + 10; + else if(*s >= 'A' && *s <= 'F') + n = n*16 + *s++ - 'A' + 10; + else + break; + } + } else + while(*s >= '0' && *s <= '7') + n = n*8 + *s++ - '0'; + } else + while(*s >= '0' && *s <= '9') + n = n*10 + *s++ - '0'; + if(f) + n = -n; + return n; +} diff --git a/src/liblink/list5.c b/src/liblink/list5.c new file mode 100644 index 000000000..4a4e8c71f --- /dev/null +++ b/src/liblink/list5.c @@ -0,0 +1,356 @@ +// 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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/5l/5.out.h" + +enum +{ + STRINGSZ = 1000 +}; + +static int Aconv(Fmt *fp); +static int Dconv(Fmt *fp); +static int Mconv(Fmt *fp); +static int Pconv(Fmt *fp); +static int Rconv(Fmt *fp); +static int RAconv(Fmt *fp); +static int DSconv(Fmt *fp); + +#pragma varargck type "$" char* +#pragma varargck type "M" Addr* +#pragma varargck type "@" Addr* + +void +listinit5(void) +{ + fmtinstall('A', Aconv); + fmtinstall('D', Dconv); + fmtinstall('P', Pconv); + fmtinstall('R', Rconv); + + // for internal use + fmtinstall('$', DSconv); + fmtinstall('M', Mconv); + fmtinstall('@', RAconv); +} + +static char *extra [] = { + ".EQ", ".NE", ".CS", ".CC", + ".MI", ".PL", ".VS", ".VC", + ".HI", ".LS", ".GE", ".LT", + ".GT", ".LE", "", ".NV", +}; + +static Prog* bigP; + +static int +Pconv(Fmt *fp) +{ + char str[STRINGSZ], sc[20]; + Prog *p; + int a, s; + + p = va_arg(fp->args, Prog*); + bigP = p; + a = p->as; + s = p->scond; + strcpy(sc, extra[s & C_SCOND]); + if(s & C_SBIT) + strcat(sc, ".S"); + if(s & C_PBIT) + strcat(sc, ".P"); + if(s & C_WBIT) + strcat(sc, ".W"); + if(s & C_UBIT) /* ambiguous with FBIT */ + strcat(sc, ".U"); + if(a == AMOVM) { + if(p->from.type == D_CONST) + sprint(str, "%.5lld (%L) %A%s %@,%D", p->pc, p->lineno, a, sc, &p->from, &p->to); + else + if(p->to.type == D_CONST) + sprint(str, "%.5lld (%L) %A%s %D,%@", p->pc, p->lineno, a, sc, &p->from, &p->to); + else + sprint(str, "%.5lld (%L) %A%s %D,%D", p->pc, p->lineno, a, sc, &p->from, &p->to); + } else + if(a == ADATA) + sprint(str, "%.5lld (%L) %A %D/%d,%D", p->pc, p->lineno, a, &p->from, p->reg, &p->to); + else + if(p->as == ATEXT) + sprint(str, "%.5lld (%L) %A %D,%d,%D", p->pc, p->lineno, a, &p->from, p->reg, &p->to); + else + if(p->reg == NREG) + sprint(str, "%.5lld (%L) %A%s %D,%D", p->pc, p->lineno, a, sc, &p->from, &p->to); + else + if(p->from.type != D_FREG) + sprint(str, "%.5lld (%L) %A%s %D,R%d,%D", p->pc, p->lineno, a, sc, &p->from, p->reg, &p->to); + else + sprint(str, "%.5lld (%L) %A%s %D,F%d,%D", p->pc, p->lineno, a, sc, &p->from, p->reg, &p->to); + bigP = nil; + return fmtstrcpy(fp, str); +} + +static int +Aconv(Fmt *fp) +{ + char *s; + int a; + + a = va_arg(fp->args, int); + s = "???"; + if(a >= AXXX && a < ALAST) + s = anames5[a]; + return fmtstrcpy(fp, s); +} + +static int +Dconv(Fmt *fp) +{ + char str[STRINGSZ]; + Addr *a; + const char *op; + int v; + + a = va_arg(fp->args, Addr*); + switch(a->type) { + + default: + sprint(str, "GOK-type(%d)", a->type); + break; + + case D_NONE: + str[0] = 0; + if(a->name != D_NONE || a->reg != NREG || a->sym != nil) + sprint(str, "%M(R%d)(NONE)", a, a->reg); + break; + + case D_CONST: + if(a->reg != NREG) + sprint(str, "$%M(R%d)", a, a->reg); + else + sprint(str, "$%M", a); + break; + + case D_CONST2: + sprint(str, "$%lld-%d", a->offset, a->offset2); + break; + + case D_SHIFT: + v = a->offset; + op = &"<<>>->@>"[(((v>>5) & 3) << 1)]; + if(v & (1<<4)) + sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); + else + sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); + if(a->reg != NREG) + sprint(str+strlen(str), "(R%d)", a->reg); + break; + + case D_OREG: + if(a->reg != NREG) + sprint(str, "%M(R%d)", a, a->reg); + else + sprint(str, "%M", a); + break; + + case D_REG: + sprint(str, "R%d", a->reg); + if(a->name != D_NONE || a->sym != nil) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + + case D_FREG: + sprint(str, "F%d", a->reg); + if(a->name != D_NONE || a->sym != nil) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + + case D_PSR: + sprint(str, "PSR"); + if(a->name != D_NONE || a->sym != nil) + sprint(str, "%M(PSR)(REG)", a); + break; + + case D_BRANCH: + if(a->sym != nil) + sprint(str, "%s(SB)", a->sym->name); + else if(bigP != nil && bigP->pcond != nil) + sprint(str, "%lld", bigP->pcond->pc); + else if(a->u.branch != nil) + sprint(str, "%lld", a->u.branch->pc); + else + sprint(str, "%lld(PC)", a->offset/*-pc*/); + break; + + case D_FCONST: + sprint(str, "$%.17g", a->u.dval); + break; + + case D_SCONST: + sprint(str, "$\"%$\"", a->u.sval); + break; + } + return fmtstrcpy(fp, str); +} + +static int +RAconv(Fmt *fp) +{ + char str[STRINGSZ]; + Addr *a; + int i, v; + + a = va_arg(fp->args, Addr*); + sprint(str, "GOK-reglist"); + switch(a->type) { + case D_CONST: + case D_CONST2: + if(a->reg != NREG) + break; + if(a->sym != nil) + break; + v = a->offset; + strcpy(str, ""); + for(i=0; i<NREG; i++) { + if(v & (1<<i)) { + if(str[0] == 0) + strcat(str, "[R"); + else + strcat(str, ",R"); + sprint(strchr(str, 0), "%d", i); + } + } + strcat(str, "]"); + } + return fmtstrcpy(fp, str); +} + +static int +DSconv(Fmt *fp) +{ + int i, c; + char str[STRINGSZ], *p, *a; + + a = va_arg(fp->args, char*); + p = str; + for(i=0; i<NSNAME; i++) { + c = a[i] & 0xff; + if(c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z' || + c >= '0' && c <= '9' || + c == ' ' || c == '%') { + *p++ = c; + continue; + } + *p++ = '\\'; + switch(c) { + case 0: + *p++ = 'z'; + continue; + case '\\': + case '"': + *p++ = c; + continue; + case '\n': + *p++ = 'n'; + continue; + case '\t': + *p++ = 't'; + continue; + case '\r': + *p++ = 'r'; + continue; + case '\f': + *p++ = 'f'; + continue; + } + *p++ = (c>>6) + '0'; + *p++ = ((c>>3) & 7) + '0'; + *p++ = (c & 7) + '0'; + } + *p = 0; + return fmtstrcpy(fp, str); +} + +static int +Rconv(Fmt *fp) +{ + int r; + char str[STRINGSZ]; + + r = va_arg(fp->args, int); + sprint(str, "R%d", r); + return fmtstrcpy(fp, str); +} + +static int +Mconv(Fmt *fp) +{ + char str[STRINGSZ]; + Addr *a; + LSym *s; + + a = va_arg(fp->args, Addr*); + s = a->sym; + if(s == nil) { + sprint(str, "%d", (int)a->offset); + goto out; + } + switch(a->name) { + default: + sprint(str, "GOK-name(%d)", a->name); + break; + + case D_NONE: + sprint(str, "%lld", a->offset); + break; + + case D_EXTERN: + sprint(str, "%s+%d(SB)", s->name, (int)a->offset); + break; + + case D_STATIC: + sprint(str, "%s<>+%d(SB)", s->name, (int)a->offset); + break; + + case D_AUTO: + sprint(str, "%s-%d(SP)", s->name, (int)-a->offset); + break; + + case D_PARAM: + sprint(str, "%s+%d(FP)", s->name, (int)a->offset); + break; + } +out: + return fmtstrcpy(fp, str); +} diff --git a/src/liblink/list6.c b/src/liblink/list6.c new file mode 100644 index 000000000..fe708d877 --- /dev/null +++ b/src/liblink/list6.c @@ -0,0 +1,406 @@ +// Inferno utils/6c/list.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/6l/6.out.h" + +// +// Format conversions +// %A int Opcodes (instruction mnemonics) +// +// %D Addr* Addresses (instruction operands) +// Flags: "%lD": seperate the high and low words of a constant by "-" +// +// %P Prog* Instructions +// +// %R int Registers +// +// %$ char* String constant addresses (for internal use only) + +static int Aconv(Fmt *fp); +static int Dconv(Fmt *fp); +static int Pconv(Fmt *fp); +static int Rconv(Fmt *fp); +static int DSconv(Fmt *fp); + +enum +{ + STRINGSZ = 1000 +}; + +#pragma varargck type "$" char* + +void +listinit6(void) +{ + fmtinstall('A', Aconv); + fmtinstall('D', Dconv); + fmtinstall('P', Pconv); + fmtinstall('R', Rconv); + + // for internal use + fmtinstall('$', DSconv); +} + +static Prog* bigP; + +static int +Pconv(Fmt *fp) +{ + char str[STRINGSZ]; + Prog *p; + + p = va_arg(fp->args, Prog*); + bigP = p; + switch(p->as) { + case ADATA: + sprint(str, "%.5lld (%L) %A %D/%d,%D", + p->pc, p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + + case ATEXT: + if(p->from.scale) { + sprint(str, "%.5lld (%L) %A %D,%d,%lD", + p->pc, p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + } + sprint(str, "%.5lld (%L) %A %D,%lD", + p->pc, p->lineno, p->as, &p->from, &p->to); + break; + + default: + sprint(str, "%.5lld (%L) %A %D,%D", + p->pc, p->lineno, p->as, &p->from, &p->to); + break; + } + bigP = nil; + return fmtstrcpy(fp, str); +} + +static int +Aconv(Fmt *fp) +{ + int i; + + i = va_arg(fp->args, int); + return fmtstrcpy(fp, anames6[i]); +} + +static int +Dconv(Fmt *fp) +{ + char str[STRINGSZ], s[STRINGSZ]; + Addr *a; + int i; + + a = va_arg(fp->args, Addr*); + i = a->type; + + if(fp->flags & FmtLong) { + if(i == D_CONST) + sprint(str, "$%lld-%lld", a->offset&0xffffffffLL, a->offset>>32); + else { + // ATEXT dst is not constant + sprint(str, "!!%D", a); + } + goto brk; + } + + if(i >= D_INDIR) { + if(a->offset) + sprint(str, "%lld(%R)", a->offset, i-D_INDIR); + else + sprint(str, "(%R)", i-D_INDIR); + goto brk; + } + switch(i) { + default: + if(a->offset) + sprint(str, "$%lld,%R", a->offset, i); + else + sprint(str, "%R", i); + break; + + case D_NONE: + str[0] = 0; + break; + + case D_BRANCH: + if(a->sym != nil) + sprint(str, "%s(SB)", a->sym->name); + else if(bigP != nil && bigP->pcond != nil) + sprint(str, "%lld", bigP->pcond->pc); + else if(a->u.branch != nil) + sprint(str, "%lld", a->u.branch->pc); + else + sprint(str, "%lld(PC)", a->offset); + break; + + case D_EXTERN: + sprint(str, "%s+%lld(SB)", a->sym->name, a->offset); + break; + + case D_STATIC: + sprint(str, "%s<>+%lld(SB)", a->sym->name, a->offset); + break; + + case D_AUTO: + if(a->sym) + sprint(str, "%s+%lld(SP)", a->sym->name, a->offset); + else + sprint(str, "%lld(SP)", a->offset); + break; + + case D_PARAM: + if(a->sym) + sprint(str, "%s+%lld(FP)", a->sym->name, a->offset); + else + sprint(str, "%lld(FP)", a->offset); + break; + + case D_CONST: + sprint(str, "$%lld", a->offset); + break; + + case D_FCONST: + sprint(str, "$(%.17g)", a->u.dval); + break; + + case D_SCONST: + sprint(str, "$\"%$\"", a->u.sval); + break; + + case D_ADDR: + a->type = a->index; + a->index = D_NONE; + sprint(str, "$%D", a); + a->index = a->type; + a->type = D_ADDR; + goto conv; + } +brk: + if(a->index != D_NONE) { + sprint(s, "(%R*%d)", (int)a->index, (int)a->scale); + strcat(str, s); + } +conv: + return fmtstrcpy(fp, str); +} + +char* regstr[] = +{ + "AL", /* [D_AL] */ + "CL", + "DL", + "BL", + "SPB", + "BPB", + "SIB", + "DIB", + "R8B", + "R9B", + "R10B", + "R11B", + "R12B", + "R13B", + "R14B", + "R15B", + + "AX", /* [D_AX] */ + "CX", + "DX", + "BX", + "SP", + "BP", + "SI", + "DI", + "R8", + "R9", + "R10", + "R11", + "R12", + "R13", + "R14", + "R15", + + "AH", + "CH", + "DH", + "BH", + + "F0", /* [D_F0] */ + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + + "M0", + "M1", + "M2", + "M3", + "M4", + "M5", + "M6", + "M7", + + "X0", + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + "X8", + "X9", + "X10", + "X11", + "X12", + "X13", + "X14", + "X15", + + "CS", /* [D_CS] */ + "SS", + "DS", + "ES", + "FS", + "GS", + + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ + + "CR0", /* [D_CR] */ + "CR1", + "CR2", + "CR3", + "CR4", + "CR5", + "CR6", + "CR7", + "CR8", + "CR9", + "CR10", + "CR11", + "CR12", + "CR13", + "CR14", + "CR15", + + "DR0", /* [D_DR] */ + "DR1", + "DR2", + "DR3", + "DR4", + "DR5", + "DR6", + "DR7", + + "TR0", /* [D_TR] */ + "TR1", + "TR2", + "TR3", + "TR4", + "TR5", + "TR6", + "TR7", + + "TLS", /* [D_TLS] */ + "NONE", /* [D_NONE] */ +}; + +static int +Rconv(Fmt *fp) +{ + char str[STRINGSZ]; + int r; + + r = va_arg(fp->args, int); + if(r >= D_AL && r <= D_NONE) + sprint(str, "%s", regstr[r-D_AL]); + else + sprint(str, "gok(%d)", r); + + return fmtstrcpy(fp, str); +} + +static int +DSconv(Fmt *fp) +{ + int i, c; + char str[STRINGSZ], *p, *a; + + a = va_arg(fp->args, char*); + p = str; + for(i=0; i<sizeof(double); i++) { + c = a[i] & 0xff; + if(c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z' || + c >= '0' && c <= '9') { + *p++ = c; + continue; + } + *p++ = '\\'; + switch(c) { + default: + if(c < 040 || c >= 0177) + break; /* not portable */ + p[-1] = c; + continue; + case 0: + *p++ = 'z'; + continue; + case '\\': + case '"': + *p++ = c; + continue; + case '\n': + *p++ = 'n'; + continue; + case '\t': + *p++ = 't'; + continue; + } + *p++ = (c>>6) + '0'; + *p++ = ((c>>3) & 7) + '0'; + *p++ = (c & 7) + '0'; + } + *p = 0; + return fmtstrcpy(fp, str); +} diff --git a/src/liblink/list8.c b/src/liblink/list8.c new file mode 100644 index 000000000..786692416 --- /dev/null +++ b/src/liblink/list8.c @@ -0,0 +1,354 @@ +// Inferno utils/8c/list.c +// http://code.google.com/p/inferno-os/source/browse/utils/8c/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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/8l/8.out.h" + +static int Aconv(Fmt *fp); +static int Dconv(Fmt *fp); +static int Pconv(Fmt *fp); +static int Rconv(Fmt *fp); +static int DSconv(Fmt *fp); + +enum +{ + STRINGSZ = 1000 +}; + +#pragma varargck type "$" char* + +void +listinit8(void) +{ + fmtinstall('A', Aconv); + fmtinstall('D', Dconv); + fmtinstall('P', Pconv); + fmtinstall('R', Rconv); + + // for internal use + fmtinstall('$', DSconv); +} + +static Prog* bigP; + +static int +Pconv(Fmt *fp) +{ + char str[STRINGSZ]; + Prog *p; + + p = va_arg(fp->args, Prog*); + bigP = p; + switch(p->as) { + case ADATA: + sprint(str, "%.5lld (%L) %A %D/%d,%D", + p->pc, p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + + case ATEXT: + if(p->from.scale) { + sprint(str, "%.5lld (%L) %A %D,%d,%lD", + p->pc, p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + } + sprint(str, "%.5lld (%L) %A %D,%lD", + p->pc, p->lineno, p->as, &p->from, &p->to); + break; + + default: + sprint(str, "%.5lld (%L) %A %D,%D", + p->pc, p->lineno, p->as, &p->from, &p->to); + break; + } + bigP = nil; + return fmtstrcpy(fp, str); +} + +static int +Aconv(Fmt *fp) +{ + int i; + + i = va_arg(fp->args, int); + return fmtstrcpy(fp, anames8[i]); +} + +static int +Dconv(Fmt *fp) +{ + char str[STRINGSZ], s[STRINGSZ]; + Addr *a; + int i; + + a = va_arg(fp->args, Addr*); + i = a->type; + + if(fp->flags & FmtLong) { + if(i == D_CONST2) + sprint(str, "$%lld-%d", a->offset, a->offset2); + else { + // ATEXT dst is not constant + sprint(str, "!!%D", a); + } + goto brk; + } + + if(i >= D_INDIR) { + if(a->offset) + sprint(str, "%lld(%R)", a->offset, i-D_INDIR); + else + sprint(str, "(%R)", i-D_INDIR); + goto brk; + } + switch(i) { + default: + if(a->offset) + sprint(str, "$%lld,%R", a->offset, i); + else + sprint(str, "%R", i); + break; + + case D_NONE: + str[0] = 0; + break; + + case D_BRANCH: + if(a->sym != nil) + sprint(str, "%s(SB)", a->sym->name); + else if(bigP != nil && bigP->pcond != nil) + sprint(str, "%lld", bigP->pcond->pc); + else if(a->u.branch != nil) + sprint(str, "%lld", a->u.branch->pc); + else + sprint(str, "%lld(PC)", a->offset); + break; + + case D_EXTERN: + sprint(str, "%s+%lld(SB)", a->sym->name, a->offset); + break; + + case D_STATIC: + sprint(str, "%s<>+%lld(SB)", a->sym->name, a->offset); + break; + + case D_AUTO: + if(a->sym) + sprint(str, "%s+%lld(SP)", a->sym->name, a->offset); + else + sprint(str, "%lld(SP)", a->offset); + break; + + case D_PARAM: + if(a->sym) + sprint(str, "%s+%lld(FP)", a->sym->name, a->offset); + else + sprint(str, "%lld(FP)", a->offset); + break; + + case D_CONST: + sprint(str, "$%lld", a->offset); + break; + + case D_CONST2: + if(!(fp->flags & FmtLong)) { + // D_CONST2 outside of ATEXT should not happen + sprint(str, "!!$%lld-%d", a->offset, a->offset2); + } + break; + + case D_FCONST: + sprint(str, "$(%.17g)", a->u.dval); + break; + + case D_SCONST: + sprint(str, "$\"%$\"", a->u.sval); + break; + + case D_ADDR: + a->type = a->index; + a->index = D_NONE; + sprint(str, "$%D", a); + a->index = a->type; + a->type = D_ADDR; + goto conv; + } +brk: + if(a->index != D_NONE) { + sprint(s, "(%R*%d)", (int)a->index, (int)a->scale); + strcat(str, s); + } +conv: + return fmtstrcpy(fp, str); +} + +char* regstr[] = +{ + "AL", /* [D_AL] */ + "CL", + "DL", + "BL", + "AH", + "CH", + "DH", + "BH", + + "AX", /* [D_AX] */ + "CX", + "DX", + "BX", + "SP", + "BP", + "SI", + "DI", + + "F0", /* [D_F0] */ + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + + "CS", /* [D_CS] */ + "SS", + "DS", + "ES", + "FS", + "GS", + + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ + + "CR0", /* [D_CR] */ + "CR1", + "CR2", + "CR3", + "CR4", + "CR5", + "CR6", + "CR7", + + "DR0", /* [D_DR] */ + "DR1", + "DR2", + "DR3", + "DR4", + "DR5", + "DR6", + "DR7", + + "TR0", /* [D_TR] */ + "TR1", + "TR2", + "TR3", + "TR4", + "TR5", + "TR6", + "TR7", + + "X0", /* [D_X0] */ + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + + "TLS", /* [D_TLS] */ + "NONE", /* [D_NONE] */ +}; + +static int +Rconv(Fmt *fp) +{ + char str[STRINGSZ]; + int r; + + r = va_arg(fp->args, int); + if(r >= D_AL && r <= D_NONE) + sprint(str, "%s", regstr[r-D_AL]); + else + sprint(str, "gok(%d)", r); + + return fmtstrcpy(fp, str); +} + +static int +DSconv(Fmt *fp) +{ + int i, c; + char str[STRINGSZ], *p, *a; + + a = va_arg(fp->args, char*); + p = str; + for(i=0; i<sizeof(double); i++) { + c = a[i] & 0xff; + if(c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z' || + c >= '0' && c <= '9') { + *p++ = c; + continue; + } + *p++ = '\\'; + switch(c) { + default: + if(c < 040 || c >= 0177) + break; /* not portable */ + p[-1] = c; + continue; + case 0: + *p++ = 'z'; + continue; + case '\\': + case '"': + *p++ = c; + continue; + case '\n': + *p++ = 'n'; + continue; + case '\t': + *p++ = 't'; + continue; + } + *p++ = (c>>6) + '0'; + *p++ = ((c>>3) & 7) + '0'; + *p++ = (c & 7) + '0'; + } + *p = 0; + return fmtstrcpy(fp, str); +} diff --git a/src/liblink/obj.c b/src/liblink/obj.c new file mode 100644 index 000000000..b8083b0ec --- /dev/null +++ b/src/liblink/obj.c @@ -0,0 +1,296 @@ +// Copyright 2009 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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +enum +{ + HISTSZ = 10, + NSYM = 50, +}; + +int +linklinefmt(Link *ctxt, Fmt *fp) +{ + struct + { + Hist* incl; /* start of this include file */ + int32 idel; /* delta line number to apply to include */ + Hist* line; /* start of this #line directive */ + int32 ldel; /* delta line number to apply to #line */ + } a[HISTSZ]; + int32 lno, d; + int i, n; + Hist *h; + + lno = va_arg(fp->args, int32); + + n = 0; + for(h=ctxt->hist; h!=nil; h=h->link) { + if(h->offset < 0) + continue; + if(lno < h->line) + break; + if(h->name) { + if(h->offset > 0) { + // #line directive + if(n > 0 && n < HISTSZ) { + a[n-1].line = h; + a[n-1].ldel = h->line - h->offset + 1; + } + } else { + // beginning of file + if(n < HISTSZ) { + a[n].incl = h; + a[n].idel = h->line; + a[n].line = 0; + } + n++; + } + continue; + } + n--; + if(n > 0 && n < HISTSZ) { + d = h->line - a[n].incl->line; + a[n-1].ldel += d; + a[n-1].idel += d; + } + } + + if(n > HISTSZ) + n = HISTSZ; + + for(i=n-1; i>=0; i--) { + if(i != n-1) { + if(fp->flags & ~(FmtWidth|FmtPrec)) + break; + fmtprint(fp, " "); + } + if(ctxt->debugline || (fp->flags&FmtLong)) + fmtprint(fp, "%s/", ctxt->pathname); + if(a[i].line) + fmtprint(fp, "%s:%d[%s:%d]", + a[i].line->name, lno-a[i].ldel+1, + a[i].incl->name, lno-a[i].idel+1); + else + fmtprint(fp, "%s:%d", + a[i].incl->name, lno-a[i].idel+1); + lno = a[i].incl->line - 1; // now print out start of this file + } + if(n == 0) + fmtprint(fp, "<unknown line number>"); + + return 0; +} + +// Does s have t as a path prefix? +// That is, does s == t or does s begin with t followed by a slash? +// For portability, we allow ASCII case folding, so that haspathprefix("a/b/c", "A/B") is true. +// Similarly, we allow slash folding, so that haspathprefix("a/b/c", "a\\b") is true. +static int +haspathprefix(char *s, char *t) +{ + int i, cs, ct; + + if(t == nil) + return 0; + for(i=0; t[i]; i++) { + cs = s[i]; + ct = t[i]; + if('A' <= cs && cs <= 'Z') + cs += 'a' - 'A'; + if('A' <= ct && ct <= 'Z') + ct += 'a' - 'A'; + if(cs == '\\') + cs = '/'; + if(ct == '\\') + ct = '/'; + if(cs != ct) + return 0; + } + return s[i] == '\0' || s[i] == '/' || s[i] == '\\'; +} + +// This is a simplified copy of linklinefmt above. +// It doesn't allow printing the full stack, and it returns the file name and line number separately. +// TODO: Unify with linklinefmt somehow. +void +linkgetline(Link *ctxt, int32 line, LSym **f, int32 *l) +{ + struct + { + Hist* incl; /* start of this include file */ + int32 idel; /* delta line number to apply to include */ + Hist* line; /* start of this #line directive */ + int32 ldel; /* delta line number to apply to #line */ + } a[HISTSZ]; + int32 lno, d, dlno; + int n; + Hist *h; + char buf[1024], buf1[1024], *file; + + lno = line; + n = 0; + for(h=ctxt->hist; h!=nil; h=h->link) { + if(h->offset < 0) + continue; + if(lno < h->line) + break; + if(h->name) { + if(h->offset > 0) { + // #line directive + if(n > 0 && n < HISTSZ) { + a[n-1].line = h; + a[n-1].ldel = h->line - h->offset + 1; + } + } else { + // beginning of file + if(n < HISTSZ) { + a[n].incl = h; + a[n].idel = h->line; + a[n].line = 0; + } + n++; + } + continue; + } + n--; + if(n > 0 && n < HISTSZ) { + d = h->line - a[n].incl->line; + a[n-1].ldel += d; + a[n-1].idel += d; + } + } + + if(n > HISTSZ) + n = HISTSZ; + + if(n <= 0) { + *f = linklookup(ctxt, "??", HistVersion); + *l = 0; + return; + } + + n--; + if(a[n].line) { + file = a[n].line->name; + dlno = a[n].ldel-1; + } else { + file = a[n].incl->name; + dlno = a[n].idel-1; + } + if((!ctxt->windows && file[0] == '/') || (ctxt->windows && file[1] == ':') || file[0] == '<') + snprint(buf, sizeof buf, "%s", file); + else + snprint(buf, sizeof buf, "%s/%s", ctxt->pathname, file); + + // Remove leading ctxt->trimpath, or else rewrite $GOROOT to $GOROOT_FINAL. + if(haspathprefix(buf, ctxt->trimpath)) { + if(strlen(buf) == strlen(ctxt->trimpath)) + strcpy(buf, "??"); + else { + snprint(buf1, sizeof buf1, "%s", buf+strlen(ctxt->trimpath)+1); + if(buf1[0] == '\0') + strcpy(buf1, "??"); + strcpy(buf, buf1); + } + } else if(ctxt->goroot_final != nil && haspathprefix(buf, ctxt->goroot)) { + snprint(buf1, sizeof buf1, "%s%s", ctxt->goroot_final, buf+strlen(ctxt->goroot)); + strcpy(buf, buf1); + } + + lno -= dlno; + *f = linklookup(ctxt, buf, HistVersion); + *l = lno; +} + +void +linklinehist(Link *ctxt, int lineno, char *f, int offset) +{ + Hist *h; + + if(0) // debug['f'] + if(f) { + if(offset) + print("%4d: %s (#line %d)\n", lineno, f, offset); + else + print("%4d: %s\n", lineno, f); + } else + print("%4d: <pop>\n", lineno); + + h = malloc(sizeof(Hist)); + memset(h, 0, sizeof *h); + h->name = f; + h->line = lineno; + h->offset = offset; + h->link = nil; + if(ctxt->ehist == nil) { + ctxt->hist = h; + ctxt->ehist = h; + return; + } + ctxt->ehist->link = h; + ctxt->ehist = h; +} + +void +linkprfile(Link *ctxt, int32 l) +{ + int i, n; + Hist a[HISTSZ], *h; + int32 d; + + n = 0; + for(h = ctxt->hist; h != nil; h = h->link) { + if(l < h->line) + break; + if(h->name) { + if(h->offset == 0) { + if(n >= 0 && n < HISTSZ) + a[n] = *h; + n++; + continue; + } + if(n > 0 && n < HISTSZ) + if(a[n-1].offset == 0) { + a[n] = *h; + n++; + } else + a[n-1] = *h; + continue; + } + n--; + if(n >= 0 && n < HISTSZ) { + d = h->line - a[n].line; + for(i=0; i<n; i++) + a[i].line += d; + } + } + if(n > HISTSZ) + n = HISTSZ; + for(i=0; i<n; i++) + print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1)); +} + +/* + * start a new Prog list. + */ +Plist* +linknewplist(Link *ctxt) +{ + Plist *pl; + + pl = malloc(sizeof(*pl)); + memset(pl, 0, sizeof *pl); + if(ctxt->plist == nil) + ctxt->plist = pl; + else + ctxt->plast->link = pl; + ctxt->plast = pl; + + return pl; +} diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c new file mode 100644 index 000000000..ccd4c81c7 --- /dev/null +++ b/src/liblink/obj5.c @@ -0,0 +1,1068 @@ +// 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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/5l/5.out.h" +#include "../pkg/runtime/stack.h" + +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 int +symtype(Addr *a) +{ + return a->name; +} + +static int +isdata(Prog *p) +{ + return p->as == ADATA || p->as == AGLOBL; +} + +static int +iscall(Prog *p) +{ + return p->as == ABL; +} + +static int +datasize(Prog *p) +{ + return p->reg; +} + +static int +textflag(Prog *p) +{ + return p->reg; +} + +static void +settextflag(Prog *p, int f) +{ + p->reg = f; +} + +static void +progedit(Link *ctxt, Prog *p) +{ + char literal[64]; + LSym *s; + LSym *tlsfallback; + + p->from.class = 0; + p->to.class = 0; + + // Rewrite B/BL to symbol as D_BRANCH. + switch(p->as) { + case AB: + case ABL: + case ADUFFZERO: + case 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) { + case AMRC: + // If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it. + if(ctxt->goarm < 7 && (p->to.offset & 0xffff0fff) == 0xee1d0f70) { + tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0); + + // BL runtime.read_tls_fallback(SB) + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = tlsfallback; + p->to.offset = 0; + } else { + // 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)) { + int32 i32; + float32 f32; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + sprint(literal, "$f32.%08ux", (uint32)i32); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + 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; + } + break; + + 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)) { + int64 i64; + memmove(&i64, &p->from.u.dval, 8); + sprint(literal, "$f64.%016llux", (uvlong)i64); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + 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) { + // Shared libraries use R_ARM_TLS_IE32 instead of + // R_ARM_TLS_LE32, replacing the link time constant TLS offset in + // runtime.tlsgm with an address to a GOT entry containing the + // offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to + // compensate. + if(ctxt->gmsym == nil) + ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); + + if(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym) + p->from.type = D_OREG; + if(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym) + p->to.type = D_OREG; + } +} + +static Prog* +prg(void) +{ + Prog *p; + + p = emallocz(sizeof(*p)); + *p = zprg; + return p; +} + +static Prog* stacksplit(Link*, Prog*, int32, int); +static void initdiv(Link*); +static void softfloat(Link*, LSym*); + +// Prog.mark +enum +{ + FOLL = 1<<0, + LABEL = 1<<1, + LEAF = 1<<2, +}; + +static void +linkcase(Prog *casep) +{ + Prog *p; + + 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; + } + } +} + +static void +nocache(Prog *p) +{ + p->optab = 0; + p->from.class = 0; + p->to.class = 0; +} + +static void +addstacksplit(Link *ctxt, LSym *cursym) +{ + Prog *p, *pl, *q, *q1, *q2; + int o; + int32 autosize, autoffset; + + autosize = 0; + + if(ctxt->symmorestack[0] == nil) { + ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0); + ctxt->symmorestack[1] = 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 = p->to.offset; + if(autoffset < 0) + autoffset = 0; + cursym->locals = autoffset; + cursym->args = p->to.offset2; + + if(ctxt->debugzerostack) { + if(autoffset && !(p->reg&NOSPLIT)) { + // MOVW $4(R13), R1 + p = 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 = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = 13; + p->from.offset = 4 + autoffset; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW $0, R3 + p = 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 + p = pl = appendp(ctxt, p); + 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 = appendp(ctxt, p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + + p = 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) + linkcase(p); + break; + + case ATEXT: + p->mark |= LEAF; + break; + + case ARET: + break; + + case ADIV: + case ADIVU: + case AMOD: + case 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: + case ABX: + case ADUFFZERO: + case ADUFFCOPY: + cursym->text->mark &= ~LEAF; + + case ABCASE: + case AB: + + case ABEQ: + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + q1 = p->pcond; + if(q1 != nil) { + while(q1->as == ANOP) { + q1 = q1->link; + p->pcond = q1; + } + } + break; + } + q = p; + } + + for(p = cursym->text; p != nil; p = p->link) { + o = p->as; + switch(o) { + case ATEXT: + autosize = p->to.offset + 4; + if(autosize <= 4) + if(cursym->text->mark & LEAF) { + p->to.offset = -4; + autosize = 0; + } + + if(!autosize && !(cursym->text->mark & LEAF)) { + if(ctxt->debugvlog) { + Bprint(ctxt->bso, "save suppressed in: %s\n", + cursym->name); + Bflush(ctxt->bso); + } + cursym->text->mark |= LEAF; + } + if(cursym->text->mark & LEAF) { + cursym->leaf = 1; + if(!autosize) + break; + } + + if(!(p->reg & NOSPLIT)) + p = stacksplit(ctxt, p, autosize, !(cursym->text->reg&NEEDCTXT)); // emit split check + + // MOVW.W R14,$-autosize(SP) + p = 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 = -autosize; + p->to.reg = REGSP; + p->spadj = autosize; + + if(cursym->text->reg & WRAPPER) { + // g->panicwrap += autosize; + // MOVW panicwrap_offset(g), R3 + // ADD $autosize, R3 + // MOVW R3 panicwrap_offset(g) + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->from.offset = 2*ctxt->arch->ptrsize; + p->to.type = D_REG; + p->to.reg = 3; + + p = appendp(ctxt, p); + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 3; + + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = REGG; + p->to.offset = 2*ctxt->arch->ptrsize; + } + break; + + case ARET: + nocache(p); + if(cursym->text->mark & LEAF) { + if(!autosize) { + p->as = AB; + p->from = zprg.from; + if(p->to.sym) { // retjmp + p->to.type = D_BRANCH; + } else { + p->to.type = D_OREG; + p->to.offset = 0; + p->to.reg = REGLINK; + } + break; + } + } + + if(cursym->text->reg & WRAPPER) { + int scond; + + // Preserve original RET's cond, to allow RET.EQ + // in the implementation of reflect.call. + scond = p->scond; + p->scond = C_SCOND_NONE; + + // g->panicwrap -= autosize; + // MOVW panicwrap_offset(g), R3 + // SUB $autosize, R3 + // MOVW R3 panicwrap_offset(g) + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->from.offset = 2*ctxt->arch->ptrsize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(ctxt, p); + + p->as = ASUB; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(ctxt, p); + + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = REGG; + p->to.offset = 2*ctxt->arch->ptrsize; + p = appendp(ctxt, p); + + p->scond = scond; + } + + p->as = AMOVW; + p->scond |= C_PBIT; + p->from.type = D_OREG; + p->from.offset = 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) { // retjmp + p->to.reg = REGLINK; + q2 = appendp(ctxt, p); + q2->as = AB; + q2->to.type = D_BRANCH; + q2->to.sym = p->to.sym; + p->to.sym = nil; + p = q2; + } + break; + + case AADD: + if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) + p->spadj = -p->from.offset; + break; + + case ASUB: + if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) + p->spadj = p->from.offset; + break; + + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + if(ctxt->debugdivmod) + break; + if(p->from.type != D_REG) + break; + if(p->to.type != D_REG) + break; + q1 = p; + + /* MOV a,4(SP) */ + p = 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 = appendp(ctxt, p); + p->as = AMOVW; + p->lineno = q1->lineno; + p->from.type = D_REG; + p->from.reg = 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 = 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; + break; + case ADIVU: + p->to.sym = ctxt->sym_divu; + break; + case AMOD: + p->to.sym = ctxt->sym_mod; + break; + case AMODU: + p->to.sym = ctxt->sym_modu; + break; + } + + /* MOV REGTMP, b */ + p = 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 = 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 = 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; + + break; + case AMOVW: + if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP) + p->spadj = -p->to.offset; + if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC) + p->spadj = -p->from.offset; + if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP) + p->spadj = -p->from.offset; + break; + } + } +} + +static void +softfloat(Link *ctxt, LSym *cursym) +{ + Prog *p, *next; + LSym *symsfloat; + int wasfloat; + + if(ctxt->goarm > 5) + return; + + symsfloat = 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: + case AMOVWF: + case AMOVDW: + case AMOVFW: + case AMOVFD: + case AMOVDF: + case AMOVF: + case AMOVD: + + case ACMPF: + case ACMPD: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + case ASQRTF: + case ASQRTD: + case AABSF: + case AABSD: + goto soft; + + default: + goto notsoft; + + soft: + if (!wasfloat || (p->mark&LABEL)) { + next = ctxt->arch->prg(); + *next = *p; + + // BL _sfloat(SB) + *p = zprg; + p->link = next; + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = symsfloat; + p->lineno = next->lineno; + + p = next; + wasfloat = 1; + } + break; + + notsoft: + wasfloat = 0; + } + } +} + +static Prog* +stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt) +{ + int32 arg; + + // MOVW g_stackguard(g), R1 + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->to.type = D_REG; + p->to.reg = 1; + + if(framesize <= StackSmall) { + // small stack: SP < stackguard + // CMP stackguard, SP + p = appendp(ctxt, p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = REGSP; + } else if(framesize <= StackBig) { + // large stack: SP-framesize < stackguard-StackSmall + // MOVW $-framesize(SP), R2 + // CMP stackguard, R2 + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = -framesize; + p->to.type = D_REG; + p->to.reg = 2; + + p = 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 = appendp(ctxt, p); + p->as = ACMP; + p->from.type = D_CONST; + p->from.offset = (uint32)StackPreempt; + p->reg = 1; + + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = StackGuard; + p->to.type = D_REG; + p->to.reg = 2; + p->scond = C_SCOND_NE; + + p = 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 = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = framesize + (StackGuard - StackSmall); + p->to.type = D_REG; + p->to.reg = 3; + p->scond = C_SCOND_NE; + + p = 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 $framesize, R1 + p = appendp(ctxt, p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + p->from.offset = framesize; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW.LS $args, R2 + p = appendp(ctxt, p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + arg = ctxt->cursym->text->to.offset2; + if(arg == 1) // special marker for known 0 + arg = 0; + if(arg&3) + ctxt->diag("misaligned argument size in stack split"); + p->from.offset = arg; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW.LS R14, R3 + p = 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 = appendp(ctxt, p); + p->as = ABL; + p->scond = C_SCOND_LS; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[noctxt]; + + // BLS start + p = appendp(ctxt, p); + p->as = ABLS; + p->to.type = D_BRANCH; + p->pcond = ctxt->cursym->text->link; + + return p; +} + +static void +initdiv(Link *ctxt) +{ + if(ctxt->sym_div != nil) + return; + ctxt->sym_div = linklookup(ctxt, "_div", 0); + ctxt->sym_divu = linklookup(ctxt, "_divu", 0); + ctxt->sym_mod = linklookup(ctxt, "_mod", 0); + ctxt->sym_modu = linklookup(ctxt, "_modu", 0); +} + +static void xfol(Link*, Prog*, Prog**); + +static void +follow(Link *ctxt, LSym *s) +{ + Prog *firstp, *lastp; + + ctxt->cursym = s; + + firstp = ctxt->arch->prg(); + lastp = firstp; + xfol(ctxt, s->text, &lastp); + lastp->link = nil; + s->text = firstp->link; +} + +static int +relinv(int a) +{ + 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; + } + sysfatal("unknown relation: %s", anames5[a]); + return 0; +} + +static void +xfol(Link *ctxt, Prog *p, Prog **last) +{ + Prog *q, *r; + int a, i; + +loop: + if(p == nil) + return; + a = p->as; + if(a == AB) { + q = p->pcond; + if(q != nil && q->as != ATEXT) { + p->mark |= FOLL; + p = q; + if(!(p->mark & FOLL)) + goto loop; + } + } + if(p->mark & FOLL) { + for(i=0,q=p; i<4; i++,q=q->link) { + if(q == *last || q == nil) + break; + a = 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)) + continue; + if(a != ABEQ && a != ABNE) + continue; + copy: + for(;;) { + r = ctxt->arch->prg(); + *r = *p; + if(!(r->mark&FOLL)) + print("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)) + xfol(ctxt, r->link, last); + if(!(r->pcond->mark&FOLL)) + print("can't happen 2\n"); + return; + } + } + a = AB; + q = ctxt->arch->prg(); + q->as = 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 = brchain(ctxt, p->link); + if(a != ATEXT && a != ABCASE) + if(q != nil && (q->mark&FOLL)) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } + xfol(ctxt, p->link, last); + q = brchain(ctxt, p->pcond); + if(q == nil) + q = p->pcond; + if(q->mark&FOLL) { + p->pcond = q; + return; + } + p = q; + goto loop; + } + p = p->link; + goto loop; +} + +LinkArch linkarm = { + .name = "arm", + .thechar = '5', + + .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, + + .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/liblink/obj6.c b/src/liblink/obj6.c new file mode 100644 index 000000000..b1bcd0dc0 --- /dev/null +++ b/src/liblink/obj6.c @@ -0,0 +1,1171 @@ +// Inferno utils/6l/pass.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/6l/6.out.h" +#include "../pkg/runtime/stack.h" + +static Prog zprg = { + .back = 2, + .as = AGOK, + .from = { + .type = D_NONE, + .index = D_NONE, + }, + .to = { + .type = D_NONE, + .index = D_NONE, + }, +}; + +static void +nopout(Prog *p) +{ + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; +} + +static int +symtype(Addr *a) +{ + int t; + + t = a->type; + if(t == D_ADDR) + t = a->index; + return t; +} + +static int +isdata(Prog *p) +{ + return p->as == ADATA || p->as == AGLOBL; +} + +static int +iscall(Prog *p) +{ + return p->as == ACALL; +} + +static int +datasize(Prog *p) +{ + return p->from.scale; +} + +static int +textflag(Prog *p) +{ + return p->from.scale; +} + +static void +settextflag(Prog *p, int f) +{ + p->from.scale = f; +} + +static void nacladdr(Link*, Prog*, Addr*); + +static int +canuselocaltls(Link *ctxt) +{ + switch(ctxt->headtype) { +// case Hlinux: + case Hwindows: + return 0; + } + return 1; +} + +static void +progedit(Link *ctxt, Prog *p) +{ + char literal[64]; + LSym *s; + Prog *q; + + // Thread-local storage references use the TLS pseudo-register. + // As a register, TLS refers to the thread-local storage base, and it + // can only be loaded into another register: + // + // MOVQ TLS, AX + // + // An offset from the thread-local storage base is written off(reg)(TLS*1). + // Semantically it is off(reg), but the (TLS*1) annotation marks this as + // indexing from the loaded TLS base. This emits a relocation so that + // if the linker needs to adjust the offset, it can. For example: + // + // MOVQ TLS, AX + // MOVQ 8(AX)(TLS*1), CX // load m into CX + // + // On systems that support direct access to the TLS memory, this + // pair of instructions can be reduced to a direct TLS memory reference: + // + // MOVQ 8(TLS), CX // load m into CX + // + // The 2-instruction and 1-instruction forms correspond roughly to + // ELF TLS initial exec mode and ELF TLS local exec mode, respectively. + // + // We applies this rewrite on systems that support the 1-instruction form. + // The decision is made using only the operating system (and probably + // the -shared flag, eventually), not the link mode. If some link modes + // on a particular operating system require the 2-instruction form, + // then all builds for that operating system will use the 2-instruction + // form, so that the link mode decision can be delayed to link time. + // + // In this way, all supported systems use identical instructions to + // access TLS, and they are rewritten appropriately first here in + // liblink and then finally using relocations in the linker. + + if(canuselocaltls(ctxt)) { + // Reduce TLS initial exec model to TLS local exec model. + // Sequences like + // MOVQ TLS, BX + // ... off(BX)(TLS*1) ... + // become + // NOP + // ... off(TLS) ... + // + // TODO(rsc): Remove the Hsolaris special case. It exists only to + // guarantee we are producing byte-identical binaries as before this code. + // But it should be unnecessary. + if((p->as == AMOVQ || p->as == AMOVL) && p->from.type == D_TLS && D_AX <= p->to.type && p->to.type <= D_R15 && ctxt->headtype != Hsolaris) + nopout(p); + if(p->from.index == D_TLS && D_INDIR+D_AX <= p->from.type && p->from.type <= D_INDIR+D_R15) { + p->from.type = D_INDIR+D_TLS; + p->from.scale = 0; + p->from.index = D_NONE; + } + if(p->to.index == D_TLS && D_INDIR+D_AX <= p->to.type && p->to.type <= D_INDIR+D_R15) { + p->to.type = D_INDIR+D_TLS; + p->to.scale = 0; + p->to.index = D_NONE; + } + } else { + // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. + // The instruction + // MOVQ off(TLS), BX + // becomes the sequence + // MOVQ TLS, BX + // MOVQ off(BX)(TLS*1), BX + // This allows the C compilers to emit references to m and g using the direct off(TLS) form. + if((p->as == AMOVQ || p->as == AMOVL) && p->from.type == D_INDIR+D_TLS && D_AX <= p->to.type && p->to.type <= D_R15) { + q = appendp(ctxt, p); + q->as = p->as; + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->from.index = D_TLS; + q->from.scale = 2; // TODO: use 1 + q->to = p->to; + p->from.type = D_TLS; + p->from.index = D_NONE; + p->from.offset = 0; + } + } + + // TODO: Remove. + if(ctxt->headtype == Hwindows || ctxt->headtype == Hplan9) { + if(p->from.scale == 1 && p->from.index == D_TLS) + p->from.scale = 2; + if(p->to.scale == 1 && p->to.index == D_TLS) + p->to.scale = 2; + } + + if(ctxt->headtype == Hnacl) { + nacladdr(ctxt, p, &p->from); + nacladdr(ctxt, p, &p->to); + } + + // Maintain information about code generation mode. + if(ctxt->mode == 0) + ctxt->mode = 64; + p->mode = ctxt->mode; + + switch(p->as) { + case AMODE: + if(p->from.type == D_CONST || p->from.type == D_INDIR+D_NONE) { + switch((int)p->from.offset) { + case 16: + case 32: + case 64: + ctxt->mode = p->from.offset; + break; + } + } + nopout(p); + break; + } + + // Rewrite CALL/JMP/RET to symbol as D_BRANCH. + switch(p->as) { + case ACALL: + case AJMP: + case ARET: + if((p->to.type == D_EXTERN || p->to.type == D_STATIC) && p->to.sym != nil) + p->to.type = D_BRANCH; + break; + } + + // Rewrite float constants to values stored in memory. + switch(p->as) { + case AFMOVF: + case AFADDF: + case AFSUBF: + case AFSUBRF: + case AFMULF: + case AFDIVF: + case AFDIVRF: + case AFCOMF: + case AFCOMFP: + case AMOVSS: + case AADDSS: + case ASUBSS: + case AMULSS: + case ADIVSS: + case ACOMISS: + case AUCOMISS: + if(p->from.type == D_FCONST) { + int32 i32; + float32 f32; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + sprint(literal, "$f32.%08ux", (uint32)i32); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + adduint32(ctxt, s, i32); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + break; + + case AFMOVD: + case AFADDD: + case AFSUBD: + case AFSUBRD: + case AFMULD: + case AFDIVD: + case AFDIVRD: + case AFCOMD: + case AFCOMDP: + case AMOVSD: + case AADDSD: + case ASUBSD: + case AMULSD: + case ADIVSD: + case ACOMISD: + case AUCOMISD: + if(p->from.type == D_FCONST) { + int64 i64; + memmove(&i64, &p->from.u.dval, 8); + sprint(literal, "$f64.%016llux", (uvlong)i64); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + adduint64(ctxt, s, i64); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + break; + } +} + +static void +nacladdr(Link *ctxt, Prog *p, Addr *a) +{ + if(p->as == ALEAL || p->as == ALEAQ) + return; + + if(a->type == D_BP || a->type == D_INDIR+D_BP) { + ctxt->diag("invalid address: %P", p); + return; + } + if(a->type == D_INDIR+D_TLS) + a->type = D_INDIR+D_BP; + else if(a->type == D_TLS) + a->type = D_BP; + if(D_INDIR <= a->type && a->type <= D_INDIR+D_INDIR) { + switch(a->type) { + case D_INDIR+D_BP: + case D_INDIR+D_SP: + case D_INDIR+D_R15: + // all ok + break; + default: + if(a->index != D_NONE) + ctxt->diag("invalid address %P", p); + a->index = a->type - D_INDIR; + if(a->index != D_NONE) + a->scale = 1; + a->type = D_INDIR+D_R15; + break; + } + } +} + +static char* +morename[] = +{ + "runtime.morestack00", + "runtime.morestack00_noctxt", + "runtime.morestack10", + "runtime.morestack10_noctxt", + "runtime.morestack01", + "runtime.morestack01_noctxt", + "runtime.morestack11", + "runtime.morestack11_noctxt", + + "runtime.morestack8", + "runtime.morestack8_noctxt", + "runtime.morestack16", + "runtime.morestack16_noctxt", + "runtime.morestack24", + "runtime.morestack24_noctxt", + "runtime.morestack32", + "runtime.morestack32_noctxt", + "runtime.morestack40", + "runtime.morestack40_noctxt", + "runtime.morestack48", + "runtime.morestack48_noctxt", +}; + +static Prog* load_g_cx(Link*, Prog*); +static Prog* stacksplit(Link*, Prog*, int32, int32, int, Prog**); +static void indir_cx(Link*, Addr*); + +static void +parsetextconst(vlong arg, vlong *textstksiz, vlong *textarg) +{ + *textstksiz = arg & 0xffffffffLL; + if(*textstksiz & 0x80000000LL) + *textstksiz = -(-*textstksiz & 0xffffffffLL); + + *textarg = (arg >> 32) & 0xffffffffLL; + if(*textarg & 0x80000000LL) + *textarg = 0; + *textarg = (*textarg+7) & ~7LL; +} + +static void +addstacksplit(Link *ctxt, LSym *cursym) +{ + Prog *p, *q, *q1; + int32 autoffset, deltasp; + int a, pcsize; + uint32 i; + vlong textstksiz, textarg; + + if(ctxt->gmsym == nil) + ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); + if(ctxt->symmorestack[0] == nil) { + if(nelem(morename) > nelem(ctxt->symmorestack)) + sysfatal("Link.symmorestack needs at least %d elements", nelem(morename)); + for(i=0; i<nelem(morename); i++) + ctxt->symmorestack[i] = linklookup(ctxt, morename[i], 0); + } + ctxt->cursym = cursym; + + if(cursym->text == nil || cursym->text->link == nil) + return; + + p = cursym->text; + parsetextconst(p->to.offset, &textstksiz, &textarg); + autoffset = textstksiz; + if(autoffset < 0) + autoffset = 0; + + cursym->args = p->to.offset>>32; + cursym->locals = textstksiz; + + if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) { + for(q = p; q != nil; q = q->link) { + if(q->as == ACALL) + goto noleaf; + if((q->as == ADUFFCOPY || q->as == ADUFFZERO) && autoffset >= StackSmall - 8) + goto noleaf; + } + p->from.scale |= NOSPLIT; + noleaf:; + } + + q = nil; + if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) { + p = appendp(ctxt, p); + p = load_g_cx(ctxt, p); // load g into CX + } + if(!(cursym->text->from.scale & NOSPLIT)) + p = stacksplit(ctxt, p, autoffset, textarg, !(cursym->text->from.scale&NEEDCTXT), &q); // emit split check + + if(autoffset) { + if(autoffset%ctxt->arch->regsize != 0) + ctxt->diag("unaligned stack size %d", autoffset); + p = appendp(ctxt, p); + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = autoffset; + p->spadj = autoffset; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(ctxt, p); + p->as = ANOP; + p->spadj = -ctxt->arch->ptrsize; + p = appendp(ctxt, p); + p->as = ANOP; + p->spadj = ctxt->arch->ptrsize; + } + if(q != nil) + q->pcond = p; + deltasp = autoffset; + + if(cursym->text->from.scale & WRAPPER) { + // g->panicwrap += autoffset + ctxt->arch->regsize; + p = appendp(ctxt, p); + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = autoffset + ctxt->arch->regsize; + indir_cx(ctxt, &p->to); + p->to.offset = 2*ctxt->arch->ptrsize; + } + + if(ctxt->debugstack > 1 && autoffset) { + // 6l -K -K means double-check for stack overflow + // even after calling morestack and even if the + // function is marked as nosplit. + p = appendp(ctxt, p); + p->as = AMOVQ; + indir_cx(ctxt, &p->from); + p->from.offset = 0; + p->to.type = D_BX; + + p = appendp(ctxt, p); + p->as = ASUBQ; + p->from.type = D_CONST; + p->from.offset = StackSmall+32; + p->to.type = D_BX; + + p = appendp(ctxt, p); + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_BX; + + p = appendp(ctxt, p); + p->as = AJHI; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(ctxt, p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(ctxt, p); + p->as = ANOP; + q1->pcond = p; + } + + if(ctxt->debugzerostack && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 6l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(ctxt, p); + p->as = AMOVQ; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(ctxt, p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = autoffset/8; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = AREP; + + p = appendp(ctxt, p); + p->as = ASTOSQ; + } + + for(; p != nil; p = p->link) { + pcsize = p->mode/8; + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + pcsize; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + pcsize; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHQ: + case APUSHFQ: + deltasp += 8; + p->spadj = 8; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPQ: + case APOPFQ: + deltasp -= 8; + p->spadj = -8; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + ctxt->diag("unbalanced PUSH/POP"); + + if(cursym->text->from.scale & WRAPPER) { + p = load_g_cx(ctxt, p); + p = appendp(ctxt, p); + // g->panicwrap -= autoffset + ctxt->arch->regsize; + p->as = ASUBL; + p->from.type = D_CONST; + p->from.offset = autoffset + ctxt->arch->regsize; + indir_cx(ctxt, &p->to); + p->to.offset = 2*ctxt->arch->ptrsize; + p = appendp(ctxt, p); + p->as = ARET; + } + + if(autoffset) { + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = -autoffset; + p->spadj = -autoffset; + p = appendp(ctxt, p); + p->as = ARET; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so undo + // the cleanup. + p->spadj = +autoffset; + } + if(p->to.sym) // retjmp + p->as = AJMP; + } +} + +static void +indir_cx(Link *ctxt, Addr *a) +{ + if(ctxt->headtype == Hnacl) { + a->type = D_INDIR + D_R15; + a->index = D_CX; + a->scale = 1; + return; + } + + a->type = D_INDIR+D_CX; +} + +// Append code to p to load g into cx. +// Overwrites p with the first instruction (no first appendp). +// Overwriting p is unusual but it lets use this in both the +// prologue (caller must call appendp first) and in the epilogue. +// Returns last new instruction. +static Prog* +load_g_cx(Link *ctxt, Prog *p) +{ + Prog *next; + + p->as = AMOVQ; + if(ctxt->arch->ptrsize == 4) + p->as = AMOVL; + p->from.type = D_INDIR+D_TLS; + p->from.offset = 0; + p->to.type = D_CX; + + next = p->link; + progedit(ctxt, p); + while(p->link != next) + p = p->link; + + if(p->from.index == D_TLS) + p->from.scale = 2; + + return p; +} + +// Append code to p to check for stack split. +// Appends to (does not overwrite) p. +// Assumes g is in CX. +// Returns last new instruction. +// On return, *jmpok is the instruction that should jump +// to the stack frame allocation if no split is needed. +static Prog* +stacksplit(Link *ctxt, Prog *p, int32 framesize, int32 textarg, int noctxt, Prog **jmpok) +{ + Prog *q, *q1; + uint32 moreconst1, moreconst2, i; + int cmp, lea, mov, sub; + + cmp = ACMPQ; + lea = ALEAQ; + mov = AMOVQ; + sub = ASUBQ; + + if(ctxt->headtype == Hnacl) { + cmp = ACMPL; + lea = ALEAL; + mov = AMOVL; + sub = ASUBL; + } + + if(ctxt->debugstack) { + // 6l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info + + p = appendp(ctxt, p); + p->as = cmp; + indir_cx(ctxt, &p->from); + p->from.offset = 8; + p->to.type = D_SP; + + p = appendp(ctxt, p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; + + p = appendp(ctxt, p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(ctxt, p); + p->as = ANOP; + q1->pcond = p; + } + + q1 = nil; + if(framesize <= StackSmall) { + // small stack: SP <= stackguard + // CMPQ SP, stackguard + p = appendp(ctxt, p); + p->as = cmp; + p->from.type = D_SP; + indir_cx(ctxt, &p->to); + } else if(framesize <= StackBig) { + // large stack: SP-framesize <= stackguard-StackSmall + // LEAQ -xxx(SP), AX + // CMPQ AX, stackguard + p = appendp(ctxt, p); + p->as = lea; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(framesize-StackSmall); + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = cmp; + p->from.type = D_AX; + indir_cx(ctxt, &p->to); + } 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. + // + // Preemption sets stackguard to StackPreempt, a very large value. + // That breaks the math above, so we have to check for that explicitly. + // MOVQ stackguard, CX + // CMPQ CX, $StackPreempt + // JEQ label-of-call-to-morestack + // LEAQ StackGuard(SP), AX + // SUBQ CX, AX + // CMPQ AX, $(framesize+(StackGuard-StackSmall)) + + p = appendp(ctxt, p); + p->as = mov; + indir_cx(ctxt, &p->from); + p->from.offset = 0; + p->to.type = D_SI; + + p = appendp(ctxt, p); + p->as = cmp; + p->from.type = D_SI; + p->to.type = D_CONST; + p->to.offset = StackPreempt; + + p = appendp(ctxt, p); + p->as = AJEQ; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(ctxt, p); + p->as = lea; + p->from.type = D_INDIR+D_SP; + p->from.offset = StackGuard; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = sub; + p->from.type = D_SI; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = cmp; + p->from.type = D_AX; + p->to.type = D_CONST; + p->to.offset = framesize+(StackGuard-StackSmall); + } + + // common + p = appendp(ctxt, p); + p->as = AJHI; + p->to.type = D_BRANCH; + q = p; + + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. + moreconst1 = 0; + if(StackTop + textarg + ctxt->arch->ptrsize + framesize + ctxt->arch->ptrsize + StackLimit >= StackMin) + moreconst1 = framesize; + moreconst2 = textarg; + if(moreconst2 == 1) // special marker + moreconst2 = 0; + if((moreconst2&7) != 0) + ctxt->diag("misaligned argument size in stack split"); + // 4 varieties varieties (const1==0 cross const2==0) + // and 6 subvarieties of (const1==0 and const2!=0) + p = appendp(ctxt, p); + if(moreconst1 == 0 && moreconst2 == 0) { + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[0*2+noctxt]; + } else + if(moreconst1 != 0 && moreconst2 == 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst1; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[1*2+noctxt]; + } else + if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { + i = moreconst2/8 + 3; + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[i*2+noctxt]; + } else + if(moreconst1 == 0 && moreconst2 != 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst2; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[2*2+noctxt]; + } else { + // Pass framesize and argsize. + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = (uint64)moreconst2 << 32; + p->from.offset |= moreconst1; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[3*2+noctxt]; + } + + p = appendp(ctxt, p); + p->as = AJMP; + p->to.type = D_BRANCH; + p->pcond = ctxt->cursym->text->link; + + if(q != nil) + q->pcond = p->link; + if(q1 != nil) + q1->pcond = q->link; + + *jmpok = q; + return p; +} + +static void xfol(Link*, Prog*, Prog**); + +static void +follow(Link *ctxt, LSym *s) +{ + Prog *firstp, *lastp; + + ctxt->cursym = s; + + firstp = ctxt->arch->prg(); + lastp = firstp; + xfol(ctxt, s->text, &lastp); + lastp->link = nil; + s->text = firstp->link; +} + +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETQ: + case AIRETW: + case ARETFL: + case ARETFQ: + case ARETFW: + case AUNDEF: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHQ: + case APUSHFQ: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPQ: + case APOPFQ: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static int +relinv(int a) +{ + switch(a) { + case AJEQ: return AJNE; + case AJNE: return AJEQ; + case AJLE: return AJGT; + case AJLS: return AJHI; + case AJLT: return AJGE; + case AJMI: return AJPL; + case AJGE: return AJLT; + case AJPL: return AJMI; + case AJGT: return AJLE; + case AJHI: return AJLS; + case AJCS: return AJCC; + case AJCC: return AJCS; + case AJPS: return AJPC; + case AJPC: return AJPS; + case AJOS: return AJOC; + case AJOC: return AJOS; + } + sysfatal("unknown relation: %s", anames6[a]); + return 0; +} + +static void +xfol(Link *ctxt, Prog *p, Prog **last) +{ + Prog *q; + int i; + enum as a; + +loop: + if(p == nil) + return; + if(p->as == AJMP) + if((q = p->pcond) != nil && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ + p->mark = 1; + p = q; + if(p->mark == 0) + goto loop; + } + if(p->mark) { + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ + for(i=0,q=p; i<4; i++,q=q->link) { + if(q == nil) + break; + if(q == *last) + break; + a = q->as; + if(a == ANOP) { + i--; + continue; + } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy + if(q->pcond == nil || q->pcond->mark) + continue; + if(a == ACALL || a == ALOOP) + continue; + for(;;) { + if(p->as == ANOP) { + p = p->link; + continue; + } + q = copyp(ctxt, p); + p = p->link; + q->mark = 1; + (*last)->link = q; + *last = q; + if(q->as != a || q->pcond == nil || q->pcond->mark) + continue; + + q->as = relinv(q->as); + p = q->pcond; + q->pcond = q->link; + q->link = p; + xfol(ctxt, q->link, last); + p = q->link; + if(p->mark) + return; + goto loop; + } + } /* */ + q = ctxt->arch->prg(); + q->as = AJMP; + q->lineno = p->lineno; + q->to.type = D_BRANCH; + q->to.offset = p->pc; + q->pcond = p; + p = q; + } + + /* emit p */ + p->mark = 1; + (*last)->link = p; + *last = p; + a = p->as; + + /* continue loop with what comes after p */ + if(nofollow(a)) + return; + if(p->pcond != nil && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ + if((q = brchain(ctxt, p->pcond)) != nil) + p->pcond = q; + if((q = brchain(ctxt, p->link)) != nil) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } + } + xfol(ctxt, p->link, last); + if(p->pcond->mark) + return; + p = p->pcond; + goto loop; + } + p = p->link; + goto loop; +} + +static Prog* +prg(void) +{ + Prog *p; + + p = emallocz(sizeof(*p)); + *p = zprg; + return p; +} + +LinkArch linkamd64 = { + .name = "amd64", + .thechar = '6', + + .addstacksplit = addstacksplit, + .assemble = span6, + .datasize = datasize, + .follow = follow, + .iscall = iscall, + .isdata = isdata, + .prg = prg, + .progedit = progedit, + .settextflag = settextflag, + .symtype = symtype, + .textflag = textflag, + + .minlc = 1, + .ptrsize = 8, + .regsize = 8, + + .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, + + .ACALL = ACALL, + .ADATA = ADATA, + .AEND = AEND, + .AFUNCDATA = AFUNCDATA, + .AGLOBL = AGLOBL, + .AJMP = AJMP, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .ATYPE = ATYPE, + .AUSEFIELD = AUSEFIELD, +}; + +LinkArch linkamd64p32 = { + .name = "amd64p32", + .thechar = '6', + + .addstacksplit = addstacksplit, + .assemble = span6, + .datasize = datasize, + .follow = follow, + .iscall = iscall, + .isdata = isdata, + .prg = prg, + .progedit = progedit, + .settextflag = settextflag, + .symtype = symtype, + .textflag = textflag, + + .minlc = 1, + .ptrsize = 4, + .regsize = 8, + + .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, + + .ACALL = ACALL, + .ADATA = ADATA, + .AEND = AEND, + .AFUNCDATA = AFUNCDATA, + .AGLOBL = AGLOBL, + .AJMP = AJMP, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .ATYPE = ATYPE, + .AUSEFIELD = AUSEFIELD, +}; diff --git a/src/liblink/obj8.c b/src/liblink/obj8.c new file mode 100644 index 000000000..72934c149 --- /dev/null +++ b/src/liblink/obj8.c @@ -0,0 +1,859 @@ +// Inferno utils/8l/pass.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/pass.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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/8l/8.out.h" +#include "../pkg/runtime/stack.h" + +static Prog zprg = { + .back = 2, + .as = AGOK, + .from = { + .type = D_NONE, + .index = D_NONE, + .scale = 1, + }, + .to = { + .type = D_NONE, + .index = D_NONE, + .scale = 1, + }, +}; + +static int +symtype(Addr *a) +{ + int t; + + t = a->type; + if(t == D_ADDR) + t = a->index; + return t; +} + +static int +isdata(Prog *p) +{ + return p->as == ADATA || p->as == AGLOBL; +} + +static int +iscall(Prog *p) +{ + return p->as == ACALL; +} + +static int +datasize(Prog *p) +{ + return p->from.scale; +} + +static int +textflag(Prog *p) +{ + return p->from.scale; +} + +static void +settextflag(Prog *p, int f) +{ + p->from.scale = f; +} + +static int +canuselocaltls(Link *ctxt) +{ + switch(ctxt->headtype) { + case Hlinux: + case Hnacl: + case Hplan9: + case Hwindows: + return 0; + } + return 1; +} + +static void +progedit(Link *ctxt, Prog *p) +{ + char literal[64]; + LSym *s; + Prog *q; + + // See obj6.c for discussion of TLS. + if(canuselocaltls(ctxt)) { + // Reduce TLS initial exec model to TLS local exec model. + // Sequences like + // MOVL TLS, BX + // ... off(BX)(TLS*1) ... + // become + // NOP + // ... off(TLS) ... + if(p->as == AMOVL && p->from.type == D_TLS && D_AX <= p->to.type && p->to.type <= D_DI) { + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; + } + if(p->from.index == D_TLS && D_INDIR+D_AX <= p->from.type && p->from.type <= D_INDIR+D_DI) { + p->from.type = D_INDIR+D_TLS; + p->from.scale = 0; + p->from.index = D_NONE; + } + if(p->to.index == D_TLS && D_INDIR+D_AX <= p->to.type && p->to.type <= D_INDIR+D_DI) { + p->to.type = D_INDIR+D_TLS; + p->to.scale = 0; + p->to.index = D_NONE; + } + } else { + // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. + // The instruction + // MOVL off(TLS), BX + // becomes the sequence + // MOVL TLS, BX + // MOVL off(BX)(TLS*1), BX + // This allows the C compilers to emit references to m and g using the direct off(TLS) form. + if(p->as == AMOVL && p->from.type == D_INDIR+D_TLS && D_AX <= p->to.type && p->to.type <= D_DI) { + q = appendp(ctxt, p); + q->as = p->as; + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->from.index = D_TLS; + q->from.scale = 2; // TODO: use 1 + q->to = p->to; + p->from.type = D_TLS; + p->from.index = D_NONE; + p->from.offset = 0; + } + } + + // TODO: Remove. + if(ctxt->headtype == Hplan9) { + if(p->from.scale == 1 && p->from.index == D_TLS) + p->from.scale = 2; + if(p->to.scale == 1 && p->to.index == D_TLS) + p->to.scale = 2; + } + + // Rewrite CALL/JMP/RET to symbol as D_BRANCH. + switch(p->as) { + case ACALL: + case AJMP: + case ARET: + if((p->to.type == D_EXTERN || p->to.type == D_STATIC) && p->to.sym != nil) + p->to.type = D_BRANCH; + break; + } + + // Rewrite float constants to values stored in memory. + switch(p->as) { + case AFMOVF: + case AFADDF: + case AFSUBF: + case AFSUBRF: + case AFMULF: + case AFDIVF: + case AFDIVRF: + case AFCOMF: + case AFCOMFP: + case AMOVSS: + case AADDSS: + case ASUBSS: + case AMULSS: + case ADIVSS: + case ACOMISS: + case AUCOMISS: + if(p->from.type == D_FCONST) { + int32 i32; + float32 f32; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + sprint(literal, "$f32.%08ux", (uint32)i32); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + adduint32(ctxt, s, i32); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + break; + + case AFMOVD: + case AFADDD: + case AFSUBD: + case AFSUBRD: + case AFMULD: + case AFDIVD: + case AFDIVRD: + case AFCOMD: + case AFCOMDP: + case AMOVSD: + case AADDSD: + case ASUBSD: + case AMULSD: + case ADIVSD: + case ACOMISD: + case AUCOMISD: + if(p->from.type == D_FCONST) { + int64 i64; + memmove(&i64, &p->from.u.dval, 8); + sprint(literal, "$f64.%016llux", (uvlong)i64); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + s->type = SRODATA; + adduint64(ctxt, s, i64); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + break; + } +} + +static Prog* +prg(void) +{ + Prog *p; + + p = emallocz(sizeof(*p)); + *p = zprg; + return p; +} + +static Prog* load_g_cx(Link*, Prog*); +static Prog* stacksplit(Link*, Prog*, int32, int, Prog**); + +static void +addstacksplit(Link *ctxt, LSym *cursym) +{ + Prog *p, *q; + int32 autoffset, deltasp; + int a; + + if(ctxt->symmorestack[0] == nil) { + ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0); + ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0); + } + + if(ctxt->headtype == Hplan9 && ctxt->plan9tos == nil) + ctxt->plan9tos = linklookup(ctxt, "_tos", 0); + + ctxt->cursym = cursym; + + if(cursym->text == nil || cursym->text->link == nil) + return; + + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + + cursym->locals = autoffset; + cursym->args = p->to.offset2; + + q = nil; + + if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) { + p = appendp(ctxt, p); + p = load_g_cx(ctxt, p); // load g into CX + } + if(!(cursym->text->from.scale & NOSPLIT)) + p = stacksplit(ctxt, p, autoffset, !(cursym->text->from.scale&NEEDCTXT), &q); // emit split check + + if(autoffset) { + p = appendp(ctxt, p); + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = autoffset; + p->spadj = autoffset; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(ctxt, p); + p->as = ANOP; + p->spadj = -ctxt->arch->ptrsize; + p = appendp(ctxt, p); + p->as = ANOP; + p->spadj = ctxt->arch->ptrsize; + } + if(q != nil) + q->pcond = p; + deltasp = autoffset; + + if(cursym->text->from.scale & WRAPPER) { + // g->panicwrap += autoffset + ctxt->arch->ptrsize; + p = appendp(ctxt, p); + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = autoffset + ctxt->arch->ptrsize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*ctxt->arch->ptrsize; + } + + if(ctxt->debugzerostack && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 8l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = autoffset/4; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = AREP; + + p = appendp(ctxt, p); + p->as = ASTOSL; + } + + for(; p != nil; p = p->link) { + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + 4; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + 4; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + ctxt->diag("unbalanced PUSH/POP"); + + if(cursym->text->from.scale & WRAPPER) { + p = load_g_cx(ctxt, p); + p = appendp(ctxt, p); + // g->panicwrap -= autoffset + ctxt->arch->ptrsize; + p->as = ASUBL; + p->from.type = D_CONST; + p->from.offset = autoffset + ctxt->arch->ptrsize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*ctxt->arch->ptrsize; + p = appendp(ctxt, p); + p->as = ARET; + } + + if(autoffset) { + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = -autoffset; + p->spadj = -autoffset; + p = appendp(ctxt, p); + p->as = ARET; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so undo + // the cleanup. + p->spadj = +autoffset; + } + if(p->to.sym) // retjmp + p->as = AJMP; + } +} + +// Append code to p to load g into cx. +// Overwrites p with the first instruction (no first appendp). +// Overwriting p is unusual but it lets use this in both the +// prologue (caller must call appendp first) and in the epilogue. +// Returns last new instruction. +static Prog* +load_g_cx(Link *ctxt, Prog *p) +{ + Prog *next; + + p->as = AMOVL; + p->from.type = D_INDIR+D_TLS; + p->from.offset = 0; + p->to.type = D_CX; + + next = p->link; + progedit(ctxt, p); + while(p->link != next) + p = p->link; + + if(p->from.index == D_TLS) + p->from.scale = 2; + + return p; +} + +// Append code to p to check for stack split. +// Appends to (does not overwrite) p. +// Assumes g is in CX. +// Returns last new instruction. +// On return, *jmpok is the instruction that should jump +// to the stack frame allocation if no split is needed. +static Prog* +stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt, Prog **jmpok) +{ + Prog *q, *q1; + int arg; + + if(ctxt->debugstack) { + // 8l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info. + p = appendp(ctxt, p); + p->as = ACMPL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 4; + p->to.type = D_SP; + + p = appendp(ctxt, p); + p->as = AJCC; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; + + p = appendp(ctxt, p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(ctxt, p); + p->as = ANOP; + q1->pcond = p; + } + q1 = nil; + + if(framesize <= StackSmall) { + // small stack: SP <= stackguard + // CMPL SP, stackguard + p = appendp(ctxt, p); + p->as = ACMPL; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else if(framesize <= StackBig) { + // large stack: SP-framesize <= stackguard-StackSmall + // LEAL -(framesize-StackSmall)(SP), AX + // CMPL AX, stackguard + p = appendp(ctxt, p); + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(framesize-StackSmall); + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; + } 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. + // + // Preemption sets stackguard to StackPreempt, a very large value. + // That breaks the math above, so we have to check for that explicitly. + // MOVL stackguard, CX + // CMPL CX, $StackPreempt + // JEQ label-of-call-to-morestack + // LEAL StackGuard(SP), AX + // SUBL stackguard, AX + // CMPL AX, $(framesize+(StackGuard-StackSmall)) + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_SI; + + p = appendp(ctxt, p); + p->as = ACMPL; + p->from.type = D_SI; + p->to.type = D_CONST; + p->to.offset = (uint32)StackPreempt; + + p = appendp(ctxt, p); + p->as = AJEQ; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(ctxt, p); + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = StackGuard; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ASUBL; + p->from.type = D_SI; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_CONST; + p->to.offset = framesize+(StackGuard-StackSmall); + } + + // common + p = appendp(ctxt, p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q = p; + + p = appendp(ctxt, p); // save frame size in DI + p->as = AMOVL; + p->to.type = D_DI; + p->from.type = D_CONST; + + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. + if(StackTop + ctxt->cursym->text->to.offset2 + ctxt->arch->ptrsize + framesize + ctxt->arch->ptrsize + StackLimit >= StackMin) + p->from.offset = (framesize+7) & ~7LL; + + arg = ctxt->cursym->text->to.offset2; + if(arg == 1) // special marker for known 0 + arg = 0; + if(arg&3) + ctxt->diag("misaligned argument size in stack split"); + p = appendp(ctxt, p); // save arg size in AX + p->as = AMOVL; + p->to.type = D_AX; + p->from.type = D_CONST; + p->from.offset = arg; + + p = appendp(ctxt, p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->to.sym = ctxt->symmorestack[noctxt]; + + p = appendp(ctxt, p); + p->as = AJMP; + p->to.type = D_BRANCH; + p->pcond = ctxt->cursym->text->link; + + if(q != nil) + q->pcond = p->link; + if(q1 != nil) + q1->pcond = q->link; + + *jmpok = q; + return p; +} + +static void xfol(Link*, Prog*, Prog**); + +static void +follow(Link *ctxt, LSym *s) +{ + Prog *firstp, *lastp; + + ctxt->cursym = s; + + firstp = ctxt->arch->prg(); + lastp = firstp; + xfol(ctxt, s->text, &lastp); + lastp->link = nil; + s->text = firstp->link; +} + +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETW: + case AUNDEF: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static int +relinv(int a) +{ + + switch(a) { + case AJEQ: return AJNE; + case AJNE: return AJEQ; + case AJLE: return AJGT; + case AJLS: return AJHI; + case AJLT: return AJGE; + case AJMI: return AJPL; + case AJGE: return AJLT; + case AJPL: return AJMI; + case AJGT: return AJLE; + case AJHI: return AJLS; + case AJCS: return AJCC; + case AJCC: return AJCS; + case AJPS: return AJPC; + case AJPC: return AJPS; + case AJOS: return AJOC; + case AJOC: return AJOS; + } + sysfatal("unknown relation: %s", anames8[a]); + return 0; +} + +static void +xfol(Link *ctxt, Prog *p, Prog **last) +{ + Prog *q; + int i; + enum as a; + +loop: + if(p == nil) + return; + if(p->as == AJMP) + if((q = p->pcond) != nil && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ + p->mark = 1; + p = q; + if(p->mark == 0) + goto loop; + } + if(p->mark) { + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ + for(i=0,q=p; i<4; i++,q=q->link) { + if(q == nil) + break; + if(q == *last) + break; + a = q->as; + if(a == ANOP) { + i--; + continue; + } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy + if(q->pcond == nil || q->pcond->mark) + continue; + if(a == ACALL || a == ALOOP) + continue; + for(;;) { + if(p->as == ANOP) { + p = p->link; + continue; + } + q = copyp(ctxt, p); + p = p->link; + q->mark = 1; + (*last)->link = q; + *last = q; + if(q->as != a || q->pcond == nil || q->pcond->mark) + continue; + + q->as = relinv(q->as); + p = q->pcond; + q->pcond = q->link; + q->link = p; + xfol(ctxt, q->link, last); + p = q->link; + if(p->mark) + return; + goto loop; + } + } /* */ + q = ctxt->arch->prg(); + q->as = AJMP; + q->lineno = p->lineno; + q->to.type = D_BRANCH; + q->to.offset = p->pc; + q->pcond = p; + p = q; + } + + /* emit p */ + p->mark = 1; + (*last)->link = p; + *last = p; + a = p->as; + + /* continue loop with what comes after p */ + if(nofollow(a)) + return; + if(p->pcond != nil && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ + if((q = brchain(ctxt, p->pcond)) != nil) + p->pcond = q; + if((q = brchain(ctxt, p->link)) != nil) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } + } + xfol(ctxt, p->link, last); + if(p->pcond->mark) + return; + p = p->pcond; + goto loop; + } + p = p->link; + goto loop; +} + +LinkArch link386 = { + .name = "386", + .thechar = '8', + + .addstacksplit = addstacksplit, + .assemble = span8, + .datasize = datasize, + .follow = follow, + .iscall = iscall, + .isdata = isdata, + .prg = prg, + .progedit = progedit, + .settextflag = settextflag, + .symtype = symtype, + .textflag = textflag, + + .minlc = 1, + .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, + + .ACALL = ACALL, + .ADATA = ADATA, + .AEND = AEND, + .AFUNCDATA = AFUNCDATA, + .AGLOBL = AGLOBL, + .AJMP = AJMP, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .ATYPE = ATYPE, + .AUSEFIELD = AUSEFIELD, +}; diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c new file mode 100644 index 000000000..610f87954 --- /dev/null +++ b/src/liblink/objfile.c @@ -0,0 +1,746 @@ +// Copyright 2013 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. + +// Writing and reading of Go object files. +// +// Originally, Go object files were Plan 9 object files, but no longer. +// Now they are more like standard object files, in that each symbol is defined +// by an associated memory image (bytes) and a list of relocations to apply +// during linking. We do not (yet?) use a standard file format, however. +// For now, the format is chosen to be as simple as possible to read and write. +// It may change for reasons of efficiency, or we may even switch to a +// standard file format if there are compelling benefits to doing so. +// See golang.org/s/go13linker for more background. +// +// The file format is: +// +// - magic header: "\x00\x00go13ld" +// - byte 1 - version number +// - sequence of strings giving dependencies (imported packages) +// - empty string (marks end of sequence) +// - sequence of defined symbols +// - byte 0xff (marks end of sequence) +// - magic footer: "\xff\xffgo13ld" +// +// All integers are stored in a zigzag varint format. +// See golang.org/s/go12symtab for a definition. +// +// Data blocks and strings are both stored as an integer +// followed by that many bytes. +// +// A symbol reference is a string name followed by a version. +// An empty name corresponds to a nil LSym* pointer. +// +// Each symbol is laid out as the following fields (taken from LSym*): +// +// - byte 0xfe (sanity check for synchronization) +// - type [int] +// - name [string] +// - version [int] +// - dupok [int] +// - size [int] +// - gotype [symbol reference] +// - p [data block] +// - nr [int] +// - r [nr relocations, sorted by off] +// +// If type == STEXT, there are a few more fields: +// +// - args [int] +// - locals [int] +// - nosplit [int] +// - leaf [int] +// - nlocal [int] +// - local [nlocal automatics] +// - pcln [pcln table] +// +// Each relocation has the encoding: +// +// - off [int] +// - siz [int] +// - type [int] +// - add [int] +// - xadd [int] +// - sym [symbol reference] +// - xsym [symbol reference] +// +// Each local has the encoding: +// +// - asym [symbol reference] +// - offset [int] +// - type [int] +// - gotype [symbol reference] +// +// The pcln table has the encoding: +// +// - pcsp [data block] +// - pcfile [data block] +// - pcline [data block] +// - npcdata [int] +// - pcdata [npcdata data blocks] +// - nfuncdata [int] +// - funcdata [nfuncdata symbol references] +// - funcdatasym [nfuncdata ints] +// - nfile [int] +// - file [nfile symbol references] +// +// The file layout and meaning of type integers are architecture-independent. +// +// TODO(rsc): The file format is good for a first pass but needs work. +// - There are SymID in the object file that should really just be strings. +// - The actual symbol memory images are interlaced with the symbol +// metadata. They should be separated, to reduce the I/O required to +// load just the metadata. +// - The symbol references should be shortened, either with a symbol +// table or by using a simple backward index to an earlier mentioned symbol. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "../cmd/ld/textflag.h" + +static void writesym(Link*, Biobuf*, LSym*); +static void wrint(Biobuf*, int64); +static void wrstring(Biobuf*, char*); +static void wrpath(Link *, Biobuf*, char*); +static void wrdata(Biobuf*, void*, int); +static void wrsym(Biobuf*, LSym*); +static void wrpathsym(Link *ctxt, Biobuf *b, LSym *s); + +static void readsym(Link*, Biobuf*, char*, char*); +static int64 rdint(Biobuf*); +static char *rdstring(Biobuf*); +static void rddata(Biobuf*, uchar**, int*); +static LSym *rdsym(Link*, Biobuf*, char*); + +// The Go and C compilers, and the assembler, call writeobj to write +// out a Go object file. The linker does not call this; the linker +// does not write out object files. +void +writeobj(Link *ctxt, Biobuf *b) +{ + int flag; + Hist *h; + LSym *s, *text, *etext, *curtext, *data, *edata; + Plist *pl; + Prog *p, *plink; + Auto *a; + + // Build list of symbols, and assign instructions to lists. + // Ignore ctxt->plist boundaries. There are no guarantees there, + // and the C compilers and assemblers just use one big list. + text = nil; + curtext = nil; + data = nil; + etext = nil; + edata = nil; + for(pl = ctxt->plist; pl != nil; pl = pl->link) { + for(p = pl->firstpc; p != nil; p = plink) { + plink = p->link; + p->link = nil; + + if(p->as == ctxt->arch->AEND) + continue; + + if(p->as == ctxt->arch->ATYPE) { + // Assume each TYPE instruction describes + // a different local variable or parameter, + // so no dedup. + // Using only the TYPE instructions means + // that we discard location information about local variables + // in C and assembly functions; that information is inferred + // from ordinary references, because there are no TYPE + // instructions there. Without the type information, gdb can't + // use the locations, so we don't bother to save them. + // If something else could use them, we could arrange to + // preserve them. + if(curtext == nil) + continue; + a = emallocz(sizeof *a); + a->asym = p->from.sym; + a->aoffset = p->from.offset; + a->type = ctxt->arch->symtype(&p->from); + a->gotype = p->from.gotype; + a->link = curtext->autom; + curtext->autom = a; + continue; + } + + if(p->as == ctxt->arch->AGLOBL) { + s = p->from.sym; + if(s->seenglobl++) + print("duplicate %P\n", p); + if(s->onlist) + sysfatal("symbol %s listed multiple times", s->name); + s->onlist = 1; + if(data == nil) + data = s; + else + edata->next = s; + s->next = nil; + s->size = p->to.offset; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + + if(ctxt->arch->thechar == '5') + flag = p->reg; + else + flag = p->from.scale; + + if(flag & DUPOK) + s->dupok = 1; + if(flag & RODATA) + s->type = SRODATA; + else if(flag & NOPTR) + s->type = SNOPTRBSS; + edata = s; + continue; + } + + if(p->as == ctxt->arch->ADATA) { + savedata(ctxt, p->from.sym, p, "<input>"); + continue; + } + + if(p->as == ctxt->arch->ATEXT) { + s = p->from.sym; + if(s == nil) { + // func _() { } + curtext = nil; + continue; + } + if(s->text != nil) + sysfatal("duplicate TEXT for %s", s->name); + if(s->onlist) + sysfatal("symbol %s listed multiple times", s->name); + s->onlist = 1; + if(text == nil) + text = s; + else + etext->next = s; + etext = s; + if(ctxt->arch->thechar == '5') + flag = p->reg; + else + flag = p->from.scale; + if(flag & DUPOK) + s->dupok = 1; + if(flag & NOSPLIT) + s->nosplit = 1; + s->next = nil; + s->type = STEXT; + s->text = p; + s->etext = p; + curtext = s; + continue; + } + + if(curtext == nil) + continue; + s = curtext; + s->etext->link = p; + s->etext = p; + } + } + + // Turn functions into machine code images. + for(s = text; s != nil; s = s->next) { + mkfwd(s); + linkpatch(ctxt, s); + ctxt->arch->follow(ctxt, s); + ctxt->arch->addstacksplit(ctxt, s); + ctxt->arch->assemble(ctxt, s); + linkpcln(ctxt, s); + } + + // Emit header. + Bputc(b, 0); + Bputc(b, 0); + Bprint(b, "go13ld"); + Bputc(b, 1); // version + + // Emit autolib. + for(h = ctxt->hist; h != nil; h = h->link) + if(h->offset < 0) + wrstring(b, h->name); + wrstring(b, ""); + + // Emit symbols. + for(s = text; s != nil; s = s->next) + writesym(ctxt, b, s); + for(s = data; s != nil; s = s->next) + writesym(ctxt, b, s); + + // Emit footer. + Bputc(b, 0xff); + Bputc(b, 0xff); + Bprint(b, "go13ld"); +} + +static void +writesym(Link *ctxt, Biobuf *b, LSym *s) +{ + Reloc *r; + int i, j, c, n; + Pcln *pc; + Prog *p; + Auto *a; + char *name; + + if(ctxt->debugasm) { + Bprint(ctxt->bso, "%s ", s->name); + if(s->version) + Bprint(ctxt->bso, "v=%d ", s->version); + if(s->type) + Bprint(ctxt->bso, "t=%d ", s->type); + if(s->dupok) + Bprint(ctxt->bso, "dupok "); + if(s->nosplit) + Bprint(ctxt->bso, "nosplit "); + Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value); + if(s->type == STEXT) { + Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals); + if(s->leaf) + Bprint(ctxt->bso, " leaf"); + } + Bprint(ctxt->bso, "\n"); + for(p=s->text; p != nil; p = p->link) + Bprint(ctxt->bso, "\t%#06ux %P\n", (int)p->pc, p); + for(i=0; i<s->np; ) { + Bprint(ctxt->bso, "\t%#06ux", i); + for(j=i; j<i+16 && j<s->np; j++) + Bprint(ctxt->bso, " %02ux", s->p[j]); + for(; j<i+16; j++) + Bprint(ctxt->bso, " "); + Bprint(ctxt->bso, " "); + for(j=i; j<i+16 && j<s->np; j++) { + c = s->p[j]; + if(' ' <= c && c <= 0x7e) + Bprint(ctxt->bso, "%c", c); + else + Bprint(ctxt->bso, "."); + } + Bprint(ctxt->bso, "\n"); + i += 16; + } + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + name = ""; + if(r->sym != nil) + name = r->sym->name; + Bprint(ctxt->bso, "\trel %d+%d t=%d %s+%lld\n", (int)r->off, r->siz, r->type, name, (vlong)r->add); + } + } + + Bputc(b, 0xfe); + wrint(b, s->type); + wrstring(b, s->name); + wrint(b, s->version); + wrint(b, s->dupok); + wrint(b, s->size); + wrsym(b, s->gotype); + wrdata(b, s->p, s->np); + + wrint(b, s->nr); + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + wrint(b, r->off); + wrint(b, r->siz); + wrint(b, r->type); + wrint(b, r->add); + wrint(b, r->xadd); + wrsym(b, r->sym); + wrsym(b, r->xsym); + } + + if(s->type == STEXT) { + wrint(b, s->args); + wrint(b, s->locals); + wrint(b, s->nosplit); + wrint(b, s->leaf); + n = 0; + for(a = s->autom; a != nil; a = a->link) + n++; + wrint(b, n); + for(a = s->autom; a != nil; a = a->link) { + wrsym(b, a->asym); + wrint(b, a->aoffset); + if(a->type == ctxt->arch->D_AUTO) + wrint(b, A_AUTO); + else if(a->type == ctxt->arch->D_PARAM) + wrint(b, A_PARAM); + else + sysfatal("%s: invalid local variable type %d", s->name, a->type); + wrsym(b, a->gotype); + } + + pc = s->pcln; + wrdata(b, pc->pcsp.p, pc->pcsp.n); + wrdata(b, pc->pcfile.p, pc->pcfile.n); + wrdata(b, pc->pcline.p, pc->pcline.n); + wrint(b, pc->npcdata); + for(i=0; i<pc->npcdata; i++) + wrdata(b, pc->pcdata[i].p, pc->pcdata[i].n); + wrint(b, pc->nfuncdata); + for(i=0; i<pc->nfuncdata; i++) + wrsym(b, pc->funcdata[i]); + for(i=0; i<pc->nfuncdata; i++) + wrint(b, pc->funcdataoff[i]); + wrint(b, pc->nfile); + for(i=0; i<pc->nfile; i++) + wrpathsym(ctxt, b, pc->file[i]); + } +} + +static void +wrint(Biobuf *b, int64 sval) +{ + uint64 uv, v; + uchar buf[10], *p; + + uv = ((uint64)sval<<1) ^ (uint64)(int64)(sval>>63); + + p = buf; + for(v = uv; v >= 0x80; v >>= 7) + *p++ = v | 0x80; + *p++ = v; + + Bwrite(b, buf, p - buf); +} + +static void +wrstring(Biobuf *b, char *s) +{ + wrdata(b, s, strlen(s)); +} + +// wrpath writes a path just like a string, but on windows, it +// translates '\\' to '/' in the process. +static void +wrpath(Link *ctxt, Biobuf *b, char *p) +{ + int i, n; + if (!ctxt->windows || strchr(p, '\\') == nil) { + wrstring(b, p); + return; + } else { + n = strlen(p); + wrint(b, n); + for (i = 0; i < n; i++) + Bputc(b, p[i] == '\\' ? '/' : p[i]); + } +} + +static void +wrdata(Biobuf *b, void *v, int n) +{ + wrint(b, n); + Bwrite(b, v, n); +} + +static void +wrpathsym(Link *ctxt, Biobuf *b, LSym *s) +{ + if(s == nil) { + wrint(b, 0); + wrint(b, 0); + return; + } + wrpath(ctxt, b, s->name); + wrint(b, s->version); +} + +static void +wrsym(Biobuf *b, LSym *s) +{ + if(s == nil) { + wrint(b, 0); + wrint(b, 0); + return; + } + wrstring(b, s->name); + wrint(b, s->version); +} + +static char startmagic[] = "\x00\x00go13ld"; +static char endmagic[] = "\xff\xffgo13ld"; + +void +ldobjfile(Link *ctxt, Biobuf *f, char *pkg, int64 len, char *pn) +{ + int c; + uchar buf[8]; + int64 start; + char *lib; + + start = Boffset(f); + ctxt->version++; + memset(buf, 0, sizeof buf); + Bread(f, buf, sizeof buf); + if(memcmp(buf, startmagic, sizeof buf) != 0) + sysfatal("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + if((c = Bgetc(f)) != 1) + sysfatal("%s: invalid file version number %d", pn, c); + + for(;;) { + lib = rdstring(f); + if(lib[0] == 0) + break; + addlib(ctxt, pkg, pn, lib); + } + + for(;;) { + c = Bgetc(f); + Bungetc(f); + if(c == 0xff) + break; + readsym(ctxt, f, pkg, pn); + } + + memset(buf, 0, sizeof buf); + Bread(f, buf, sizeof buf); + if(memcmp(buf, endmagic, sizeof buf) != 0) + sysfatal("%s: invalid file end", pn); + + if(Boffset(f) != start+len) + sysfatal("%s: unexpected end at %lld, want %lld", pn, (vlong)Boffset(f), (vlong)(start+len)); +} + +static void +readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) +{ + int i, j, c, t, v, n, size, dupok; + static int ndup; + char *name; + Reloc *r; + LSym *s, *dup; + Pcln *pc; + Auto *a; + + if(Bgetc(f) != 0xfe) + sysfatal("readsym out of sync"); + t = rdint(f); + name = expandpkg(rdstring(f), pkg); + v = rdint(f); + if(v != 0 && v != 1) + sysfatal("invalid symbol version %d", v); + dupok = rdint(f); + size = rdint(f); + + if(v != 0) + v = ctxt->version; + s = linklookup(ctxt, name, v); + dup = nil; + if(s->type != 0 && s->type != SXREF) { + if(s->type != SBSS && s->type != SNOPTRBSS && !dupok && !s->dupok) + sysfatal("duplicate symbol %s (types %d and %d) in %s and %s", s->name, s->type, t, s->file, pn); + if(s->np > 0) { + dup = s; + s = linknewsym(ctxt, ".dup", ndup++); // scratch + } + } + s->file = pkg; + s->dupok = dupok; + if(t == SXREF) + sysfatal("bad sxref"); + if(t == 0) + sysfatal("missing type for %s in %s", name, pn); + s->type = t; + if(s->size < size) + s->size = size; + s->gotype = rdsym(ctxt, f, pkg); + rddata(f, &s->p, &s->np); + s->maxp = s->np; + n = rdint(f); + if(n > 0) { + s->r = emallocz(n * sizeof s->r[0]); + s->nr = n; + s->maxr = n; + for(i=0; i<n; i++) { + r = &s->r[i]; + r->off = rdint(f); + r->siz = rdint(f); + r->type = rdint(f); + r->add = rdint(f); + r->xadd = rdint(f); + r->sym = rdsym(ctxt, f, pkg); + r->xsym = rdsym(ctxt, f, pkg); + } + } + + if(s->np > 0 && dup != nil && dup->np > 0 && strncmp(s->name, "gclocals·", 10) == 0) { + // content-addressed garbage collection liveness bitmap symbol. + // double check for hash collisions. + if(s->np != dup->np || memcmp(s->p, dup->p, s->np) != 0) + sysfatal("dupok hash collision for %s in %s and %s", s->name, s->file, pn); + } + + if(s->type == STEXT) { + s->args = rdint(f); + s->locals = rdint(f); + s->nosplit = rdint(f); + s->leaf = rdint(f); + n = rdint(f); + for(i=0; i<n; i++) { + a = emallocz(sizeof *a); + a->asym = rdsym(ctxt, f, pkg); + a->aoffset = rdint(f); + a->type = rdint(f); + a->gotype = rdsym(ctxt, f, pkg); + a->link = s->autom; + s->autom = a; + } + + s->pcln = emallocz(sizeof *s->pcln); + pc = s->pcln; + rddata(f, &pc->pcsp.p, &pc->pcsp.n); + rddata(f, &pc->pcfile.p, &pc->pcfile.n); + rddata(f, &pc->pcline.p, &pc->pcline.n); + n = rdint(f); + pc->pcdata = emallocz(n * sizeof pc->pcdata[0]); + pc->npcdata = n; + for(i=0; i<n; i++) + rddata(f, &pc->pcdata[i].p, &pc->pcdata[i].n); + n = rdint(f); + pc->funcdata = emallocz(n * sizeof pc->funcdata[0]); + pc->funcdataoff = emallocz(n * sizeof pc->funcdataoff[0]); + pc->nfuncdata = n; + for(i=0; i<n; i++) + pc->funcdata[i] = rdsym(ctxt, f, pkg); + for(i=0; i<n; i++) + pc->funcdataoff[i] = rdint(f); + n = rdint(f); + pc->file = emallocz(n * sizeof pc->file[0]); + pc->nfile = n; + for(i=0; i<n; i++) + pc->file[i] = rdsym(ctxt, f, pkg); + + if(dup == nil) { + if(s->onlist) + sysfatal("symbol %s listed multiple times", s->name); + s->onlist = 1; + if(ctxt->etextp) + ctxt->etextp->next = s; + else + ctxt->textp = s; + ctxt->etextp = s; + } + } + + if(ctxt->debugasm) { + Bprint(ctxt->bso, "%s ", s->name); + if(s->version) + Bprint(ctxt->bso, "v=%d ", s->version); + if(s->type) + Bprint(ctxt->bso, "t=%d ", s->type); + if(s->dupok) + Bprint(ctxt->bso, "dupok "); + if(s->nosplit) + Bprint(ctxt->bso, "nosplit "); + Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value); + if(s->type == STEXT) + Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals); + Bprint(ctxt->bso, "\n"); + for(i=0; i<s->np; ) { + Bprint(ctxt->bso, "\t%#06ux", i); + for(j=i; j<i+16 && j<s->np; j++) + Bprint(ctxt->bso, " %02ux", s->p[j]); + for(; j<i+16; j++) + Bprint(ctxt->bso, " "); + Bprint(ctxt->bso, " "); + for(j=i; j<i+16 && j<s->np; j++) { + c = s->p[j]; + if(' ' <= c && c <= 0x7e) + Bprint(ctxt->bso, "%c", c); + else + Bprint(ctxt->bso, "."); + } + Bprint(ctxt->bso, "\n"); + i += 16; + } + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + Bprint(ctxt->bso, "\trel %d+%d t=%d %s+%lld\n", (int)r->off, r->siz, r->type, r->sym->name, (vlong)r->add); + } + } +} + +static int64 +rdint(Biobuf *f) +{ + int c; + uint64 uv; + int shift; + + uv = 0; + for(shift = 0;; shift += 7) { + if(shift >= 64) + sysfatal("corrupt input"); + c = Bgetc(f); + uv |= (uint64)(c & 0x7F) << shift; + if(!(c & 0x80)) + break; + } + + return (int64)(uv>>1) ^ ((int64)((uint64)uv<<63)>>63); +} + +static char* +rdstring(Biobuf *f) +{ + int n; + char *p; + + n = rdint(f); + p = emallocz(n+1); + Bread(f, p, n); + return p; +} + +static void +rddata(Biobuf *f, uchar **pp, int *np) +{ + *np = rdint(f); + *pp = emallocz(*np); + Bread(f, *pp, *np); +} + +static LSym* +rdsym(Link *ctxt, Biobuf *f, char *pkg) +{ + int n, v; + char *p; + LSym *s; + + n = rdint(f); + if(n == 0) { + rdint(f); + return nil; + } + p = emallocz(n+1); + Bread(f, p, n); + v = rdint(f); + if(v != 0) + v = ctxt->version; + s = linklookup(ctxt, expandpkg(p, pkg), v); + + if(v == 0 && s->name[0] == '$' && s->type == 0) { + if(strncmp(s->name, "$f32.", 5) == 0) { + int32 i32; + i32 = strtoul(s->name+5, nil, 16); + s->type = SRODATA; + adduint32(ctxt, s, i32); + s->reachable = 0; + } else if(strncmp(s->name, "$f64.", 5) == 0) { + int64 i64; + i64 = strtoull(s->name+5, nil, 16); + s->type = SRODATA; + adduint64(ctxt, s, i64); + s->reachable = 0; + } + } + + return s; +} diff --git a/src/liblink/pass.c b/src/liblink/pass.c new file mode 100644 index 000000000..bc8eb4367 --- /dev/null +++ b/src/liblink/pass.c @@ -0,0 +1,115 @@ +// Inferno utils/6l/pass.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.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. + +// Code and data passes. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +Prog* +brchain(Link *ctxt, Prog *p) +{ + int i; + + for(i=0; i<20; i++) { + if(p == nil || p->as != ctxt->arch->AJMP) + return p; + p = p->pcond; + } + return nil; +} + +Prog* +brloop(Link *ctxt, Prog *p) +{ + int c; + Prog *q; + + c = 0; + for(q = p; q != nil; q = q->pcond) { + if(q->as != ctxt->arch->AJMP) + break; + c++; + if(c >= 5000) + return nil; + } + return q; +} + +void +linkpatch(Link *ctxt, LSym *sym) +{ + int32 c; + Prog *p, *q; + + ctxt->cursym = sym; + + for(p = sym->text; p != nil; p = p->link) { + if(ctxt->arch->progedit) + ctxt->arch->progedit(ctxt, p); + if(p->to.type != ctxt->arch->D_BRANCH) + continue; + if(p->to.u.branch != nil) { + // TODO: Remove to.u.branch in favor of p->pcond. + p->pcond = p->to.u.branch; + continue; + } + if(p->to.sym != nil) + continue; + c = p->to.offset; + for(q = sym->text; q != nil;) { + if(c == q->pc) + break; + if(q->forwd != nil && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; + } + if(q == nil) { + ctxt->diag("branch out of range (%#ux)\n%P [%s]", + c, p, p->to.sym ? p->to.sym->name : "<nil>"); + p->to.type = ctxt->arch->D_NONE; + } + p->to.u.branch = q; + p->pcond = q; + } + + for(p = sym->text; p != nil; p = p->link) { + p->mark = 0; /* initialization for follow */ + if(p->pcond != nil) { + p->pcond = brloop(ctxt, p->pcond); + if(p->pcond != nil) + if(p->to.type == ctxt->arch->D_BRANCH) + p->to.offset = p->pcond->pc; + } + } +} diff --git a/src/liblink/pcln.c b/src/liblink/pcln.c new file mode 100644 index 000000000..4b2b85543 --- /dev/null +++ b/src/liblink/pcln.c @@ -0,0 +1,365 @@ +// Copyright 2013 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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +static void +addvarint(Link *ctxt, Pcdata *d, uint32 val) +{ + int32 n; + uint32 v; + uchar *p; + + USED(ctxt); + + n = 0; + for(v = val; v >= 0x80; v >>= 7) + n++; + n++; + + if(d->n + n > d->m) { + d->m = (d->n + n)*2; + d->p = erealloc(d->p, d->m); + } + + p = d->p + d->n; + for(v = val; v >= 0x80; v >>= 7) + *p++ = v | 0x80; + *p = v; + d->n += n; +} + +// funcpctab writes to dst a pc-value table mapping the code in func to the values +// returned by valfunc parameterized by arg. The invocation of valfunc to update the +// current value is, for each p, +// +// val = valfunc(func, val, p, 0, arg); +// record val as value at p->pc; +// val = valfunc(func, val, p, 1, arg); +// +// where func is the function, val is the current value, p is the instruction being +// considered, and arg can be used to further parameterize valfunc. +static void +funcpctab(Link *ctxt, Pcdata *dst, LSym *func, char *desc, int32 (*valfunc)(Link*, LSym*, int32, Prog*, int32, void*), void* arg) +{ + int dbg, i; + int32 oldval, val, started; + uint32 delta; + vlong pc; + Prog *p; + + // To debug a specific function, uncomment second line and change name. + dbg = 0; + //dbg = strcmp(func->name, "main.main") == 0; + //dbg = strcmp(desc, "pctofile") == 0; + + ctxt->debugpcln += dbg; + + dst->n = 0; + + if(ctxt->debugpcln) + Bprint(ctxt->bso, "funcpctab %s [valfunc=%s]\n", func->name, desc); + + val = -1; + oldval = val; + if(func->text == nil) { + ctxt->debugpcln -= dbg; + return; + } + + pc = func->text->pc; + + if(ctxt->debugpcln) + Bprint(ctxt->bso, "%6llux %6d %P\n", pc, val, func->text); + + started = 0; + for(p=func->text; p != nil; p = p->link) { + // Update val. If it's not changing, keep going. + val = valfunc(ctxt, func, val, p, 0, arg); + if(val == oldval && started) { + val = valfunc(ctxt, func, val, p, 1, arg); + if(ctxt->debugpcln) + Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); + continue; + } + + // If the pc of the next instruction is the same as the + // pc of this instruction, this instruction is not a real + // instruction. Keep going, so that we only emit a delta + // for a true instruction boundary in the program. + if(p->link && p->link->pc == p->pc) { + val = valfunc(ctxt, func, val, p, 1, arg); + if(ctxt->debugpcln) + Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); + continue; + } + + // The table is a sequence of (value, pc) pairs, where each + // pair states that the given value is in effect from the current position + // up to the given pc, which becomes the new current position. + // To generate the table as we scan over the program instructions, + // we emit a "(value" when pc == func->value, and then + // each time we observe a change in value we emit ", pc) (value". + // When the scan is over, we emit the closing ", pc)". + // + // The table is delta-encoded. The value deltas are signed and + // transmitted in zig-zag form, where a complement bit is placed in bit 0, + // and the pc deltas are unsigned. Both kinds of deltas are sent + // as variable-length little-endian base-128 integers, + // where the 0x80 bit indicates that the integer continues. + + if(ctxt->debugpcln) + Bprint(ctxt->bso, "%6llux %6d %P\n", (vlong)p->pc, val, p); + + if(started) { + addvarint(ctxt, dst, (p->pc - pc) / ctxt->arch->minlc); + pc = p->pc; + } + delta = val - oldval; + if(delta>>31) + delta = 1 | ~(delta<<1); + else + delta <<= 1; + addvarint(ctxt, dst, delta); + oldval = val; + started = 1; + val = valfunc(ctxt, func, val, p, 1, arg); + } + + if(started) { + if(ctxt->debugpcln) + Bprint(ctxt->bso, "%6llux done\n", (vlong)func->text->pc+func->size); + addvarint(ctxt, dst, (func->value+func->size - pc) / ctxt->arch->minlc); + addvarint(ctxt, dst, 0); // terminator + } + + if(ctxt->debugpcln) { + Bprint(ctxt->bso, "wrote %d bytes to %p\n", dst->n, dst); + for(i=0; i<dst->n; i++) + Bprint(ctxt->bso, " %02ux", dst->p[i]); + Bprint(ctxt->bso, "\n"); + } + + ctxt->debugpcln -= dbg; +} + +// pctofileline computes either the file number (arg == 0) +// or the line number (arg == 1) to use at p. +// Because p->lineno applies to p, phase == 0 (before p) +// takes care of the update. +static int32 +pctofileline(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) +{ + int32 i, l; + LSym *f; + Pcln *pcln; + + USED(sym); + + if(p->as == ctxt->arch->ATEXT || p->as == ctxt->arch->ANOP || p->as == ctxt->arch->AUSEFIELD || p->lineno == 0 || phase == 1) + return oldval; + linkgetline(ctxt, p->lineno, &f, &l); + if(f == nil) { + // print("getline failed for %s %P\n", ctxt->cursym->name, p); + return oldval; + } + if(arg == nil) + return l; + pcln = arg; + + if(f == pcln->lastfile) + return pcln->lastindex; + + for(i=0; i<pcln->nfile; i++) { + if(pcln->file[i] == f) { + pcln->lastfile = f; + pcln->lastindex = i; + return i; + } + } + + if(pcln->nfile >= pcln->mfile) { + pcln->mfile = (pcln->nfile+1)*2; + pcln->file = erealloc(pcln->file, pcln->mfile*sizeof pcln->file[0]); + } + pcln->file[pcln->nfile++] = f; + pcln->lastfile = f; + pcln->lastindex = i; + return i; +} + +// pctospadj computes the sp adjustment in effect. +// It is oldval plus any adjustment made by p itself. +// The adjustment by p takes effect only after p, so we +// apply the change during phase == 1. +static int32 +pctospadj(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) +{ + USED(arg); + USED(sym); + + if(oldval == -1) // starting + oldval = 0; + if(phase == 0) + return oldval; + if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) { + ctxt->diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); + sysfatal("bad code"); + } + return oldval + p->spadj; +} + +// pctopcdata computes the pcdata value in effect at p. +// A PCDATA instruction sets the value in effect at future +// non-PCDATA instructions. +// Since PCDATA instructions have no width in the final code, +// it does not matter which phase we use for the update. +static int32 +pctopcdata(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) +{ + USED(sym); + + if(phase == 0 || p->as != ctxt->arch->APCDATA || p->from.offset != (uintptr)arg) + return oldval; + if((int32)p->to.offset != p->to.offset) { + ctxt->diag("overflow in PCDATA instruction: %P", p); + sysfatal("bad code"); + } + return p->to.offset; +} + +void +linkpcln(Link *ctxt, LSym *cursym) +{ + Prog *p; + Pcln *pcln; + int i, npcdata, nfuncdata, n; + uint32 *havepc, *havefunc; + + ctxt->cursym = cursym; + + pcln = emallocz(sizeof *pcln); + cursym->pcln = pcln; + + npcdata = 0; + nfuncdata = 0; + for(p = cursym->text; p != nil; p = p->link) { + if(p->as == ctxt->arch->APCDATA && p->from.offset >= npcdata) + npcdata = p->from.offset+1; + if(p->as == ctxt->arch->AFUNCDATA && p->from.offset >= nfuncdata) + nfuncdata = p->from.offset+1; + } + + pcln->pcdata = emallocz(npcdata*sizeof pcln->pcdata[0]); + pcln->npcdata = npcdata; + pcln->funcdata = emallocz(nfuncdata*sizeof pcln->funcdata[0]); + pcln->funcdataoff = emallocz(nfuncdata*sizeof pcln->funcdataoff[0]); + pcln->nfuncdata = nfuncdata; + + funcpctab(ctxt, &pcln->pcsp, cursym, "pctospadj", pctospadj, nil); + funcpctab(ctxt, &pcln->pcfile, cursym, "pctofile", pctofileline, pcln); + funcpctab(ctxt, &pcln->pcline, cursym, "pctoline", pctofileline, nil); + + // tabulate which pc and func data we have. + n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; + havepc = emallocz(n); + havefunc = havepc + (npcdata+31)/32; + for(p = cursym->text; p != nil; p = p->link) { + if(p->as == ctxt->arch->AFUNCDATA) { + if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) + ctxt->diag("multiple definitions for FUNCDATA $%d", p->from.offset); + havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + if(p->as == ctxt->arch->APCDATA) + havepc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + // pcdata. + for(i=0; i<npcdata; i++) { + if(!(havepc[i/32]>>(i%32))&1) + continue; + funcpctab(ctxt, &pcln->pcdata[i], cursym, "pctopcdata", pctopcdata, (void*)(uintptr)i); + } + free(havepc); + + // funcdata + if(nfuncdata > 0) { + for(p = cursym->text; p != nil; p = p->link) { + if(p->as == ctxt->arch->AFUNCDATA) { + i = p->from.offset; + pcln->funcdataoff[i] = p->to.offset; + if(p->to.type != ctxt->arch->D_CONST) { + // TODO: Dedup. + //funcdata_bytes += p->to.sym->size; + pcln->funcdata[i] = p->to.sym; + } + } + } + } +} + +// iteration over encoded pcdata tables. + +static uint32 +getvarint(uchar **pp) +{ + uchar *p; + int shift; + uint32 v; + + v = 0; + p = *pp; + for(shift = 0;; shift += 7) { + v |= (uint32)(*p & 0x7F) << shift; + if(!(*p++ & 0x80)) + break; + } + *pp = p; + return v; +} + +void +pciternext(Pciter *it) +{ + uint32 v; + int32 dv; + + it->pc = it->nextpc; + if(it->done) + return; + if(it->p >= it->d.p + it->d.n) { + it->done = 1; + return; + } + + // value delta + v = getvarint(&it->p); + if(v == 0 && !it->start) { + it->done = 1; + return; + } + it->start = 0; + dv = (int32)(v>>1) ^ ((int32)(v<<31)>>31); + it->value += dv; + + // pc delta + v = getvarint(&it->p); + it->nextpc = it->pc + v*it->pcscale; +} + +void +pciterinit(Link *ctxt, Pciter *it, Pcdata *d) +{ + it->d = *d; + it->p = it->d.p; + it->pc = 0; + it->nextpc = 0; + it->value = -1; + it->start = 1; + it->done = 0; + it->pcscale = ctxt->arch->minlc; + pciternext(it); +} diff --git a/src/liblink/sym.c b/src/liblink/sym.c new file mode 100644 index 000000000..cba50e9c7 --- /dev/null +++ b/src/liblink/sym.c @@ -0,0 +1,271 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/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. + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> + +static int +yy_isalpha(int c) +{ + return c >= 0 && c <= 0xFF && isalpha(c); +} + +static struct { + char *name; + int val; +} headers[] = { + "darwin", Hdarwin, + "dragonfly", Hdragonfly, + "elf", Helf, + "freebsd", Hfreebsd, + "linux", Hlinux, + "nacl", Hnacl, + "netbsd", Hnetbsd, + "openbsd", Hopenbsd, + "plan9", Hplan9, + "solaris", Hsolaris, + "windows", Hwindows, + "windowsgui", Hwindows, + 0, 0 +}; + +int +headtype(char *name) +{ + int i; + + for(i=0; headers[i].name; i++) + if(strcmp(name, headers[i].name) == 0) + return headers[i].val; + return -1; +} + +char* +headstr(int v) +{ + static char buf[20]; + int i; + + for(i=0; headers[i].name; i++) + if(v == headers[i].val) + return headers[i].name; + snprint(buf, sizeof buf, "%d", v); + return buf; +} + +Link* +linknew(LinkArch *arch) +{ + Link *ctxt; + char *p; + char buf[1024]; + + nuxiinit(); + + ctxt = emallocz(sizeof *ctxt); + ctxt->arch = arch; + ctxt->version = HistVersion; + ctxt->goroot = getgoroot(); + ctxt->goroot_final = getenv("GOROOT_FINAL"); + if(ctxt->goroot_final != nil && ctxt->goroot_final[0] == '\0') + ctxt->goroot_final = nil; + + p = getgoarch(); + if(strcmp(p, arch->name) != 0) + sysfatal("invalid goarch %s (want %s)", p, arch->name); + + if(getwd(buf, sizeof buf) == 0) + strcpy(buf, "/???"); + if(yy_isalpha(buf[0]) && buf[1] == ':') { + // On Windows. + ctxt->windows = 1; + + // Canonicalize path by converting \ to / (Windows accepts both). + for(p=buf; *p; p++) + if(*p == '\\') + *p = '/'; + } + ctxt->pathname = strdup(buf); + + ctxt->headtype = headtype(getgoos()); + if(ctxt->headtype < 0) + sysfatal("unknown goos %s", getgoos()); + + // Record thread-local storage offset. + // TODO(rsc): Move tlsoffset back into the linker. + switch(ctxt->headtype) { + default: + sysfatal("unknown thread-local storage offset for %s", headstr(ctxt->headtype)); + case Hplan9: + ctxt->tlsoffset = -2*ctxt->arch->ptrsize; + break; + case Hwindows: + break; + case Hlinux: + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + case Hdragonfly: + case Hsolaris: + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + ctxt->tlsoffset = -2*ctxt->arch->ptrsize; + break; + + case Hnacl: + switch(ctxt->arch->thechar) { + default: + sysfatal("unknown thread-local storage offset for nacl/%s", ctxt->arch->name); + case '6': + ctxt->tlsoffset = 0; + break; + case '8': + ctxt->tlsoffset = -8; + break; + } + break; + + case Hdarwin: + /* + * OS X system constants - offset from 0(GS) to our TLS. + * Explained in ../../pkg/runtime/cgo/gcc_darwin_*.c. + */ + switch(ctxt->arch->thechar) { + default: + sysfatal("unknown thread-local storage offset for darwin/%s", ctxt->arch->name); + case '6': + ctxt->tlsoffset = 0x8a0; + break; + case '8': + ctxt->tlsoffset = 0x468; + break; + } + break; + } + + // On arm, record goarm. + if(ctxt->arch->thechar == '5') { + p = getgoarm(); + if(p != nil) + ctxt->goarm = atoi(p); + else + ctxt->goarm = 6; + } + + return ctxt; +} + +LSym* +linknewsym(Link *ctxt, char *symb, int v) +{ + LSym *s; + int l; + + l = strlen(symb) + 1; + s = malloc(sizeof(*s)); + memset(s, 0, sizeof(*s)); + + s->dynid = -1; + s->plt = -1; + s->got = -1; + s->name = malloc(l + 1); + memmove(s->name, symb, l); + s->name[l] = '\0'; + + s->type = 0; + s->version = v; + s->value = 0; + s->sig = 0; + s->size = 0; + ctxt->nsymbol++; + + s->allsym = ctxt->allsym; + ctxt->allsym = s; + + return s; +} + +static LSym* +_lookup(Link *ctxt, char *symb, int v, int creat) +{ + LSym *s; + char *p; + uint32 h; + int c; + + h = v; + for(p=symb; c = *p; p++) + h = h+h+h + c; + h &= 0xffffff; + h %= LINKHASH; + for(s = ctxt->hash[h]; s != nil; s = s->hash) + if(s->version == v && strcmp(s->name, symb) == 0) + return s; + if(!creat) + return nil; + + s = linknewsym(ctxt, symb, v); + s->extname = s->name; + s->hash = ctxt->hash[h]; + ctxt->hash[h] = s; + + return s; +} + +LSym* +linklookup(Link *ctxt, char *name, int v) +{ + return _lookup(ctxt, name, v, 1); +} + +// read-only lookup +LSym* +linkrlookup(Link *ctxt, char *name, int v) +{ + return _lookup(ctxt, name, v, 0); +} + +int +linksymfmt(Fmt *f) +{ + LSym *s; + + s = va_arg(f->args, LSym*); + if(s == nil) + return fmtstrcpy(f, "<nil>"); + + return fmtstrcpy(f, s->name); +} |