summaryrefslogtreecommitdiff
path: root/src/liblink
diff options
context:
space:
mode:
Diffstat (limited to 'src/liblink')
-rw-r--r--src/liblink/Makefile5
-rw-r--r--src/liblink/asm5.c2458
-rw-r--r--src/liblink/asm6.c3585
-rw-r--r--src/liblink/asm8.c2785
-rw-r--r--src/liblink/data.c370
-rw-r--r--src/liblink/go.c74
-rw-r--r--src/liblink/ld.c258
-rw-r--r--src/liblink/list5.c356
-rw-r--r--src/liblink/list6.c406
-rw-r--r--src/liblink/list8.c354
-rw-r--r--src/liblink/obj.c296
-rw-r--r--src/liblink/obj5.c1068
-rw-r--r--src/liblink/obj6.c1171
-rw-r--r--src/liblink/obj8.c859
-rw-r--r--src/liblink/objfile.c746
-rw-r--r--src/liblink/pass.c115
-rw-r--r--src/liblink/pcln.c365
-rw-r--r--src/liblink/sym.c271
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);
+}