3924 lines
132 KiB
C
3924 lines
132 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/elf/def.h"
|
|
#include "libc/fmt/conv.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/o.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/chibicc/file.h"
|
|
#include "third_party/gdtoa/gdtoa.h"
|
|
#include "tool/build/lib/elfwriter.h"
|
|
|
|
/**
|
|
* @fileoverview Assembler
|
|
*
|
|
* This program turns assembly into relocatable NexGen32e ELF objects.
|
|
* That process is normally an implementation detail of your compiler,
|
|
* which can embed this program or launch it as a subprocess. Much GNU
|
|
* style syntax is supported. Your code that gets embedded in an asm()
|
|
* statement will ultimately end up here. This implementation, has the
|
|
* advantage of behaving the same across platforms, in a simple single
|
|
* file implementation that compiles down to a 100kilo ape executable.
|
|
*
|
|
* Your assembler supports the following flags:
|
|
*
|
|
* -o FILE output path [default: a.out]
|
|
* -I DIR append include path [default: .]
|
|
* -W inhibit .warning
|
|
* -Z inhibit .error and .err
|
|
*
|
|
* Your assembler supports the following directives:
|
|
*
|
|
* .zero INT... emits int8
|
|
* .word INT... emits int16
|
|
* .long INT... emits int32
|
|
* .quad INT... emits int64
|
|
* .ascii STR... emits string
|
|
* .asciz STR... emits string and 0 byte
|
|
* .ident STR emits string to .comment section
|
|
* .float NUMBER... emits binary32
|
|
* .double NUMBER... emits binary64
|
|
* .float80 NUMBER... emits x86 float (10 bytes)
|
|
* .ldbl NUMBER... emits x86 float (16 bytes)
|
|
* .sleb128 NUMBER... emits LEB-128 signed varint
|
|
* .uleb128 NUMBER... emits LEB-128 unsigned varint
|
|
* .align BYTES [FILL [MAXSKIP]] emits fill bytes to boundary
|
|
* .end halts tokenization
|
|
* .abort crashes assembler
|
|
* .err aborts (ignorable w/ -Z)
|
|
* .error STR aborts (ignorable w/ -Z)
|
|
* .warning STR whines (ignorable w/ -W)
|
|
* .text enters text section (default)
|
|
* .data enters data section
|
|
* .bss enters bss section
|
|
* .section NAME [SFLG SHT] enters section
|
|
* .previous enters previous section
|
|
* .pushsection NAME [SFLG SHT] pushes section
|
|
* .popsection pops section
|
|
* .type SYM TYPE sets type of symbol
|
|
* .size SYM EXPR sets size of symbol
|
|
* .internal SYM... marks symbol STV_INTERNAL
|
|
* .hidden SYM... marks symbol STV_HIDDEN
|
|
* .protected SYM... marks symbol STV_PROTECTED
|
|
* .globl SYM... marks symbol STB_GLOBAL
|
|
* .local SYM... marks symbol STB_LOCAL
|
|
* .weak SYM... marks symbol STB_WEAK
|
|
* .include FILE assembles file source
|
|
* .incbin FILE emits file content
|
|
* .file FILENO PATH dwarf file define
|
|
* .loc FILENO LINENO dwarf source line
|
|
*
|
|
* TYPE can be one of the following:
|
|
*
|
|
* - @notype STT_NOTYPE (default)
|
|
* - @object STT_OBJECT
|
|
* - @function STT_FUNC
|
|
* - @common STT_COMMON
|
|
* - @tls_object STT_TLS
|
|
*
|
|
* SHT can be one of the following:
|
|
*
|
|
* - @progbits SHT_PROGBITS
|
|
* - @note SHT_NOTE
|
|
* - @nobits SHT_NOBITS
|
|
* - @preinit_array SHT_PREINIT_ARRAY
|
|
* - @init_array SHT_INIT_ARRAY
|
|
* - @fini_array SHT_FINI_ARRAY
|
|
*
|
|
* SFLG is a string which may have the following characters:
|
|
*
|
|
* - a SHF_ALLOC
|
|
* - w SHF_WRITE
|
|
* - x SHF_EXECINSTR
|
|
* - g SHF_GROUP
|
|
* - M SHF_MERGE
|
|
* - S SHF_STRINGS
|
|
* - T SHF_TLS
|
|
*/
|
|
|
|
#define OSZ 0x66
|
|
#define ASZ 0x67
|
|
#define REX 0x40 // byte
|
|
#define REXB 0x41 // src
|
|
#define REXX 0x42 // index
|
|
#define REXR 0x44 // dest
|
|
#define REXW 0x48 // quad
|
|
|
|
#define HASASZ 0x00010000
|
|
#define HASBASE 0x00020000
|
|
#define HASINDEX 0x00040000
|
|
#define ISRIP 0x00080000
|
|
#define ISREG 0x00100000
|
|
|
|
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
|
|
#define IS(P, N, S) (N == sizeof(S) - 1 && !strncasecmp(P, S, sizeof(S) - 1))
|
|
#define BSR(I) (__builtin_clz(I) ^ 31)
|
|
#define ROUNDUP(X, K) (((X) + (K)-1) & -(K))
|
|
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
|
|
#define LOAD128BE(S) ((unsigned __int128)LOAD64BE(S) << 64 | LOAD64BE((S) + 8))
|
|
#define LOAD64BE(S) \
|
|
((unsigned long)((unsigned char *)(S))[0] << 070 | \
|
|
(unsigned long)((unsigned char *)(S))[1] << 060 | \
|
|
(unsigned long)((unsigned char *)(S))[2] << 050 | \
|
|
(unsigned long)((unsigned char *)(S))[3] << 040 | \
|
|
(unsigned long)((unsigned char *)(S))[4] << 030 | \
|
|
(unsigned long)((unsigned char *)(S))[5] << 020 | \
|
|
(unsigned long)((unsigned char *)(S))[6] << 010 | \
|
|
(unsigned long)((unsigned char *)(S))[7] << 000)
|
|
#define ARRAYLEN(A) \
|
|
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
|
|
|
struct As {
|
|
int i; // things
|
|
int section; // sections
|
|
int previous; // sections
|
|
int inpath; // strings
|
|
int outpath; // strings
|
|
int counter;
|
|
int pcrelative;
|
|
bool inhibiterr;
|
|
bool inhibitwarn;
|
|
struct Ints {
|
|
unsigned long n;
|
|
long *p;
|
|
} ints;
|
|
struct Floats {
|
|
unsigned long n;
|
|
long double *p;
|
|
} floats;
|
|
struct Slices {
|
|
unsigned long n;
|
|
struct Slice {
|
|
unsigned long n;
|
|
char *p;
|
|
} * p;
|
|
} slices;
|
|
struct Sauces {
|
|
unsigned long n;
|
|
struct Sauce {
|
|
unsigned path; // strings
|
|
unsigned line; // 1-indexed
|
|
} * p;
|
|
} sauces;
|
|
struct Things {
|
|
unsigned long n;
|
|
struct Thing {
|
|
enum ThingType {
|
|
TT_INT,
|
|
TT_FLOAT,
|
|
TT_SLICE,
|
|
TT_PUNCT,
|
|
TT_FORWARD,
|
|
TT_BACKWARD,
|
|
} t : 4;
|
|
unsigned s : 28; // sauces
|
|
unsigned i; // identity,ints,floats,slices
|
|
} * p;
|
|
} things;
|
|
struct Sections {
|
|
unsigned long n;
|
|
struct Section {
|
|
unsigned name; // strings
|
|
int flags;
|
|
int type;
|
|
int align;
|
|
struct Slice binary;
|
|
} * p;
|
|
} sections;
|
|
struct Symbols {
|
|
unsigned long n;
|
|
struct Symbol {
|
|
bool isused;
|
|
unsigned char stb; // STB_*
|
|
unsigned char stv; // STV_*
|
|
unsigned char type; // STT_*
|
|
unsigned name; // slices
|
|
unsigned section; // sections
|
|
long offset;
|
|
long size;
|
|
struct ElfWriterSymRef ref;
|
|
} * p;
|
|
} symbols;
|
|
struct HashTable {
|
|
unsigned i, n;
|
|
struct HashEntry {
|
|
unsigned h;
|
|
unsigned i;
|
|
} * p;
|
|
} symbolindex;
|
|
struct Labels {
|
|
unsigned long n;
|
|
struct Label {
|
|
unsigned id;
|
|
unsigned tok; // things
|
|
unsigned symbol; // symbols
|
|
} * p;
|
|
} labels;
|
|
struct Relas {
|
|
unsigned long n;
|
|
struct Rela {
|
|
bool isdead;
|
|
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
|
|
unsigned expr; // exprs
|
|
unsigned section; // sections
|
|
long offset;
|
|
long addend;
|
|
} * p;
|
|
} relas;
|
|
struct Exprs {
|
|
unsigned long n;
|
|
struct Expr {
|
|
enum ExprKind {
|
|
EX_INT, // integer
|
|
EX_SYM, // things (then symbols after eval)
|
|
EX_NEG, // unary -
|
|
EX_NOT, // unary !
|
|
EX_BITNOT, // unary ~
|
|
EX_ADD, // +
|
|
EX_SUB, // -
|
|
EX_MUL, // *
|
|
EX_DIV, // /
|
|
EX_REM, // %
|
|
EX_AND, // &
|
|
EX_OR, // |
|
|
EX_XOR, // ^
|
|
EX_SHL, // <<
|
|
EX_SHR, // >>
|
|
EX_EQ, // ==
|
|
EX_NE, // !=
|
|
EX_LT, // <
|
|
EX_LE, // <=
|
|
} kind;
|
|
enum ExprMod {
|
|
EM_NORMAL,
|
|
EM_GOTPCREL,
|
|
EM_DTPOFF,
|
|
EM_TPOFF,
|
|
} em;
|
|
unsigned tok;
|
|
int lhs;
|
|
int rhs;
|
|
long x;
|
|
bool isvisited;
|
|
bool isevaluated;
|
|
} * p;
|
|
} exprs;
|
|
struct Strings {
|
|
unsigned long n;
|
|
char **p;
|
|
} strings, incpaths;
|
|
struct SectionStack {
|
|
unsigned long n;
|
|
int *p;
|
|
} sectionstack;
|
|
};
|
|
|
|
static const char kPrefixByte[30] = {
|
|
0x67, 0x2E, 0x66, 0x3E, 0x26, 0x64, 0x65, 0xF0, 0xF3, 0xF3,
|
|
0xF2, 0xF2, 0xF3, 0x40, 0x41, 0x44, 0x45, 0x46, 0x47, 0x48,
|
|
0x49, 0x4C, 0x4D, 0x4E, 0x4F, 0x4A, 0x4B, 0x42, 0x43, 0x36,
|
|
};
|
|
|
|
static const char kPrefix[30][8] = {
|
|
"addr32", "cs", "data16", "ds", "es", "fs",
|
|
"gs", "lock", "rep", "repe", "repne", "repnz",
|
|
"repz", "rex", "rex.b", "rex.r", "rex.rb", "rex.rx",
|
|
"rex.rxb", "rex.w", "rex.wb", "rex.wr", "rex.wrb", "rex.wrx",
|
|
"rex.wrxb", "rex.wx", "rex.wxb", "rex.x", "rex.xb", "ss",
|
|
};
|
|
|
|
static const char kSegmentByte[6] = {0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36};
|
|
static const char kSegment[6][2] = {"cs", "ds", "es", "fs", "gs", "ss"};
|
|
|
|
/**
|
|
* Context-sensitive register encoding information.
|
|
*
|
|
* ┌rex
|
|
* │ ┌log₂size
|
|
* │ │ ┌reg
|
|
* ├──────┐ ├─┐├─┐
|
|
* 0b0000000000000000
|
|
*/
|
|
static const struct Reg {
|
|
char s[8];
|
|
short reg;
|
|
short rm;
|
|
short base;
|
|
short index;
|
|
} kRegs[] = /* clang-format off */ {
|
|
{"ah", 4, 4, -1, -1 },
|
|
{"al", 0, 0, -1, -1 },
|
|
{"ax", 0 | 1<<3, 0 | 1<<3, -1, -1 },
|
|
{"bh", 7, 7, -1, -1 },
|
|
{"bl", 3, 3, -1, -1 },
|
|
{"bp", 5 | 1<<3, 5 | 1<<3, -1, -1 },
|
|
{"bpl", 5 | REX<<8, 5 | REX<<8, -1, -1 },
|
|
{"bx", 3 | 1<<3, 3 | 1<<3, -1, -1 },
|
|
{"ch", 5, 5, -1, -1 },
|
|
{"cl", 1, 1, -1, -1 },
|
|
{"cx", 1 | 1<<3, 1 | 1<<3, -1, -1 },
|
|
{"dh", 6, 6, -1, -1 },
|
|
{"di", 7 | 1<<3, 7 | 1<<3, -1, -1 },
|
|
{"dil", 7 | REX<<8, 7 | REX<<8, -1, -1 },
|
|
{"dl", 2, 2, -1, -1 },
|
|
{"dx", 2 | 1<<3, 2 | 1<<3, -1, -1 },
|
|
{"eax", 0 | 2<<3, 0 | 2<<3, 0 | 2<<3, 0 | 2<<3 },
|
|
{"ebp", 5 | 2<<3, 5 | 2<<3, 5 | 2<<3, 5 | 2<<3 },
|
|
{"ebx", 3 | 2<<3, 3 | 2<<3, 3 | 2<<3, 3 | 2<<3 },
|
|
{"ecx", 1 | 2<<3, 1 | 2<<3, 1 | 2<<3, 1 | 2<<3 },
|
|
{"edi", 7 | 2<<3, 7 | 2<<3, 7 | 2<<3, 7 | 2<<3 },
|
|
{"edx", 2 | 2<<3, 2 | 2<<3, 2 | 2<<3, 2 | 2<<3 },
|
|
{"eiz", -1, -1, -1, 4 | 2<<3 },
|
|
{"esi", 6 | 2<<3, 6 | 2<<3, 6 | 2<<3, 6 | 2<<3 },
|
|
{"esp", 4 | 2<<3, 4 | 2<<3, 4 | 2<<3, 4 | 2<<3 },
|
|
{"mm0", 0 | 3<<3, 0 | 3<<3, -1, -1 },
|
|
{"mm1", 1 | 3<<3, 1 | 3<<3, -1, -1 },
|
|
{"mm2", 2 | 3<<3, 2 | 3<<3, -1, -1 },
|
|
{"mm3", 3 | 3<<3, 3 | 3<<3, -1, -1 },
|
|
{"mm4", 4 | 3<<3, 4 | 3<<3, -1, -1 },
|
|
{"mm5", 5 | 3<<3, 5 | 3<<3, -1, -1 },
|
|
{"mm6", 6 | 3<<3, 6 | 3<<3, -1, -1 },
|
|
{"mm7", 7 | 3<<3, 7 | 3<<3, -1, -1 },
|
|
{"mm8", 0 | 3<<3 | REXR<<8, 0 | 3<<3 | REXB<<8, -1, -1 },
|
|
{"mm9", 1 | 3<<3 | REXR<<8, 1 | 3<<3 | REXB<<8, -1, -1 },
|
|
{"r10", 2 | 3<<3 | REXR<<8 | REXW<<8, 2 | 3<<3 | REXB<<8 | REXW<<8, 2 | 3<<3 | REXB<<8, 2 | 3<<3 | REXX<<8 },
|
|
{"r10b", 2 | REXR<<8, 2 | REXB<<8, -1, -1 },
|
|
{"r10d", 2 | 2<<3 | REXR<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXX<<8 },
|
|
{"r10w", 2 | 1<<3 | REXR<<8, 2 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r11", 3 | 3<<3 | REXR<<8 | REXW<<8, 3 | 3<<3 | REXB<<8 | REXW<<8, 3 | 3<<3 | REXB<<8, 3 | 3<<3 | REXX<<8 },
|
|
{"r11b", 3 | REXR<<8, 3 | REXB<<8, -1, -1 },
|
|
{"r11d", 3 | 2<<3 | REXR<<8, 3 | 2<<3 | REXB<<8, 3 | 2<<3 | REXB<<8, 3 | 2<<3 | REXX<<8 },
|
|
{"r11w", 3 | 1<<3 | REXR<<8, 3 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r12", 4 | 3<<3 | REXR<<8 | REXW<<8, 4 | 3<<3 | REXB<<8 | REXW<<8, 4 | 3<<3 | REXB<<8, 4 | 3<<3 | REXX<<8 },
|
|
{"r12b", 4 | REXR<<8, 4 | REXB<<8, -1, -1 },
|
|
{"r12d", 4 | 2<<3 | REXR<<8, 4 | 2<<3 | REXB<<8, 4 | 2<<3 | REXB<<8, 4 | 2<<3 | REXX<<8 },
|
|
{"r12w", 4 | 1<<3 | REXR<<8, 4 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r13", 5 | 3<<3 | REXR<<8 | REXW<<8, 5 | 3<<3 | REXB<<8 | REXW<<8, 5 | 3<<3 | REXB<<8, 5 | 3<<3 | REXX<<8 },
|
|
{"r13b", 5 | REXR<<8, 5 | REXB<<8, -1, -1 },
|
|
{"r13d", 5 | 2<<3 | REXR<<8, 5 | 2<<3 | REXB<<8, 5 | 2<<3 | REXB<<8, 5 | 2<<3 | REXX<<8 },
|
|
{"r13w", 5 | 1<<3 | REXR<<8, 5 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r14", 6 | 3<<3 | REXR<<8 | REXW<<8, 6 | 3<<3 | REXB<<8 | REXW<<8, 6 | 3<<3 | REXB<<8, 6 | 3<<3 | REXX<<8 },
|
|
{"r14b", 6 | REXR<<8, 6 | REXB<<8, -1, -1 },
|
|
{"r14d", 6 | 2<<3 | REXR<<8, 6 | 2<<3 | REXB<<8, 6 | 2<<3 | REXB<<8, 6 | 2<<3 | REXX<<8 },
|
|
{"r14w", 6 | 1<<3 | REXR<<8, 6 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r15", 7 | 3<<3 | REXR<<8 | REXW<<8, 7 | 3<<3 | REXB<<8 | REXW<<8, 7 | 3<<3 | REXB<<8, 7 | 3<<3 | REXX<<8 },
|
|
{"r15b", 7 | REXR<<8, 7 | REXB<<8, -1, -1 },
|
|
{"r15d", 7 | 2<<3 | REXR<<8, 7 | 2<<3 | REXB<<8, 7 | 2<<3 | REXB<<8, 7 | 2<<3 | REXX<<8 },
|
|
{"r15w", 7 | 1<<3 | REXR<<8, 7 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r8", 0 | 3<<3 | REXR<<8 | REXW<<8, 0 | 3<<3 | REXB<<8 | REXW<<8, 0 | 3<<3 | REXB<<8, 0 | 3<<3 | REXX<<8 },
|
|
{"r8b", 0 | REXR<<8, 0 | REXB<<8, -1, -1 },
|
|
{"r8d", 0 | 2<<3 | REXR<<8, 0 | 2<<3 | REXB<<8, 0 | 2<<3 | REXB<<8, 0 | 2<<3 | REXX<<8 },
|
|
{"r8w", 0 | 1<<3 | REXR<<8, 0 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"r9", 1 | 3<<3 | REXR<<8 | REXW<<8, 1 | 3<<3 | REXB<<8 | REXW<<8, 1 | 3<<3 | REXB<<8, 1 | 3<<3 | REXX<<8 },
|
|
{"r9b", 1 | REXR<<8, 1 | REXB<<8, -1, -1 },
|
|
{"r9d", 1 | 2<<3 | REXR<<8, 1 | 2<<3 | REXB<<8, 1 | 2<<3 | REXB<<8, 1 | 2<<3 | REXX<<8 },
|
|
{"r9w", 1 | 1<<3 | REXR<<8, 1 | 1<<3 | REXB<<8, -1, -1 },
|
|
{"rax", 0 | 3<<3 | REXW<<8, 0 | 3<<3 | REXW<<8, 0 | 3<<3, 0 | 3<<3 },
|
|
{"rbp", 5 | 3<<3 | REXW<<8, 5 | 3<<3 | REXW<<8, 5 | 3<<3, 5 | 3<<3 },
|
|
{"rbx", 3 | 3<<3 | REXW<<8, 3 | 3<<3 | REXW<<8, 3 | 3<<3, 3 | 3<<3 },
|
|
{"rcx", 1 | 3<<3 | REXW<<8, 1 | 3<<3 | REXW<<8, 1 | 3<<3, 1 | 3<<3 },
|
|
{"rdi", 7 | 3<<3 | REXW<<8, 7 | 3<<3 | REXW<<8, 7 | 3<<3, 7 | 3<<3 },
|
|
{"rdx", 2 | 3<<3 | REXW<<8, 2 | 3<<3 | REXW<<8, 2 | 3<<3, 2 | 3<<3 },
|
|
{"riz", -1, -1, -1, 4 | 3<<3 },
|
|
{"rsi", 6 | 3<<3 | REXW<<8, 6 | 3<<3 | REXW<<8, 6 | 3<<3, 6 | 3<<3 },
|
|
{"rsp", 4 | 3<<3 | REXW<<8, 4 | 3<<3 | REXW<<8, 4 | 3<<3, 4 | 3<<3 },
|
|
{"si", 6 | 1<<3, 6 | 1<<3, 6 | 1<<3, 6 | 1<<3 },
|
|
{"sil", 6 | REX<<8, 6 | REX<<8, 6 | REX<<8, 6 | REX<<8 },
|
|
{"sp", 4 | 1<<3, 4 | 1<<3, 4 | 1<<3, 4 | 1<<3 },
|
|
{"spl", 4 | REX<<8, 4 | REX<<8, 4 | REX<<8, 4 | REX<<8 },
|
|
{"st", 0 | 4<<3, 0 | 4<<3, -1, -1 },
|
|
{"st(0)", 0 | 4<<3, 0 | 4<<3, -1, -1 },
|
|
{"st(1)", 1 | 4<<3, 1 | 4<<3, -1, -1 },
|
|
{"st(2)", 2 | 4<<3, 2 | 4<<3, -1, -1 },
|
|
{"st(3)", 3 | 4<<3, 3 | 4<<3, -1, -1 },
|
|
{"st(4)", 4 | 4<<3, 4 | 4<<3, -1, -1 },
|
|
{"st(5)", 5 | 4<<3, 5 | 4<<3, -1, -1 },
|
|
{"st(6)", 6 | 4<<3, 6 | 4<<3, -1, -1 },
|
|
{"st(7)", 7 | 4<<3, 7 | 4<<3, -1, -1 },
|
|
{"xmm0", 0 | 4<<3, 0 | 4<<3, -1, -1 },
|
|
{"xmm1", 1 | 4<<3, 1 | 4<<3, -1, -1 },
|
|
{"xmm10", 2 | 4<<3 | REXR<<8, 2 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm11", 3 | 4<<3 | REXR<<8, 3 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm12", 4 | 4<<3 | REXR<<8, 4 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm13", 5 | 4<<3 | REXR<<8, 5 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm14", 6 | 4<<3 | REXR<<8, 6 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm15", 7 | 4<<3 | REXR<<8, 7 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm2", 2 | 4<<3, 2 | 4<<3, -1, -1 },
|
|
{"xmm3", 3 | 4<<3, 3 | 4<<3, -1, -1 },
|
|
{"xmm4", 4 | 4<<3, 4 | 4<<3, -1, -1 },
|
|
{"xmm5", 5 | 4<<3, 5 | 4<<3, -1, -1 },
|
|
{"xmm6", 6 | 4<<3, 6 | 4<<3, -1, -1 },
|
|
{"xmm7", 7 | 4<<3, 7 | 4<<3, -1, -1 },
|
|
{"xmm8", 0 | 4<<3 | REXR<<8, 0 | 4<<3 | REXB<<8, -1, -1 },
|
|
{"xmm9", 1 | 4<<3 | REXR<<8, 1 | 4<<3 | REXB<<8, -1, -1 },
|
|
} /* clang-format on */;
|
|
|
|
static unsigned Hash(const void *p, unsigned long n) {
|
|
unsigned h, i;
|
|
for (h = i = 0; i < n; i++) {
|
|
h += ((unsigned char *)p)[i];
|
|
h *= 0x9e3779b1;
|
|
}
|
|
return MAX(1, h);
|
|
}
|
|
|
|
static bool IsPunctMergeable(int c) {
|
|
switch (c) {
|
|
case ';':
|
|
case '$':
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static char *PunctToStr(int p, char b[4]) {
|
|
int c, i, j;
|
|
memset(b, 0, 4);
|
|
for (j = 0, i = 2; i >= 0; --i) {
|
|
if ((c = (p >> (i * 8)) & 0xff)) {
|
|
b[j++] = c;
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static void PrintSlice(struct Slice s) {
|
|
fprintf(stderr, "%.*s\n", s.n, s.p);
|
|
}
|
|
|
|
static char *SaveString(struct Strings *l, char *p) {
|
|
APPEND((*l));
|
|
l->p[l->n - 1] = p;
|
|
return p;
|
|
}
|
|
|
|
static int StrDup(struct As *a, const char *s) {
|
|
SaveString(&a->strings, strdup(s));
|
|
return a->strings.n - 1;
|
|
}
|
|
|
|
static int SliceDup(struct As *a, struct Slice s) {
|
|
SaveString(&a->strings, strndup(s.p, s.n));
|
|
return a->strings.n - 1;
|
|
}
|
|
|
|
static int AppendSauce(struct As *a, int path, int line) {
|
|
if (!a->sauces.n || (line != a->sauces.p[a->sauces.n - 1].line ||
|
|
path != a->sauces.p[a->sauces.n - 1].path)) {
|
|
APPEND(a->sauces);
|
|
a->sauces.p[a->sauces.n - 1].path = path;
|
|
a->sauces.p[a->sauces.n - 1].line = line;
|
|
}
|
|
return a->sauces.n - 1;
|
|
}
|
|
|
|
static void AppendExpr(struct As *a) {
|
|
APPEND(a->exprs);
|
|
memset(a->exprs.p + a->exprs.n - 1, 0, sizeof(*a->exprs.p));
|
|
a->exprs.p[a->exprs.n - 1].tok = a->i;
|
|
a->exprs.p[a->exprs.n - 1].lhs = -1;
|
|
a->exprs.p[a->exprs.n - 1].rhs = -1;
|
|
}
|
|
|
|
static void AppendThing(struct As *a) {
|
|
APPEND(a->things);
|
|
memset(a->things.p + a->things.n - 1, 0, sizeof(*a->things.p));
|
|
}
|
|
|
|
static void AppendRela(struct As *a) {
|
|
APPEND(a->relas);
|
|
memset(a->relas.p + a->relas.n - 1, 0, sizeof(*a->relas.p));
|
|
}
|
|
|
|
static void AppendSlice(struct As *a) {
|
|
APPEND(a->slices);
|
|
memset(a->slices.p + a->slices.n - 1, 0, sizeof(*a->slices.p));
|
|
}
|
|
|
|
static int AppendSection(struct As *a, int name, int flags, int type) {
|
|
int i;
|
|
APPEND(a->sections);
|
|
i = a->sections.n - 1;
|
|
a->sections.p[i].name = name;
|
|
a->sections.p[i].flags = flags;
|
|
a->sections.p[i].type = type;
|
|
a->sections.p[i].align = 1;
|
|
a->sections.p[i].binary.p = NULL;
|
|
a->sections.p[i].binary.n = 0;
|
|
return i;
|
|
}
|
|
|
|
static struct As *NewAssembler(void) {
|
|
struct As *a = calloc(1, sizeof(struct As));
|
|
AppendSlice(a);
|
|
AppendSection(a, StrDup(a, ""), 0, SHT_NULL);
|
|
AppendSection(a, StrDup(a, ".text"), SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS);
|
|
AppendSection(a, StrDup(a, ".data"), SHF_ALLOC | SHF_WRITE, SHT_PROGBITS);
|
|
AppendSection(a, StrDup(a, ".bss"), SHF_ALLOC | SHF_WRITE, SHT_NOBITS);
|
|
a->section = 1;
|
|
return a;
|
|
}
|
|
|
|
static void FreeAssembler(struct As *a) {
|
|
int i;
|
|
for (i = 0; i < a->sections.n; ++i) free(a->sections.p[i].binary.p);
|
|
for (i = 0; i < a->strings.n; ++i) free(a->strings.p[i]);
|
|
for (i = 0; i < a->incpaths.n; ++i) free(a->incpaths.p[i]);
|
|
free(a->ints.p);
|
|
free(a->floats.p);
|
|
free(a->slices.p);
|
|
free(a->sauces.p);
|
|
free(a->things.p);
|
|
free(a->sections.p);
|
|
free(a->symbols.p);
|
|
free(a->symbolindex.p);
|
|
free(a->labels.p);
|
|
free(a->relas.p);
|
|
free(a->exprs.p);
|
|
free(a->strings.p);
|
|
free(a->incpaths.p);
|
|
free(a->sectionstack.p);
|
|
free(a);
|
|
}
|
|
|
|
static void ReadFlags(struct As *a, int argc, char *argv[]) {
|
|
int i;
|
|
a->inpath = StrDup(a, "-");
|
|
a->outpath = StrDup(a, "a.out");
|
|
for (i = 1; i < argc; ++i) {
|
|
if (!strcmp(argv[i], "-o")) {
|
|
a->outpath = StrDup(a, argv[++i]);
|
|
} else if (startswith(argv[i], "-o")) {
|
|
a->outpath = StrDup(a, argv[i] + 2);
|
|
} else if (!strcmp(argv[i], "-I")) {
|
|
SaveString(&a->incpaths, strdup(argv[++i]));
|
|
} else if (startswith(argv[i], "-I")) {
|
|
SaveString(&a->incpaths, strdup(argv[i] + 2));
|
|
} else if (!strcmp(argv[i], "-Z")) {
|
|
a->inhibiterr = true;
|
|
} else if (!strcmp(argv[i], "-W")) {
|
|
a->inhibitwarn = true;
|
|
} else if (argv[i][0] != '-') {
|
|
a->inpath = StrDup(a, argv[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) {
|
|
if (c != '\\') return c;
|
|
switch ((c = p[(*i)++])) {
|
|
case 'a':
|
|
return '\a';
|
|
case 'b':
|
|
return '\b';
|
|
case 't':
|
|
return '\t';
|
|
case 'n':
|
|
return '\n';
|
|
case 'v':
|
|
return '\v';
|
|
case 'f':
|
|
return '\f';
|
|
case 'r':
|
|
return '\r';
|
|
case 'e':
|
|
return 033;
|
|
case 'x':
|
|
if (isxdigit(p[*i])) {
|
|
c = hextoint(p[(*i)++]);
|
|
if (isxdigit(p[*i])) {
|
|
c = c * 16 + hextoint(p[(*i)++]);
|
|
}
|
|
}
|
|
return c;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
c -= '0';
|
|
if ('0' <= p[*i] && p[*i] <= '7') {
|
|
c = c * 8 + (p[(*i)++] - '0');
|
|
if ('0' <= p[*i] && p[*i] <= '7') {
|
|
c = c * 8 + (p[(*i)++] - '0');
|
|
}
|
|
}
|
|
return c;
|
|
default:
|
|
return c;
|
|
}
|
|
}
|
|
|
|
static void PrintLocation(struct As *a) {
|
|
fprintf(stderr,
|
|
"%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[a->i].s].path],
|
|
a->sauces.p[a->things.p[a->i].s].line);
|
|
}
|
|
|
|
static wontreturn void Fail(struct As *a, const char *fmt, ...) {
|
|
va_list va;
|
|
PrintLocation(a);
|
|
va_start(va, fmt);
|
|
vfprintf(stderr, fmt, va);
|
|
va_end(va);
|
|
fputc('\n', stderr);
|
|
__die();
|
|
}
|
|
|
|
static wontreturn void InvalidRegister(struct As *a) {
|
|
Fail(a, "invalid register");
|
|
}
|
|
|
|
static char *FindInclude(struct As *a, const char *file) {
|
|
int i;
|
|
char *path;
|
|
struct stat st;
|
|
for (i = 0; i < a->incpaths.n; ++i) {
|
|
path = xjoinpaths(a->incpaths.p[i], file);
|
|
if (stat(path, &st) != -1 && S_ISREG(st.st_mode)) return path;
|
|
free(path);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void Tokenize(struct As *a, int path) {
|
|
int c, i, line;
|
|
char *p, *path2;
|
|
struct Slice buf;
|
|
bool bol, isfloat, isfpu;
|
|
p = SaveString(&a->strings, read_file(a->strings.p[path]));
|
|
p = skip_bom(p);
|
|
canonicalize_newline(p);
|
|
remove_backslash_newline(p);
|
|
line = 1;
|
|
bol = true;
|
|
while ((c = *p)) {
|
|
if (c == '/' && p[1] == '*') {
|
|
for (i = 2; p[i]; ++i) {
|
|
if (p[i] == '\n') {
|
|
++line;
|
|
bol = true;
|
|
} else {
|
|
bol = false;
|
|
if (p[i] == '*' && p[i + 1] == '/') {
|
|
i += 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
p += i;
|
|
continue;
|
|
}
|
|
if (c == '#' || (c == '/' && bol) || (c == '/' && p[1] == '/')) {
|
|
p = strchr(p, '\n');
|
|
continue;
|
|
}
|
|
if (c == '\n') {
|
|
AppendThing(a);
|
|
a->things.p[a->things.n - 1].t = TT_PUNCT;
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
a->things.p[a->things.n - 1].i = ';';
|
|
++p;
|
|
bol = true;
|
|
++line;
|
|
continue;
|
|
}
|
|
bol = false;
|
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' ||
|
|
c == '\v' || c == ',') {
|
|
++p;
|
|
continue;
|
|
}
|
|
if ((c & 0x80) || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
|
|
c == '_' || c == '%' || c == '@' ||
|
|
(c == '.' && !('0' <= p[1] && p[1] <= '9'))) {
|
|
isfpu = false;
|
|
for (i = 1;; ++i) {
|
|
if (!((p[i] & 0x80) || ('a' <= p[i] && p[i] <= 'z') ||
|
|
('A' <= p[i] && p[i] <= 'Z') || ('0' <= p[i] && p[i] <= '9') ||
|
|
p[i] == '.' || p[i] == '_' || p[i] == '$' ||
|
|
(isfpu && (p[i] == '(' || p[i] == ')')))) {
|
|
break;
|
|
}
|
|
if (i == 2 && p[i - 2] == '%' && p[i - 1] == 's' && p[i] == 't') {
|
|
isfpu = true;
|
|
}
|
|
}
|
|
if (i == 4 && !strncasecmp(p, ".end", 4)) break;
|
|
AppendThing(a);
|
|
a->things.p[a->things.n - 1].t = TT_SLICE;
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
a->things.p[a->things.n - 1].i = a->slices.n;
|
|
AppendSlice(a);
|
|
a->slices.p[a->slices.n - 1].p = p;
|
|
a->slices.p[a->slices.n - 1].n = i;
|
|
p += i;
|
|
continue;
|
|
}
|
|
if (('0' <= c && c <= '9') || (c == '.' && '0' <= p[1] && p[1] <= '9')) {
|
|
isfloat = c == '.';
|
|
if (c == '0' && p[1] != '.') {
|
|
if (p[1] == 'x' || p[1] == 'X') {
|
|
for (i = 2;; ++i) {
|
|
if (!(('0' <= p[i] && p[i] <= '9') ||
|
|
('a' <= p[i] && p[i] <= 'f') ||
|
|
('A' <= p[i] && p[i] <= 'F'))) {
|
|
break;
|
|
}
|
|
}
|
|
} else if ((p[1] == 'b' || p[1] == 'B') &&
|
|
('0' <= p[2] && p[2] <= '9')) {
|
|
for (i = 2;; ++i) {
|
|
if (!(p[i] == '0' || p[i] == '1')) break;
|
|
}
|
|
} else {
|
|
for (i = 1;; ++i) {
|
|
if (!('0' <= p[i] && p[i] <= '7')) break;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 1;; ++i) {
|
|
if (('0' <= p[i] && p[i] <= '9') || p[i] == '-' || p[i] == '+') {
|
|
continue;
|
|
} else if (p[i] == '.' || p[i] == 'e' || p[i] == 'E' || p[i] == 'e') {
|
|
isfloat = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
AppendThing(a);
|
|
if (isfloat) {
|
|
APPEND(a->floats);
|
|
a->floats.p[a->floats.n - 1] = strtold(p, NULL);
|
|
a->things.p[a->things.n - 1].i = a->floats.n - 1;
|
|
a->things.p[a->things.n - 1].t = TT_FLOAT;
|
|
} else {
|
|
APPEND(a->ints);
|
|
a->ints.p[a->ints.n - 1] = strtol(p, NULL, 0);
|
|
a->things.p[a->things.n - 1].i = a->ints.n - 1;
|
|
if (p[i] == 'f' || p[i] == 'F') {
|
|
a->things.p[a->things.n - 1].t = TT_FORWARD;
|
|
++i;
|
|
} else if (p[i] == 'b' || p[i] == 'B') {
|
|
a->things.p[a->things.n - 1].t = TT_BACKWARD;
|
|
++i;
|
|
} else {
|
|
a->things.p[a->things.n - 1].t = TT_INT;
|
|
}
|
|
}
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
p += i;
|
|
continue;
|
|
}
|
|
if (c == '\'') {
|
|
i = 1;
|
|
c = p[i++];
|
|
c = ReadCharLiteral(&buf, c, p, &i);
|
|
if (p[i] == '\'') ++i;
|
|
p += i;
|
|
AppendThing(a);
|
|
a->things.p[a->things.n - 1].t = TT_INT;
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
a->things.p[a->things.n - 1].i = a->ints.n;
|
|
APPEND(a->ints);
|
|
a->ints.p[a->ints.n - 1] = c;
|
|
continue;
|
|
}
|
|
if (c == '"') {
|
|
buf.n = 0;
|
|
buf.p = NULL;
|
|
for (i = 1; (c = p[i++]);) {
|
|
if (c == '"') break;
|
|
c = ReadCharLiteral(&buf, c, p, &i);
|
|
APPEND(buf);
|
|
buf.p[buf.n - 1] = c;
|
|
}
|
|
p += i;
|
|
if (a->things.n && a->things.p[a->things.n - 1].t == TT_SLICE &&
|
|
IS(a->slices.p[a->things.p[a->things.n - 1].i].p,
|
|
a->slices.p[a->things.p[a->things.n - 1].i].n, ".include")) {
|
|
APPEND(buf);
|
|
buf.p[buf.n - 1] = '\0';
|
|
--a->things.n;
|
|
if ((path2 = FindInclude(a, buf.p))) {
|
|
Tokenize(a, StrDup(a, path2));
|
|
free(path2);
|
|
free(buf.p);
|
|
} else {
|
|
Fail(a, "not found: %s", buf.p);
|
|
}
|
|
} else {
|
|
SaveString(&a->strings, buf.p);
|
|
AppendThing(a);
|
|
a->things.p[a->things.n - 1].t = TT_SLICE;
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
a->things.p[a->things.n - 1].i = a->slices.n;
|
|
AppendSlice(a);
|
|
a->slices.p[a->slices.n - 1] = buf;
|
|
}
|
|
continue;
|
|
}
|
|
if (IsPunctMergeable(c) && a->things.n &&
|
|
a->things.p[a->things.n - 1].t == TT_PUNCT &&
|
|
IsPunctMergeable(a->things.p[a->things.n - 1].i)) {
|
|
a->things.p[a->things.n - 1].i = a->things.p[a->things.n - 1].i << 8 | c;
|
|
} else {
|
|
AppendThing(a);
|
|
a->things.p[a->things.n - 1].t = TT_PUNCT;
|
|
a->things.p[a->things.n - 1].s = AppendSauce(a, path, line);
|
|
a->things.p[a->things.n - 1].i = c;
|
|
}
|
|
++p;
|
|
}
|
|
}
|
|
|
|
static int GetSymbol(struct As *a, int name) {
|
|
struct HashEntry *p;
|
|
unsigned i, j, k, n, m, h, n2;
|
|
h = Hash(a->slices.p[name].p, a->slices.p[name].n);
|
|
n = a->symbolindex.n;
|
|
i = 0;
|
|
if (n) {
|
|
k = 0;
|
|
do {
|
|
i = (h + k + ((k + 1) >> 1)) & (n - 1);
|
|
if (a->symbolindex.p[i].h == h &&
|
|
a->slices.p[a->symbols.p[a->symbolindex.p[i].i].name].n ==
|
|
a->slices.p[name].n &&
|
|
!memcmp(a->slices.p[a->symbols.p[a->symbolindex.p[i].i].name].p,
|
|
a->slices.p[name].p, a->slices.p[name].n)) {
|
|
return a->symbolindex.p[i].i;
|
|
}
|
|
++k;
|
|
} while (a->symbolindex.p[i].h);
|
|
}
|
|
if (++a->symbolindex.i >= (n >> 1)) {
|
|
m = n ? n << 1 : 16;
|
|
p = calloc(m, sizeof(struct HashEntry));
|
|
for (j = 0; j < n; ++j) {
|
|
if (a->symbolindex.p[j].h) {
|
|
k = 0;
|
|
do {
|
|
i = (a->symbolindex.p[j].h + k + ((k + 1) >> 1)) & (m - 1);
|
|
++k;
|
|
} while (p[i].h);
|
|
p[i].h = a->symbolindex.p[j].h;
|
|
p[i].i = a->symbolindex.p[j].i;
|
|
}
|
|
}
|
|
k = 0;
|
|
do {
|
|
i = (h + k + ((k + 1) >> 1)) & (m - 1);
|
|
++k;
|
|
} while (p[i].h);
|
|
free(a->symbolindex.p);
|
|
a->symbolindex.p = p;
|
|
a->symbolindex.n = m;
|
|
}
|
|
APPEND(a->symbols);
|
|
memset(a->symbols.p + a->symbols.n - 1, 0, sizeof(*a->symbols.p));
|
|
a->symbolindex.p[i].h = h;
|
|
a->symbolindex.p[i].i = a->symbols.n - 1;
|
|
a->symbols.p[a->symbols.n - 1].name = name;
|
|
return a->symbols.n - 1;
|
|
}
|
|
|
|
static void OnSymbol(struct As *a, int name) {
|
|
int i = GetSymbol(a, name);
|
|
if (a->symbols.p[i].section) {
|
|
Fail(a, "already defined: %.*s", a->slices.p[name].n, a->slices.p[name].p);
|
|
}
|
|
a->symbols.p[i].section = a->section;
|
|
a->symbols.p[i].offset = a->sections.p[a->section].binary.n;
|
|
a->i += 2;
|
|
}
|
|
|
|
static void OnLocalLabel(struct As *a, int id) {
|
|
int i;
|
|
char *name;
|
|
name = xasprintf(".Label.%d", a->counter++);
|
|
SaveString(&a->strings, name);
|
|
AppendSlice(a);
|
|
a->slices.p[a->slices.n - 1].p = name;
|
|
a->slices.p[a->slices.n - 1].n = strlen(name);
|
|
i = GetSymbol(a, a->slices.n - 1);
|
|
a->symbols.p[i].section = a->section;
|
|
a->symbols.p[i].offset = a->sections.p[a->section].binary.n;
|
|
APPEND(a->labels);
|
|
a->labels.p[a->labels.n - 1].id = id;
|
|
a->labels.p[a->labels.n - 1].tok = a->i;
|
|
a->labels.p[a->labels.n - 1].symbol = i;
|
|
a->i += 2;
|
|
}
|
|
|
|
static void SetSection(struct As *a, int section) {
|
|
a->previous = a->section;
|
|
a->section = section;
|
|
}
|
|
|
|
static bool IsInt(struct As *a, int i) {
|
|
return a->things.p[i].t == TT_INT;
|
|
}
|
|
static bool IsFloat(struct As *a, int i) {
|
|
return a->things.p[i].t == TT_FLOAT;
|
|
}
|
|
static bool IsSlice(struct As *a, int i) {
|
|
return a->things.p[i].t == TT_SLICE;
|
|
}
|
|
static bool IsPunct(struct As *a, int i, int c) {
|
|
return a->things.p[i].t == TT_PUNCT && a->things.p[i].i == c;
|
|
}
|
|
static bool IsForward(struct As *a, int i) {
|
|
return a->things.p[i].t == TT_FORWARD;
|
|
}
|
|
static bool IsBackward(struct As *a, int i) {
|
|
return a->things.p[i].t == TT_BACKWARD;
|
|
}
|
|
|
|
static bool IsRegister(struct As *a, int i) {
|
|
return IsSlice(a, i) && (a->slices.p[a->things.p[i].i].n &&
|
|
*a->slices.p[a->things.p[i].i].p == '%');
|
|
}
|
|
|
|
static void ConsumePunct(struct As *a, int c) {
|
|
char pb[4];
|
|
if (IsPunct(a, a->i, c)) {
|
|
++a->i;
|
|
} else {
|
|
Fail(a, "expected %s", PunctToStr(c, pb));
|
|
}
|
|
}
|
|
|
|
static int NewPrimary(struct As *a, enum ExprKind k, long x) {
|
|
AppendExpr(a);
|
|
a->exprs.p[a->exprs.n - 1].kind = k;
|
|
a->exprs.p[a->exprs.n - 1].x = x;
|
|
return a->exprs.n - 1;
|
|
}
|
|
|
|
static int NewUnary(struct As *a, enum ExprKind k, int lhs) {
|
|
AppendExpr(a);
|
|
a->exprs.p[a->exprs.n - 1].kind = k;
|
|
a->exprs.p[a->exprs.n - 1].lhs = lhs;
|
|
return a->exprs.n - 1;
|
|
}
|
|
|
|
static int NewBinary(struct As *a, enum ExprKind k, int lhs, int rhs) {
|
|
AppendExpr(a);
|
|
a->exprs.p[a->exprs.n - 1].kind = k;
|
|
a->exprs.p[a->exprs.n - 1].lhs = lhs;
|
|
a->exprs.p[a->exprs.n - 1].rhs = rhs;
|
|
return a->exprs.n - 1;
|
|
}
|
|
|
|
// primary = int
|
|
// | symbol
|
|
// | reference
|
|
static int ParsePrimary(struct As *a, int *rest, int i) {
|
|
int e;
|
|
if (IsInt(a, i)) {
|
|
*rest = i + 1;
|
|
return NewPrimary(a, EX_INT, a->ints.p[a->things.p[i].i]);
|
|
} else if (IsForward(a, i) || IsBackward(a, i) ||
|
|
(IsSlice(a, i) && (a->slices.p[a->things.p[i].i].n &&
|
|
a->slices.p[a->things.p[i].i].p[0] != '%' &&
|
|
a->slices.p[a->things.p[i].i].p[0] != '@'))) {
|
|
*rest = i + 1;
|
|
return NewPrimary(a, EX_SYM, i);
|
|
} else {
|
|
Fail(a, "expected int or label");
|
|
}
|
|
}
|
|
|
|
// postfix = primary "@gotpcrel"
|
|
// | primary "@dtpoff"
|
|
// | primary "@tpoff"
|
|
// | primary
|
|
static int ParsePostfix(struct As *a, int *rest, int i) {
|
|
int x;
|
|
struct Slice suffix;
|
|
x = ParsePrimary(a, &i, i);
|
|
if (IsSlice(a, i)) {
|
|
suffix = a->slices.p[a->things.p[i].i];
|
|
if (suffix.n && suffix.p[0] == '@') {
|
|
if (IS(suffix.p, suffix.n, "@gotpcrel")) {
|
|
a->exprs.p[x].em = EM_GOTPCREL;
|
|
++i;
|
|
} else if (IS(suffix.p, suffix.n, "@dtpoff")) {
|
|
a->exprs.p[x].em = EM_DTPOFF;
|
|
++i;
|
|
} else if (IS(suffix.p, suffix.n, "@tpoff")) {
|
|
a->exprs.p[x].em = EM_TPOFF;
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
|
|
// unary = ("+" | "-" | "!" | "~") unary
|
|
// | postfix
|
|
static int ParseUnary(struct As *a, int *rest, int i) {
|
|
int x;
|
|
if (IsPunct(a, i, '+')) {
|
|
x = ParseUnary(a, rest, i + 1);
|
|
} else if (IsPunct(a, i, '-')) {
|
|
x = ParseUnary(a, rest, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT) {
|
|
a->exprs.p[x].x = -a->exprs.p[x].x;
|
|
} else {
|
|
x = NewPrimary(a, EX_NEG, x);
|
|
}
|
|
} else if (IsPunct(a, i, '!')) {
|
|
x = ParseUnary(a, rest, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT) {
|
|
a->exprs.p[x].x = !a->exprs.p[x].x;
|
|
} else {
|
|
x = NewPrimary(a, EX_NOT, x);
|
|
}
|
|
} else if (IsPunct(a, i, '~')) {
|
|
x = ParseUnary(a, rest, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT) {
|
|
a->exprs.p[x].x = ~a->exprs.p[x].x;
|
|
} else {
|
|
x = NewPrimary(a, EX_BITNOT, x);
|
|
}
|
|
} else {
|
|
x = ParsePostfix(a, rest, i);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
// mul = unary ("*" unary | "/" unary | "%" unary)*
|
|
static int ParseMul(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseUnary(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '*')) {
|
|
y = ParseUnary(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x *= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_MUL, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '/')) {
|
|
y = ParseUnary(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x /= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_DIV, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '%')) {
|
|
y = ParseUnary(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x %= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_REM, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add = mul ("+" mul | "-" mul)*
|
|
static int ParseAdd(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseMul(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '+')) {
|
|
y = ParseMul(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x += a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_ADD, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '-')) {
|
|
y = ParseMul(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x -= a->exprs.p[y].x;
|
|
} else if (a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[y].x = -a->exprs.p[y].x;
|
|
x = NewBinary(a, EX_ADD, x, y);
|
|
} else {
|
|
x = NewBinary(a, EX_SUB, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// shift = add ("<<" add | ">>" add)*
|
|
static int ParseShift(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseAdd(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '<' << 8 | '<')) {
|
|
y = ParseAdd(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x <<= a->exprs.p[y].x & 63;
|
|
} else {
|
|
x = NewBinary(a, EX_SHL, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '>' << 8 | '>')) {
|
|
y = ParseAdd(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x >>= a->exprs.p[y].x & 63;
|
|
} else {
|
|
x = NewBinary(a, EX_SHR, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)*
|
|
static int ParseRelational(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseShift(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '<')) {
|
|
y = ParseShift(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[x].x < a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_LT, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '>')) {
|
|
y = ParseShift(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[y].x < a->exprs.p[x].x;
|
|
} else {
|
|
x = NewBinary(a, EX_LT, y, x);
|
|
}
|
|
} else if (IsPunct(a, i, '<' << 8 | '=')) {
|
|
y = ParseShift(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[x].x <= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_LE, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '>' << 8 | '=')) {
|
|
y = ParseShift(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[y].x <= a->exprs.p[x].x;
|
|
} else {
|
|
x = NewBinary(a, EX_LE, y, x);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// equality = relational ("==" relational | "!=" relational)*
|
|
static int ParseEquality(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseRelational(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '=' << 8 | '=')) {
|
|
y = ParseRelational(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[x].x == a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_EQ, x, y);
|
|
}
|
|
} else if (IsPunct(a, i, '!' << 8 | '=')) {
|
|
y = ParseRelational(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x = a->exprs.p[x].x != a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_NE, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// and = equality ("&" equality)*
|
|
static int ParseAnd(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseEquality(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '&')) {
|
|
y = ParseEquality(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x &= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_AND, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// xor = and ("^" and)*
|
|
static int ParseXor(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseAnd(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '^')) {
|
|
y = ParseAnd(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x ^= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_XOR, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// or = xor ("|" xor)*
|
|
static int ParseOr(struct As *a, int *rest, int i) {
|
|
int x, y;
|
|
x = ParseXor(a, &i, i);
|
|
for (;;) {
|
|
if (IsPunct(a, i, '|')) {
|
|
y = ParseXor(a, &i, i + 1);
|
|
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
|
a->exprs.p[x].x |= a->exprs.p[y].x;
|
|
} else {
|
|
x = NewBinary(a, EX_OR, x, y);
|
|
}
|
|
} else {
|
|
*rest = i;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int Parse(struct As *a) {
|
|
return ParseOr(a, &a->i, a->i);
|
|
}
|
|
|
|
static long GetInt(struct As *a) {
|
|
int x;
|
|
x = Parse(a);
|
|
if (a->exprs.p[x].kind == EX_INT) {
|
|
return a->exprs.p[x].x;
|
|
} else {
|
|
Fail(a, "expected constexpr int");
|
|
}
|
|
}
|
|
|
|
static long double GetFloat(struct As *a) {
|
|
long double res;
|
|
if (IsFloat(a, a->i)) {
|
|
res = a->floats.p[a->things.p[a->i].i];
|
|
++a->i;
|
|
return res;
|
|
} else {
|
|
Fail(a, "expected float");
|
|
}
|
|
}
|
|
|
|
static struct Slice GetSlice(struct As *a) {
|
|
struct Slice res;
|
|
if (IsSlice(a, a->i)) {
|
|
res = a->slices.p[a->things.p[a->i].i];
|
|
++a->i;
|
|
return res;
|
|
} else {
|
|
Fail(a, "expected string");
|
|
}
|
|
}
|
|
|
|
static void EmitData(struct As *a, const void *p, unsigned long n) {
|
|
struct Slice *s;
|
|
s = &a->sections.p[a->section].binary;
|
|
s->p = realloc(s->p, s->n + n);
|
|
memcpy(s->p + s->n, p, n);
|
|
s->n += n;
|
|
}
|
|
|
|
static void EmitByte(struct As *a, unsigned long x) {
|
|
unsigned char b[1];
|
|
b[0] = x >> 000;
|
|
EmitData(a, b, 1);
|
|
}
|
|
|
|
static void EmitWord(struct As *a, unsigned long x) {
|
|
unsigned char b[2];
|
|
b[0] = x >> 000;
|
|
b[1] = x >> 010;
|
|
EmitData(a, b, 2);
|
|
}
|
|
|
|
static void EmitLong(struct As *a, unsigned long x) {
|
|
unsigned char b[4];
|
|
b[0] = x >> 000;
|
|
b[1] = x >> 010;
|
|
b[2] = x >> 020;
|
|
b[3] = x >> 030;
|
|
EmitData(a, b, 4);
|
|
}
|
|
|
|
void EmitQuad(struct As *a, unsigned long x) {
|
|
unsigned char b[8];
|
|
b[0] = x >> 000;
|
|
b[1] = x >> 010;
|
|
b[2] = x >> 020;
|
|
b[3] = x >> 030;
|
|
b[4] = x >> 040;
|
|
b[5] = x >> 050;
|
|
b[6] = x >> 060;
|
|
b[7] = x >> 070;
|
|
EmitData(a, b, 8);
|
|
}
|
|
|
|
static void EmitVarword(struct As *a, unsigned long x) {
|
|
if (x > 255) EmitVarword(a, x >> 8);
|
|
EmitByte(a, x);
|
|
}
|
|
|
|
static void OnSleb128(struct As *a, struct Slice s) {
|
|
int c;
|
|
long x;
|
|
while (!IsPunct(a, a->i, ';')) {
|
|
x = GetInt(a);
|
|
for (;;) {
|
|
c = x & 0x7f;
|
|
x >>= 7;
|
|
if ((x == 0 && !(c & 0x40)) || (x == -1 && (c & 0x40))) {
|
|
break;
|
|
} else {
|
|
c |= 0x80;
|
|
}
|
|
EmitByte(a, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OnUleb128(struct As *a, struct Slice s) {
|
|
int c;
|
|
unsigned long x;
|
|
while (!IsPunct(a, a->i, ';')) {
|
|
x = GetInt(a);
|
|
do {
|
|
c = x & 0x7f;
|
|
x >>= 7;
|
|
if (x) c |= 0x80;
|
|
EmitByte(a, c);
|
|
} while (x);
|
|
}
|
|
}
|
|
|
|
static void OnZero(struct As *a, struct Slice s) {
|
|
long n;
|
|
char *p;
|
|
while (IsInt(a, a->i)) {
|
|
n = GetInt(a);
|
|
p = calloc(n, 1);
|
|
EmitData(a, p, n);
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
static void OnSpace(struct As *a, struct Slice s) {
|
|
long n;
|
|
char *p;
|
|
p = malloc((n = GetInt(a)));
|
|
memset(p, IsInt(a, a->i) ? GetInt(a) : 0, n);
|
|
EmitData(a, p, n);
|
|
free(p);
|
|
}
|
|
|
|
static long GetRelaAddend(int kind) {
|
|
switch (kind) {
|
|
case R_X86_64_PC8:
|
|
return -1;
|
|
case R_X86_64_PC16:
|
|
return -2;
|
|
case R_X86_64_PC32:
|
|
case R_X86_64_PLT32:
|
|
case R_X86_64_GOTPCRELX:
|
|
return -4;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void EmitExpr(struct As *a, int expr, int kind,
|
|
void emitter(struct As *, unsigned long)) {
|
|
if (expr == -1) {
|
|
emitter(a, 0);
|
|
} else if (a->exprs.p[expr].kind == EX_INT) {
|
|
emitter(a, a->exprs.p[expr].x);
|
|
} else {
|
|
AppendRela(a);
|
|
a->relas.p[a->relas.n - 1].kind = kind;
|
|
a->relas.p[a->relas.n - 1].expr = expr;
|
|
a->relas.p[a->relas.n - 1].section = a->section;
|
|
a->relas.p[a->relas.n - 1].offset = a->sections.p[a->section].binary.n;
|
|
a->relas.p[a->relas.n - 1].addend = GetRelaAddend(kind);
|
|
emitter(a, 0);
|
|
}
|
|
}
|
|
|
|
static void OnByte(struct As *a, struct Slice s) {
|
|
do {
|
|
EmitExpr(a, Parse(a), R_X86_64_8, EmitByte);
|
|
} while (!IsPunct(a, a->i, ';'));
|
|
}
|
|
|
|
static void OnWord(struct As *a, struct Slice s) {
|
|
do {
|
|
EmitExpr(a, Parse(a), R_X86_64_16, EmitWord);
|
|
} while (!IsPunct(a, a->i, ';'));
|
|
}
|
|
|
|
static void OnLong(struct As *a, struct Slice s) {
|
|
do {
|
|
EmitExpr(a, Parse(a), R_X86_64_32, EmitLong);
|
|
} while (!IsPunct(a, a->i, ';'));
|
|
}
|
|
|
|
static void OnQuad(struct As *a, struct Slice s) {
|
|
do {
|
|
EmitExpr(a, Parse(a), R_X86_64_64, EmitQuad);
|
|
} while (!IsPunct(a, a->i, ';'));
|
|
}
|
|
|
|
static void OnFloat(struct As *a, struct Slice s) {
|
|
float f;
|
|
char b[4];
|
|
for (;;) {
|
|
if (IsFloat(a, a->i)) {
|
|
f = GetFloat(a);
|
|
} else if (IsInt(a, a->i)) {
|
|
f = GetInt(a);
|
|
} else {
|
|
break;
|
|
}
|
|
memcpy(b, &f, 4);
|
|
EmitData(a, b, 4);
|
|
}
|
|
}
|
|
|
|
static void OnDouble(struct As *a, struct Slice s) {
|
|
double f;
|
|
char b[8];
|
|
for (;;) {
|
|
if (IsFloat(a, a->i)) {
|
|
f = GetFloat(a);
|
|
} else if (IsInt(a, a->i)) {
|
|
f = GetInt(a);
|
|
} else {
|
|
break;
|
|
}
|
|
memcpy(b, &f, 8);
|
|
EmitData(a, b, 8);
|
|
}
|
|
}
|
|
|
|
static void OnLongDouble(struct As *a, int n) {
|
|
char b[16];
|
|
long double f;
|
|
for (;;) {
|
|
if (IsFloat(a, a->i)) {
|
|
f = GetFloat(a);
|
|
} else if (IsInt(a, a->i)) {
|
|
f = GetInt(a);
|
|
} else {
|
|
break;
|
|
}
|
|
memset(b, 0, 16);
|
|
memcpy(b, &f, sizeof(f));
|
|
EmitData(a, b, n);
|
|
}
|
|
}
|
|
|
|
static void OnFloat80(struct As *a, struct Slice s) {
|
|
OnLongDouble(a, 10);
|
|
}
|
|
|
|
static void OnLdbl(struct As *a, struct Slice s) {
|
|
OnLongDouble(a, 16);
|
|
}
|
|
|
|
static void OnAscii(struct As *a, struct Slice s) {
|
|
struct Slice arg;
|
|
while (IsSlice(a, a->i)) {
|
|
arg = GetSlice(a);
|
|
EmitData(a, arg.p, arg.n);
|
|
}
|
|
}
|
|
|
|
static void OnAsciz(struct As *a, struct Slice s) {
|
|
struct Slice arg;
|
|
while (IsSlice(a, a->i)) {
|
|
arg = GetSlice(a);
|
|
EmitData(a, arg.p, arg.n);
|
|
EmitByte(a, 0);
|
|
}
|
|
}
|
|
|
|
static void OnAbort(struct As *a, struct Slice s) {
|
|
Fail(a, "aborted");
|
|
}
|
|
|
|
static void OnErr(struct As *a, struct Slice s) {
|
|
if (a->inhibiterr) return;
|
|
Fail(a, "error");
|
|
}
|
|
|
|
static void OnError(struct As *a, struct Slice s) {
|
|
struct Slice msg = GetSlice(a);
|
|
if (a->inhibiterr) return;
|
|
Fail(a, "%.*s", msg.n, msg.p);
|
|
}
|
|
|
|
static void OnWarning(struct As *a, struct Slice s) {
|
|
struct Slice msg = GetSlice(a);
|
|
if (a->inhibitwarn) return;
|
|
PrintLocation(a);
|
|
fprintf(stderr, "%.*s\n", msg.n, msg.p);
|
|
}
|
|
|
|
static void OnText(struct As *a, struct Slice s) {
|
|
SetSection(a, 1);
|
|
}
|
|
|
|
static void OnData(struct As *a, struct Slice s) {
|
|
SetSection(a, 2);
|
|
}
|
|
|
|
static void OnBss(struct As *a, struct Slice s) {
|
|
SetSection(a, 3);
|
|
}
|
|
|
|
static void OnPrevious(struct As *a, struct Slice s) {
|
|
SetSection(a, a->previous);
|
|
}
|
|
|
|
static void OnAlign(struct As *a, struct Slice s) {
|
|
long i, n, align, fill, maxskip;
|
|
align = GetInt(a);
|
|
if (__builtin_popcountl(align) != 1) Fail(a, "alignment not power of 2");
|
|
fill = (a->sections.p[a->section].flags & SHF_EXECINSTR) ? 0x90 : 0;
|
|
maxskip = 268435456;
|
|
if (IsInt(a, a->i)) {
|
|
fill = GetInt(a);
|
|
if (IsInt(a, a->i)) {
|
|
maxskip = GetInt(a);
|
|
}
|
|
}
|
|
i = a->sections.p[a->section].binary.n;
|
|
n = ROUNDUP(i, align) - i;
|
|
if (n > maxskip) return;
|
|
a->sections.p[a->section].align = MAX(a->sections.p[a->section].align, align);
|
|
for (i = 0; i < n; ++i) EmitByte(a, fill);
|
|
}
|
|
|
|
static int SectionFlag(struct As *a, int c) {
|
|
switch (c) {
|
|
case 'a':
|
|
return SHF_ALLOC;
|
|
case 'w':
|
|
return SHF_WRITE;
|
|
case 'x':
|
|
return SHF_EXECINSTR;
|
|
case 'g':
|
|
return SHF_GROUP;
|
|
case 'M':
|
|
return SHF_MERGE;
|
|
case 'S':
|
|
return SHF_STRINGS;
|
|
case 'T':
|
|
return SHF_TLS;
|
|
default:
|
|
Fail(a, "unknown section flag: %`'c", c);
|
|
}
|
|
}
|
|
|
|
static int SectionFlags(struct As *a, struct Slice s) {
|
|
int i, flags;
|
|
for (flags = i = 0; i < s.n; ++i) {
|
|
flags |= SectionFlag(a, s.p[i]);
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
static int SectionType(struct As *a, struct Slice s) {
|
|
if (IS(s.p, s.n, "@progbits") || IS(s.p, s.n, "SHT_PROGBITS")) {
|
|
return SHT_PROGBITS;
|
|
} else if (IS(s.p, s.n, "@note") || IS(s.p, s.n, "SHT_NOTE")) {
|
|
return SHT_NOTE;
|
|
} else if (IS(s.p, s.n, "@nobits") || IS(s.p, s.n, "SHT_NOBITS")) {
|
|
return SHT_NOBITS;
|
|
} else if (IS(s.p, s.n, "@preinit_array") ||
|
|
IS(s.p, s.n, "SHT_PREINIT_ARRAY")) {
|
|
return SHT_PREINIT_ARRAY;
|
|
} else if (IS(s.p, s.n, "@init_array") || IS(s.p, s.n, "SHT_INIT_ARRAY")) {
|
|
return SHT_INIT_ARRAY;
|
|
} else if (IS(s.p, s.n, "@fini_array") || IS(s.p, s.n, "SHT_FINI_ARRAY")) {
|
|
return SHT_FINI_ARRAY;
|
|
} else {
|
|
Fail(a, "unknown section type: %.*s", s.n, s.p);
|
|
}
|
|
}
|
|
|
|
static int SymbolType(struct As *a, struct Slice s) {
|
|
if (IS(s.p, s.n, "@object") || IS(s.p, s.n, "STT_OBJECT")) {
|
|
return STT_OBJECT;
|
|
} else if (IS(s.p, s.n, "@function") || IS(s.p, s.n, "STT_FUNC")) {
|
|
return STT_FUNC;
|
|
} else if (IS(s.p, s.n, "@common") || IS(s.p, s.n, "STT_COMMON")) {
|
|
return STT_COMMON;
|
|
} else if (IS(s.p, s.n, "@notype") || IS(s.p, s.n, "STT_NOTYPE")) {
|
|
return STT_NOTYPE;
|
|
} else if (IS(s.p, s.n, "@tls_object") || IS(s.p, s.n, "STT_TLS")) {
|
|
return STT_TLS;
|
|
} else {
|
|
Fail(a, "unknown symbol type: %.*s", s.n, s.p);
|
|
}
|
|
}
|
|
|
|
static int GrabSection(struct As *a, int name, int flags, int type) {
|
|
int i;
|
|
for (i = 0; i < a->sections.n; ++i) {
|
|
if (!strcmp(a->strings.p[name], a->strings.p[a->sections.p[i].name])) {
|
|
return i;
|
|
}
|
|
}
|
|
return AppendSection(a, name, flags, type);
|
|
}
|
|
|
|
static void OnSection(struct As *a, struct Slice s) {
|
|
int name, flags, type;
|
|
name = SliceDup(a, GetSlice(a));
|
|
if (startswith(a->strings.p[name], ".text")) {
|
|
flags = SHF_ALLOC | SHF_EXECINSTR;
|
|
type = SHT_PROGBITS;
|
|
} else if (startswith(a->strings.p[name], ".data")) {
|
|
flags = SHF_ALLOC | SHF_WRITE;
|
|
type = SHT_PROGBITS;
|
|
} else if (startswith(a->strings.p[name], ".bss")) {
|
|
flags = SHF_ALLOC | SHF_WRITE;
|
|
type = SHT_NOBITS;
|
|
} else {
|
|
flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE;
|
|
type = SHT_PROGBITS;
|
|
}
|
|
if (IsSlice(a, a->i)) {
|
|
flags = SectionFlags(a, GetSlice(a));
|
|
if (IsSlice(a, a->i)) {
|
|
type = SectionType(a, GetSlice(a));
|
|
}
|
|
}
|
|
SetSection(a, GrabSection(a, name, flags, type));
|
|
}
|
|
|
|
static void OnPushsection(struct As *a, struct Slice s) {
|
|
APPEND(a->sectionstack);
|
|
a->sectionstack.p[a->sectionstack.n - 1] = a->section;
|
|
OnSection(a, s);
|
|
}
|
|
|
|
static void OnPopsection(struct As *a, struct Slice s) {
|
|
if (!a->sectionstack.n) Fail(a, "stack smashed");
|
|
a->section = a->sectionstack.p[--a->sectionstack.n];
|
|
}
|
|
|
|
static void OnIdent(struct As *a, struct Slice s) {
|
|
struct Slice arg;
|
|
int comment, oldsection;
|
|
comment = GrabSection(a, StrDup(a, ".comment"), SHF_MERGE | SHF_STRINGS,
|
|
SHT_PROGBITS);
|
|
oldsection = a->section;
|
|
a->section = comment;
|
|
arg = GetSlice(a);
|
|
EmitData(a, arg.p, arg.n);
|
|
EmitByte(a, 0);
|
|
a->section = oldsection;
|
|
}
|
|
|
|
static void OnIncbin(struct As *a, struct Slice s) {
|
|
int fd;
|
|
struct stat st;
|
|
char *path, *path2;
|
|
struct Slice *data, arg;
|
|
arg = GetSlice(a);
|
|
path = strndup(arg.p, arg.n);
|
|
if ((path2 = FindInclude(a, path))) {
|
|
if ((fd = open(path2, O_RDONLY)) == -1 || fstat(fd, &st) == -1) {
|
|
Fail(a, "open failed: %s", path2);
|
|
}
|
|
data = &a->sections.p[a->section].binary;
|
|
data->p = realloc(data->p, data->n + st.st_size);
|
|
if (read(fd, data->p, st.st_size) != st.st_size) {
|
|
Fail(a, "read failed: %s", path2);
|
|
}
|
|
data->n += st.st_size;
|
|
close(fd);
|
|
free(path2);
|
|
} else {
|
|
Fail(a, "not found: %s", path);
|
|
}
|
|
free(path);
|
|
}
|
|
|
|
static void OnType(struct As *a, struct Slice s) {
|
|
int i;
|
|
i = GetSymbol(a, a->things.p[a->i++].i);
|
|
a->symbols.p[i].type = SymbolType(a, GetSlice(a));
|
|
}
|
|
|
|
static void OnSize(struct As *a, struct Slice s) {
|
|
int i;
|
|
i = GetSymbol(a, a->things.p[a->i++].i);
|
|
a->symbols.p[i].size = GetInt(a);
|
|
}
|
|
|
|
static void OpVisibility(struct As *a, int visibility) {
|
|
int i;
|
|
while (IsSlice(a, a->i)) {
|
|
i = GetSymbol(a, a->things.p[a->i++].i);
|
|
a->symbols.p[i].stv = visibility;
|
|
}
|
|
}
|
|
|
|
static void OnInternal(struct As *a, struct Slice s) {
|
|
OpVisibility(a, STV_INTERNAL);
|
|
}
|
|
|
|
static void OnHidden(struct As *a, struct Slice s) {
|
|
OpVisibility(a, STV_HIDDEN);
|
|
}
|
|
|
|
static void OnProtected(struct As *a, struct Slice s) {
|
|
OpVisibility(a, STV_PROTECTED);
|
|
}
|
|
|
|
static void OpBind(struct As *a, int bind) {
|
|
int i;
|
|
while (IsSlice(a, a->i)) {
|
|
i = GetSymbol(a, a->things.p[a->i++].i);
|
|
a->symbols.p[i].stb = bind;
|
|
}
|
|
}
|
|
|
|
static void OnLocal(struct As *a, struct Slice s) {
|
|
OpBind(a, STB_LOCAL);
|
|
}
|
|
|
|
static void OnWeak(struct As *a, struct Slice s) {
|
|
OpBind(a, STB_WEAK);
|
|
}
|
|
|
|
static void OnGlobal(struct As *a, struct Slice s) {
|
|
OpBind(a, STB_GLOBAL);
|
|
}
|
|
|
|
static int GetOpSize(struct As *a, struct Slice s, int modrm, int i) {
|
|
if (modrm & ISREG) {
|
|
return (modrm & 070) >> 3;
|
|
} else {
|
|
switch (s.p[s.n - i]) {
|
|
case 'b':
|
|
case 'B':
|
|
return 0;
|
|
case 'w':
|
|
case 'W':
|
|
return 1;
|
|
case 'l':
|
|
case 'L':
|
|
return 2;
|
|
case 'q':
|
|
case 'Q':
|
|
return 3;
|
|
default:
|
|
Fail(a, "could not size instruction");
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool ConsumeSegment(struct As *a) {
|
|
int i;
|
|
struct Slice s;
|
|
if (IsSlice(a, a->i)) {
|
|
s = a->slices.p[a->things.p[a->i].i];
|
|
if (s.n == 3 && *s.p == '%') {
|
|
for (i = 0; i < ARRAYLEN(kSegment); ++i) {
|
|
if (s.p[1] == kSegment[i][0] && s.p[2] == kSegment[i][1]) {
|
|
++a->i;
|
|
EmitByte(a, kSegmentByte[i]);
|
|
ConsumePunct(a, ':');
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void CopyLower(char *k, const char *p, int n) {
|
|
int i;
|
|
for (i = 0; i < n; ++i) {
|
|
k[i] = tolower(p[i]);
|
|
}
|
|
}
|
|
|
|
static unsigned long MakeKey64(const char *p, int n) {
|
|
char k[8] = {0};
|
|
CopyLower(k, p, n);
|
|
return LOAD64BE(k);
|
|
}
|
|
|
|
static unsigned __int128 MakeKey128(const char *p, int n) {
|
|
char k[16] = {0};
|
|
CopyLower(k, p, n);
|
|
return LOAD128BE(k);
|
|
}
|
|
|
|
static bool Prefix(struct As *a, const char *p, int n) {
|
|
int m, l, r;
|
|
unsigned long x, y;
|
|
if (n && n <= 8) {
|
|
x = MakeKey64(p, n);
|
|
l = 0;
|
|
r = ARRAYLEN(kPrefix) - 1;
|
|
while (l <= r) {
|
|
m = (l + r) >> 1;
|
|
y = LOAD64BE(kPrefix[m]);
|
|
if (x < y) {
|
|
r = m - 1;
|
|
} else if (x > y) {
|
|
l = m + 1;
|
|
} else {
|
|
EmitByte(a, kPrefixByte[m]);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool FindReg(const char *p, int n, struct Reg *out_reg) {
|
|
int m, l, r;
|
|
unsigned long x, y;
|
|
if (n && n <= 8 && *p == '%') {
|
|
++p;
|
|
--n;
|
|
x = MakeKey64(p, n);
|
|
l = 0;
|
|
r = ARRAYLEN(kRegs) - 1;
|
|
while (l <= r) {
|
|
m = (l + r) >> 1;
|
|
y = LOAD64BE(kRegs[m].s);
|
|
if (x < y) {
|
|
r = m - 1;
|
|
} else if (x > y) {
|
|
l = m + 1;
|
|
} else {
|
|
*out_reg = kRegs[m];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int FindRegReg(struct Slice s) {
|
|
struct Reg reg;
|
|
if (!FindReg(s.p, s.n, ®)) return -1;
|
|
return reg.reg;
|
|
}
|
|
|
|
static int FindRegRm(struct Slice s) {
|
|
struct Reg reg;
|
|
if (!FindReg(s.p, s.n, ®)) return -1;
|
|
return reg.rm;
|
|
}
|
|
|
|
static int FindRegBase(struct Slice s) {
|
|
struct Reg reg;
|
|
if (!FindReg(s.p, s.n, ®)) return -1;
|
|
return reg.base;
|
|
}
|
|
|
|
static int FindRegIndex(struct Slice s) {
|
|
struct Reg reg;
|
|
if (!FindReg(s.p, s.n, ®)) return -1;
|
|
return reg.index;
|
|
}
|
|
|
|
static int RemoveRexw(int x) {
|
|
if (x == -1) return x;
|
|
x &= ~0x0800;
|
|
if (((x & 0xff00) >> 8) == REX) x &= ~0xff00;
|
|
return x;
|
|
}
|
|
|
|
static int GetRegisterReg(struct As *a) {
|
|
int reg;
|
|
struct Slice wut;
|
|
if ((reg = FindRegReg(GetSlice(a))) == -1) InvalidRegister(a);
|
|
return reg;
|
|
}
|
|
|
|
static int GetRegisterRm(struct As *a) {
|
|
int reg;
|
|
struct Slice wut;
|
|
if ((reg = FindRegRm(GetSlice(a))) == -1) InvalidRegister(a);
|
|
return reg;
|
|
}
|
|
|
|
static int ParseModrm(struct As *a, int *disp) {
|
|
/* ┌isreg
|
|
│┌isrip
|
|
││┌hasindex
|
|
│││┌hasbase
|
|
││││┌hasasz
|
|
│││││┌rex
|
|
││││││ ┌scale
|
|
││││││ │ ┌index or size
|
|
││││││ │ │ ┌base or reg
|
|
│││││├──────┐├┐├─┐├─┐
|
|
0b00000000000000000000000000000000*/
|
|
struct Slice str;
|
|
int reg, scale, modrm = 0;
|
|
if (!ConsumeSegment(a) && IsRegister(a, a->i)) {
|
|
*disp = 0;
|
|
modrm = GetRegisterRm(a) | ISREG;
|
|
} else {
|
|
if (!IsPunct(a, a->i, '(')) {
|
|
*disp = Parse(a);
|
|
} else {
|
|
*disp = -1;
|
|
}
|
|
if (IsPunct(a, a->i, '(')) {
|
|
++a->i;
|
|
if ((str = GetSlice(a)).n) {
|
|
modrm |= HASBASE;
|
|
if (!strncasecmp(str.p, "%rip", str.n)) {
|
|
modrm |= ISRIP;
|
|
} else {
|
|
reg = FindRegBase(str);
|
|
if (reg == -1) InvalidRegister(a);
|
|
modrm |= reg & 007; // reg
|
|
modrm |= reg & 0xff00; // rex
|
|
if (((reg & 070) >> 3) == 2) modrm |= HASASZ; // asz
|
|
}
|
|
}
|
|
if (!IsPunct(a, a->i, ')')) {
|
|
modrm |= HASINDEX;
|
|
reg = FindRegIndex(GetSlice(a));
|
|
if (reg == -1) InvalidRegister(a);
|
|
modrm |= (reg & 007) << 3; // index
|
|
modrm |= reg & 0xff00; // rex
|
|
if (((reg & 070) >> 3) == 2) modrm |= HASASZ; // asz
|
|
if (!IsPunct(a, a->i, ')')) {
|
|
modrm |= (BSR(GetInt(a)) & 3) << 6;
|
|
}
|
|
} else {
|
|
modrm |= 4 << 3; // puttin' on the riz
|
|
}
|
|
ConsumePunct(a, ')');
|
|
}
|
|
if (modrm & HASASZ) {
|
|
EmitByte(a, ASZ);
|
|
}
|
|
}
|
|
return modrm;
|
|
}
|
|
|
|
static void EmitImm(struct As *a, int reg, int imm) {
|
|
switch ((reg & 030) >> 3) {
|
|
case 0:
|
|
EmitExpr(a, imm, R_X86_64_8, EmitByte);
|
|
break;
|
|
case 1:
|
|
EmitExpr(a, imm, R_X86_64_16, EmitWord);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
EmitExpr(a, imm, R_X86_64_32S, EmitLong);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void EmitModrm(struct As *a, int reg, int modrm, int disp) {
|
|
reg &= 7;
|
|
reg <<= 3;
|
|
if (modrm & ISREG) {
|
|
EmitByte(a, 0300 | reg | modrm & 7);
|
|
} else {
|
|
if (modrm & ISRIP) {
|
|
EmitByte(a, 005 | reg);
|
|
} else if (modrm & (HASBASE | HASINDEX)) {
|
|
EmitByte(a, 0204 | reg); // suboptimal
|
|
EmitByte(a, modrm);
|
|
} else {
|
|
EmitByte(a, 004 | reg);
|
|
EmitByte(a, 045);
|
|
}
|
|
EmitExpr(
|
|
a, disp,
|
|
a->pcrelative && ((modrm & ISRIP) || !(modrm & (HASBASE | HASINDEX)))
|
|
? a->pcrelative
|
|
: R_X86_64_32S,
|
|
EmitLong);
|
|
}
|
|
}
|
|
|
|
static void EmitRex(struct As *a, int x) {
|
|
if (x & 0xff00) {
|
|
EmitByte(a, x >> 8);
|
|
}
|
|
}
|
|
|
|
static void EmitOpModrm(struct As *a, long op, int reg, int modrm, int disp,
|
|
int skew) {
|
|
switch ((reg & 070) >> 3) {
|
|
case 0:
|
|
EmitVarword(a, op);
|
|
EmitModrm(a, reg, modrm, disp);
|
|
break;
|
|
case 1:
|
|
EmitByte(a, OSZ);
|
|
EmitVarword(a, op + skew);
|
|
EmitModrm(a, reg, modrm, disp);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
EmitVarword(a, op + skew);
|
|
EmitModrm(a, reg, modrm, disp);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void EmitRexOpModrm(struct As *a, long op, int reg, int modrm, int disp,
|
|
int skew) {
|
|
EmitRex(a, reg | modrm);
|
|
EmitOpModrm(a, op, reg, modrm, disp, skew);
|
|
}
|
|
|
|
static void OnLea(struct As *a, struct Slice s) {
|
|
int modrm, reg, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, 0x8D, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static void OnMov(struct As *a, struct Slice s) {
|
|
int reg, modrm, disp, imm;
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
imm = Parse(a);
|
|
if (IsSlice(a, a->i)) { // imm -> reg
|
|
reg = GetRegisterRm(a);
|
|
switch ((reg & 070) >> 3) {
|
|
case 0:
|
|
EmitRex(a, reg);
|
|
EmitByte(a, 0xb0 + (reg & 7));
|
|
EmitExpr(a, imm, R_X86_64_8, EmitByte);
|
|
break;
|
|
case 1:
|
|
EmitRex(a, reg);
|
|
EmitByte(a, OSZ);
|
|
EmitByte(a, 0xb8 + (reg & 7));
|
|
EmitExpr(a, imm, R_X86_64_16, EmitWord);
|
|
break;
|
|
case 2:
|
|
EmitRex(a, reg);
|
|
EmitByte(a, 0xb8 + (reg & 7));
|
|
EmitExpr(a, imm, R_X86_64_32, EmitLong);
|
|
break;
|
|
case 3:
|
|
EmitRex(a, reg);
|
|
EmitByte(a, 0xb8 + (reg & 7)); // suboptimal
|
|
EmitExpr(a, imm, R_X86_64_64, EmitQuad);
|
|
break;
|
|
default:
|
|
Fail(a, "todo movd/movq");
|
|
}
|
|
} else { // imm -> modrm
|
|
modrm = ParseModrm(a, &disp);
|
|
switch (GetOpSize(a, s, modrm, 1)) {
|
|
case 0:
|
|
EmitRex(a, modrm);
|
|
EmitByte(a, 0xc6);
|
|
EmitModrm(a, 0, modrm, disp);
|
|
EmitExpr(a, imm, R_X86_64_8, EmitByte);
|
|
break;
|
|
case 1:
|
|
EmitByte(a, OSZ);
|
|
EmitRex(a, modrm);
|
|
EmitByte(a, 0xc7);
|
|
EmitModrm(a, 0, modrm, disp);
|
|
EmitExpr(a, imm, R_X86_64_16, EmitWord);
|
|
break;
|
|
case 2:
|
|
EmitRex(a, modrm);
|
|
EmitByte(a, 0xc7);
|
|
EmitModrm(a, 0, modrm, disp);
|
|
EmitExpr(a, imm, R_X86_64_32, EmitLong);
|
|
break;
|
|
case 3:
|
|
EmitRex(a, modrm | REXW << 8);
|
|
EmitByte(a, 0xc7); // suboptimal
|
|
EmitModrm(a, 0, modrm, disp);
|
|
EmitExpr(a, imm, R_X86_64_32, EmitLong);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
} else if (IsSlice(a, a->i)) { // reg -> reg/modrm
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x88, reg, modrm, disp, 1);
|
|
} else { // modrm -> reg
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, 0x8A, reg, modrm, disp, 1);
|
|
}
|
|
}
|
|
|
|
static void EmitMovx(struct As *a, struct Slice opname, int op) {
|
|
int reg, modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRex(a, reg);
|
|
if (((reg & 070) >> 3) == 1) EmitByte(a, OSZ);
|
|
EmitVarword(a, op + !!GetOpSize(a, opname, modrm, 2));
|
|
EmitModrm(a, reg, modrm, disp);
|
|
}
|
|
|
|
static void OnMovzbwx(struct As *a, struct Slice s) {
|
|
EmitMovx(a, s, 0x0FB6);
|
|
}
|
|
|
|
static void OnMovsbwx(struct As *a, struct Slice s) {
|
|
EmitMovx(a, s, 0x0FBE);
|
|
}
|
|
|
|
static void OnMovslq(struct As *a, struct Slice s) {
|
|
int reg, modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitByte(a, REXW);
|
|
EmitByte(a, 0x63);
|
|
EmitModrm(a, reg, modrm, disp);
|
|
}
|
|
|
|
static noinline void OpAluImpl(struct As *a, struct Slice opname, int op) {
|
|
int reg, modrm, imm, disp;
|
|
if (IsPunct(a, a->i, '$')) { // imm -> reg/modrm
|
|
++a->i;
|
|
imm = Parse(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetOpSize(a, opname, modrm, 1) << 3 | op;
|
|
EmitRexOpModrm(a, 0x80, reg, modrm, disp, 1); // suboptimal
|
|
EmitImm(a, reg, imm);
|
|
} else if (IsSlice(a, a->i)) { // reg -> reg/modrm
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, op << 3, reg, modrm, disp, 1);
|
|
} else { // modrm -> reg
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, op << 3 | 2, reg, modrm, disp, 1);
|
|
}
|
|
}
|
|
|
|
static noinline void OpAlu(struct As *a, struct Slice opname, int op) {
|
|
OpAluImpl(a, opname, op);
|
|
}
|
|
|
|
static noinline void OpBsuImpl(struct As *a, struct Slice opname, int op) {
|
|
int reg, modrm, imm, disp;
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
imm = Parse(a);
|
|
} else if (IsSlice(a, a->i) &&
|
|
(a->slices.p[a->things.p[a->i].i].n == 3 &&
|
|
!strncasecmp(a->slices.p[a->things.p[a->i].i].p, "%cl", 3)) &&
|
|
!IsPunct(a, a->i + 1, ';')) {
|
|
++a->i;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetOpSize(a, opname, modrm, 1) << 3 | op;
|
|
EmitRexOpModrm(a, 0xC0, reg, modrm, disp, 1);
|
|
return;
|
|
} else {
|
|
AppendExpr(a);
|
|
a->exprs.p[a->exprs.n - 1].kind = EX_INT;
|
|
a->exprs.p[a->exprs.n - 1].tok = a->i;
|
|
a->exprs.p[a->exprs.n - 1].x = 1;
|
|
imm = a->exprs.n - 1;
|
|
}
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetOpSize(a, opname, modrm, 1) << 3 | op;
|
|
EmitRexOpModrm(a, 0xC0, reg, modrm, disp, 1); // suboptimal
|
|
EmitByte(a, imm);
|
|
}
|
|
|
|
static noinline void OpBsu(struct As *a, struct Slice opname, int op) {
|
|
OpBsuImpl(a, opname, op);
|
|
}
|
|
|
|
static noinline int OpF6Impl(struct As *a, struct Slice s, int reg) {
|
|
int modrm, imm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg |= GetOpSize(a, s, modrm, 1) << 3;
|
|
EmitRexOpModrm(a, 0xF6, reg, modrm, disp, 1);
|
|
return reg;
|
|
}
|
|
|
|
static noinline int OpF6(struct As *a, struct Slice s, int reg) {
|
|
return OpF6Impl(a, s, reg);
|
|
}
|
|
|
|
static void OnTest(struct As *a, struct Slice s) {
|
|
int reg, modrm, imm, disp;
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
imm = Parse(a);
|
|
reg = OpF6(a, s, 0); // suboptimal
|
|
EmitImm(a, reg, imm);
|
|
} else {
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x84, reg, modrm, disp, 1);
|
|
}
|
|
}
|
|
|
|
static void OnImul(struct As *a, struct Slice s) {
|
|
int reg, modrm, imm, disp;
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
imm = Parse(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, 0x69, reg, modrm, disp, 0);
|
|
EmitImm(a, reg, imm);
|
|
} else {
|
|
modrm = ParseModrm(a, &disp);
|
|
if (IsPunct(a, a->i, ';')) {
|
|
reg = GetOpSize(a, s, modrm, 1) << 3 | 5;
|
|
EmitRexOpModrm(a, 0xF6, reg, modrm, disp, 1);
|
|
} else {
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, 0x0FAF, reg, modrm, disp, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OpBit(struct As *a, int op) {
|
|
int reg, modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, op, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static void OnXchg(struct As *a, struct Slice s) {
|
|
int reg, modrm, disp;
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x86, reg, modrm, disp, 1);
|
|
}
|
|
|
|
static void OpBump(struct As *a, struct Slice s, int reg) {
|
|
int modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0xFE, GetOpSize(a, s, modrm, 1) << 3 | reg, modrm, disp, 1);
|
|
}
|
|
|
|
static void OpShrld(struct As *a, struct Slice s, int op) {
|
|
int imm, reg, modrm, disp, skew;
|
|
if (IsSlice(a, a->i) &&
|
|
(a->slices.p[a->things.p[a->i].i].n == 3 &&
|
|
!strncasecmp(a->slices.p[a->things.p[a->i].i].p, "%cl", 3))) {
|
|
skew = 1;
|
|
++a->i;
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x0F00 | op | skew, reg, modrm, disp, 0);
|
|
} else {
|
|
skew = 0;
|
|
ConsumePunct(a, '$');
|
|
imm = GetInt(a);
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x0F00 | op | skew, reg, modrm, disp, 0);
|
|
EmitByte(a, imm);
|
|
}
|
|
}
|
|
|
|
static void OnShrd(struct As *a, struct Slice s) {
|
|
OpShrld(a, s, 0xAC);
|
|
}
|
|
|
|
static void OnShld(struct As *a, struct Slice s) {
|
|
OpShrld(a, s, 0xA4);
|
|
}
|
|
|
|
static void OpSseMov(struct As *a, int opWsdVsd, int opVsdWsd) {
|
|
int reg, modrm, disp;
|
|
if (IsRegister(a, a->i)) {
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, opWsdVsd, reg, modrm, disp, 0);
|
|
} else {
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, opVsdWsd, reg, modrm, disp, 0);
|
|
}
|
|
}
|
|
|
|
static void OpMovdqx(struct As *a, int op) {
|
|
OpSseMov(a, op + 0x10, op);
|
|
}
|
|
|
|
static void OnMovdqu(struct As *a, struct Slice s) {
|
|
EmitByte(a, 0xF3);
|
|
OpMovdqx(a, 0x0F6F);
|
|
}
|
|
|
|
static void OnMovups(struct As *a, struct Slice s) {
|
|
OpSseMov(a, 0x0F11, 0x0F10);
|
|
}
|
|
|
|
static void OnMovupd(struct As *a, struct Slice s) {
|
|
EmitByte(a, 0x66);
|
|
OnMovups(a, s);
|
|
}
|
|
|
|
static void OnMovdqa(struct As *a, struct Slice s) {
|
|
EmitByte(a, 0x66);
|
|
OpMovdqx(a, 0x0F6F);
|
|
}
|
|
|
|
static void OnMovaps(struct As *a, struct Slice s) {
|
|
OpSseMov(a, 0x0F29, 0x0F28);
|
|
}
|
|
|
|
static void OnMovapd(struct As *a, struct Slice s) {
|
|
EmitByte(a, 0x66);
|
|
OpSseMov(a, 0x0F29, 0x0F28);
|
|
}
|
|
|
|
static void OnMovdq(struct As *a, struct Slice s) {
|
|
int reg, modrm, boop, disp;
|
|
EmitByte(a, 0x66); // todo mmx
|
|
if (IsRegister(a, a->i)) {
|
|
reg = GetRegisterReg(a);
|
|
modrm = ParseModrm(a, &disp);
|
|
boop = ((reg & 070) >> 3) == 4 ? 0x10 : 0;
|
|
} else {
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
boop = ((reg & 070) >> 3) == 4 ? 0x10 : 0;
|
|
}
|
|
EmitRexOpModrm(a, 0x0F6E + boop, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static void OnMovss(struct As *a, struct Slice s) {
|
|
OpSseMov(a, 0xF30F11, 0xF30F10);
|
|
}
|
|
|
|
static void OnMovsd(struct As *a, struct Slice s) {
|
|
OpSseMov(a, 0xF20F11, 0xF20F10);
|
|
}
|
|
|
|
static bool IsSsePrefix(int c) {
|
|
return c == 0x66 || c == 0xF2 || c == 0xF3; // must come before rex
|
|
}
|
|
|
|
static noinline void OpSseImpl(struct As *a, int op) {
|
|
int reg, modrm, disp;
|
|
if (IsSsePrefix((op & 0xff000000) >> 24)) {
|
|
EmitByte(a, (op & 0xff000000) >> 24);
|
|
op &= 0xffffff;
|
|
}
|
|
if (IsSsePrefix((op & 0x00ff0000) >> 16)) {
|
|
EmitByte(a, (op & 0x00ff0000) >> 16);
|
|
op &= 0xffff;
|
|
}
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, op, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static noinline void OpSse(struct As *a, int op) {
|
|
OpSseImpl(a, op);
|
|
}
|
|
|
|
static noinline void OpSseIbImpl(struct As *a, int op) {
|
|
int imm;
|
|
ConsumePunct(a, '$');
|
|
imm = Parse(a);
|
|
OpSse(a, op);
|
|
EmitExpr(a, imm, R_X86_64_8, EmitByte);
|
|
}
|
|
|
|
static noinline void OpSseIb(struct As *a, int op) {
|
|
OpSseIbImpl(a, op);
|
|
}
|
|
|
|
static bool HasXmmOnLine(struct As *a) {
|
|
int i;
|
|
for (i = 0; !IsPunct(a, a->i + i, ';'); ++i) {
|
|
if (IsSlice(a, a->i + i) && a->slices.p[a->things.p[a->i + i].i].n >= 4 &&
|
|
(startswith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") ||
|
|
startswith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void OnMovq(struct As *a, struct Slice s) {
|
|
if (HasXmmOnLine(a)) {
|
|
OnMovdq(a, s);
|
|
} else {
|
|
OnMov(a, s);
|
|
}
|
|
}
|
|
|
|
static void OnPush(struct As *a, struct Slice s) {
|
|
int modrm, disp;
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
EmitByte(a, 0x68);
|
|
EmitLong(a, GetInt(a));
|
|
} else {
|
|
modrm = RemoveRexw(ParseModrm(a, &disp));
|
|
EmitRexOpModrm(a, 0xFF, 6, modrm, disp, 0); // suboptimal
|
|
}
|
|
}
|
|
|
|
static void OnPop(struct As *a, struct Slice s) {
|
|
int modrm, disp;
|
|
modrm = RemoveRexw(ParseModrm(a, &disp));
|
|
EmitRexOpModrm(a, 0x8F, 0, modrm, disp, 0); // suboptimal
|
|
}
|
|
|
|
static void OnNop(struct As *a, struct Slice opname) {
|
|
int modrm, disp;
|
|
if (IsPunct(a, a->i, ';')) {
|
|
EmitByte(a, 0x90);
|
|
} else {
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x0F1F, 6, modrm, disp, 0);
|
|
}
|
|
}
|
|
|
|
static void OnRet(struct As *a, struct Slice s) {
|
|
if (IsPunct(a, a->i, '$')) {
|
|
++a->i;
|
|
EmitByte(a, 0xC2);
|
|
EmitWord(a, GetInt(a));
|
|
} else {
|
|
EmitByte(a, 0xC3);
|
|
}
|
|
}
|
|
|
|
static noinline void OpCmovccImpl(struct As *a, int cc) {
|
|
int reg, modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
reg = GetRegisterReg(a);
|
|
EmitRexOpModrm(a, 0x0F40 | cc, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static noinline void OpCmovcc(struct As *a, int cc) {
|
|
OpCmovccImpl(a, cc);
|
|
}
|
|
|
|
static noinline void OpSetccImpl(struct As *a, int cc) {
|
|
int modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, 0x0F90 | cc, 6, modrm, disp, 0);
|
|
}
|
|
|
|
static noinline void OpSetcc(struct As *a, int cc) {
|
|
OpSetccImpl(a, cc);
|
|
}
|
|
|
|
static void OnFile(struct As *a, struct Slice s) {
|
|
int fileno;
|
|
struct Slice path;
|
|
fileno = GetInt(a);
|
|
path = GetSlice(a);
|
|
// TODO: DWARF
|
|
}
|
|
|
|
static void OnLoc(struct As *a, struct Slice s) {
|
|
int fileno, lineno;
|
|
fileno = GetInt(a);
|
|
lineno = GetInt(a);
|
|
// TODO: DWARF
|
|
}
|
|
|
|
static void OnCall(struct As *a, struct Slice s) {
|
|
int modrm, disp;
|
|
if (IsPunct(a, a->i, '*')) ++a->i;
|
|
modrm = RemoveRexw(ParseModrm(a, &disp));
|
|
if (modrm & (ISREG | ISRIP | HASINDEX | HASBASE)) {
|
|
if (modrm & ISRIP) a->pcrelative = R_X86_64_GOTPCRELX;
|
|
EmitRexOpModrm(a, 0xFF, 2, modrm, disp, 0);
|
|
a->pcrelative = 0;
|
|
} else {
|
|
EmitByte(a, 0xE8);
|
|
EmitExpr(a, disp, R_X86_64_PC32, EmitLong);
|
|
}
|
|
}
|
|
|
|
static noinline void OpJmpImpl(struct As *a, int cc) {
|
|
int modrm, disp;
|
|
if (IsPunct(a, a->i, '*')) ++a->i;
|
|
modrm = RemoveRexw(ParseModrm(a, &disp));
|
|
if (cc == -1) {
|
|
if ((modrm & ISRIP) || !(modrm & (HASBASE | HASINDEX))) {
|
|
modrm |= ISRIP;
|
|
a->pcrelative = R_X86_64_GOTPCRELX;
|
|
}
|
|
EmitRexOpModrm(a, 0xFF, 4, modrm, disp, 0);
|
|
a->pcrelative = 0;
|
|
} else {
|
|
EmitByte(a, 0x0F);
|
|
EmitByte(a, 0x80 + cc);
|
|
EmitExpr(a, disp, R_X86_64_PC32, EmitLong);
|
|
}
|
|
}
|
|
|
|
static noinline void OpJmp(struct As *a, int cc) {
|
|
OpJmpImpl(a, cc);
|
|
}
|
|
|
|
static noinline void OpFpu1Impl(struct As *a, int op, int reg) {
|
|
int modrm, disp;
|
|
modrm = ParseModrm(a, &disp);
|
|
EmitRexOpModrm(a, op, reg, modrm, disp, 0);
|
|
}
|
|
|
|
static noinline void OpFpu1(struct As *a, int op, int reg) {
|
|
OpFpu1Impl(a, op, reg);
|
|
}
|
|
|
|
static void OnFxch(struct As *a, struct Slice s) {
|
|
int rm;
|
|
rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1;
|
|
EmitByte(a, 0xD9);
|
|
EmitByte(a, 0310 | rm & 7);
|
|
}
|
|
|
|
static void OnBswap(struct As *a, struct Slice s) {
|
|
int srm;
|
|
srm = GetRegisterRm(a);
|
|
EmitRex(a, srm);
|
|
EmitByte(a, 0x0F);
|
|
EmitByte(a, 0310 | srm & 7);
|
|
}
|
|
|
|
static noinline void OpFcomImpl(struct As *a, int op) {
|
|
int reg, rm;
|
|
rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1;
|
|
reg = !IsPunct(a, a->i, ';') ? GetRegisterReg(a) : 0;
|
|
if (reg & 7) Fail(a, "bad register");
|
|
EmitVarword(a, op | rm & 7);
|
|
}
|
|
|
|
static noinline void OpFcom(struct As *a, int op) {
|
|
OpFcomImpl(a, op);
|
|
}
|
|
|
|
// clang-format off
|
|
static void OnAdc(struct As *a, struct Slice s) { OpAlu(a, s, 2); }
|
|
static void OnAdd(struct As *a, struct Slice s) { OpAlu(a, s, 0); }
|
|
static void OnAddpd(struct As *a, struct Slice s) { OpSse(a, 0x660F58); }
|
|
static void OnAddps(struct As *a, struct Slice s) { OpSse(a, 0x0F58); }
|
|
static void OnAddsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F58); }
|
|
static void OnAddss(struct As *a, struct Slice s) { OpSse(a, 0xF30F58); }
|
|
static void OnAddsubpd(struct As *a, struct Slice s) { OpSse(a, 0x660FD0); }
|
|
static void OnAddsubps(struct As *a, struct Slice s) { OpSse(a, 0xF20FD0); }
|
|
static void OnAnd(struct As *a, struct Slice s) { OpAlu(a, s, 4); }
|
|
static void OnAndnpd(struct As *a, struct Slice s) { OpSse(a, 0x660F55); }
|
|
static void OnAndnps(struct As *a, struct Slice s) { OpSse(a, 0x0F55); }
|
|
static void OnAndpd(struct As *a, struct Slice s) { OpSse(a, 0x660F54); }
|
|
static void OnAndps(struct As *a, struct Slice s) { OpSse(a, 0x0F54); }
|
|
static void OnBlendpd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0D); }
|
|
static void OnBlendvpd(struct As *a, struct Slice s) { OpSse(a, 0x660F3815); }
|
|
static void OnBsf(struct As *a, struct Slice s) { OpBit(a, 0x0FBC); }
|
|
static void OnBsr(struct As *a, struct Slice s) { OpBit(a, 0x0FBD); }
|
|
static void OnCbtw(struct As *a, struct Slice s) { EmitVarword(a, 0x6698); }
|
|
static void OnClc(struct As *a, struct Slice s) { EmitByte(a, 0xF8); }
|
|
static void OnCld(struct As *a, struct Slice s) { EmitByte(a, 0xFC); }
|
|
static void OnCli(struct As *a, struct Slice s) { EmitByte(a, 0xFA); }
|
|
static void OnCltd(struct As *a, struct Slice s) { EmitByte(a, 0x99); }
|
|
static void OnCltq(struct As *a, struct Slice s) { EmitVarword(a, 0x4898); }
|
|
static void OnCmc(struct As *a, struct Slice s) { EmitByte(a, 0xF5); }
|
|
static void OnCmovb(struct As *a, struct Slice s) { OpCmovcc(a, 2); }
|
|
static void OnCmovbe(struct As *a, struct Slice s) { OpCmovcc(a, 6); }
|
|
static void OnCmovl(struct As *a, struct Slice s) { OpCmovcc(a, 12); }
|
|
static void OnCmovle(struct As *a, struct Slice s) { OpCmovcc(a, 14); }
|
|
static void OnCmovnb(struct As *a, struct Slice s) { OpCmovcc(a, 3); }
|
|
static void OnCmovnbe(struct As *a, struct Slice s) { OpCmovcc(a, 7); }
|
|
static void OnCmovnl(struct As *a, struct Slice s) { OpCmovcc(a, 13); }
|
|
static void OnCmovnle(struct As *a, struct Slice s) { OpCmovcc(a, 15); }
|
|
static void OnCmovno(struct As *a, struct Slice s) { OpCmovcc(a, 1); }
|
|
static void OnCmovnp(struct As *a, struct Slice s) { OpCmovcc(a, 11); }
|
|
static void OnCmovns(struct As *a, struct Slice s) { OpCmovcc(a, 9); }
|
|
static void OnCmovnz(struct As *a, struct Slice s) { OpCmovcc(a, 5); }
|
|
static void OnCmovo(struct As *a, struct Slice s) { OpCmovcc(a, 0); }
|
|
static void OnCmovp(struct As *a, struct Slice s) { OpCmovcc(a, 10); }
|
|
static void OnCmovs(struct As *a, struct Slice s) { OpCmovcc(a, 8); }
|
|
static void OnCmovz(struct As *a, struct Slice s) { OpCmovcc(a, 4); }
|
|
static void OnCmp(struct As *a, struct Slice s) { OpAlu(a, s, 7); }
|
|
static void OnCmppd(struct As *a, struct Slice s) { OpSseIb(a, 0x660FC2); }
|
|
static void OnCmpps(struct As *a, struct Slice s) { OpSseIb(a, 0x0FC2); }
|
|
static void OnCmpsd(struct As *a, struct Slice s) { OpSseIb(a, 0xF20FC2); }
|
|
static void OnCmpss(struct As *a, struct Slice s) { OpSseIb(a, 0xF30FC2); }
|
|
static void OnComisd(struct As *a, struct Slice s) { OpSse(a, 0x660F2F); }
|
|
static void OnComiss(struct As *a, struct Slice s) { OpSse(a, 0x0F2F); }
|
|
static void OnCqto(struct As *a, struct Slice s) { EmitVarword(a, 0x4899); }
|
|
static void OnCvtdq2pd(struct As *a, struct Slice s) { OpSse(a, 0xF30FE6); }
|
|
static void OnCvtdq2ps(struct As *a, struct Slice s) { OpSse(a, 0xF5B); }
|
|
static void OnCvtpd2dq(struct As *a, struct Slice s) { OpSse(a, 0xF20FE6); }
|
|
static void OnCvtpd2ps(struct As *a, struct Slice s) { OpSse(a, 0x660F5A); }
|
|
static void OnCvtps2dq(struct As *a, struct Slice s) { OpSse(a, 0x660F5B); }
|
|
static void OnCvtps2pd(struct As *a, struct Slice s) { OpSse(a, 0x0F5A); }
|
|
static void OnCvtsd2ss(struct As *a, struct Slice s) { OpSse(a, 0xF20F5A); }
|
|
static void OnCvtsi2sd(struct As *a, struct Slice s) { OpSse(a, 0xF20F2A); }
|
|
static void OnCvtsi2ss(struct As *a, struct Slice s) { OpSse(a, 0xF30F2A); }
|
|
static void OnCvtss2sd(struct As *a, struct Slice s) { OpSse(a, 0xF30F5A); }
|
|
static void OnCvttpd2dq(struct As *a, struct Slice s) { OpSse(a, 0x660FE6); }
|
|
static void OnCvttps2dq(struct As *a, struct Slice s) { OpSse(a, 0xF30F5B); }
|
|
static void OnCvttsd2si(struct As *a, struct Slice s) { OpSse(a, 0xF20F2C); }
|
|
static void OnCvttss2si(struct As *a, struct Slice s) { OpSse(a, 0xF30F2C); }
|
|
static void OnCwtd(struct As *a, struct Slice s) { EmitVarword(a, 0x6699); }
|
|
static void OnCwtl(struct As *a, struct Slice s) { EmitByte(a, 0x98); }
|
|
static void OnDec(struct As *a, struct Slice s) { OpBump(a, s, 1); }
|
|
static void OnDiv(struct As *a, struct Slice s) { OpF6(a, s, 6); }
|
|
static void OnDivpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5E); }
|
|
static void OnDivps(struct As *a, struct Slice s) { OpSse(a, 0x0F5E); }
|
|
static void OnDivsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5E); }
|
|
static void OnDivss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5E); }
|
|
static void OnDppd(struct As *a, struct Slice s) { OpSse(a, 0x660F3A41); }
|
|
static void OnFabs(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E1); }
|
|
static void OnFaddl(struct As *a, struct Slice s) { OpFpu1(a, 0xDC, 0); }
|
|
static void OnFaddp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEC1); }
|
|
static void OnFadds(struct As *a, struct Slice s) { OpFpu1(a, 0xD8, 0); }
|
|
static void OnFchs(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E0); }
|
|
static void OnFcmovb(struct As *a, struct Slice s) { OpFcom(a, 0xDAC0); }
|
|
static void OnFcmovbe(struct As *a, struct Slice s) { OpFcom(a, 0xDAD0); }
|
|
static void OnFcmove(struct As *a, struct Slice s) { OpFcom(a, 0xDAC8); }
|
|
static void OnFcmovnb(struct As *a, struct Slice s) { OpFcom(a, 0xDBC0); }
|
|
static void OnFcmovnbe(struct As *a, struct Slice s) { OpFcom(a, 0xDBD0); }
|
|
static void OnFcmovne(struct As *a, struct Slice s) { OpFcom(a, 0xDBC8); }
|
|
static void OnFcmovnu(struct As *a, struct Slice s) { OpFcom(a, 0xDBD8); }
|
|
static void OnFcmovu(struct As *a, struct Slice s) { OpFcom(a, 0xDAD8); }
|
|
static void OnFcomi(struct As *a, struct Slice s) { OpFcom(a, 0xDBF0); }
|
|
static void OnFcomip(struct As *a, struct Slice s) { OpFcom(a, 0xDFF0); }
|
|
static void OnFdivrp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEF9); }
|
|
static void OnFildl(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 0); }
|
|
static void OnFildll(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); }
|
|
static void OnFildq(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); }
|
|
static void OnFilds(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 0); }
|
|
static void OnFisttpq(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 1); }
|
|
static void OnFisttps(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 1); }
|
|
static void OnFld(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 0); }
|
|
static void OnFld1(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e8); }
|
|
static void OnFldcw(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 5); }
|
|
static void OnFldl(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 0); }
|
|
static void OnFldl2e(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ea); }
|
|
static void OnFldl2t(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e9); }
|
|
static void OnFldlg2(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ec); }
|
|
static void OnFldln2(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ed); }
|
|
static void OnFldpi(struct As *a, struct Slice s) { EmitVarword(a, 0xd9eb); }
|
|
static void OnFlds(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 0); }
|
|
static void OnFldt(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 0); }
|
|
static void OnFldz(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ee); }
|
|
static void OnFmulp(struct As *a, struct Slice s) { EmitVarword(a, 0xdec9); }
|
|
static void OnFnstcw(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 7); }
|
|
static void OnFnstsw(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 4); }
|
|
static void OnFstp(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 3); }
|
|
static void OnFstpl(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 3); }
|
|
static void OnFstps(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 3); }
|
|
static void OnFstpt(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 7); }
|
|
static void OnFsubrp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEE9); }
|
|
static void OnFtst(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E4); }
|
|
static void OnFucomi(struct As *a, struct Slice s) { OpFcom(a, 0xDBE8); }
|
|
static void OnFucomip(struct As *a, struct Slice s) { OpFcom(a, 0xDFE8); }
|
|
static void OnFwait(struct As *a, struct Slice s) { EmitByte(a, 0x9B); }
|
|
static void OnFxam(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E5); }
|
|
static void OnFxtract(struct As *a, struct Slice s) { EmitVarword(a, 0xD9F4); }
|
|
static void OnHaddpd(struct As *a, struct Slice s) { OpSse(a, 0x660F7C); }
|
|
static void OnHaddps(struct As *a, struct Slice s) { OpSse(a, 0xF20F7C); }
|
|
static void OnHlt(struct As *a, struct Slice s) { EmitByte(a, 0xF4); }
|
|
static void OnHsubpd(struct As *a, struct Slice s) { OpSse(a, 0x660F7D); }
|
|
static void OnHsubps(struct As *a, struct Slice s) { OpSse(a, 0xF20F7D); }
|
|
static void OnIdiv(struct As *a, struct Slice s) { OpF6(a, s, 7); }
|
|
static void OnInc(struct As *a, struct Slice s) { OpBump(a, s, 0); }
|
|
static void OnInt1(struct As *a, struct Slice s) { EmitByte(a, 0xF1); }
|
|
static void OnInt3(struct As *a, struct Slice s) { EmitByte(a, 0xCC); }
|
|
static void OnJb(struct As *a, struct Slice s) { OpJmp(a, 2); }
|
|
static void OnJbe(struct As *a, struct Slice s) { OpJmp(a, 6); }
|
|
static void OnJl(struct As *a, struct Slice s) { OpJmp(a, 12); }
|
|
static void OnJle(struct As *a, struct Slice s) { OpJmp(a, 14); }
|
|
static void OnJmp(struct As *a, struct Slice s) { OpJmp(a, -1); }
|
|
static void OnJnb(struct As *a, struct Slice s) { OpJmp(a, 3); }
|
|
static void OnJnbe(struct As *a, struct Slice s) { OpJmp(a, 7); }
|
|
static void OnJnl(struct As *a, struct Slice s) { OpJmp(a, 13); }
|
|
static void OnJnle(struct As *a, struct Slice s) { OpJmp(a, 15); }
|
|
static void OnJno(struct As *a, struct Slice s) { OpJmp(a, 1); }
|
|
static void OnJnp(struct As *a, struct Slice s) { OpJmp(a, 11); }
|
|
static void OnJns(struct As *a, struct Slice s) { OpJmp(a, 9); }
|
|
static void OnJnz(struct As *a, struct Slice s) { OpJmp(a, 5); }
|
|
static void OnJo(struct As *a, struct Slice s) { OpJmp(a, 0); }
|
|
static void OnJp(struct As *a, struct Slice s) { OpJmp(a, 10); }
|
|
static void OnJs(struct As *a, struct Slice s) { OpJmp(a, 8); }
|
|
static void OnJz(struct As *a, struct Slice s) { OpJmp(a, 4); }
|
|
static void OnLeave(struct As *a, struct Slice s) { EmitByte(a, 0xC9); }
|
|
static void OnLodsb(struct As *a, struct Slice s) { EmitByte(a, 0xAC); }
|
|
static void OnLodsl(struct As *a, struct Slice s) { EmitByte(a, 0xAD); }
|
|
static void OnLodsq(struct As *a, struct Slice s) { EmitVarword(a, 0x48AD); }
|
|
static void OnLodsw(struct As *a, struct Slice s) { EmitVarword(a, 0x66AD); }
|
|
static void OnMaxpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5F); }
|
|
static void OnMaxps(struct As *a, struct Slice s) { OpSse(a, 0x0F5F); }
|
|
static void OnMaxsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5F); }
|
|
static void OnMaxss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5F); }
|
|
static void OnMinpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5D); }
|
|
static void OnMinps(struct As *a, struct Slice s) { OpSse(a, 0x0F5D); }
|
|
static void OnMinsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5D); }
|
|
static void OnMinss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5D); }
|
|
static void OnMovmskpd(struct As *a, struct Slice s) { OpSse(a, 0x660F50); }
|
|
static void OnMovmskps(struct As *a, struct Slice s) { OpSse(a, 0x0F50); }
|
|
static void OnMovsb(struct As *a, struct Slice s) { EmitByte(a, 0xA4); }
|
|
static void OnMovsl(struct As *a, struct Slice s) { EmitByte(a, 0xA5); }
|
|
static void OnMovsq(struct As *a, struct Slice s) { EmitVarword(a, 0x48A5); }
|
|
static void OnMovsw(struct As *a, struct Slice s) { EmitVarword(a, 0x66A5); }
|
|
static void OnMpsadbw(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A42); }
|
|
static void OnMul(struct As *a, struct Slice s) { OpF6(a, s, 4); }
|
|
static void OnMulpd(struct As *a, struct Slice s) { OpSse(a, 0x660F59); }
|
|
static void OnMulps(struct As *a, struct Slice s) { OpSse(a, 0x0F59); }
|
|
static void OnMulsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F59); }
|
|
static void OnMulss(struct As *a, struct Slice s) { OpSse(a, 0xF30F59); }
|
|
static void OnNeg(struct As *a, struct Slice s) { OpF6(a, s, 3); }
|
|
static void OnNot(struct As *a, struct Slice s) { OpF6(a, s, 2); }
|
|
static void OnOr(struct As *a, struct Slice s) { OpAlu(a, s, 1); }
|
|
static void OnOrpd(struct As *a, struct Slice s) { OpSse(a, 0x660F56); }
|
|
static void OnOrps(struct As *a, struct Slice s) { OpSse(a, 0x0F56); }
|
|
static void OnPabsb(struct As *a, struct Slice s) { OpSse(a, 0x660F381C); }
|
|
static void OnPabsd(struct As *a, struct Slice s) { OpSse(a, 0x660F381E); }
|
|
static void OnPabsw(struct As *a, struct Slice s) { OpSse(a, 0x660F381D); }
|
|
static void OnPackssdw(struct As *a, struct Slice s) { OpSse(a, 0x660F6B); }
|
|
static void OnPacksswb(struct As *a, struct Slice s) { OpSse(a, 0x660F63); }
|
|
static void OnPackusdw(struct As *a, struct Slice s) { OpSse(a, 0x660F382B); }
|
|
static void OnPackuswb(struct As *a, struct Slice s) { OpSse(a, 0x660F67); }
|
|
static void OnPaddb(struct As *a, struct Slice s) { OpSse(a, 0x660FFC); }
|
|
static void OnPaddd(struct As *a, struct Slice s) { OpSse(a, 0x660FFE); }
|
|
static void OnPaddq(struct As *a, struct Slice s) { OpSse(a, 0x660FD4); }
|
|
static void OnPaddsb(struct As *a, struct Slice s) { OpSse(a, 0x660FEC); }
|
|
static void OnPaddsw(struct As *a, struct Slice s) { OpSse(a, 0x660FED); }
|
|
static void OnPaddusb(struct As *a, struct Slice s) { OpSse(a, 0x660FDC); }
|
|
static void OnPaddusw(struct As *a, struct Slice s) { OpSse(a, 0x660FDD); }
|
|
static void OnPaddw(struct As *a, struct Slice s) { OpSse(a, 0x660FFD); }
|
|
static void OnPalignr(struct As *a, struct Slice s) { OpSse(a, 0x660F3A0F); }
|
|
static void OnPand(struct As *a, struct Slice s) { OpSse(a, 0x660FDB); }
|
|
static void OnPandn(struct As *a, struct Slice s) { OpSse(a, 0x660FDF); }
|
|
static void OnPause(struct As *a, struct Slice s) { EmitVarword(a, 0xF390); }
|
|
static void OnPavgb(struct As *a, struct Slice s) { OpSse(a, 0x660FE0); }
|
|
static void OnPavgw(struct As *a, struct Slice s) { OpSse(a, 0x660FE3); }
|
|
static void OnPblendvb(struct As *a, struct Slice s) { OpSse(a, 0x660F3810); }
|
|
static void OnPblendw(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0E); }
|
|
static void OnPcmpeqb(struct As *a, struct Slice s) { OpSse(a, 0x660F74); }
|
|
static void OnPcmpeqd(struct As *a, struct Slice s) { OpSse(a, 0x660F76); }
|
|
static void OnPcmpeqq(struct As *a, struct Slice s) { OpSse(a, 0x660F3829); }
|
|
static void OnPcmpeqw(struct As *a, struct Slice s) { OpSse(a, 0x660F75); }
|
|
static void OnPcmpgtb(struct As *a, struct Slice s) { OpSse(a, 0x660F64); }
|
|
static void OnPcmpgtd(struct As *a, struct Slice s) { OpSse(a, 0x660F66); }
|
|
static void OnPcmpgtq(struct As *a, struct Slice s) { OpSse(a, 0x660F3837); }
|
|
static void OnPcmpgtw(struct As *a, struct Slice s) { OpSse(a, 0x660F65); }
|
|
static void OnPcmpistri(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A63); }
|
|
static void OnPcmpistrm(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A62); }
|
|
static void OnPhaddd(struct As *a, struct Slice s) { OpSse(a, 0x660F3802); }
|
|
static void OnPhaddsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3803); }
|
|
static void OnPhaddw(struct As *a, struct Slice s) { OpSse(a, 0x660F3801); }
|
|
static void OnPhminposuw(struct As *a, struct Slice s) { OpSse(a, 0x660F3841); }
|
|
static void OnPhsubd(struct As *a, struct Slice s) { OpSse(a, 0x660F3806); }
|
|
static void OnPhsubsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3807); }
|
|
static void OnPhsubw(struct As *a, struct Slice s) { OpSse(a, 0x660F3805); }
|
|
static void OnPmaddubsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3804); }
|
|
static void OnPmaddwd(struct As *a, struct Slice s) { OpSse(a, 0x660FF5); }
|
|
static void OnPmaxsb(struct As *a, struct Slice s) { OpSse(a, 0x660F383C); }
|
|
static void OnPmaxsd(struct As *a, struct Slice s) { OpSse(a, 0x660F383D); }
|
|
static void OnPmaxsw(struct As *a, struct Slice s) { OpSse(a, 0x660FEE); }
|
|
static void OnPmaxub(struct As *a, struct Slice s) { OpSse(a, 0x660FDE); }
|
|
static void OnPmaxud(struct As *a, struct Slice s) { OpSse(a, 0x660F383F); }
|
|
static void OnPmaxuw(struct As *a, struct Slice s) { OpSse(a, 0x660F383E); }
|
|
static void OnPminsb(struct As *a, struct Slice s) { OpSse(a, 0x660F3838); }
|
|
static void OnPminsd(struct As *a, struct Slice s) { OpSse(a, 0x660F3839); }
|
|
static void OnPminsw(struct As *a, struct Slice s) { OpSse(a, 0x660FEA); }
|
|
static void OnPminub(struct As *a, struct Slice s) { OpSse(a, 0x660FDA); }
|
|
static void OnPminud(struct As *a, struct Slice s) { OpSse(a, 0x660F383B); }
|
|
static void OnPminuw(struct As *a, struct Slice s) { OpSse(a, 0x660F383A); }
|
|
static void OnPmovmskb(struct As *a, struct Slice s) { OpSse(a, 0x660FD7); }
|
|
static void OnPmuldq(struct As *a, struct Slice s) { OpSse(a, 0x660F3828); }
|
|
static void OnPmulhrsw(struct As *a, struct Slice s) { OpSse(a, 0x660F380B); }
|
|
static void OnPmulhuw(struct As *a, struct Slice s) { OpSse(a, 0x660FE4); }
|
|
static void OnPmulhw(struct As *a, struct Slice s) { OpSse(a, 0x660FE5); }
|
|
static void OnPmulld(struct As *a, struct Slice s) { OpSse(a, 0x660F3840); }
|
|
static void OnPmullw(struct As *a, struct Slice s) { OpSse(a, 0x660FD5); }
|
|
static void OnPmuludq(struct As *a, struct Slice s) { OpSse(a, 0x660FF4); }
|
|
static void OnPopcnt(struct As *a, struct Slice s) { OpBit(a, 0xF30FB8); }
|
|
static void OnPor(struct As *a, struct Slice s) { OpSse(a, 0x660FEB); }
|
|
static void OnPsadbw(struct As *a, struct Slice s) { OpSse(a, 0x660FF6); }
|
|
static void OnPshufb(struct As *a, struct Slice s) { OpSse(a, 0x660F3800); }
|
|
static void OnPshufd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F70); }
|
|
static void OnPshufhw(struct As *a, struct Slice s) { OpSseIb(a, 0xF30F70); }
|
|
static void OnPshuflw(struct As *a, struct Slice s) { OpSseIb(a, 0xF20F70); }
|
|
static void OnPsignb(struct As *a, struct Slice s) { OpSse(a, 0x660F3808); }
|
|
static void OnPsignd(struct As *a, struct Slice s) { OpSse(a, 0x660F380A); }
|
|
static void OnPsignw(struct As *a, struct Slice s) { OpSse(a, 0x660F3809); }
|
|
static void OnPslld(struct As *a, struct Slice s) { OpSse(a, 0x660FF2); }
|
|
static void OnPsllq(struct As *a, struct Slice s) { OpSse(a, 0x660FF3); }
|
|
static void OnPsllw(struct As *a, struct Slice s) { OpSse(a, 0x660FF1); }
|
|
static void OnPsrad(struct As *a, struct Slice s) { OpSse(a, 0x660FE2); }
|
|
static void OnPsraw(struct As *a, struct Slice s) { OpSse(a, 0x660FE1); }
|
|
static void OnPsrld(struct As *a, struct Slice s) { OpSse(a, 0x660FD2); }
|
|
static void OnPsrlq(struct As *a, struct Slice s) { OpSse(a, 0x660FD3); }
|
|
static void OnPsrlw(struct As *a, struct Slice s) { OpSse(a, 0x660FD1); }
|
|
static void OnPsubb(struct As *a, struct Slice s) { OpSse(a, 0x660FF8); }
|
|
static void OnPsubd(struct As *a, struct Slice s) { OpSse(a, 0x660FFA); }
|
|
static void OnPsubq(struct As *a, struct Slice s) { OpSse(a, 0x660FFB); }
|
|
static void OnPsubsb(struct As *a, struct Slice s) { OpSse(a, 0x660FE8); }
|
|
static void OnPsubsw(struct As *a, struct Slice s) { OpSse(a, 0x660FE9); }
|
|
static void OnPsubusb(struct As *a, struct Slice s) { OpSse(a, 0x660FD8); }
|
|
static void OnPsubusw(struct As *a, struct Slice s) { OpSse(a, 0x660FD9); }
|
|
static void OnPsubw(struct As *a, struct Slice s) { OpSse(a, 0x660FF9); }
|
|
static void OnPtest(struct As *a, struct Slice s) { OpSse(a, 0x660F3817); }
|
|
static void OnPunpckhbw(struct As *a, struct Slice s) { OpSse(a, 0x660F68); }
|
|
static void OnPunpckhdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6A); }
|
|
static void OnPunpckhqdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6D); }
|
|
static void OnPunpckhwd(struct As *a, struct Slice s) { OpSse(a, 0x660F69); }
|
|
static void OnPunpcklbw(struct As *a, struct Slice s) { OpSse(a, 0x660F60); }
|
|
static void OnPunpckldq(struct As *a, struct Slice s) { OpSse(a, 0x660F62); }
|
|
static void OnPunpcklqdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6C); }
|
|
static void OnPunpcklwd(struct As *a, struct Slice s) { OpSse(a, 0x660F61); }
|
|
static void OnPxor(struct As *a, struct Slice s) { OpSse(a, 0x660FEF); }
|
|
static void OnRcl(struct As *a, struct Slice s) { OpBsu(a, s, 2); }
|
|
static void OnRcpps(struct As *a, struct Slice s) { OpSse(a, 0x0F53); }
|
|
static void OnRcpss(struct As *a, struct Slice s) { OpSse(a, 0xF30F53); }
|
|
static void OnRcr(struct As *a, struct Slice s) { OpBsu(a, s, 3); }
|
|
static void OnRol(struct As *a, struct Slice s) { OpBsu(a, s, 0); }
|
|
static void OnRor(struct As *a, struct Slice s) { OpBsu(a, s, 1); }
|
|
static void OnRoundsd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0B); }
|
|
static void OnRoundss(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0A); }
|
|
static void OnRsqrtps(struct As *a, struct Slice s) { OpSse(a, 0x0F52); }
|
|
static void OnRsqrtss(struct As *a, struct Slice s) { OpSse(a, 0xF30F52); }
|
|
static void OnSal(struct As *a, struct Slice s) { OpBsu(a, s, 6); }
|
|
static void OnSar(struct As *a, struct Slice s) { OpBsu(a, s, 7); }
|
|
static void OnSbb(struct As *a, struct Slice s) { OpAlu(a, s, 3); }
|
|
static void OnSetb(struct As *a, struct Slice s) { OpSetcc(a, 2); }
|
|
static void OnSetbe(struct As *a, struct Slice s) { OpSetcc(a, 6); }
|
|
static void OnSetl(struct As *a, struct Slice s) { OpSetcc(a, 12); }
|
|
static void OnSetle(struct As *a, struct Slice s) { OpSetcc(a, 14); }
|
|
static void OnSetnb(struct As *a, struct Slice s) { OpSetcc(a, 3); }
|
|
static void OnSetnbe(struct As *a, struct Slice s) { OpSetcc(a, 7); }
|
|
static void OnSetnl(struct As *a, struct Slice s) { OpSetcc(a, 13); }
|
|
static void OnSetnle(struct As *a, struct Slice s) { OpSetcc(a, 15); }
|
|
static void OnSetno(struct As *a, struct Slice s) { OpSetcc(a, 1); }
|
|
static void OnSetnp(struct As *a, struct Slice s) { OpSetcc(a, 11); }
|
|
static void OnSetns(struct As *a, struct Slice s) { OpSetcc(a, 9); }
|
|
static void OnSetnz(struct As *a, struct Slice s) { OpSetcc(a, 5); }
|
|
static void OnSeto(struct As *a, struct Slice s) { OpSetcc(a, 0); }
|
|
static void OnSetp(struct As *a, struct Slice s) { OpSetcc(a, 10); }
|
|
static void OnSets(struct As *a, struct Slice s) { OpSetcc(a, 8); }
|
|
static void OnSetz(struct As *a, struct Slice s) { OpSetcc(a, 4); }
|
|
static void OnShl(struct As *a, struct Slice s) { OpBsu(a, s, 4); }
|
|
static void OnShr(struct As *a, struct Slice s) { OpBsu(a, s, 5); }
|
|
static void OnShufpd(struct As *a, struct Slice s) { OpSseIb(a, 0x660FC6); }
|
|
static void OnShufps(struct As *a, struct Slice s) { OpSseIb(a, 0x0FC6); }
|
|
static void OnSqrtpd(struct As *a, struct Slice s) { OpSse(a, 0x660F51); }
|
|
static void OnSqrtps(struct As *a, struct Slice s) { OpSse(a, 0x0F51); }
|
|
static void OnSqrtsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F51); }
|
|
static void OnSqrtss(struct As *a, struct Slice s) { OpSse(a, 0xF30F51); }
|
|
static void OnStc(struct As *a, struct Slice s) { EmitByte(a, 0xF9); }
|
|
static void OnStd(struct As *a, struct Slice s) { EmitByte(a, 0xFD); }
|
|
static void OnSti(struct As *a, struct Slice s) { EmitByte(a, 0xFB); }
|
|
static void OnStosb(struct As *a, struct Slice s) { EmitByte(a, 0xAA); }
|
|
static void OnStosl(struct As *a, struct Slice s) { EmitByte(a, 0xAB); }
|
|
static void OnStosq(struct As *a, struct Slice s) { EmitVarword(a, 0x48AB); }
|
|
static void OnStosw(struct As *a, struct Slice s) { EmitVarword(a, 0x66AB); }
|
|
static void OnSub(struct As *a, struct Slice s) { OpAlu(a, s, 5); }
|
|
static void OnSubpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5C); }
|
|
static void OnSubps(struct As *a, struct Slice s) { OpSse(a, 0x0F5C); }
|
|
static void OnSubsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5C); }
|
|
static void OnSubss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5C); }
|
|
static void OnUcomisd(struct As *a, struct Slice s) { OpSse(a, 0x660F2E); }
|
|
static void OnUcomiss(struct As *a, struct Slice s) { OpSse(a, 0x0F2E); }
|
|
static void OnUd2(struct As *a, struct Slice s) { EmitVarword(a, 0x0F0B); }
|
|
static void OnUnpckhpd(struct As *a, struct Slice s) { OpSse(a, 0x660F15); }
|
|
static void OnUnpcklpd(struct As *a, struct Slice s) { OpSse(a, 0x660F14); }
|
|
static void OnXor(struct As *a, struct Slice s) { OpAlu(a, s, 6); }
|
|
static void OnXorpd(struct As *a, struct Slice s) { OpSse(a, 0x660F57); }
|
|
static void OnXorps(struct As *a, struct Slice s) { OpSse(a, 0x0F57); }
|
|
// clang-format on
|
|
|
|
static const struct Directive8 {
|
|
char s[8];
|
|
void (*f)(struct As *, struct Slice);
|
|
} kDirective8[] = {
|
|
{".abort", OnAbort}, //
|
|
{".align", OnAlign}, //
|
|
{".ascii", OnAscii}, //
|
|
{".asciz", OnAsciz}, //
|
|
{".balign", OnAlign}, //
|
|
{".bss", OnBss}, //
|
|
{".byte", OnByte}, //
|
|
{".data", OnData}, //
|
|
{".double", OnDouble}, //
|
|
{".err", OnErr}, //
|
|
{".error", OnError}, //
|
|
{".file", OnFile}, //
|
|
{".float", OnFloat}, //
|
|
{".float80", OnFloat80}, //
|
|
{".global", OnGlobal}, //
|
|
{".globl", OnGlobal}, //
|
|
{".hidden", OnHidden}, //
|
|
{".ident", OnIdent}, //
|
|
{".incbin", OnIncbin}, //
|
|
{".ldbl", OnLdbl}, //
|
|
{".loc", OnLoc}, //
|
|
{".local", OnLocal}, //
|
|
{".long", OnLong}, //
|
|
{".quad", OnQuad}, //
|
|
{".section", OnSection}, //
|
|
{".short", OnWord}, //
|
|
{".size", OnSize}, //
|
|
{".sleb128", OnSleb128}, //
|
|
{".space", OnSpace}, //
|
|
{".text", OnText}, //
|
|
{".type", OnType}, //
|
|
{".uleb128", OnUleb128}, //
|
|
{".warning", OnWarning}, //
|
|
{".weak", OnWeak}, //
|
|
{".word", OnWord}, //
|
|
{".zero", OnZero}, //
|
|
{"adc", OnAdc}, //
|
|
{"adcb", OnAdc}, //
|
|
{"adcl", OnAdc}, //
|
|
{"adcq", OnAdc}, //
|
|
{"adcw", OnAdc}, //
|
|
{"add", OnAdd}, //
|
|
{"addb", OnAdd}, //
|
|
{"addl", OnAdd}, //
|
|
{"addpd", OnAddpd}, //
|
|
{"addps", OnAddps}, //
|
|
{"addq", OnAdd}, //
|
|
{"addsd", OnAddsd}, //
|
|
{"addss", OnAddss}, //
|
|
{"addsubpd", OnAddsubpd}, //
|
|
{"addsubps", OnAddsubps}, //
|
|
{"addw", OnAdd}, //
|
|
{"and", OnAnd}, //
|
|
{"andb", OnAnd}, //
|
|
{"andl", OnAnd}, //
|
|
{"andnpd", OnAndnpd}, //
|
|
{"andnps", OnAndnps}, //
|
|
{"andpd", OnAndpd}, //
|
|
{"andps", OnAndps}, //
|
|
{"andq", OnAnd}, //
|
|
{"andw", OnAnd}, //
|
|
{"blendpd", OnBlendpd}, //
|
|
{"blendvpd", OnBlendvpd}, //
|
|
{"bsf", OnBsf}, //
|
|
{"bsr", OnBsr}, //
|
|
{"bswap", OnBswap}, //
|
|
{"call", OnCall}, //
|
|
{"callq", OnCall}, //
|
|
{"cbtw", OnCbtw}, //
|
|
{"cbw", OnCbtw}, //
|
|
{"cdq", OnCltd}, //
|
|
{"cdqe", OnCltq}, //
|
|
{"clc", OnClc}, //
|
|
{"cld", OnCld}, //
|
|
{"cli", OnCli}, //
|
|
{"cltd", OnCltd}, //
|
|
{"cltq", OnCltq}, //
|
|
{"cmc", OnCmc}, //
|
|
{"cmova", OnCmovnbe}, //
|
|
{"cmovae", OnCmovnb}, //
|
|
{"cmovb", OnCmovb}, //
|
|
{"cmovbe", OnCmovbe}, //
|
|
{"cmovc", OnCmovb}, //
|
|
{"cmove", OnCmovz}, //
|
|
{"cmovg", OnCmovnle}, //
|
|
{"cmovge", OnCmovnl}, //
|
|
{"cmovl", OnCmovl}, //
|
|
{"cmovle", OnCmovle}, //
|
|
{"cmovna", OnCmovbe}, //
|
|
{"cmovnae", OnCmovb}, //
|
|
{"cmovnb", OnCmovnb}, //
|
|
{"cmovnbe", OnCmovnbe}, //
|
|
{"cmovnc", OnCmovnb}, //
|
|
{"cmovne", OnCmovnz}, //
|
|
{"cmovng", OnCmovle}, //
|
|
{"cmovnge", OnCmovl}, //
|
|
{"cmovnl", OnCmovnl}, //
|
|
{"cmovnle", OnCmovnle}, //
|
|
{"cmovno", OnCmovno}, //
|
|
{"cmovnp", OnCmovnp}, //
|
|
{"cmovns", OnCmovns}, //
|
|
{"cmovnz", OnCmovnz}, //
|
|
{"cmovo", OnCmovo}, //
|
|
{"cmovp", OnCmovp}, //
|
|
{"cmovpe", OnCmovp}, //
|
|
{"cmovpo", OnCmovnp}, //
|
|
{"cmovs", OnCmovs}, //
|
|
{"cmovz", OnCmovz}, //
|
|
{"cmp", OnCmp}, //
|
|
{"cmpb", OnCmp}, //
|
|
{"cmpl", OnCmp}, //
|
|
{"cmppd", OnCmppd}, //
|
|
{"cmpps", OnCmpps}, //
|
|
{"cmpq", OnCmp}, //
|
|
{"cmpsd", OnCmpsd}, //
|
|
{"cmpss", OnCmpss}, //
|
|
{"cmpw", OnCmp}, //
|
|
{"comisd", OnComisd}, //
|
|
{"comiss", OnComiss}, //
|
|
{"cqo", OnCqto}, //
|
|
{"cqto", OnCqto}, //
|
|
{"cvtdq2pd", OnCvtdq2pd}, //
|
|
{"cvtdq2ps", OnCvtdq2ps}, //
|
|
{"cvtpd2dq", OnCvtpd2dq}, //
|
|
{"cvtpd2ps", OnCvtpd2ps}, //
|
|
{"cvtps2dq", OnCvtps2dq}, //
|
|
{"cvtps2pd", OnCvtps2pd}, //
|
|
{"cvtsd2ss", OnCvtsd2ss}, //
|
|
{"cvtsd2ss", OnCvtsd2ss}, //
|
|
{"cvtsi2sd", OnCvtsi2sd}, //
|
|
{"cvtsi2sd", OnCvtsi2sd}, //
|
|
{"cvtsi2ss", OnCvtsi2ss}, //
|
|
{"cvtsi2ss", OnCvtsi2ss}, //
|
|
{"cvtss2sd", OnCvtss2sd}, //
|
|
{"cwd", OnCwtd}, //
|
|
{"cwde", OnCwtl}, //
|
|
{"cwtd", OnCwtd}, //
|
|
{"cwtl", OnCwtl}, //
|
|
{"dec", OnDec}, //
|
|
{"decb", OnDec}, //
|
|
{"decl", OnDec}, //
|
|
{"decq", OnDec}, //
|
|
{"decw", OnDec}, //
|
|
{"div", OnDiv}, //
|
|
{"divpd", OnDivpd}, //
|
|
{"divps", OnDivps}, //
|
|
{"divsd", OnDivsd}, //
|
|
{"divss", OnDivss}, //
|
|
{"dppd", OnDppd}, //
|
|
{"fabs", OnFabs}, //
|
|
{"faddl", OnFaddl}, //
|
|
{"faddp", OnFaddp}, //
|
|
{"fadds", OnFadds}, //
|
|
{"fchs", OnFchs}, //
|
|
{"fcmovb", OnFcmovb}, //
|
|
{"fcmovbe", OnFcmovbe}, //
|
|
{"fcmove", OnFcmove}, //
|
|
{"fcmovnb", OnFcmovnb}, //
|
|
{"fcmovnbe", OnFcmovnbe}, //
|
|
{"fcmovne", OnFcmovne}, //
|
|
{"fcmovnu", OnFcmovnu}, //
|
|
{"fcmovu", OnFcmovu}, //
|
|
{"fcomi", OnFcomi}, //
|
|
{"fcomip", OnFcomip}, //
|
|
{"fdivrp", OnFdivrp}, //
|
|
{"fildl", OnFildl}, //
|
|
{"fildll", OnFildll}, //
|
|
{"fildq", OnFildq}, //
|
|
{"filds", OnFilds}, //
|
|
{"fisttpll", OnFisttpq}, //
|
|
{"fisttpq", OnFisttpq}, //
|
|
{"fisttps", OnFisttps}, //
|
|
{"fld", OnFld}, //
|
|
{"fld1", OnFld1}, //
|
|
{"fldcw", OnFldcw}, //
|
|
{"fldl", OnFldl}, //
|
|
{"fldl2e", OnFldl2e}, //
|
|
{"fldl2t", OnFldl2t}, //
|
|
{"fldlg2", OnFldlg2}, //
|
|
{"fldln2", OnFldln2}, //
|
|
{"fldpi", OnFldpi}, //
|
|
{"flds", OnFlds}, //
|
|
{"fldt", OnFldt}, //
|
|
{"fldz", OnFldz}, //
|
|
{"fmulp", OnFmulp}, //
|
|
{"fnstcw", OnFnstcw}, //
|
|
{"fnstsw", OnFnstsw}, //
|
|
{"fstp", OnFstp}, //
|
|
{"fstpl", OnFstpl}, //
|
|
{"fstps", OnFstps}, //
|
|
{"fstpt", OnFstpt}, //
|
|
{"fsubrp", OnFsubrp}, //
|
|
{"ftst", OnFtst}, //
|
|
{"fucomi", OnFucomi}, //
|
|
{"fucomip", OnFucomip}, //
|
|
{"fwait", OnFwait}, //
|
|
{"fxam", OnFxam}, //
|
|
{"fxch", OnFxch}, //
|
|
{"fxtract", OnFxtract}, //
|
|
{"haddpd", OnHaddpd}, //
|
|
{"haddps", OnHaddps}, //
|
|
{"hlt", OnHlt}, //
|
|
{"hsubpd", OnHsubpd}, //
|
|
{"hsubps", OnHsubps}, //
|
|
{"icebp", OnInt1}, //
|
|
{"idiv", OnIdiv}, //
|
|
{"imul", OnImul}, //
|
|
{"inc", OnInc}, //
|
|
{"incb", OnInc}, //
|
|
{"incl", OnInc}, //
|
|
{"incq", OnInc}, //
|
|
{"incw", OnInc}, //
|
|
{"int1", OnInt1}, //
|
|
{"int3", OnInt3}, //
|
|
{"ja", OnJnbe}, //
|
|
{"jae", OnJnb}, //
|
|
{"jb", OnJb}, //
|
|
{"jbe", OnJbe}, //
|
|
{"jc", OnJb}, //
|
|
{"je", OnJz}, //
|
|
{"jg", OnJnle}, //
|
|
{"jge", OnJnl}, //
|
|
{"jl", OnJl}, //
|
|
{"jle", OnJle}, //
|
|
{"jmp", OnJmp}, //
|
|
{"jmpq", OnJmp}, //
|
|
{"jna", OnJbe}, //
|
|
{"jnae", OnJb}, //
|
|
{"jnb", OnJnb}, //
|
|
{"jnbe", OnJnbe}, //
|
|
{"jnc", OnJnb}, //
|
|
{"jne", OnJnz}, //
|
|
{"jng", OnJle}, //
|
|
{"jnge", OnJl}, //
|
|
{"jnl", OnJnl}, //
|
|
{"jnle", OnJnle}, //
|
|
{"jno", OnJno}, //
|
|
{"jnp", OnJnp}, //
|
|
{"jns", OnJns}, //
|
|
{"jnz", OnJnz}, //
|
|
{"jo", OnJo}, //
|
|
{"jp", OnJp}, //
|
|
{"jpe", OnJp}, //
|
|
{"jpo", OnJnp}, //
|
|
{"js", OnJs}, //
|
|
{"jz", OnJz}, //
|
|
{"lea", OnLea}, //
|
|
{"leave", OnLeave}, //
|
|
{"lodsb", OnLodsb}, //
|
|
{"lodsl", OnLodsl}, //
|
|
{"lodsq", OnLodsq}, //
|
|
{"lodsw", OnLodsw}, //
|
|
{"maxpd", OnMaxpd}, //
|
|
{"maxps", OnMaxps}, //
|
|
{"maxsd", OnMaxsd}, //
|
|
{"maxss", OnMaxss}, //
|
|
{"minpd", OnMinpd}, //
|
|
{"minps", OnMinps}, //
|
|
{"minsd", OnMinsd}, //
|
|
{"minss", OnMinss}, //
|
|
{"mov", OnMov}, //
|
|
{"movabs", OnMov}, //
|
|
{"movapd", OnMovapd}, //
|
|
{"movaps", OnMovaps}, //
|
|
{"movb", OnMov}, //
|
|
{"movd", OnMovdq}, //
|
|
{"movdqa", OnMovdqa}, //
|
|
{"movdqa", OnMovdqa}, //
|
|
{"movdqu", OnMovdqu}, //
|
|
{"movdqu", OnMovdqu}, //
|
|
{"movl", OnMov}, //
|
|
{"movmskpd", OnMovmskpd}, //
|
|
{"movmskps", OnMovmskps}, //
|
|
{"movq", OnMovq}, //
|
|
{"movsb", OnMovsb}, //
|
|
{"movsbl", OnMovsbwx}, //
|
|
{"movsbq", OnMovsbwx}, //
|
|
{"movsbw", OnMovsbwx}, //
|
|
{"movsd", OnMovsd}, //
|
|
{"movsd", OnMovsd}, //
|
|
{"movsl", OnMovsl}, //
|
|
{"movslq", OnMovslq}, //
|
|
{"movsq", OnMovsq}, //
|
|
{"movss", OnMovss}, //
|
|
{"movss", OnMovss}, //
|
|
{"movsw", OnMovsw}, //
|
|
{"movswl", OnMovsbwx}, //
|
|
{"movswq", OnMovsbwx}, //
|
|
{"movupd", OnMovupd}, //
|
|
{"movups", OnMovups}, //
|
|
{"movw", OnMov}, //
|
|
{"movzbl", OnMovzbwx}, //
|
|
{"movzbq", OnMovzbwx}, //
|
|
{"movzbw", OnMovzbwx}, //
|
|
{"movzwl", OnMovzbwx}, //
|
|
{"movzwq", OnMovzbwx}, //
|
|
{"mpsadbw", OnMpsadbw}, //
|
|
{"mul", OnMul}, //
|
|
{"mulpd", OnMulpd}, //
|
|
{"mulps", OnMulps}, //
|
|
{"mulsd", OnMulsd}, //
|
|
{"mulss", OnMulss}, //
|
|
{"neg", OnNeg}, //
|
|
{"negb", OnNeg}, //
|
|
{"negl", OnNeg}, //
|
|
{"negq", OnNeg}, //
|
|
{"negw", OnNeg}, //
|
|
{"nop", OnNop}, //
|
|
{"nopb", OnNop}, //
|
|
{"nopl", OnNop}, //
|
|
{"nopq", OnNop}, //
|
|
{"nopw", OnNop}, //
|
|
{"not", OnNot}, //
|
|
{"notb", OnNot}, //
|
|
{"notl", OnNot}, //
|
|
{"notq", OnNot}, //
|
|
{"notw", OnNot}, //
|
|
{"or", OnOr}, //
|
|
{"orb", OnOr}, //
|
|
{"orl", OnOr}, //
|
|
{"orpd", OnOrpd}, //
|
|
{"orps", OnOrps}, //
|
|
{"orq", OnOr}, //
|
|
{"orw", OnOr}, //
|
|
{"pabsb", OnPabsb}, //
|
|
{"pabsd", OnPabsd}, //
|
|
{"pabsw", OnPabsw}, //
|
|
{"packssdw", OnPackssdw}, //
|
|
{"packsswb", OnPacksswb}, //
|
|
{"packusdw", OnPackusdw}, //
|
|
{"packuswb", OnPackuswb}, //
|
|
{"paddb", OnPaddb}, //
|
|
{"paddd", OnPaddd}, //
|
|
{"paddq", OnPaddq}, //
|
|
{"paddsb", OnPaddsb}, //
|
|
{"paddsw", OnPaddsw}, //
|
|
{"paddusb", OnPaddusb}, //
|
|
{"paddusw", OnPaddusw}, //
|
|
{"paddw", OnPaddw}, //
|
|
{"palignr", OnPalignr}, //
|
|
{"pand", OnPand}, //
|
|
{"pandn", OnPandn}, //
|
|
{"pause", OnPause}, //
|
|
{"pavgb", OnPavgb}, //
|
|
{"pavgw", OnPavgw}, //
|
|
{"pblendvb", OnPblendvb}, //
|
|
{"pblendw", OnPblendw}, //
|
|
{"pcmpeqb", OnPcmpeqb}, //
|
|
{"pcmpeqd", OnPcmpeqd}, //
|
|
{"pcmpeqq", OnPcmpeqq}, //
|
|
{"pcmpeqw", OnPcmpeqw}, //
|
|
{"pcmpgtb", OnPcmpgtb}, //
|
|
{"pcmpgtd", OnPcmpgtd}, //
|
|
{"pcmpgtq", OnPcmpgtq}, //
|
|
{"pcmpgtw", OnPcmpgtw}, //
|
|
{"phaddd", OnPhaddd}, //
|
|
{"phaddsw", OnPhaddsw}, //
|
|
{"phaddw", OnPhaddw}, //
|
|
{"phsubd", OnPhsubd}, //
|
|
{"phsubsw", OnPhsubsw}, //
|
|
{"phsubw", OnPhsubw}, //
|
|
{"pmaddwd", OnPmaddwd}, //
|
|
{"pmaxsb", OnPmaxsb}, //
|
|
{"pmaxsd", OnPmaxsd}, //
|
|
{"pmaxsw", OnPmaxsw}, //
|
|
{"pmaxub", OnPmaxub}, //
|
|
{"pmaxud", OnPmaxud}, //
|
|
{"pmaxuw", OnPmaxuw}, //
|
|
{"pminsb", OnPminsb}, //
|
|
{"pminsd", OnPminsd}, //
|
|
{"pminsw", OnPminsw}, //
|
|
{"pminub", OnPminub}, //
|
|
{"pminud", OnPminud}, //
|
|
{"pminuw", OnPminuw}, //
|
|
{"pmovmskb", OnPmovmskb}, //
|
|
{"pmuldq", OnPmuldq}, //
|
|
{"pmulhrsw", OnPmulhrsw}, //
|
|
{"pmulhuw", OnPmulhuw}, //
|
|
{"pmulhw", OnPmulhw}, //
|
|
{"pmulld", OnPmulld}, //
|
|
{"pmullw", OnPmullw}, //
|
|
{"pmuludq", OnPmuludq}, //
|
|
{"pop", OnPop}, //
|
|
{"popcnt", OnPopcnt}, //
|
|
{"por", OnPor}, //
|
|
{"psadbw", OnPsadbw}, //
|
|
{"pshufb", OnPshufb}, //
|
|
{"pshufd", OnPshufd}, //
|
|
{"pshufhw", OnPshufhw}, //
|
|
{"pshuflw", OnPshuflw}, //
|
|
{"psignb", OnPsignb}, //
|
|
{"psignd", OnPsignd}, //
|
|
{"psignw", OnPsignw}, //
|
|
{"pslld", OnPslld}, //
|
|
{"psllq", OnPsllq}, //
|
|
{"psllw", OnPsllw}, //
|
|
{"psrad", OnPsrad}, //
|
|
{"psraw", OnPsraw}, //
|
|
{"psrld", OnPsrld}, //
|
|
{"psrlq", OnPsrlq}, //
|
|
{"psrlw", OnPsrlw}, //
|
|
{"psubb", OnPsubb}, //
|
|
{"psubd", OnPsubd}, //
|
|
{"psubq", OnPsubq}, //
|
|
{"psubsb", OnPsubsb}, //
|
|
{"psubsw", OnPsubsw}, //
|
|
{"psubusb", OnPsubusb}, //
|
|
{"psubusw", OnPsubusw}, //
|
|
{"psubw", OnPsubw}, //
|
|
{"ptest", OnPtest}, //
|
|
{"push", OnPush}, //
|
|
{"pxor", OnPxor}, //
|
|
{"rcl", OnRcl}, //
|
|
{"rclb", OnRcl}, //
|
|
{"rcll", OnRcl}, //
|
|
{"rclq", OnRcl}, //
|
|
{"rclw", OnRcl}, //
|
|
{"rcpps", OnRcpps}, //
|
|
{"rcpss", OnRcpss}, //
|
|
{"rcr", OnRcr}, //
|
|
{"rcrb", OnRcr}, //
|
|
{"rcrl", OnRcr}, //
|
|
{"rcrq", OnRcr}, //
|
|
{"rcrw", OnRcr}, //
|
|
{"ret", OnRet}, //
|
|
{"rol", OnRol}, //
|
|
{"rolb", OnRol}, //
|
|
{"roll", OnRol}, //
|
|
{"rolq", OnRol}, //
|
|
{"rolw", OnRol}, //
|
|
{"ror", OnRor}, //
|
|
{"rorb", OnRor}, //
|
|
{"rorl", OnRor}, //
|
|
{"rorq", OnRor}, //
|
|
{"rorw", OnRor}, //
|
|
{"roundsd", OnRoundsd}, //
|
|
{"roundss", OnRoundss}, //
|
|
{"rsqrtps", OnRsqrtps}, //
|
|
{"rsqrtss", OnRsqrtss}, //
|
|
{"sal", OnSal}, //
|
|
{"salb", OnSal}, //
|
|
{"sall", OnSal}, //
|
|
{"salq", OnSal}, //
|
|
{"salw", OnSal}, //
|
|
{"sar", OnSar}, //
|
|
{"sarb", OnSar}, //
|
|
{"sarl", OnSar}, //
|
|
{"sarq", OnSar}, //
|
|
{"sarw", OnSar}, //
|
|
{"sbb", OnSbb}, //
|
|
{"sbbb", OnSbb}, //
|
|
{"sbbl", OnSbb}, //
|
|
{"sbbq", OnSbb}, //
|
|
{"sbbw", OnSbb}, //
|
|
{"seta", OnSetnbe}, //
|
|
{"setae", OnSetnb}, //
|
|
{"setb", OnSetb}, //
|
|
{"setbe", OnSetbe}, //
|
|
{"setc", OnSetb}, //
|
|
{"sete", OnSetz}, //
|
|
{"setg", OnSetnle}, //
|
|
{"setge", OnSetnl}, //
|
|
{"setl", OnSetl}, //
|
|
{"setle", OnSetle}, //
|
|
{"setna", OnSetbe}, //
|
|
{"setnae", OnSetb}, //
|
|
{"setnb", OnSetnb}, //
|
|
{"setnbe", OnSetnbe}, //
|
|
{"setnc", OnSetnb}, //
|
|
{"setne", OnSetnz}, //
|
|
{"setng", OnSetle}, //
|
|
{"setnge", OnSetl}, //
|
|
{"setnl", OnSetnl}, //
|
|
{"setnle", OnSetnle}, //
|
|
{"setno", OnSetno}, //
|
|
{"setnp", OnSetnp}, //
|
|
{"setns", OnSetns}, //
|
|
{"setnz", OnSetnz}, //
|
|
{"seto", OnSeto}, //
|
|
{"setp", OnSetp}, //
|
|
{"setpe", OnSetp}, //
|
|
{"setpo", OnSetnp}, //
|
|
{"sets", OnSets}, //
|
|
{"setz", OnSetz}, //
|
|
{"shl", OnShl}, //
|
|
{"shlb", OnShl}, //
|
|
{"shld", OnShld}, //
|
|
{"shll", OnShl}, //
|
|
{"shlq", OnShl}, //
|
|
{"shlw", OnShl}, //
|
|
{"shr", OnShr}, //
|
|
{"shrb", OnShr}, //
|
|
{"shrd", OnShrd}, //
|
|
{"shrl", OnShr}, //
|
|
{"shrq", OnShr}, //
|
|
{"shrw", OnShr}, //
|
|
{"shufpd", OnShufpd}, //
|
|
{"shufps", OnShufps}, //
|
|
{"sqrtpd", OnSqrtpd}, //
|
|
{"sqrtps", OnSqrtps}, //
|
|
{"sqrtsd", OnSqrtsd}, //
|
|
{"sqrtss", OnSqrtss}, //
|
|
{"stc", OnStc}, //
|
|
{"std", OnStd}, //
|
|
{"sti", OnSti}, //
|
|
{"stosb", OnStosb}, //
|
|
{"stosl", OnStosl}, //
|
|
{"stosq", OnStosq}, //
|
|
{"stosw", OnStosw}, //
|
|
{"sub", OnSub}, //
|
|
{"subb", OnSub}, //
|
|
{"subl", OnSub}, //
|
|
{"subpd", OnSubpd}, //
|
|
{"subps", OnSubps}, //
|
|
{"subq", OnSub}, //
|
|
{"subsd", OnSubsd}, //
|
|
{"subss", OnSubss}, //
|
|
{"subw", OnSub}, //
|
|
{"test", OnTest}, //
|
|
{"testb", OnTest}, //
|
|
{"testl", OnTest}, //
|
|
{"testq", OnTest}, //
|
|
{"testw", OnTest}, //
|
|
{"ucomisd", OnUcomisd}, //
|
|
{"ucomiss", OnUcomiss}, //
|
|
{"ud2", OnUd2}, //
|
|
{"unpckhpd", OnUnpckhpd}, //
|
|
{"unpcklpd", OnUnpcklpd}, //
|
|
{"wait", OnFwait}, //
|
|
{"xchg", OnXchg}, //
|
|
{"xor", OnXor}, //
|
|
{"xorb", OnXor}, //
|
|
{"xorl", OnXor}, //
|
|
{"xorpd", OnXorpd}, //
|
|
{"xorps", OnXorps}, //
|
|
{"xorq", OnXor}, //
|
|
{"xorw", OnXor}, //
|
|
};
|
|
|
|
static const struct Directive16 {
|
|
char s[16];
|
|
void (*f)(struct As *, struct Slice);
|
|
} kDirective16[] = {
|
|
{".internal", OnInternal}, //
|
|
{".popsection", OnPopsection}, //
|
|
{".previous", OnPrevious}, //
|
|
{".protected", OnProtected}, //
|
|
{".pushsection", OnPushsection}, //
|
|
{"cvtsd2ssl", OnCvtsd2ss}, //
|
|
{"cvtsd2ssq", OnCvtsd2ss}, //
|
|
{"cvtsi2sdl", OnCvtsi2sd}, //
|
|
{"cvtsi2sdq", OnCvtsi2sd}, //
|
|
{"cvtsi2sdq", OnCvtsi2sd}, //
|
|
{"cvtsi2ssl", OnCvtsi2ss}, //
|
|
{"cvtsi2ssq", OnCvtsi2ss}, //
|
|
{"cvttpd2dq", OnCvttpd2dq}, //
|
|
{"cvttps2dq", OnCvttps2dq}, //
|
|
{"cvttsd2si", OnCvttsd2si}, //
|
|
{"cvttsd2sil", OnCvttsd2si}, //
|
|
{"cvttsd2siq", OnCvttsd2si}, //
|
|
{"cvttss2si", OnCvttss2si}, //
|
|
{"cvttss2sil", OnCvttss2si}, //
|
|
{"cvttss2siq", OnCvttss2si}, //
|
|
{"pcmpistri", OnPcmpistri}, //
|
|
{"pcmpistrm", OnPcmpistrm}, //
|
|
{"phminposuw", OnPhminposuw}, //
|
|
{"pmaddubsw", OnPmaddubsw}, //
|
|
{"punpckhbw", OnPunpckhbw}, //
|
|
{"punpckhdq", OnPunpckhdq}, //
|
|
{"punpckhqdq", OnPunpckhqdq}, //
|
|
{"punpckhwd", OnPunpckhwd}, //
|
|
{"punpcklbw", OnPunpcklbw}, //
|
|
{"punpckldq", OnPunpckldq}, //
|
|
{"punpcklqdq", OnPunpcklqdq}, //
|
|
{"punpcklwd", OnPunpcklwd}, //
|
|
};
|
|
|
|
static bool OnDirective8(struct As *a, struct Slice s) {
|
|
int m, l, r;
|
|
uint64_t x, y;
|
|
if (s.n && s.n <= 8) {
|
|
x = MakeKey64(s.p, s.n);
|
|
l = 0;
|
|
r = ARRAYLEN(kDirective8) - 1;
|
|
while (l <= r) {
|
|
m = (l + r) >> 1;
|
|
y = LOAD64BE(kDirective8[m].s);
|
|
if (x < y) {
|
|
r = m - 1;
|
|
} else if (x > y) {
|
|
l = m + 1;
|
|
} else {
|
|
kDirective8[m].f(a, s);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool OnDirective16(struct As *a, struct Slice s) {
|
|
int m, l, r;
|
|
unsigned __int128 x, y;
|
|
if (s.n && s.n <= 16) {
|
|
x = MakeKey128(s.p, s.n);
|
|
l = 0;
|
|
r = ARRAYLEN(kDirective16) - 1;
|
|
while (l <= r) {
|
|
m = (l + r) >> 1;
|
|
y = LOAD128BE(kDirective16[m].s);
|
|
if (x < y) {
|
|
r = m - 1;
|
|
} else if (x > y) {
|
|
l = m + 1;
|
|
} else {
|
|
kDirective16[m].f(a, s);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void OnDirective(struct As *a) {
|
|
struct Slice s;
|
|
for (;;) {
|
|
s = GetSlice(a);
|
|
if (Prefix(a, s.p, s.n)) {
|
|
if (IsPunct(a, a->i, ';')) {
|
|
return;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!OnDirective8(a, s)) {
|
|
if (!OnDirective16(a, s)) {
|
|
Fail(a, "unexpected op: %.*s", s.n, s.p);
|
|
}
|
|
}
|
|
ConsumePunct(a, ';');
|
|
}
|
|
|
|
static void Assemble(struct As *a) {
|
|
while (a->i < a->things.n) {
|
|
if (IsPunct(a, a->i, ';')) {
|
|
++a->i;
|
|
continue;
|
|
}
|
|
switch (a->things.p[a->i].t) {
|
|
case TT_SLICE:
|
|
if (IsPunct(a, a->i + 1, ':')) {
|
|
OnSymbol(a, a->things.p[a->i].i);
|
|
} else {
|
|
OnDirective(a);
|
|
}
|
|
break;
|
|
case TT_INT:
|
|
if (IsPunct(a, a->i + 1, ':')) {
|
|
OnLocalLabel(a, a->ints.p[a->things.p[a->i].i]);
|
|
break;
|
|
}
|
|
Fail(a, "unexpected token");
|
|
default:
|
|
Fail(a, "unexpected token");
|
|
}
|
|
}
|
|
}
|
|
|
|
static int FindLabelForward(struct As *a, int id) {
|
|
int i;
|
|
for (i = 0; i < a->labels.n; ++i) {
|
|
if (a->labels.p[i].id == id && a->labels.p[i].tok > a->i) {
|
|
return a->labels.p[i].symbol;
|
|
}
|
|
}
|
|
Fail(a, "label not found");
|
|
}
|
|
|
|
static int FindLabelBackward(struct As *a, int id) {
|
|
int i;
|
|
for (i = a->labels.n; i--;) {
|
|
if (a->labels.p[i].id == id && a->labels.p[i].tok < a->i) {
|
|
return a->labels.p[i].symbol;
|
|
}
|
|
}
|
|
Fail(a, "label not found");
|
|
}
|
|
|
|
static int ResolveSymbol(struct As *a, int i) {
|
|
switch (a->things.p[i].t) {
|
|
case TT_SLICE:
|
|
return GetSymbol(a, a->things.p[i].i);
|
|
case TT_BACKWARD:
|
|
return FindLabelBackward(a, a->ints.p[a->things.p[i].i]);
|
|
case TT_FORWARD:
|
|
return FindLabelForward(a, a->ints.p[a->things.p[i].i]);
|
|
default:
|
|
Fail(a, "this corruption %d", a->things.p[i].t);
|
|
}
|
|
}
|
|
|
|
static void EvaluateExpr(struct As *a, int i) {
|
|
if (i == -1) return;
|
|
if (a->exprs.p[i].isevaluated) return;
|
|
if (a->exprs.p[i].isvisited) Fail(a, "circular expr");
|
|
a->exprs.p[i].isvisited = true;
|
|
EvaluateExpr(a, a->exprs.p[i].lhs);
|
|
EvaluateExpr(a, a->exprs.p[i].rhs);
|
|
a->i = a->exprs.p[i].tok;
|
|
switch (a->exprs.p[i].kind) {
|
|
case EX_INT:
|
|
break;
|
|
case EX_SYM:
|
|
a->exprs.p[i].x = ResolveSymbol(a, a->exprs.p[i].x);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
a->exprs.p[i].isevaluated = true;
|
|
}
|
|
|
|
static void Write32(char b[4], int x) {
|
|
b[0] = x >> 000;
|
|
b[1] = x >> 010;
|
|
b[2] = x >> 020;
|
|
b[3] = x >> 030;
|
|
}
|
|
|
|
static void MarkUsedSymbols(struct As *a, int i) {
|
|
if (i == -1) return;
|
|
MarkUsedSymbols(a, a->exprs.p[i].lhs);
|
|
MarkUsedSymbols(a, a->exprs.p[i].rhs);
|
|
if (a->exprs.p[i].kind == EX_SYM) {
|
|
a->symbols.p[a->exprs.p[i].x].isused = true;
|
|
}
|
|
}
|
|
|
|
static void Evaluate(struct As *a) {
|
|
int i;
|
|
struct Expr *e;
|
|
for (i = 0; i < a->relas.n; ++i) {
|
|
EvaluateExpr(a, a->relas.p[i].expr);
|
|
if (a->relas.p[i].kind == R_X86_64_PC32) {
|
|
e = a->exprs.p + a->relas.p[i].expr;
|
|
if (e->kind == EX_SYM && a->symbols.p[e->x].stb == STB_LOCAL &&
|
|
a->symbols.p[e->x].section == a->relas.p[i].section) {
|
|
a->relas.p[i].isdead = true;
|
|
Write32((a->sections.p[a->relas.p[i].section].binary.p +
|
|
a->relas.p[i].offset),
|
|
(a->symbols.p[e->x].offset - a->relas.p[i].offset +
|
|
a->relas.p[i].addend));
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < a->relas.n; ++i) {
|
|
if (a->relas.p[i].isdead) continue;
|
|
MarkUsedSymbols(a, a->relas.p[i].expr);
|
|
}
|
|
}
|
|
|
|
static void MarkUndefinedSymbolsGlobal(struct As *a) {
|
|
int i;
|
|
for (i = 0; i < a->symbols.n; ++i) {
|
|
if (a->symbols.p[i].isused && !a->symbols.p[i].section &&
|
|
a->symbols.p[i].stb == STB_LOCAL) {
|
|
a->symbols.p[i].stb = STB_GLOBAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsLocal(struct As *a, int name) {
|
|
if (name < 0) return true;
|
|
return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2);
|
|
}
|
|
|
|
static bool IsLiveSymbol(struct As *a, int i) {
|
|
return !(!a->symbols.p[i].isused && a->symbols.p[i].stb == STB_LOCAL &&
|
|
a->symbols.p[i].section && IsLocal(a, a->symbols.p[i].name));
|
|
}
|
|
|
|
static void Objectify(struct As *a, int path) {
|
|
char *p;
|
|
int i, j, s, e;
|
|
struct ElfWriter *elf;
|
|
elf = elfwriter_open(a->strings.p[path], 0644);
|
|
for (i = 0; i < a->symbols.n; ++i) {
|
|
if (!IsLiveSymbol(a, i)) continue;
|
|
p = strndup(a->slices.p[a->symbols.p[i].name].p,
|
|
a->slices.p[a->symbols.p[i].name].n);
|
|
a->symbols.p[i].ref = elfwriter_appendsym(
|
|
elf, p, ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type),
|
|
a->symbols.p[i].stv, a->symbols.p[i].offset, a->symbols.p[i].size);
|
|
free(p);
|
|
}
|
|
for (i = 0; i < a->sections.n; ++i) {
|
|
elfwriter_align(elf, a->sections.p[i].align, 0);
|
|
s = elfwriter_startsection(elf, a->strings.p[a->sections.p[i].name],
|
|
a->sections.p[i].type, a->sections.p[i].flags);
|
|
for (j = 0; j < a->symbols.n; ++j) {
|
|
if (!IsLiveSymbol(a, j)) continue;
|
|
if (a->symbols.p[j].section != i) continue;
|
|
elfwriter_setsection(elf, a->symbols.p[j].ref, s);
|
|
}
|
|
for (j = 0; j < a->relas.n; ++j) {
|
|
if (a->relas.p[j].isdead) continue;
|
|
if (a->relas.p[j].section != i) continue;
|
|
e = a->relas.p[j].expr;
|
|
a->i = a->exprs.p[e].tok;
|
|
switch (a->exprs.p[e].kind) {
|
|
case EX_INT:
|
|
break;
|
|
case EX_SYM:
|
|
elfwriter_appendrela(elf, a->relas.p[j].offset,
|
|
a->symbols.p[a->exprs.p[e].x].ref,
|
|
a->relas.p[j].kind, a->relas.p[j].addend);
|
|
break;
|
|
case EX_ADD:
|
|
if (a->exprs.p[a->exprs.p[e].lhs].kind == EX_SYM &&
|
|
a->exprs.p[a->exprs.p[e].rhs].kind == EX_INT) {
|
|
elfwriter_appendrela(
|
|
elf, a->relas.p[j].offset,
|
|
a->symbols.p[a->exprs.p[a->exprs.p[e].lhs].x].ref,
|
|
a->relas.p[j].kind,
|
|
a->relas.p[j].addend + a->exprs.p[a->exprs.p[e].rhs].x);
|
|
} else {
|
|
Fail(a, "bad addend");
|
|
}
|
|
break;
|
|
default:
|
|
Fail(a, "unsupported relocation type");
|
|
}
|
|
}
|
|
memcpy(elfwriter_reserve(elf, a->sections.p[i].binary.n),
|
|
a->sections.p[i].binary.p, a->sections.p[i].binary.n);
|
|
elfwriter_commit(elf, a->sections.p[i].binary.n);
|
|
elfwriter_finishsection(elf);
|
|
}
|
|
elfwriter_close(elf);
|
|
}
|
|
|
|
static void CheckIntegrity(struct As *a) {
|
|
int i;
|
|
for (i = 0; i < a->things.n; ++i) {
|
|
CHECK_LT((int)a->things.p[i].s, a->sauces.n);
|
|
switch (a->things.p[i].t) {
|
|
case TT_INT:
|
|
case TT_FORWARD:
|
|
case TT_BACKWARD:
|
|
CHECK_LT(a->things.p[i].i, a->ints.n);
|
|
break;
|
|
case TT_FLOAT:
|
|
CHECK_LT(a->things.p[i].i, a->floats.n);
|
|
break;
|
|
case TT_SLICE:
|
|
CHECK_LT(a->things.p[i].i, a->slices.n);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
for (i = 0; i < a->sections.n; ++i) {
|
|
CHECK_LT(a->sections.p[i].name, a->strings.n);
|
|
}
|
|
for (i = 0; i < a->symbols.n; ++i) {
|
|
CHECK_LT(a->symbols.p[i].name, a->slices.n);
|
|
CHECK_LT(a->symbols.p[i].section, a->sections.n);
|
|
}
|
|
for (i = 0; i < a->labels.n; ++i) {
|
|
CHECK_LT(a->labels.p[i].tok, a->things.n);
|
|
CHECK_LT(a->labels.p[i].symbol, a->symbols.n);
|
|
}
|
|
for (i = 0; i < a->relas.n; ++i) {
|
|
CHECK_LT(a->relas.p[i].expr, a->exprs.n);
|
|
CHECK_LT(a->relas.p[i].section, a->sections.n);
|
|
}
|
|
for (i = 0; i < a->exprs.n; ++i) {
|
|
CHECK_LT(a->exprs.p[i].tok, a->things.n);
|
|
if (a->exprs.p[i].lhs != -1) CHECK_LT(a->exprs.p[i].lhs, a->exprs.n);
|
|
if (a->exprs.p[i].rhs != -1) CHECK_LT(a->exprs.p[i].rhs, a->exprs.n);
|
|
switch (a->exprs.p[i].kind) {
|
|
case EX_SYM:
|
|
CHECK_LT(a->exprs.p[i].x, a->things.n);
|
|
CHECK(a->things.p[a->exprs.p[i].x].t == TT_SLICE ||
|
|
a->things.p[a->exprs.p[i].x].t == TT_FORWARD ||
|
|
a->things.p[a->exprs.p[i].x].t == TT_BACKWARD);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PrintThings(struct As *a) {
|
|
int i;
|
|
char pbuf[4], fbuf[32];
|
|
for (i = 0; i < a->things.n; ++i) {
|
|
printf("%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[i].s].path],
|
|
a->sauces.p[a->things.p[i].s].line);
|
|
switch (a->things.p[i].t) {
|
|
case TT_INT:
|
|
printf("TT_INT %ld\n", a->ints.p[a->things.p[i].i]);
|
|
break;
|
|
case TT_FLOAT:
|
|
g_xfmt_p(fbuf, &a->floats.p[a->things.p[i].i], 19, sizeof(fbuf), 0);
|
|
printf("TT_FLOAT %s\n", fbuf);
|
|
break;
|
|
case TT_SLICE:
|
|
printf("TT_SLICE %.*s\n", a->slices.p[a->things.p[i].i].n,
|
|
a->slices.p[a->things.p[i].i].p);
|
|
break;
|
|
case TT_PUNCT:
|
|
printf("TT_PUNCT %s\n", PunctToStr(a->things.p[i].i, pbuf));
|
|
break;
|
|
case TT_BACKWARD:
|
|
printf("TT_BACKWARD %d\n", a->ints.p[a->things.p[i].i]);
|
|
break;
|
|
case TT_FORWARD:
|
|
printf("TT_FORWARD %d\n", a->ints.p[a->things.p[i].i]);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Assembler(int argc, char *argv[]) {
|
|
struct As *a;
|
|
a = NewAssembler();
|
|
ReadFlags(a, argc, argv);
|
|
SaveString(&a->incpaths, strdup("."));
|
|
SaveString(&a->incpaths, xdirname(a->strings.p[a->inpath]));
|
|
Tokenize(a, a->inpath);
|
|
/* PrintThings(a); */
|
|
Assemble(a);
|
|
/* CheckIntegrity(a); */
|
|
Evaluate(a);
|
|
MarkUndefinedSymbolsGlobal(a);
|
|
Objectify(a, a->outpath);
|
|
/* malloc_stats(); */
|
|
FreeAssembler(a);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
showcrashreports();
|
|
if (argc == 1) {
|
|
system("o//third_party/chibicc/as.com -o /tmp/o /home/jart/trash/hog.s");
|
|
system("objdump -xwd /tmp/o");
|
|
exit(0);
|
|
}
|
|
Assembler(argc, argv);
|
|
return 0;
|
|
}
|