From 7d507dc6e608e800bc26a5850259bbdb05abdf65 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 8 Dec 2013 22:49:37 -0500 Subject: liblink: create new library based on linker code There is an enormous amount of code moving around in this CL, but the code is the same, and it is invoked in the same ways. This CL is preparation for the new linker structure, not the new structure itself. The new library's definition is in include/link.h. The main change is the use of a Link structure to hold all the linker-relevant state, replacing the smattering of global variables. The Link structure should both make it clearer which state must be carried around and make it possible to parallelize more easily later. The main body of the linker has moved into the architecture-independent cmd/ld directory. That includes the list of known header types, so the distinction between Hplan9x32 and Hplan9x64 is removed (no other header type distinguished 32- and 64-bit formats), and code for unused formats such as ipaq kernels has been deleted. The code being deleted from 5l, 6l, and 8l reappears in liblink or in ld. Because multiple files are being merged in the liblink directory, it is not possible to show the diffs nicely in hg. The Prog and Addr structures have been unified into an architecture-independent form and moved to link.h, where they will be shared by all tools: the assemblers, the compilers, and the linkers. The unification makes it possible to write architecture-independent traversal of Prog lists, among other benefits. The Sym structures cannot be unified: they are too fundamentally different between the linker and the compilers. Instead, liblink defines an LSym - a linker Sym - to be used in the Prog and Addr structures, and the linker now refers exclusively to LSyms. The compilers will keep using their own syms but will fill out the corresponding LSyms in the Prog and Addr structures. Although code from 5l, 6l, and 8l is now in a single library, the code has been arranged so that only one architecture needs to be linked into a particular program: 5l will not contain the code needed for x86 instruction layout, for example. The object file writing code in liblink/obj.c is from cmd/gc/obj.c. Preparation for golang.org/s/go13linker work. This CL does not build by itself. It depends on 35740044 and will be submitted at the same time. R=iant CC=golang-dev https://golang.org/cl/35790044 --- src/liblink/Makefile | 5 + src/liblink/asm5.c | 2443 +++++++++++++++++++++++++++++++++++++ src/liblink/asm6.c | 3289 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/liblink/asm8.c | 2571 +++++++++++++++++++++++++++++++++++++++ src/liblink/data.c | 366 ++++++ src/liblink/go.c | 74 ++ src/liblink/ld.c | 572 +++++++++ src/liblink/obj.c | 403 +++++++ src/liblink/obj5.c | 1187 ++++++++++++++++++ src/liblink/obj6.c | 1078 +++++++++++++++++ src/liblink/obj8.c | 937 ++++++++++++++ src/liblink/pass.c | 115 ++ src/liblink/pcln.c | 298 +++++ src/liblink/rdobj5.c | 585 +++++++++ src/liblink/rdobj6.c | 495 ++++++++ src/liblink/rdobj8.c | 466 +++++++ src/liblink/sym.c | 158 +++ 17 files changed, 15042 insertions(+) create mode 100644 src/liblink/Makefile create mode 100644 src/liblink/asm5.c create mode 100644 src/liblink/asm6.c create mode 100644 src/liblink/asm8.c create mode 100644 src/liblink/data.c create mode 100644 src/liblink/go.c create mode 100644 src/liblink/ld.c create mode 100644 src/liblink/obj.c create mode 100644 src/liblink/obj5.c create mode 100644 src/liblink/obj6.c create mode 100644 src/liblink/obj8.c create mode 100644 src/liblink/pass.c create mode 100644 src/liblink/pcln.c create mode 100644 src/liblink/rdobj5.c create mode 100644 src/liblink/rdobj6.c create mode 100644 src/liblink/rdobj8.c create mode 100644 src/liblink/sym.c (limited to 'src/liblink') diff --git a/src/liblink/Makefile b/src/liblink/Makefile new file mode 100644 index 0000000000..2a317462b3 --- /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 0000000000..8ed8bea57e --- /dev/null +++ b/src/liblink/asm5.c @@ -0,0 +1,2443 @@ +// 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 +#include +#include +#include +#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 }, + + { 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*, LSym*); +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 int chipzero(Link*, float64); +static int chipfloat(Link*, float64); +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 = 14, + .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; + LSym *gmsym; + + 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)) + c = p->pc = scan(ctxt, op, p, c); + } + 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) == 14) + flushpool(ctxt, p, 0, 0); + break; + } + if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) + 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); + + c += c&4; + + /* + * 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. + */ + gmsym = nil; + if(ctxt->linkmode == LinkExternal) + 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, gmsym); + for(i=0; isize/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 %ux: 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(chipzero(ctxt, a->u.dval) >= 0) + return C_ZFCON; + if(chipfloat(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 %O %O %O\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(; oa2 == a2) + if(c1[o->a1]) + if(c3[o->a3]) { + p->optab = (o-optab)+1; + return o; + } + ctxt->diag("illegal combination %A %O %O %O, %d %d", + p->as, a1, a2, a3, p->from.type, p->to.type); + 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; iflag_shared) + optab[n].size += optab[n].pcrelsiz; + else + optab[n].flag &= ~LPCREL; + } + } + qsort(optab, n, sizeof(optab[0]), ocmp); + for(i=0; idiag("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 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; + } + } +} + +void +asmout(Link *ctxt, Prog *p, Optab *o, int32 *out, LSym *gmsym) +{ + 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; + rel->add = o1 | ((v >> 2) & 0xffffff); + rel->type = D_CALL; + 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; + 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<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) { + rel = addrel(ctxt->cursym); + rel->off = ctxt->pc; + rel->siz = 4; + rel->sym = p->to.sym; + rel->add = p->to.offset; + if(rel->sym == gmsym) { + rel->type = D_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 = D_PCREL; + rel->add += ctxt->pc - p->pcrel->pc - 8; + } else + rel->type = D_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"); + 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 == 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< 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 = D_PCREL; + rel->add += ctxt->pc - p->pcrel->pc - 16 + rel->siz; + } else + rel->type = D_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 = chipfloat(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 guranteed 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 +} + +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; +} + +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) + 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; +} + +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; +} + +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; +} + +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; +} + +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; +} + + +int32 +osrr(Link *ctxt, int r, int i, int b, int sc) +{ + + return olr(ctxt, i, b, r, sc) ^ ((1<<25) | (1<<20)); +} + +int32 +oshrr(Link *ctxt, int r, int i, int b, int sc) +{ + return olhr(ctxt, i, b, r, sc) ^ ((1<<22) | (1<<20)); +} + +int32 +olrr(Link *ctxt, int i, int b, int r, int sc) +{ + + return olr(ctxt, i, b, r, sc) ^ (1<<25); +} + +int32 +olhrr(Link *ctxt, int i, int b, int r, int sc) +{ + return olhr(ctxt, i, b, r, sc) ^ (1<<22); +} + +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; +} + +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; +} + +static int +chipzero(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; +} + +static int +chipfloat(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 0000000000..019ec06849 --- /dev/null +++ b/src/liblink/asm6.c @@ -0,0 +1,3289 @@ +// 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. ctxt->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 ctxt->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, ctxt->and/or sell +// copies of the Software, ctxt->and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice ctxt->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 +#include +#include +#include +#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, + // ctxt->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, + // ctxt->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 ctxt->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, + Ymax, + + Zxxx = 0, + + Zlit, + Zlitm_r, + Z_rp, + Zbr, + Zcall, + 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, Zo_m64, 0, + Yrx, Yrx, Zo_m64, 2, + Ynone, Ybr, 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, + * ctxt->and p->from ctxt->and p->to as operands (Addr*). The linker scans optab to find + * the entry with the given p->as ctxt->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 ctxt->and p->to operands. The function + * oclass in span.c computes the specific Ytype of an operand ctxt->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 ctxt->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, ctxt->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) ctxt->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 }, + * + * ctxt->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, ctxt->and + * possible states used to lay them down (Ztype ctxt->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), ctxt->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 }, + { AFATVARDEF }, + + { AEND }, + 0 +}; + +static Optab* opindex[ALAST+1]; +static vlong vaddr(Link*, Addr*, Reloc*); + +// single-instruction no-ops of various lengths. +// constructed by hand ctxt->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}, + {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); + +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 = p->mode != 64? AADDL: AADDQ; + if(v < 0) { + p->as = p->mode != 64? 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 = p->mode != 64? AADDL: AADDQ; + if(v < 0) { + p->as = p->mode != 64? 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((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; + + asmins(ctxt, p); + p->pc = c; + m = ctxt->andptr-ctxt->and; + 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); + 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; inp; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; inr; 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= 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(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; + } + 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) + 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_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) { + if(s->type == STLSBSS) { + r->xadd = r->add - r->siz; + r->type = D_TLS; + r->xsym = s; + } else + r->type = D_PCREL; + } else + r->type = D_ADDR; + } + 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_FS && a->index != D_GS) { + if(t < D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + if(ctxt->flag_shared) + 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; + + ctxt->rexflag |= (regrex[t] & Rxb) | rex; + if(t == D_NONE || (D_CS <= t && t <= D_GS)) { + if(ctxt->flag_shared && 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(v == 0 && t != D_BP && t != D_R13) { + *ctxt->andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + return; + } + if(v >= -128 && v < 128) { + 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; + } else if(ctxt->iself && ctxt->linkmode == LinkExternal && a->type == D_INDIR+D_FS + && ctxt->headtype != Hopenbsd) { + Reloc *r; + LSym *s; + + r = addrel(ctxt->cursym); + r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and; + r->add = a->offset - ctxt->tlsoffset; + r->xadd = r->add; + r->siz = 4; + r->type = D_TLS; + s = linklookup(ctxt, "runtime.tlsgm", 0); + r->sym = s; + r->xsym = s; + v = 0; + } + + 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}, + 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(&p->from); + if(pre) + *ctxt->andptr++ = pre; + pre = prefixof(&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 ctxt->and opcode escape */ + *ctxt->andptr++ = Pe; + *ctxt->andptr++ = Pm; + break; + case Pq3: /* 16 bit escape, Rex.w, ctxt->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: + *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->type = D_PCREL; + r->siz = 4; + put4(ctxt, 0); + break; + + 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 = D_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 ctxt->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; + } +} + +static void +asmins(Link *ctxt, Prog *p) +{ + int n, np, c; + Reloc *r; + + ctxt->rexflag = 0; + ctxt->andptr = ctxt->and; + 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 + * (ctxt->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 - ctxt->and; + for(np = 0; np < n; np++) { + c = ctxt->and[np]; + if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) + break; + } + memmove(ctxt->and+np+1, ctxt->and+np, n-np); + ctxt->and[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 == D_PCREL) + r->add -= p->pc + n - (r->off + r->siz); + } +} diff --git a/src/liblink/asm8.c b/src/liblink/asm8.c new file mode 100644 index 0000000000..6246276567 --- /dev/null +++ b/src/liblink/asm8.c @@ -0,0 +1,2571 @@ +// 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 +#include +#include +#include +#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, + + 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, + 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,1, + Ynone, Yml, Zpseudo,1, + Ynone, Yrf, Zpseudo,1, + Yml, Ynone, Zpseudo,1, + Yrf, 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, Zo_m, 0, + Yrx, Yrx, Zo_m, 2, + Ynone, Ycol, Zcallind, 2, + Ynone, Ybr, Zcall, 0, + Ynone, Yi32, Zcallcon, 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 }, + { 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 }, + { AFATVARDEF }, + + 0 +}; + +static int32 vaddr(Link*, Addr*, Reloc*); + +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) { + 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; + + asmins(ctxt, p); + p->pc = c; + m = ctxt->andptr-ctxt->and; + 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); + s->size = c; + + if(0 /* debug['a'] > 1 */) { + print("span1 %s %d (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; inp; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; inr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+d\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= 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(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; + } + 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_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 = D_ADDR; + r->siz = 4; + r->off = -1; + r->sym = s; + r->add = v; + v = 0; + } + } + return v; +} + +static int +istls(Link *ctxt, Addr *a) +{ + if(ctxt->headtype == Hlinux) + return a->index == D_GS; + return a->type == D_INDIR+D_GS; +} + +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_FS && a->index != D_GS) { + 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_NONE || (D_CS <= t && t <= D_GS)) { + *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(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 && a->index != D_FS && a->index != D_GS) { + 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; + } else if(ctxt->iself && ctxt->linkmode == LinkExternal && istls(ctxt, a) && ctxt->headtype != Hopenbsd) { + Reloc *r; + LSym *s; + + r = addrel(ctxt->cursym); + r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and; + r->add = a->offset - ctxt->tlsoffset; + r->xadd = r->add; + r->siz = 4; + r->type = D_TLS; + s = linklookup(ctxt, "runtime.tlsgm", 0); + r->sym = s; + r->xsym = s; + v = 0; + } + + 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, + 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(&p->from); + if(pre) + *ctxt->andptr++ = pre; + pre = prefixof(&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: + *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 = D_PCREL; + r->siz = 4; + r->sym = p->to.sym; + 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 = D_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 = D_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 = D_ADDR; + r->siz = 4; + r->add = p->to.offset; + r->sym = p->to.sym; + put4(ctxt, 0); + 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; + } + } + 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; + } +} + +static void +asmins(Link *ctxt, Prog *p) +{ + ctxt->andptr = ctxt->and; + 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 0000000000..97d2260418 --- /dev/null +++ b/src/liblink/data.c @@ -0,0 +1,366 @@ +// 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 +#include +#include +#include + +void +mangle(char *file) +{ + sysfatal("%s: mangled input file", file); +} + +void +symgrow(Link *ctxt, LSym *s, int32 siz) +{ + USED(ctxt); + + 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; ip[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 || p->to.type == ctxt->arch->D_SIZE) { + Addr: + r = addrel(s); + r->off = off; + r->siz = siz; + r->sym = p->to.sym; + r->type = p->to.type; + if(r->type != ctxt->arch->D_SIZE) + r->type = ctxt->arch->D_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 = ctxt->arch->D_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 = ctxt->arch->D_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 = ctxt->arch->D_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 = ctxt->arch->D_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 = ctxt->arch->D_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 0000000000..9f5a423d38 --- /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 +#include +#include +#include + +// 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 0000000000..f6632877bb --- /dev/null +++ b/src/liblink/ld.c @@ -0,0 +1,572 @@ +// 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 +#include +#include +#include + +void +copyhistfrog(Link *ctxt, char *buf, int nbuf) +{ + char *p, *ep; + int i; + + p = buf; + ep = buf + nbuf; + for(i=0; ihistfrogp; i++) { + p = seprint(p, ep, "%s", ctxt->histfrog[i]->name+1); + if(i+1histfrogp && (p == buf || p[-1] != '/')) + p = seprint(p, ep, "/"); + } +} + +void +addhist(Link *ctxt, int32 line, int type) +{ + Auto *u; + LSym *s; + int i, j, k; + + u = emallocz(sizeof(Auto)); + s = emallocz(sizeof(LSym)); + s->name = emallocz(2*(ctxt->histfrogp+1) + 1); + + u->asym = s; + u->type = type; + u->aoffset = line; + u->link = ctxt->curhist; + ctxt->curhist = u; + + s->name[0] = 0; + j = 1; + for(i=0; ihistfrogp; i++) { + k = ctxt->histfrog[i]->value; + s->name[j+0] = k>>8; + s->name[j+1] = k; + j += 2; + } + s->name[j] = 0; + s->name[j+1] = 0; +} + +void +histtoauto(Link *ctxt) +{ + Auto *l; + + while(l = ctxt->curhist) { + ctxt->curhist = l->link; + l->link = ctxt->curauto; + ctxt->curauto = l; + } +} + +void +collapsefrog(Link *ctxt, LSym *s) +{ + int i; + + /* + * bad encoding of path components only allows + * MAXHIST components. if there is an overflow, + * first try to collapse xxx/.. + */ + for(i=1; ihistfrogp; i++) + if(strcmp(ctxt->histfrog[i]->name+1, "..") == 0) { + memmove(ctxt->histfrog+i-1, ctxt->histfrog+i+1, + (ctxt->histfrogp-i-1)*sizeof(ctxt->histfrog[0])); + ctxt->histfrogp--; + goto out; + } + + /* + * next try to collapse . + */ + for(i=0; ihistfrogp; i++) + if(strcmp(ctxt->histfrog[i]->name+1, ".") == 0) { + memmove(ctxt->histfrog+i, ctxt->histfrog+i+1, + (ctxt->histfrogp-i-1)*sizeof(ctxt->histfrog[0])); + goto out; + } + + /* + * last chance, just truncate from front + */ + memmove(ctxt->histfrog+0, ctxt->histfrog+1, + (ctxt->histfrogp-1)*sizeof(ctxt->histfrog[0])); + +out: + ctxt->histfrog[ctxt->histfrogp-1] = s; +} + +// Saved history stacks encountered while reading archives. +// Keeping them allows us to answer virtual lineno -> file:line +// queries. +// +// The history stack is a complex data structure, described best at the +// bottom of http://plan9.bell-labs.com/magic/man2html/6/a.out. +// One of the key benefits of interpreting it here is that the runtime +// does not have to. Perhaps some day the compilers could generate +// a simpler linker input too. + +// savehist processes a single line, off history directive +// found in the input object file. +void +savehist(Link *ctxt, int32 line, int32 off) +{ + char tmp[1024]; + LSym *file; + Hist2 *h; + + // NOTE(rsc): We used to do the copyctxt->histfrog first and this + // condition was if(tmp[0] != '\0') to check for an empty string, + // implying that ctxt->histfrogp == 0, implying that this is a history pop. + // However, on Windows in the misc/cgo test, the linker is + // presented with an ANAME corresponding to an empty string, + // that ANAME ends up being the only ctxt->histfrog, and thus we have + // a situation where ctxt->histfrogp > 0 (not a pop) but the path we find + // is the empty string. Really that shouldn't happen, but it doesn't + // seem to be bothering anyone yet, and it's easier to fix the condition + // to test ctxt->histfrogp than to track down where that empty string is + // coming from. Probably it is coming from go tool pack's P command. + if(ctxt->histfrogp > 0) { + tmp[0] = '\0'; + copyhistfrog(ctxt, tmp, sizeof tmp); + file = linklookup(ctxt, tmp, HistVersion); + } else + file = nil; + + if(file != nil && line == 1 && off == 0) { + // start of new stack + if(ctxt->histdepth != 0) + sysfatal("history stack phase error: unexpected start of new stack depth=%d file=%s", ctxt->histdepth, tmp); + ctxt->nhist2 = 0; + ctxt->histcopy = nil; + } + + if(ctxt->nhist2 >= ctxt->maxhist2) { + if(ctxt->maxhist2 == 0) + ctxt->maxhist2 = 1; + ctxt->maxhist2 *= 2; + ctxt->hist2 = erealloc(ctxt->hist2, ctxt->maxhist2*sizeof ctxt->hist2[0]); + } + h = &ctxt->hist2[ctxt->nhist2++]; + h->line = line; + h->off = off; + h->file = file; + + if(file != nil) { + if(off == 0) + ctxt->histdepth++; + } else { + if(off != 0) + sysfatal("history stack phase error: bad offset in pop"); + ctxt->histdepth--; + } +} + +// gethist returns the history stack currently in effect. +// The result is valid indefinitely. +Hist2* +gethist(Link *ctxt) +{ + if(ctxt->histcopy == nil) { + if(ctxt->nhist2 == 0) + return nil; + ctxt->histcopy = emallocz((ctxt->nhist2+1)*sizeof ctxt->hist2[0]); + memmove(ctxt->histcopy, ctxt->hist2, ctxt->nhist2*sizeof ctxt->hist2[0]); + ctxt->histcopy[ctxt->nhist2].line = -1; + } + return ctxt->histcopy; +} + +typedef struct Hstack Hstack; +struct Hstack +{ + Hist2 *h; + int delta; +}; + +// getline sets *f to the file number and *l to the line number +// of the virtual line number line according to the history stack h. +void +linkgetline(Link *ctxt, Hist2 *h, int32 line, LSym **f, int32 *l) +{ + Hstack stk[100]; + int nstk, start; + Hist2 *top, *h0; + static Hist2 *lasth; + static int32 laststart, lastend, lastdelta; + static LSym *lastfile; + + h0 = h; + *f = 0; + *l = 0; + start = 0; + if(h == nil || line == 0) { + print("%s: getline: h=%p line=%d\n", ctxt->cursym->name, h, line); + return; + } + + // Cache span used during last lookup, so that sequential + // translation of line numbers in compiled code is efficient. + if(!ctxt->debughist && lasth == h && laststart <= line && line < lastend) { + *f = lastfile; + *l = line - lastdelta; + return; + } + + if(ctxt->debughist) + print("getline %d laststart=%d lastend=%d\n", line, laststart, lastend); + + nstk = 0; + for(; h->line != -1; h++) { + if(ctxt->debughist) + print("\t%s %d %d\n", h->file ? h->file->name : "?", h->line, h->off); + + if(h->line > line) { + if(nstk == 0) + sysfatal("history stack phase error: empty stack at line %d", (int)line); + top = stk[nstk-1].h; + lasth = h; + lastfile = top->file; + laststart = start; + lastend = h->line; + lastdelta = stk[nstk-1].delta; + *f = lastfile; + *l = line - lastdelta; + if(ctxt->debughist) + print("\tgot %d %d [%d %d %d]\n", *f, *l, laststart, lastend, lastdelta); + return; + } + if(h->file == nil) { + // pop included file + if(nstk == 0) + sysfatal("history stack phase error: stack underflow"); + nstk--; + if(nstk > 0) + stk[nstk-1].delta += h->line - stk[nstk].h->line; + start = h->line; + } else if(h->off == 0) { + // push included file + if(nstk >= nelem(stk)) + sysfatal("history stack phase error: stack overflow"); + start = h->line; + stk[nstk].h = h; + stk[nstk].delta = h->line - 1; + nstk++; + } else { + // #line directive + if(nstk == 0) + sysfatal("history stack phase error: stack underflow"); + stk[nstk-1].h = h; + stk[nstk-1].delta = h->line - h->off; + start = h->line; + } + if(ctxt->debughist) + print("\t\tnstk=%d delta=%d\n", nstk, stk[nstk].delta); + } + + sysfatal("history stack phase error: cannot find line for %d", line); + nstk = 0; + for(h = h0; h->line != -1; h++) { + print("\t%d %d %s\n", h->line, h->off, h->file ? h->file->name : ""); + if(h->file == nil) + nstk--; + else if(h->off == 0) + nstk++; + } +} + +void +addlib(Link *ctxt, char *src, char *obj) +{ + char name[1024], pname[1024], comp[256], *p; + int i, search; + + if(ctxt->histfrogp <= 0) + return; + + search = 0; + if(ctxt->histfrog[0]->name[1] == '/') { + sprint(name, ""); + i = 1; + } else + if(isalpha((uchar)ctxt->histfrog[0]->name[1]) && ctxt->histfrog[0]->name[2] == ':') { + strcpy(name, ctxt->histfrog[0]->name+1); + i = 1; + } else + if(ctxt->histfrog[0]->name[1] == '.') { + sprint(name, "."); + i = 0; + } else { + sprint(name, ""); + i = 0; + search = 1; + } + + for(; ihistfrogp; i++) { + snprint(comp, sizeof comp, "%s", ctxt->histfrog[i]->name+1); + for(;;) { + p = strstr(comp, "$O"); + if(p == 0) + break; + memmove(p+1, p+2, strlen(p+2)+1); + p[0] = ctxt->thechar; + } + for(;;) { + p = strstr(comp, "$M"); + if(p == 0) + break; + if(strlen(comp)+strlen(ctxt->thestring)-2+1 >= sizeof comp) + sysfatal("library component too long"); + memmove(p+strlen(ctxt->thestring), p+2, strlen(p+2)+1); + memmove(p, ctxt->thestring, strlen(ctxt->thestring)); + } + if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) + sysfatal("library component too long"); + if(i > 0 || !search) + strcat(name, "/"); + strcat(name, comp); + } + 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; ilibraryp; i++) + if(strcmp(ctxt->library[i].pkg, name) == 0) + return; + + // runtime -> runtime.a for search + if(p != nil) + *p = '.'; + + if(search) { + // try dot, -L "libdir", and then goroot. + for(i=0; inlibdir; i++) { + snprint(pname, sizeof pname, "%s/%s", ctxt->libdir[i], name); + if(access(pname, AEXIST) >= 0) + break; + } + }else + strcpy(pname, name); + 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; ilibraryp; 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; itext; 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/obj.c b/src/liblink/obj.c new file mode 100644 index 0000000000..eacbc4011a --- /dev/null +++ b/src/liblink/obj.c @@ -0,0 +1,403 @@ +// 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 +#include +#include +#include + +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, ""); + + return 0; +} + +static void +outzfile(Link *ctxt, Biobuf *b, char *p) +{ + char *q, *q2; + + while(p) { + q = utfrune(p, '/'); + if(ctxt->windows) { + q2 = utfrune(p, '\\'); + if(q2 && (!q || q2 < q)) + q = q2; + } + if(!q) { + ctxt->arch->zfile(b, p, strlen(p)); + return; + } + if(q > p) + ctxt->arch->zfile(b, p, q-p); + p = q + 1; + } +} + +#define isdelim(c) (c == '/' || c == '\\') + +static void +outwinname(Link *ctxt, Biobuf *b, Hist *h, char *ds, char *p) +{ + if(isdelim(p[0])) { + // full rooted name + ctxt->arch->zfile(b, ds, 3); // leading "c:/" + outzfile(ctxt, b, p+1); + } else { + // relative name + if(h->offset >= 0 && ctxt->pathname && ctxt->pathname[1] == ':') { + if(tolowerrune(ds[0]) == tolowerrune(ctxt->pathname[0])) { + // using current drive + ctxt->arch->zfile(b, ctxt->pathname, 3); // leading "c:/" + outzfile(ctxt, b, ctxt->pathname+3); + } else { + // using drive other then current, + // we don't have any simple way to + // determine current working directory + // there, therefore will output name as is + ctxt->arch->zfile(b, ds, 2); // leading "c:" + } + } + outzfile(ctxt, b, p); + } +} + +void +linkouthist(Link *ctxt, Biobuf *b) +{ + Hist *h; + char *p, ds[] = {'c', ':', '/', 0}; + char *tofree; + int n; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; + for(h = ctxt->hist; h != nil; h = h->link) { + p = h->name; + if(p) { + if(goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } + if(ctxt->windows) { + // if windows variable is set, then, we know already, + // pathname is started with windows drive specifier + // and all '\' were replaced with '/' (see lex.c) + if(isdelim(p[0]) && isdelim(p[1])) { + // file name has network name in it, + // like \\server\share\dir\file.go + ctxt->arch->zfile(b, "//", 2); // leading "//" + outzfile(ctxt, b, p+2); + } else if(p[1] == ':') { + // file name has drive letter in it + ds[0] = p[0]; + outwinname(ctxt, b, h, ds, p+2); + } else { + // no drive letter in file name + outwinname(ctxt, b, h, ctxt->pathname, p); + } + } else { + if(p[0] == '/') { + // full rooted name, like /home/rsc/dir/file.go + ctxt->arch->zfile(b, "/", 1); // leading "/" + outzfile(ctxt, b, p+1); + } else { + // relative name, like dir/file.go + if(h->offset >= 0 && ctxt->pathname && ctxt->pathname[0] == '/') { + ctxt->arch->zfile(b, "/", 1); // leading "/" + outzfile(ctxt, b, ctxt->pathname+1); + } + outzfile(ctxt, b, p); + } + } + } + ctxt->arch->zhist(b, h->line, h->offset); + if(tofree) { + free(tofree); + tofree = nil; + } + } +} + +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: \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 HISTSZ) + n = HISTSZ; + for(i=0; iplist == nil) + ctxt->plist = pl; + else + ctxt->plast->link = pl; + ctxt->plast = pl; + + return pl; +} + +static struct { + struct { LSym *sym; short type; } h[NSYM]; + int sym; +} z; + +static void +zsymreset(void) +{ + for(z.sym=0; z.symsymid; + if(i < 0 || i >= NSYM) + i = 0; + if(z.h[i].type == t && z.h[i].sym == s) + return i; + i = z.sym; + s->symid = i; + ctxt->arch->zname(b, s, t); + z.h[i].sym = s; + z.h[i].type = t; + if(++z.sym >= NSYM) + z.sym = 1; + *new = 1; + return i; +} + +static int +zsymaddr(Link *ctxt, Biobuf *b, Addr *a, int *new) +{ + return zsym(ctxt, b, a->sym, ctxt->arch->symtype(a), new); +} + +void +linkwritefuncs(Link *ctxt, Biobuf *b) +{ + int32 pcloc; + Plist *pl; + LSym *s; + Prog *p; + int sf, st, gf, gt, new; + + zsymreset(); + + // fix up pc + pcloc = 0; + for(pl=ctxt->plist; pl!=nil; pl=pl->link) { + if(pl->name != nil && strcmp(pl->name->name, "_") == 0) + continue; + for(p=pl->firstpc; p!=nil; p=p->link) { + p->loc = pcloc; + if(!ctxt->arch->isdata(p)) + pcloc++; + } + } + + // put out functions + for(pl=ctxt->plist; pl!=nil; pl=pl->link) { + if(pl->name != nil && strcmp(pl->name->name, "_") == 0) + continue; + + // -S prints code; -S -S prints code and data + if(ctxt->debugasm && (pl->name || ctxt->debugasm>1)) { + s = pl->name; + print("\n--- prog list \"%lS\" ---\n", s); + for(p=pl->firstpc; p!=nil; p=p->link) + print("%P\n", p); + } + + for(p=pl->firstpc; p!=nil; p=p->link) { + for(;;) { + sf = zsymaddr(ctxt, b, &p->from, &new); + gf = zsym(ctxt, b, p->from.gotype, ctxt->arch->D_EXTERN, &new); + if(new && sf == gf) + continue; + st = zsymaddr(ctxt, b, &p->to, &new); + if(new && (st == sf || st == gf)) + continue; + gt = zsym(ctxt, b, p->to.gotype, ctxt->arch->D_EXTERN, &new); + if(new && (gt == sf || gt == gf || gt == st)) + continue; + break; + } + ctxt->arch->zprog(ctxt, b, p, sf, gf, st, gt); + } + } +} diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c new file mode 100644 index 0000000000..e9c0b57318 --- /dev/null +++ b/src/liblink/obj5.c @@ -0,0 +1,1187 @@ +// 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 +#include +#include +#include +#include "../cmd/5l/5.out.h" +#include "../pkg/runtime/stack.h" + +static Addr noaddr = { + .type = D_NONE, + .name = D_NONE, + .reg = NREG, +}; + +static Prog zprg = { + .as = AGOK, + .scond = 14, + .reg = NREG, + .from = { + .name = D_NONE, + .type = D_NONE, + .reg = NREG, + }, + .to = { + .name = D_NONE, + .type = D_NONE, + .reg = NREG, + }, +}; + +static void +zname(Biobuf *b, LSym *s, int t) +{ + BPUTC(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->symid); /* sym */ + Bwrite(b, s->name, strlen(s->name)+1); +} + +static void +zfile(Biobuf *b, char *p, int n) +{ + BPUTC(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); + Bwrite(b, p, n); + BPUTC(b, 0); +} + +static void +zaddr(Biobuf *b, Addr *a, int s, int gotype) +{ + int32 l; + uint64 e; + int i; + char *n; + + switch(a->type) { + case D_STATIC: + case D_AUTO: + case D_EXTERN: + case D_PARAM: + // TODO(kaib): remove once everything seems to work + sysfatal("We should no longer generate these as types"); + + default: + BPUTC(b, a->type); + BPUTC(b, a->reg); + BPUTC(b, s); + BPUTC(b, a->name); + BPUTC(b, gotype); + } + + switch(a->type) { + default: + print("unknown type %d in zaddr\n", a->type); + + case D_NONE: + case D_REG: + case D_FREG: + case D_PSR: + break; + + case D_CONST2: + l = a->offset2; + BPUTLE4(b, l); // fall through + case D_OREG: + case D_CONST: + case D_SHIFT: + case D_STATIC: + case D_AUTO: + case D_EXTERN: + case D_PARAM: + l = a->offset; + BPUTLE4(b, l); + break; + + case D_BRANCH: + if(a->offset == 0 || a->u.branch != nil) { + if(a->u.branch == nil) + sysfatal("unpatched branch %D", a); + a->offset = a->u.branch->loc; + } + l = a->offset; + BPUTLE4(b, l); + break; + + case D_SCONST: + n = a->u.sval; + for(i=0; ioffset); + break; + + case D_FCONST: + double2ieee(&e, a->u.dval); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); + break; + } +} + +static void +zhist(Biobuf *b, int line, vlong offset) +{ + Addr a; + + BPUTC(b, AHISTORY); + BPUTC(b, C_SCOND_NONE); + BPUTC(b, NREG); + BPUTLE4(b, line); + zaddr(b, &noaddr, 0, 0); + a = noaddr; + if(offset != 0) { + a.offset = offset; + a.type = D_CONST; + } + zaddr(b, &a, 0, 0); +} + +static int +symtype(Addr *a) +{ + return a->name; +} + +static void +zprog(Link *ctxt, Biobuf *b, Prog *p, int sf, int gf, int st, int gt) +{ + USED(ctxt); + + BPUTC(b, p->as); + BPUTC(b, p->scond); + BPUTC(b, p->reg); + BPUTLE4(b, p->lineno); + zaddr(b, &p->from, sf, gf); + zaddr(b, &p->to, st, gt); +} + +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 Prog* +prg(void) +{ + Prog *p; + + p = emallocz(sizeof(*p)); + *p = zprg; + return p; +} + +static Prog* stacksplit(Link*, Prog*, int32); +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; + LSym *tlsfallback; + int32 autosize, autoffset; + + autosize = 0; + + if(ctxt->symmorestack[0] == nil) + ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0); + + tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0); + ctxt->gmsym = nil; + if(ctxt->linkmode == LinkExternal) + ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); + q = nil; + + ctxt->cursym = cursym; + + if(cursym->text == nil || cursym->text->link == nil) + return; + + softfloat(ctxt, cursym); + + if(ctxt->debugzerostack) { + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + 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 + * fixup TLS + */ + + 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: + 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; + case AWORD: + // Rewrite TLS register fetch: MRC 15, 0, , C13, C0, 3 + if((p->to.offset & 0xffff0fff) == 0xee1d0f70) { + if(ctxt->headtype == Hopenbsd) { + p->as = ARET; + } else if(ctxt->goarm < 7) { + if(tlsfallback->type != STEXT) { + ctxt->diag("runtime·read_tls_fallback not defined"); + sysfatal("tlsfallback"); + } + // BL runtime.read_tls_fallback(SB) + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = tlsfallback; + p->pcond = tlsfallback->text; + p->to.offset = 0; + cursym->text->mark &= ~LEAF; + } + if(ctxt->linkmode == LinkExternal) { + // runtime.tlsgm is relocated with R_ARM_TLS_LE32 + // and $runtime.tlsgm will contain the TLS offset. + // + // MOV $runtime.tlsgm+ctxt->tlsoffset(SB), REGTMP + // ADD REGTMP, + // + // In shared mode, runtime.tlsgm is relocated with + // R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point + // to the GOT entry containing the TLS offset. + // + // MOV runtime.tlsgm(SB), REGTMP + // ADD REGTMP, + // SUB -ctxt->tlsoffset, + // + // The SUB compensates for ctxt->tlsoffset + // used in runtime.save_gm and runtime.load_gm. + q = p; + p = appendp(ctxt, p); + p->as = AMOVW; + p->scond = 14; + p->reg = NREG; + if(ctxt->flag_shared) { + p->from.type = D_OREG; + p->from.offset = 0; + } else { + p->from.type = D_CONST; + p->from.offset = ctxt->tlsoffset; + } + p->from.sym = ctxt->gmsym; + p->from.name = D_EXTERN; + p->to.type = D_REG; + p->to.reg = REGTMP; + p->to.offset = 0; + + p = appendp(ctxt, p); + p->as = AADD; + p->scond = 14; + p->reg = NREG; + p->from.type = D_REG; + p->from.reg = REGTMP; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + + if(ctxt->flag_shared) { + p = appendp(ctxt, p); + p->as = ASUB; + p->scond = 14; + p->reg = NREG; + p->from.type = D_CONST; + p->from.offset = -ctxt->tlsoffset; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + } + } + } + } + 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); // 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; + p->pcond = p->to.sym->text; + } 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; + q2->pcond = p->to.sym->text; + 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; + p->pcond = p; + 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, *psfloat; + LSym *symsfloat; + int wasfloat; + + if(!ctxt->debugfloat) + return; + + symsfloat = linklookup(ctxt, "_sfloat", 0); + psfloat = nil; + if(symsfloat->type == STEXT) + psfloat = symsfloat->text; + + 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 (psfloat == nil) + ctxt->diag("floats used with _sfloat not defined"); + 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->pcond = psfloat; + p->lineno = next->lineno; + + p = next; + wasfloat = 1; + } + break; + + notsoft: + wasfloat = 0; + } + } +} + +static Prog* +stacksplit(Link *ctxt, Prog *p, int32 framesize) +{ + 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[0]; + + // 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 == 14) || 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 == 14) || 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 == 14) || 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", + + .addstacksplit = addstacksplit, + .assemble = span5, + .datasize = datasize, + .follow = follow, + .iscall = iscall, + .isdata = isdata, + .ldobj = ldobj5, + .nopout = nopout5, + .prg = prg, + .settextflag = settextflag, + .symtype = symtype, + .textflag = textflag, + .zfile = zfile, + .zhist = zhist, + .zname = zname, + .zprog = zprog, + + .minlc = 4, + .ptrsize = 4, + + .D_ADDR = D_ADDR, + .D_BRANCH = D_BRANCH, + .D_CONST = D_CONST, + .D_EXTERN = D_EXTERN, + .D_FCONST = D_FCONST, + .D_NONE = D_NONE, + .D_PCREL = D_PCREL, + .D_SCONST = D_SCONST, + .D_SIZE = D_SIZE, + + .ACALL = ABL, + .AFUNCDATA = AFUNCDATA, + .AJMP = AB, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .AUSEFIELD = AUSEFIELD, +}; diff --git a/src/liblink/obj6.c b/src/liblink/obj6.c new file mode 100644 index 0000000000..bd24d1d9e8 --- /dev/null +++ b/src/liblink/obj6.c @@ -0,0 +1,1078 @@ +// 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 +#include +#include +#include +#include "../cmd/6l/6.out.h" +#include "../pkg/runtime/stack.h" + +static Addr noaddr = { + .type = D_NONE, + .index = D_NONE, + .scale = 0, +}; + +static Prog zprg = { + .back = 2, + .as = AGOK, + .from = { + .type = D_NONE, + .index = D_NONE, + }, + .to = { + .type = D_NONE, + .index = D_NONE, + }, +}; + +static void +zname(Biobuf *b, LSym *s, int t) +{ + BPUTLE2(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->symid); /* sym */ + Bwrite(b, s->name, strlen(s->name)+1); +} + +static void +zfile(Biobuf *b, char *p, int n) +{ + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); + Bwrite(b, p, n); + BPUTC(b, 0); +} + +static void +zaddr(Biobuf *b, Addr *a, int s, int gotype) +{ + int32 l; + uint64 e; + int i, t; + char *n; + + t = 0; + if(a->index != D_NONE || a->scale != 0) + t |= T_INDEX; + if(s != 0) + t |= T_SYM; + if(gotype != 0) + t |= T_GOTYPE; + + switch(a->type) { + + case D_BRANCH: + if(a->offset == 0 || a->u.branch != nil) { + if(a->u.branch == nil) + sysfatal("unpatched branch %D", a); + a->offset = a->u.branch->loc; + } + + default: + t |= T_TYPE; + + case D_NONE: + if(a->offset != 0) { + t |= T_OFFSET; + l = a->offset; + if((vlong)l != a->offset) + t |= T_64; + } + break; + case D_FCONST: + t |= T_FCONST; + break; + case D_SCONST: + t |= T_SCONST; + break; + } + BPUTC(b, t); + + if(t & T_INDEX) { /* implies index, scale */ + BPUTC(b, a->index); + BPUTC(b, a->scale); + } + if(t & T_OFFSET) { /* implies offset */ + l = a->offset; + BPUTLE4(b, l); + if(t & T_64) { + l = a->offset>>32; + BPUTLE4(b, l); + } + } + if(t & T_SYM) /* implies sym */ + BPUTC(b, s); + if(t & T_FCONST) { + double2ieee(&e, a->u.dval); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); + return; + } + if(t & T_SCONST) { + n = a->u.sval; + for(i=0; itype); + if(t & T_GOTYPE) + BPUTC(b, gotype); +} + +static void +zhist(Biobuf *b, int line, vlong offset) +{ + Addr a; + + BPUTLE2(b, AHISTORY); + BPUTLE4(b, line); + zaddr(b, &noaddr, 0, 0); + a = noaddr; + if(offset != 0) { + a.offset = offset; + a.type = D_CONST; + } + zaddr(b, &a, 0, 0); +} + +static int +symtype(Addr *a) +{ + int t; + + t = a->type; + if(t == D_ADDR) + t = a->index; + return t; +} + +static void +zprog(Link *ctxt, Biobuf *b, Prog *p, int sf, int gf, int st, int gt) +{ + USED(ctxt); + + BPUTLE2(b, p->as); + BPUTLE4(b, p->lineno); + zaddr(b, &p->from, sf, gf); + zaddr(b, &p->to, st, gt); +} + +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 +progedit(Link *ctxt, Prog *p) +{ + Prog *q; + LSym *gmsym; + + gmsym = nil; // TODO + + if(ctxt->headtype == Hwindows) { + // Windows + // Convert + // op n(GS), reg + // to + // MOVL 0x28(GS), reg + // op n(reg), reg + // The purpose of this patch is to fix some accesses + // to extern register variables (TLS) on Windows, as + // a different method is used to access them. + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI + && p->from.offset <= 8) { + q = appendp(ctxt, p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVQ; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0x28; + } + } + if(ctxt->headtype == Hlinux || ctxt->headtype == Hfreebsd + || ctxt->headtype == Hopenbsd || ctxt->headtype == Hnetbsd + || ctxt->headtype == Hplan9 || ctxt->headtype == Hdragonfly) { + // ELF uses FS instead of GS. + if(p->from.type == D_INDIR+D_GS) + p->from.type = D_INDIR+D_FS; + if(p->to.type == D_INDIR+D_GS) + p->to.type = D_INDIR+D_FS; + if(p->from.index == D_GS) + p->from.index = D_FS; + if(p->to.index == D_GS) + p->to.index = D_FS; + } + if(!ctxt->flag_shared) { + // Convert g() or m() accesses of the form + // op n(reg)(GS*1), reg + // to + // op n(GS*1), reg + if(p->from.index == D_FS || p->from.index == D_GS) { + p->from.type = D_INDIR + p->from.index; + p->from.index = D_NONE; + } + // Convert g() or m() accesses of the form + // op reg, n(reg)(GS*1) + // to + // op reg, n(GS*1) + if(p->to.index == D_FS || p->to.index == D_GS) { + p->to.type = D_INDIR + p->to.index; + p->to.index = D_NONE; + } + // Convert get_tls access of the form + // op runtime.tlsgm(SB), reg + // to + // NOP + if(gmsym != nil && p->from.sym == gmsym) { + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; + p->from.sym = nil; + p->to.sym = nil; + return; + } + } else { + /* + // Convert TLS reads of the form + // op n(GS), reg + // to + // MOVQ $runtime.tlsgm(SB), reg + // op n(reg)(GS*1), reg + if((p->from.type == D_INDIR+D_FS || p->from.type == D_INDIR + D_GS) && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(ctxt, p); + q->to = p->to; + q->as = p->as; + q->from.type = D_INDIR+p->to.type; + q->from.index = p->from.type - D_INDIR; + q->from.scale = 1; + q->from.offset = p->from.offset; + p->as = AMOVQ; + p->from.type = D_EXTERN; + p->from.sym = gmsym; + p->from.offset = 0; + } + */ + } +} + +static char* +morename[] = +{ + "runtime.morestack00", + "runtime.morestack10", + "runtime.morestack01", + "runtime.morestack11", + + "runtime.morestack8", + "runtime.morestack16", + "runtime.morestack24", + "runtime.morestack32", + "runtime.morestack40", + "runtime.morestack48", +}; + +static Prog* load_g_cx(Link*, Prog*); +static Prog* stacksplit(Link*, Prog*, int32, int32, Prog**); + +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(nelem(morename) > nelem(ctxt->symmorestack)) + sysfatal("Link.symmorestack needs at least %d elements", nelem(morename)); + for(i=0; isymmorestack[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; + + if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) { + for(q = p; q != nil; q = q->link) + if(q->as == ACALL) + goto noleaf; + p->from.scale |= NOSPLIT; + noleaf:; + } + + if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) + ctxt->diag("nosplit func likely to overflow stack"); + + 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, &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->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; + p->from.type = D_INDIR+D_CX; + 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->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) +{ + if(ctxt->flag_shared) { + // Load TLS offset with MOVQ $runtime.tlsgm(SB), CX + p->as = AMOVQ; + p->from.type = D_EXTERN; + p->from.sym = ctxt->gmsym; + p->to.type = D_CX; + p = appendp(ctxt, p); + } + p->as = AMOVQ; + if(ctxt->headtype == Hlinux || ctxt->headtype == Hfreebsd + || ctxt->headtype == Hopenbsd || ctxt->headtype == Hnetbsd + || ctxt->headtype == Hplan9 || ctxt->headtype == Hdragonfly) + // ELF uses FS + p->from.type = D_INDIR+D_FS; + else + p->from.type = D_INDIR+D_GS; + if(ctxt->flag_shared) { + // Add TLS offset stored in CX + p->from.index = p->from.type - D_INDIR; + p->from.type = D_INDIR + D_CX; + } + p->from.offset = ctxt->tlsoffset+0; + p->to.type = D_CX; + if(ctxt->headtype == Hwindows) { + // movq %gs:0x28, %rcx + // movq (%rcx), %rcx + p->as = AMOVQ; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0x28; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + } + 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, Prog **jmpok) +{ + Prog *q, *q1; + uint32 moreconst1, moreconst2, i; + + 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 = ACMPQ; + p->from.type = D_INDIR+D_CX; + 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; + } + + q = nil; + q1 = nil; + if(framesize <= StackSmall) { + // small stack: SP <= stackguard + // CMPQ SP, stackguard + p = appendp(ctxt, p); + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else if(framesize <= StackBig) { + // large stack: SP-framesize <= stackguard-StackSmall + // LEAQ -xxx(SP), AX + // CMPQ AX, stackguard + p = appendp(ctxt, p); + p->as = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(framesize-StackSmall); + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACMPQ; + 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. + // 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 = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_SI; + + p = appendp(ctxt, p); + p->as = ACMPQ; + 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 = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = StackGuard; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ASUBQ; + p->from.type = D_SI; + p->to.type = D_AX; + + p = appendp(ctxt, p); + p->as = ACMPQ; + 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]; + } 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]; + } 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]; + } 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]; + } else { + 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]; + } + + 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", + + .zprog = zprog, + .zhist = zhist, + .zfile = zfile, + .zname = zname, + .isdata = isdata, + .ldobj = ldobj6, + .nopout = nopout6, + .symtype = symtype, + .iscall = iscall, + .datasize = datasize, + .textflag = textflag, + .settextflag = settextflag, + .progedit = progedit, + .prg = prg, + .addstacksplit = addstacksplit, + .assemble = span6, + .follow = follow, + + .minlc = 1, + .ptrsize = 8, + + .D_ADDR = D_ADDR, + .D_BRANCH = D_BRANCH, + .D_CONST = D_CONST, + .D_EXTERN = D_EXTERN, + .D_FCONST = D_FCONST, + .D_NONE = D_NONE, + .D_PCREL = D_PCREL, + .D_SCONST = D_SCONST, + .D_SIZE = D_SIZE, + + .ACALL = ACALL, + .AFUNCDATA = AFUNCDATA, + .AJMP = AJMP, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .AUSEFIELD = AUSEFIELD, +}; diff --git a/src/liblink/obj8.c b/src/liblink/obj8.c new file mode 100644 index 0000000000..e744abe55e --- /dev/null +++ b/src/liblink/obj8.c @@ -0,0 +1,937 @@ +// 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 +#include +#include +#include +#include "../cmd/8l/8.out.h" +#include "../pkg/runtime/stack.h" + +static Addr noaddr = { + .type = D_NONE, + .index = D_NONE, + .scale = 1, +}; + +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 void +zname(Biobuf *b, LSym *s, int t) +{ + BPUTLE2(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->symid); /* sym */ + Bwrite(b, s->name, strlen(s->name)+1); +} + +static void +zfile(Biobuf *b, char *p, int n) +{ + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); + Bwrite(b, p, n); + BPUTC(b, 0); +} + +static void +zaddr(Biobuf *b, Addr *a, int s, int gotype) +{ + int32 l; + uint64 e; + int i, t; + char *n; + + t = 0; + if(a->index != D_NONE || a->scale != 0) + t |= T_INDEX; + if(s != 0) + t |= T_SYM; + if(gotype != 0) + t |= T_GOTYPE; + + switch(a->type) { + + case D_BRANCH: + if(a->offset == 0 || a->u.branch != nil) { + if(a->u.branch == nil) + sysfatal("unpatched branch %D", a); + a->offset = a->u.branch->loc; + } + + default: + t |= T_TYPE; + + case D_NONE: + if(a->offset != 0) + t |= T_OFFSET; + if(a->offset2 != 0) + t |= T_OFFSET2; + break; + case D_FCONST: + t |= T_FCONST; + break; + case D_SCONST: + t |= T_SCONST; + break; + } + BPUTC(b, t); + + if(t & T_INDEX) { /* implies index, scale */ + BPUTC(b, a->index); + BPUTC(b, a->scale); + } + if(t & T_OFFSET) { /* implies offset */ + l = a->offset; + BPUTLE4(b, l); + } + if(t & T_OFFSET2) { /* implies offset */ + l = a->offset2; + BPUTLE4(b, l); + } + if(t & T_SYM) /* implies sym */ + BPUTC(b, s); + if(t & T_FCONST) { + double2ieee(&e, a->u.dval); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); + return; + } + if(t & T_SCONST) { + n = a->u.sval; + for(i=0; itype); + if(t & T_GOTYPE) + BPUTC(b, gotype); +} + +static void +zhist(Biobuf *b, int line, vlong offset) +{ + Addr a; + + BPUTLE2(b, AHISTORY); + BPUTLE4(b, line); + zaddr(b, &noaddr, 0, 0); + a = noaddr; + if(offset != 0) { + a.offset = offset; + a.type = D_CONST; + } + zaddr(b, &a, 0, 0); +} + +static int +symtype(Addr *a) +{ + int t; + + t = a->type; + if(t == D_ADDR) + t = a->index; + return t; +} + +static void +zprog(Link *ctxt, Biobuf *b, Prog *p, int sf, int gf, int st, int gt) +{ + USED(ctxt); + + BPUTLE2(b, p->as); + BPUTLE4(b, p->lineno); + zaddr(b, &p->from, sf, gf); + zaddr(b, &p->to, st, gt); +} + +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 +progedit(Link *ctxt, Prog *p) +{ + Prog *q; + + if(ctxt->headtype == Hwindows) { + // Convert + // op n(GS), reg + // to + // MOVL 0x14(FS), reg + // op n(reg), reg + // The purpose of this patch is to fix some accesses + // to extern register variables (TLS) on Windows, as + // a different method is used to access them. + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(ctxt, p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x14; + } + } + if(ctxt->headtype == Hlinux) { + // Running binaries under Xen requires using + // MOVL 0(GS), reg + // and then off(reg) instead of saying off(GS) directly + // when the offset is negative. + // In external mode we just produce a reloc. + if(p->from.type == D_INDIR+D_GS && p->from.offset < 0 + && p->to.type >= D_AX && p->to.type <= D_DI) { + if(ctxt->linkmode != LinkExternal) { + q = appendp(ctxt, p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; + } else { + // Add signals to relocate. + p->from.index = D_GS; + p->from.scale = 1; + } + } + } + /* TODO + if(ctxt->headtype == Hplan9) { + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(ctxt, p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_EXTERN; + p->from.sym = plan9_tos; + p->from.offset = 0; + } + } + */ +} + +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, 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); + + 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; + + 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, &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) +{ + switch(ctxt->headtype) { + case Hwindows: + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x14; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + break; + + case Hlinux: + if(ctxt->linkmode != LinkExternal) { + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = ctxt->tlsoffset + 0; + p->to.type = D_CX; + } else { + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = ctxt->tlsoffset + 0; + p->to.type = D_CX; + p->from.index = D_GS; + p->from.scale = 1; + } + break; + + case Hplan9: + p->as = AMOVL; + p->from.type = D_EXTERN; + p->from.sym = ctxt->plan9tos; + p->to.type = D_CX; + + p = appendp(ctxt, p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = ctxt->tlsoffset + 0; + p->to.type = D_CX; + break; + + default: + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = ctxt->tlsoffset + 0; + p->to.type = D_CX; + } + 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, 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[0]; + + 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", + + .addstacksplit = addstacksplit, + .assemble = span8, + .datasize = datasize, + .follow = follow, + .iscall = iscall, + .isdata = isdata, + .ldobj = ldobj8, + .nopout = nopout8, + .prg = prg, + .progedit = progedit, + .settextflag = settextflag, + .symtype = symtype, + .textflag = textflag, + .zfile = zfile, + .zhist = zhist, + .zname = zname, + .zprog = zprog, + + .minlc = 1, + .ptrsize = 4, + + .D_ADDR = D_ADDR, + .D_BRANCH = D_BRANCH, + .D_CONST = D_CONST, + .D_EXTERN = D_EXTERN, + .D_FCONST = D_FCONST, + .D_NONE = D_NONE, + .D_PCREL = D_PCREL, + .D_SCONST = D_SCONST, + .D_SIZE = D_SIZE, + + .ACALL = ACALL, + .AFUNCDATA = AFUNCDATA, + .AJMP = AJMP, + .ANOP = ANOP, + .APCDATA = APCDATA, + .ARET = ARET, + .ATEXT = ATEXT, + .AUSEFIELD = AUSEFIELD, +}; diff --git a/src/liblink/pass.c b/src/liblink/pass.c new file mode 100644 index 0000000000..3fe77d61bf --- /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 +#include +#include +#include + +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; + LSym *s; + + ctxt->cursym = sym; + + for(p = sym->text; p != nil; p = p->link) { + if(ctxt->arch->progedit) + ctxt->arch->progedit(ctxt, p); + if(p->as == ctxt->arch->ACALL || (p->as == ctxt->arch->AJMP && p->to.type != ctxt->arch->D_BRANCH) || (p->as == ctxt->arch->ARET && p->to.sym != nil)) { + s = p->to.sym; + if(s) { + p->to.type = ctxt->arch->D_BRANCH; + continue; + } + } + if(p->to.type != ctxt->arch->D_BRANCH) + 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 : ""); + p->to.type = ctxt->arch->D_NONE; + } + 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 0000000000..21eb94414f --- /dev/null +++ b/src/liblink/pcln.c @@ -0,0 +1,298 @@ +// 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 +#include +#include +#include + +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) + 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; in; 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; + + 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, sym->hist, 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; infile; 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>(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; + } + } + } + } +} diff --git a/src/liblink/rdobj5.c b/src/liblink/rdobj5.c new file mode 100644 index 0000000000..f2a8b82231 --- /dev/null +++ b/src/liblink/rdobj5.c @@ -0,0 +1,585 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.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 +#include +#include +#include +#include "../cmd/5l/5.out.h" + +// TODO: remove duplicate chipzero, chipfloat + +static void finish(Link*); + +static int +chipzero(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; +} + +static int +chipfloat(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; +} + +static LSym* +zsym(char *pn, Biobuf *f, LSym *h[]) +{ + int o; + + o = BGETC(f); + if(o == 0) + return nil; + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(Link *ctxt, char *pn, Biobuf *f, Addr *a, LSym *h[], LSym **pgotype) +{ + int i, c; + int32 l; + LSym *s, *gotype; + Auto *u; + uint64 v; + + a->type = BGETC(f); + a->reg = BGETC(f); + c = BGETC(f); + if(c < 0 || c > NSYM){ + print("sym out of range: %d\n", c); + BPUTC(f, ALAST+1); + return; + } + a->sym = h[c]; + a->name = BGETC(f); + gotype = zsym(pn, f, h); + if(pgotype) + *pgotype = gotype; + + if((schar)a->reg < 0 || a->reg > NREG) { + print("register out of range %d\n", a->reg); + BPUTC(f, ALAST+1); + return; /* force real diagnostic */ + } + + if(a->type == D_CONST || a->type == D_OCONST) { + if(a->name == D_EXTERN || a->name == D_STATIC) { + s = a->sym; + if(s != nil && (s->type == STEXT || s->type == SCONST || s->type == SXREF)) { + if(0 && !s->fnptr && s->name[0] != '.') + print("%s used as function pointer\n", s->name); + s->fnptr = 1; // over the top cos of SXREF + } + } + } + + switch(a->type) { + default: + print("unknown type %d\n", a->type); + BPUTC(f, ALAST+1); + return; /* force real diagnostic */ + + case D_NONE: + case D_REG: + case D_FREG: + case D_PSR: + case D_FPCR: + break; + + case D_REGREG: + case D_REGREG2: + a->offset = BGETC(f); + break; + + case D_CONST2: + a->offset2 = BGETLE4(f); // fall through + case D_BRANCH: + case D_OREG: + case D_CONST: + case D_OCONST: + case D_SHIFT: + a->offset = BGETLE4(f); + break; + + case D_SCONST: + Bread(f, a->u.sval, NSNAME); + break; + + case D_FCONST: + v = (uint32)BGETLE4(f); + v |= (uint64)BGETLE4(f)<<32; + memmove(&a->u.dval, &v, 8); + break; + } + s = a->sym; + if(s == nil) + return; + i = a->name; + if(i != D_AUTO && i != D_PARAM) { + if(s && gotype) + s->gotype = gotype; + return; + } + + l = a->offset; + for(u=ctxt->curauto; u; u=u->link) + if(u->asym == s) + if(u->type == i) { + if(u->aoffset > l) + u->aoffset = l; + if(gotype) + u->gotype = gotype; + return; + } + + u = emallocz(sizeof(Auto)); + u->link = ctxt->curauto; + ctxt->curauto = u; + u->asym = s; + u->aoffset = l; + u->type = i; + u->gotype = gotype; +} + +void +nopout5(Prog *p) +{ + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; +} + +void +ldobj5(Link *ctxt, Biobuf *f, char *pkg, int64 len, char *pn) +{ + int32 ipc; + Prog *p; + LSym *h[NSYM], *s; + int v, o, r, skip; + uint32 sig; + char *name; + int ntext; + int32 eof, autosize; + char src[1024], *x, literal[64]; + Prog *lastp; + LSym *fromgotype; + + lastp = nil; + ntext = 0; + eof = Boffset(f) + len; + src[0] = 0; + pn = estrdup(pn); // we keep it in LSym* references + +newloop: + memset(h, 0, sizeof(h)); + ctxt->version++; + ctxt->histfrogp = 0; + ipc = ctxt->pc; + skip = 0; + +loop: + if(f->state == Bracteof || Boffset(f) >= eof) + goto eof; + o = BGETC(f); + if(o == Beof) + goto eof; + + if(o <= AXXX || o >= ALAST) { + ctxt->diag("%s:#%lld: opcode out of range: %#ux", pn, Boffset(f), o); + sysfatal("probably not a .5 file"); + } + if(o == ANAME || o == ASIGNAME) { + sig = 0; + if(o == ASIGNAME) + sig = BGETLE4(f); + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ + r = 0; + if(v == D_STATIC) + r = ctxt->version; + name = Brdline(f, '\0'); + if(name == nil) { + if(Blinelen(f) > 0) { + fprint(2, "%s: name too long\n", pn); + sysfatal("invalid object file"); + } + goto eof; + } + x = expandpkg(name, pkg); + s = linklookup(ctxt, x, r); + if(x != name) + free(x); + + if(sig != 0){ + if(s->sig != 0 && s->sig != sig) + ctxt->diag("incompatible type signatures %ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); + s->sig = sig; + s->file = pn; + } + + if(ctxt->debugread) + print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) { + fprint(2, "%s: mangled input file\n", pn); + sysfatal("invalid object"); + } + h[o] = s; + if((v == D_EXTERN || v == D_STATIC) && s->type == 0) + s->type = SXREF; + if(v == D_FILE) { + if(s->type != SFILE) { + ctxt->histgen++; + s->type = SFILE; + s->value = ctxt->histgen; + } + if(ctxt->histfrogp < LinkMaxHist) { + ctxt->histfrog[ctxt->histfrogp] = s; + ctxt->histfrogp++; + } else + collapsefrog(ctxt, s); + ctxt->dwarfaddfrag(s->value, s->name); + } + goto loop; + } + + p = emallocz(sizeof(Prog)); + p->as = o; + p->scond = BGETC(f); + p->reg = BGETC(f); + p->lineno = BGETLE4(f); + + zaddr(ctxt, pn, f, &p->from, h, &fromgotype); + zaddr(ctxt, pn, f, &p->to, h, nil); + + if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG) + ctxt->diag("register out of range %A %d", p->as, p->reg); + + p->link = nil; + p->pcond = nil; + + if(ctxt->debugread) + print("%P\n", p); + + switch(o) { + case AHISTORY: + if(p->to.offset == -1) { + addlib(ctxt, src, pn); + ctxt->histfrogp = 0; + goto loop; + } + if(src[0] == '\0') + copyhistfrog(ctxt, src, sizeof src); + addhist(ctxt, p->lineno, D_FILE); /* 'z' */ + if(p->to.offset) + addhist(ctxt, p->to.offset, D_FILE1); /* 'Z' */ + savehist(ctxt, p->lineno, p->to.offset); + ctxt->histfrogp = 0; + goto loop; + + case AEND: + finish(ctxt); + if(Boffset(f) == eof) + return; + goto newloop; + + case AGLOBL: + s = p->from.sym; + if(s == nil) { + ctxt->diag("GLOBL must have a name\n%P", p); + sysfatal("mangled input"); + } + if(s->type == 0 || s->type == SXREF) { + s->type = SBSS; + s->value = 0; + } + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { + ctxt->diag("redefinition: %s\n%P", s->name, p); + s->type = SBSS; + s->value = 0; + } + if(p->to.offset > s->size) + s->size = p->to.offset; + if(p->reg & DUPOK) + s->dupok = 1; + if(p->reg & RODATA) + s->type = SRODATA; + else if(p->reg & NOPTR) + s->type = SNOPTRBSS; + break; + + case ADATA: + // Assume that AGLOBL comes after ADATA. + // If we've seen an AGLOBL that said this sym was DUPOK, + // ignore any more ADATA we see, which must be + // redefinitions. + s = p->from.sym; + if(s->dupok) { +// if(debug['v']) +// Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); + goto loop; + } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + ctxt->diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + sysfatal("mangled input"); + } + savedata(ctxt, s, p, pn); + free(p); + break; + + case AGOK: + ctxt->diag("unknown opcode\n%P", p); + p->pc = ctxt->pc; + ctxt->pc++; + break; + + case ATYPE: + if(skip) + goto casedef; + ctxt->pc++; + goto loop; + + case ATEXT: + if(ctxt->cursym != nil && ctxt->cursym->text) + finish(ctxt); + s = p->from.sym; + if(s == nil) { + ctxt->diag("TEXT must have a name\n%P", p); + sysfatal("mangled input"); + } + ctxt->cursym = s; + if(s->type != 0 && s->type != SXREF && (p->reg & DUPOK)) { + skip = 1; + goto casedef; + } + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(ctxt->debugvlog) + Bprint(ctxt->bso, "skipping: %s: redefinition: %s", pn, s->name); + return; + } + skip = 0; + if(s->type != 0 && s->type != SXREF) + ctxt->diag("redefinition: %s\n%P", s->name, p); + if(ctxt->etextp) + ctxt->etextp->next = s; + else + ctxt->textp = s; + if(fromgotype) { + if(s->gotype && s->gotype != fromgotype) + ctxt->diag("%s: type mismatch for %s", pn, s->name); + s->gotype = fromgotype; + } + ctxt->etextp = s; + autosize = (p->to.offset+3L) & ~3L; + p->to.offset = autosize; + autosize += 4; + s->type = STEXT; + s->hist = gethist(ctxt); + s->text = p; + s->value = ctxt->pc; + s->args = p->to.offset2; + lastp = p; + p->pc = ctxt->pc; + ctxt->pc++; + break; + + case ASUB: + if(p->from.type == D_CONST) + if(p->from.name == D_NONE) + if(p->from.offset < 0) { + p->from.offset = -p->from.offset; + p->as = AADD; + } + goto casedef; + + case AADD: + if(p->from.type == D_CONST) + if(p->from.name == D_NONE) + if(p->from.offset < 0) { + p->from.offset = -p->from.offset; + p->as = ASUB; + } + goto casedef; + + 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: + goto casedef; + + case AMOVF: + if(skip) + goto casedef; + + if(p->from.type == D_FCONST && chipfloat(ctxt, p->from.u.dval) < 0 && + (chipzero(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { + /* size sb 9 max */ + sprint(literal, "$%.17gf", (float32)p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + float32 f32; + int32 i32; + s->type = SRODATA; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + 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; + } + goto casedef; + + case AMOVD: + if(skip) + goto casedef; + + if(p->from.type == D_FCONST && chipfloat(ctxt, p->from.u.dval) < 0 && + (chipzero(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { + /* size sb 18 max */ + sprint(literal, "$%.17g", p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + int64 i64; + s->type = SRODATA; + memmove(&i64, &p->from.u.dval, 8); + 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; + } + goto casedef; + + default: + casedef: + if(skip) + nopout5(p); + p->pc = ctxt->pc; + ctxt->pc++; + if(p->to.type == D_BRANCH) + p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + ctxt->diag("unexpected instruction: %P", p); + break; + } + lastp->link = p; + lastp = p; + break; + } + goto loop; + +eof: + ctxt->diag("truncated object file: %s", pn); +} + +static void +finish(Link *ctxt) +{ + LSym *s; + + histtoauto(ctxt); + if(ctxt->cursym != nil && ctxt->cursym->text) { + s = ctxt->cursym; + s->autom = ctxt->curauto; + // mkfwd(s); + // linkpatch(ctxt, s); + // ctxt->arch->follow(ctxt, s); + // ctxt->arch->addstacksplit(ctxt, s); + // ctxt->arch->assemble(ctxt, s); + // linkpcln(ctxt, s); + } + + ctxt->curauto = 0; + ctxt->cursym = nil; +} + diff --git a/src/liblink/rdobj6.c b/src/liblink/rdobj6.c new file mode 100644 index 0000000000..52ed18b93d --- /dev/null +++ b/src/liblink/rdobj6.c @@ -0,0 +1,495 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.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 +#include +#include +#include +#include "../cmd/6l/6.out.h" + +static LSym* +zsym(char *pn, Biobuf *f, LSym *h[]) +{ + int o; + + o = BGETC(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void finish(Link*); + +static void +zaddr(Link *ctxt, char *pn, Biobuf *f, Addr *a, LSym *h[], LSym **pgotype) +{ + int t; + int32 l; + LSym *s, *gotype; + Auto *u; + uint64 v; + + t = BGETC(f); + a->index = D_NONE; + a->scale = 0; + if(t & T_INDEX) { + a->index = BGETC(f); + a->scale = BGETC(f); + } + a->offset = 0; + if(t & T_OFFSET) { + a->offset = BGETLE4(f); + if(t & T_64) { + a->offset &= 0xFFFFFFFFULL; + a->offset |= (uvlong)BGETLE4(f) << 32; + } + } + a->sym = nil; + if(t & T_SYM) + a->sym = zsym(pn, f, h); + a->type = D_NONE; + if(t & T_FCONST) { + v = (uint32)BGETLE4(f); + v |= (uint64)BGETLE4(f)<<32; + memmove(&a->u.dval, &v, 8); + a->type = D_FCONST; + } else + if(t & T_SCONST) { + Bread(f, a->u.sval, NSNAME); + a->type = D_SCONST; + } + if(t & T_TYPE) + a->type = BGETC(f); + if(a->type < 0 || a->type >= D_SIZE) + mangle(pn); + gotype = nil; + if(t & T_GOTYPE) + gotype = zsym(pn, f, h); + if(pgotype) + *pgotype = gotype; + s = a->sym; + t = a->type; + if(t == D_INDIR+D_GS || a->index == D_GS) + a->offset += ctxt->tlsoffset; + if(t != D_AUTO && t != D_PARAM) { + if(s && gotype) + s->gotype = gotype; + return; + } + l = a->offset; + for(u=ctxt->curauto; u; u=u->link) { + if(u->asym == s) + if(u->type == t) { + if(u->aoffset > l) + u->aoffset = l; + if(gotype) + u->gotype = gotype; + return; + } + } + + switch(t) { + case D_FILE: + case D_FILE1: + case D_AUTO: + case D_PARAM: + if(s == nil) + mangle(pn); + } + + u = emallocz(sizeof(*u)); + u->link = ctxt->curauto; + ctxt->curauto = u; + u->asym = s; + u->aoffset = l; + u->type = t; + u->gotype = gotype; +} + +void +nopout6(Prog *p) +{ + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; +} + +void +ldobj6(Link *ctxt, Biobuf *f, char *pkg, int64 len, char *pn) +{ + vlong ipc; + Prog *p; + int v, o, r, skip, mode; + LSym *h[NSYM], *s; + uint32 sig; + char *name, *x; + int ntext; + vlong eof; + char src[1024], literal[64]; + Prog *lastp; + LSym *fromgotype; + + lastp = nil; + ntext = 0; + eof = Boffset(f) + len; + src[0] = 0; + pn = estrdup(pn); // we keep it in LSym* references + +newloop: + memset(h, 0, sizeof(h)); + ctxt->version++; + ctxt->histfrogp = 0; + ipc = ctxt->pc; + skip = 0; + mode = 64; + +loop: + if(f->state == Bracteof || Boffset(f) >= eof) + goto eof; + o = BGETC(f); + if(o == Beof) + goto eof; + o |= BGETC(f) << 8; + if(o <= AXXX || o >= ALAST) { + if(o < 0) + goto eof; + sysfatal("%s:#%lld: opcode out of range: %#ux\n\tprobably not a .6 file", pn, Boffset(f), o); + } + + if(o == ANAME || o == ASIGNAME) { + sig = 0; + if(o == ASIGNAME) + sig = BGETLE4(f); + USED(sig); + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ + r = 0; + if(v == D_STATIC) + r = ctxt->version; + name = Brdline(f, '\0'); + if(name == nil) { + if(Blinelen(f) > 0) + sysfatal("%s: name too long", pn); + goto eof; + } + x = expandpkg(name, pkg); + s = linklookup(ctxt, x, r); + if(x != name) + free(x); + + if(ctxt->debugread) + print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); + h[o] = s; + if((v == D_EXTERN || v == D_STATIC) && s->type == 0) + s->type = SXREF; + if(v == D_FILE) { + if(s->type != SFILE) { + ctxt->histgen++; + s->type = SFILE; + s->value = ctxt->histgen; + } + if(ctxt->histfrogp < LinkMaxHist) { + ctxt->histfrog[ctxt->histfrogp] = s; + ctxt->histfrogp++; + } else + collapsefrog(ctxt, s); + if(ctxt->dwarfaddfrag) + ctxt->dwarfaddfrag(s->value, s->name); + } + goto loop; + } + + p = emallocz(sizeof(*p)); + p->as = o; + p->lineno = BGETLE4(f); + p->back = 2; + p->mode = mode; + zaddr(ctxt, pn, f, &p->from, h, &fromgotype); + zaddr(ctxt, pn, f, &p->to, h, nil); + + switch(p->as) { + case ATEXT: + case ADATA: + case AGLOBL: + if(p->from.sym == nil) + mangle(pn); + break; + } + + if(ctxt->debugread) + print("%P\n", p); + + switch(p->as) { + case AHISTORY: + if(p->to.offset == -1) { + addlib(ctxt, src, pn); + ctxt->histfrogp = 0; + goto loop; + } + if(src[0] == '\0') + copyhistfrog(ctxt, src, sizeof src); + addhist(ctxt, p->lineno, D_FILE); /* 'z' */ + if(p->to.offset) + addhist(ctxt, p->to.offset, D_FILE1); /* 'Z' */ + savehist(ctxt, p->lineno, p->to.offset); + ctxt->histfrogp = 0; + goto loop; + + case AEND: + finish(ctxt); + if(Boffset(f) == eof) + return; + goto newloop; + + case AGLOBL: + s = p->from.sym; + if(s->type == 0 || s->type == SXREF) { + s->type = SBSS; + s->size = 0; + } + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { + ctxt->diag("%s: redefinition: %s in %s", + pn, s->name, ctxt->cursym ? ctxt->cursym->name : ""); + s->type = SBSS; + s->size = 0; + } + if(p->to.offset > s->size) + s->size = p->to.offset; + if(p->from.scale & DUPOK) + s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; + else if(p->from.scale & NOPTR) + s->type = SNOPTRBSS; + goto loop; + + case ADATA: + // Assume that AGLOBL comes after ADATA. + // If we've seen an AGLOBL that said this sym was DUPOK, + // ignore any more ADATA we see, which must be + // redefinitions. + s = p->from.sym; + if(s->dupok) { +// if(ctxt->debugvlog) +// Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); + goto loop; + } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) + sysfatal("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + savedata(ctxt, s, p, pn); + free(p); + goto loop; + + case AGOK: + ctxt->diag("%s: GOK opcode in %s", pn, ctxt->cursym ? ctxt->cursym->name : ""); + ctxt->pc++; + goto loop; + + case ATYPE: + if(skip) + goto casdef; + ctxt->pc++; + goto loop; + + case ATEXT: + s = p->from.sym; + if(s->text != nil) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } + ctxt->diag("%s: %s: redefinition", pn, s->name); + return; + } + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(ctxt->debugvlog && ctxt->bso) + Bprint(ctxt->bso, "skipping: %s: redefinition: %s", pn, s->name); + return; + } + if(ctxt->cursym != nil && ctxt->cursym->text) + finish(ctxt); + skip = 0; + if(ctxt->etextp) + ctxt->etextp->next = s; + else + ctxt->textp = s; + ctxt->etextp = s; + s->text = p; + ctxt->cursym = s; + if(s->type != 0 && s->type != SXREF) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } + ctxt->diag("%s: redefinition: %s\n%P", pn, s->name, p); + } + if(fromgotype) { + if(s->gotype && s->gotype != fromgotype) + ctxt->diag("%s: type mismatch for %s", pn, s->name); + s->gotype = fromgotype; + } + s->type = STEXT; + s->hist = gethist(ctxt); + s->value = ctxt->pc; + s->args = p->to.offset >> 32; + lastp = p; + p->pc = ctxt->pc++; + goto loop; + + 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: + mode = p->from.offset; + break; + } + } + goto loop; + + 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(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 9 max */ + sprint(literal, "$%.17gf", (float32)p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + int32 i32; + float32 f32; + s->type = SRODATA; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + adduint32(ctxt, s, i32); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + 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(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 18 max */ + sprint(literal, "$%.17g", p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + int64 i64; + s->type = SRODATA; + memmove(&i64, &p->from.u.dval, 8); + adduint64(ctxt, s, i64); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + casdef: + default: + if(skip) + nopout6(p); + p->pc = ctxt->pc; + ctxt->pc++; + + if(p->to.type == D_BRANCH) + p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + ctxt->diag("unexpected instruction: %P", p); + goto loop; + } + lastp->link = p; + lastp = p; + goto loop; + } + +eof: + ctxt->diag("truncated object file: %s", pn); +} + +static void +finish(Link *ctxt) +{ + LSym *s; + + histtoauto(ctxt); + if(ctxt->cursym != nil && ctxt->cursym->text) { + s = ctxt->cursym; + s->autom = ctxt->curauto; + // mkfwd(s); + // linkpatch(ctxt, s); + // ctxt->arch->follow(ctxt, s); + // ctxt->arch->addstacksplit(ctxt, s); + // ctxt->arch->assemble(ctxt, s); + // linkpcln(ctxt, s); + } + + ctxt->curauto = 0; + ctxt->cursym = nil; +} diff --git a/src/liblink/rdobj8.c b/src/liblink/rdobj8.c new file mode 100644 index 0000000000..d84e42889a --- /dev/null +++ b/src/liblink/rdobj8.c @@ -0,0 +1,466 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.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 +#include +#include +#include +#include "../cmd/8l/8.out.h" + +static LSym* +zsym(char *pn, Biobuf *f, LSym *h[]) +{ + int o; + + o = BGETC(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void finish(Link*); + +static void +zaddr(Link *ctxt, char *pn, Biobuf *f, Addr *a, LSym *h[], LSym **pgotype) +{ + int t; + int32 l; + LSym *s, *gotype; + Auto *u; + uint64 v; + + t = BGETC(f); + a->index = D_NONE; + a->scale = 0; + if(t & T_INDEX) { + a->index = BGETC(f); + a->scale = BGETC(f); + } + a->type = D_NONE; + a->offset = 0; + if(t & T_OFFSET) + a->offset = BGETLE4(f); + a->offset2 = 0; + if(t & T_OFFSET2) { + a->offset2 = BGETLE4(f); + a->type = D_CONST2; + } + a->sym = nil; + if(t & T_SYM) + a->sym = zsym(pn, f, h); + if(t & T_FCONST) { + v = (uint32)BGETLE4(f); + v |= (uint64)BGETLE4(f)<<32; + memmove(&a->u.dval, &v, 8); + a->type = D_FCONST; + } else + if(t & T_SCONST) { + Bread(f, a->u.sval, NSNAME); + a->type = D_SCONST; + } + if(t & T_TYPE) + a->type = BGETC(f); + gotype = nil; + if(t & T_GOTYPE) + gotype = zsym(pn, f, h); + if(pgotype) + *pgotype = gotype; + t = a->type; + if(t == D_INDIR+D_GS) + a->offset += ctxt->tlsoffset; + + s = a->sym; + if(s == nil) + return; + if(t != D_AUTO && t != D_PARAM) { + if(gotype) + s->gotype = gotype; + return; + } + l = a->offset; + for(u=ctxt->curauto; u; u=u->link) { + if(u->asym == s) + if(u->type == t) { + if(u->aoffset > l) + u->aoffset = l; + if(gotype) + u->gotype = gotype; + return; + } + } + + u = emallocz(sizeof(*u)); + u->link = ctxt->curauto; + ctxt->curauto = u; + u->asym = s; + u->aoffset = l; + u->type = t; + u->gotype = gotype; +} + +void +nopout8(Prog *p) +{ + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; +} + +void +ldobj8(Link *ctxt, Biobuf *f, char *pkg, int64 len, char *pn) +{ + int32 ipc; + Prog *p; + int v, o, r, skip; + LSym *h[NSYM], *s; + uint32 sig; + int ntext; + int32 eof; + char *name, *x; + char src[1024], literal[64]; + Prog *lastp; + LSym *fromgotype; + + lastp = nil; + ntext = 0; + eof = Boffset(f) + len; + src[0] = 0; + pn = estrdup(pn); // we keep it in LSym* references + +newloop: + memset(h, 0, sizeof(h)); + ctxt->version++; + ctxt->histfrogp = 0; + ipc = ctxt->pc; + skip = 0; + +loop: + if(f->state == Bracteof || Boffset(f) >= eof) + goto eof; + o = BGETC(f); + if(o == Beof) + goto eof; + o |= BGETC(f) << 8; + if(o <= AXXX || o >= ALAST) { + if(o < 0) + goto eof; + ctxt->diag("%s:#%lld: opcode out of range: %#ux", pn, Boffset(f), o); + print(" probably not a .%c file\n", ctxt->thechar); + sysfatal("invalid file"); + } + + if(o == ANAME || o == ASIGNAME) { + sig = 0; + if(o == ASIGNAME) + sig = BGETLE4(f); + USED(sig); + + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ + r = 0; + if(v == D_STATIC) + r = ctxt->version; + name = Brdline(f, '\0'); + if(name == nil) { + if(Blinelen(f) > 0) + sysfatal("%s: name too long", pn); + goto eof; + } + x = expandpkg(name, pkg); + s = linklookup(ctxt, x, r); + if(x != name) + free(x); + + if(ctxt->debugread) + print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); + h[o] = s; + if((v == D_EXTERN || v == D_STATIC) && s->type == 0) + s->type = SXREF; + if(v == D_FILE) { + if(s->type != SFILE) { + ctxt->histgen++; + s->type = SFILE; + s->value = ctxt->histgen; + } + if(ctxt->histfrogp < LinkMaxHist) { + ctxt->histfrog[ctxt->histfrogp] = s; + ctxt->histfrogp++; + } else + collapsefrog(ctxt, s); + ctxt->dwarfaddfrag(s->value, s->name); + } + goto loop; + } + + p = emallocz(sizeof(*p)); + p->as = o; + p->lineno = BGETLE4(f); + p->back = 2; + zaddr(ctxt, pn, f, &p->from, h, &fromgotype); + zaddr(ctxt, pn, f, &p->to, h, nil); + + if(ctxt->debugread) + print("%P\n", p); + + switch(p->as) { + case AHISTORY: + if(p->to.offset == -1) { + addlib(ctxt, src, pn); + ctxt->histfrogp = 0; + goto loop; + } + if(src[0] == '\0') + copyhistfrog(ctxt, src, sizeof src); + addhist(ctxt, p->lineno, D_FILE); /* 'z' */ + if(p->to.offset) + addhist(ctxt, p->to.offset, D_FILE1); /* 'Z' */ + savehist(ctxt, p->lineno, p->to.offset); + ctxt->histfrogp = 0; + goto loop; + + case AEND: + finish(ctxt); + if(Boffset(f) == eof) + return; + goto newloop; + + case AGLOBL: + s = p->from.sym; + if(s->type == 0 || s->type == SXREF) { + s->type = SBSS; + s->size = 0; + } + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { + ctxt->diag("%s: redefinition: %s in %s", + pn, s->name, ctxt->cursym ? ctxt->cursym->name : ""); + s->type = SBSS; + s->size = 0; + } + if(p->to.offset > s->size) + s->size = p->to.offset; + if(p->from.scale & DUPOK) + s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; + else if(p->from.scale & NOPTR) + s->type = SNOPTRBSS; + goto loop; + + case ADATA: + // Assume that AGLOBL comes after ADATA. + // If we've seen an AGLOBL that said this sym was DUPOK, + // ignore any more ADATA we see, which must be + // redefinitions. + s = p->from.sym; + if(s->dupok) { +// if(ctxt->debugvlog) +// Bprint(ctxt->bso, "skipping %s in %s: dupok\n", s->name, pn); + goto loop; + } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + ctxt->diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + sysfatal("multiple init"); + } + savedata(ctxt, s, p, pn); + free(p); + goto loop; + + case AGOK: + ctxt->diag("%s: GOK opcode in %s", pn, ctxt->cursym ? ctxt->cursym->name : ""); + ctxt->pc++; + goto loop; + + case ATYPE: + if(skip) + goto casdef; + ctxt->pc++; + goto loop; + + case ATEXT: + s = p->from.sym; + if(s->text != nil) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } + ctxt->diag("%s: %s: redefinition", pn, s->name); + return; + } + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(ctxt->debugvlog) + ctxt->diag("skipping: %s: redefinition: %s", pn, s->name); + return; + } + if(ctxt->cursym != nil && ctxt->cursym->text) + finish(ctxt); + skip = 0; + if(ctxt->etextp) + ctxt->etextp->next = s; + else + ctxt->textp = s; + ctxt->etextp = s; + s->text = p; + ctxt->cursym = s; + if(s->type != 0 && s->type != SXREF) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } + ctxt->diag("%s: redefinition: %s\n%P", pn, s->name, p); + } + s->type = STEXT; + s->hist = gethist(ctxt); + s->value = ctxt->pc; + s->args = p->to.offset2; + lastp = p; + p->pc = ctxt->pc++; + goto loop; + + 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(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 9 max */ + sprint(literal, "$(%.17gf)", (float32)p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + float32 f32; + int32 i32; + s->type = SRODATA; + f32 = p->from.u.dval; + memmove(&i32, &f32, 4); + adduint32(ctxt, s, i32); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + 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(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 18 max */ + sprint(literal, "$%.17g", + p->from.u.dval); + s = linklookup(ctxt, literal, 0); + if(s->type == 0) { + int64 i64; + s->type = SRODATA; + memmove(&i64, &p->from.u.dval, 8); + adduint64(ctxt, s, i64); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + casdef: + default: + if(skip) + nopout8(p); + p->pc = ctxt->pc; + ctxt->pc++; + + if(p->to.type == D_BRANCH) + p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + ctxt->diag("unexpected instruction: %P", p); + goto loop; + } + lastp->link = p; + lastp = p; + goto loop; + } + +eof: + ctxt->diag("truncated object file: %s", pn); +} + +static void +finish(Link *ctxt) +{ + LSym *s; + + histtoauto(ctxt); + if(ctxt->cursym != nil && ctxt->cursym->text) { + s = ctxt->cursym; + s->autom = ctxt->curauto; + // mkfwd(s); + // linkpatch(ctxt, s); + // ctxt->arch->follow(ctxt, s); + // ctxt->arch->addstacksplit(ctxt, s); + // ctxt->arch->assemble(ctxt, s); + // linkpcln(ctxt, s); + } + + ctxt->curauto = 0; + ctxt->cursym = nil; +} diff --git a/src/liblink/sym.c b/src/liblink/sym.c new file mode 100644 index 0000000000..e876a5ca04 --- /dev/null +++ b/src/liblink/sym.c @@ -0,0 +1,158 @@ +// 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 +#include +#include +#include + +static int +yy_isalpha(int c) +{ + return c >= 0 && c <= 0xFF && isalpha(c); +} + +Link* +linknew(LinkArch *arch) +{ + Link *ctxt; + char *p; + char buf[1024]; + + nuxiinit(); + + ctxt = emallocz(sizeof *ctxt); + ctxt->arch = arch; + ctxt->version = HistVersion; + + // TODO: Make caller pass in ctxt->arch, + // so that for example 6g only has the linkamd64 code. + p = getgoarch(); + if(strncmp(p, arch->name, strlen(arch->name)) != 0) + sysfatal("invalid goarch %s (want %s or derivative)", 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); + + 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(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, ""); + + return fmtstrcpy(f, s->name); +} -- cgit v1.3