diff options
Diffstat (limited to 'src')
64 files changed, 22926 insertions, 0 deletions
diff --git a/src/3p/README b/src/3p/README new file mode 100644 index 0000000..85b225c --- /dev/null +++ b/src/3p/README @@ -0,0 +1,24 @@ +These are imported 3rd party library sources, wrangled for ease of plonking into +the build (e.g. relative #includes, etc.). + +Used in SST itself: +  - libmpack +  - monocypher +  - udis86 + +Used at build time: +  - chibicc (somewhat hacked up for use as a lexer) +  - asprintf() from OpenBSD (for compatibility on Windows) + +Most of the C sources have wrappers in the parent directory to build proper +objects for use in the project, and wrapper headers to get the full APIs as +conveniently as possible. In other words, most of these files aren't built or +used directly. + +It is possible that these libraries may end up lightly modified; we err on the +side of changing things to fit our use case rather than working around problems +in outer layers. A couple of the libraries are pretty old and don't see much +upstream change, but are small enough to be comfortably maintained as vendored. + +IMPORTANT! Libraries are distributed subject to their copyright notices; please +refer to those! diff --git a/src/3p/chibicc/LICENSE b/src/3p/chibicc/LICENSE new file mode 100644 index 0000000..2d1fd94 --- /dev/null +++ b/src/3p/chibicc/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Rui Ueyama + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/3p/chibicc/chibicc.h b/src/3p/chibicc/chibicc.h new file mode 100644 index 0000000..1719bc5 --- /dev/null +++ b/src/3p/chibicc/chibicc.h @@ -0,0 +1,486 @@ +// include guards: upstream doesn't have these but we add them so we can cat +// source files together (or #include them, in particular) +#ifndef INC_CHIBICC_H +#define INC_CHIBICC_H + +// note: removing defs/headers that aren't needed in tokenize.c and/or don't +// exist on Windows, in order to get our stuff working. total hack; oh well. +//#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include <ctype.h> +#include <errno.h> +//#include <glob.h> +//#include <libgen.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +// stdnoreturn means we can't use our noreturn (_Noreturn void) +// there are no noreturns in tokenize.c anyway, and the ones in this header have +// been changed to just _Noreturn to avoid any possible conflict +//#include <stdnoreturn.h> +#include <string.h> +//#include <strings.h> +#include <sys/stat.h> +//#include <sys/types.h> +//#include <sys/wait.h> +#include <time.h> +//#include <unistd.h> + +// exists on all Unixes but normally hidden _GNU_SOURCE on Linux. +// missing entirely on Windows (implemented in 3p/openbsd/asprintf.c for compat) +int vasprintf(char **str, const char *fmt, va_list ap); + +#define MAX(x, y) ((x) < (y) ? (y) : (x)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#if !defined(__GNUC__) && !defined(__clang__) +# define __attribute__(x) +#endif + +typedef struct Type Type; +typedef struct Node Node; +typedef struct Member Member; +typedef struct Relocation Relocation; +typedef struct Hideset Hideset; + +// +// strings.c +// + +typedef struct { +  char **data; +  int capacity; +  int len; +} StringArray; + +void strarray_push(StringArray *arr, char *s); + +// +// tokenize.c +// + +// Token +typedef enum { +  TK_IDENT,   // Identifiers +  TK_PUNCT,   // Punctuators +  TK_KEYWORD, // Keywords +  TK_STR,     // String literals +  TK_NUM,     // Numeric literals +  TK_PP_NUM,  // Preprocessing numbers +  TK_EOF,     // End-of-file markers +} TokenKind; + +typedef struct { +  char *name; +  int file_no; +  char *contents; + +  // For #line directive +  char *display_name; +  int line_delta; +} File; + +// Token type +typedef struct Token Token; +struct Token { +  TokenKind kind;   // Token kind +  Token *next;      // Next token +  int64_t val;      // If kind is TK_NUM, its value +  long double fval; // If kind is TK_NUM, its value +  char *loc;        // Token location +  int len;          // Token length +  Type *ty;         // Used if TK_NUM or TK_STR +  char *str;        // String literal contents including terminating '\0' + +  File *file;       // Source location +  char *filename;   // Filename +  int line_no;      // Line number +  int line_delta;   // Line number +  bool at_bol;      // True if this token is at beginning of line +  bool has_space;   // True if this token follows a space character +  Hideset *hideset; // For macro expansion +  Token *origin;    // If this is expanded from a macro, the original token +}; + +_Noreturn void error(char *fmt, ...) __attribute__((format(printf, 1, 2))); +_Noreturn void error_at(char *loc, char *fmt, ...) __attribute__((format(printf, 2, 3))); +_Noreturn void error_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3))); +void warn_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3))); +bool equal(Token *tok, char *op); +Token *skip(Token *tok, char *op); +bool consume(Token **rest, Token *tok, char *str); +void convert_pp_tokens(Token *tok); +File **get_input_files(void); +File *new_file(char *name, int file_no, char *contents); +Token *tokenize_string_literal(Token *tok, Type *basety); +Token *tokenize(File *file); +//Token *tokenize_file(char *filename); +Token *tokenize_buf(const char *name, char *p); + +// note: replacing memstream-based format with asprintf version. moved down here +// as error() is declared above. +//char *format(char *fmt, ...) __attribute__((format(printf, 1, 2))); +__attribute__((format(printf, 1, 2))) +static inline char *format(const char *fmt, ...) { +  char *ret; +  va_list va; +  va_start(va, fmt); +  if (vasprintf(&ret, fmt, va) == -1) error("couldn't allocate memory"); +  va_end(va); +  return ret; +} + +#define unreachable() \ +  error("internal error at %s:%d", __FILE__, __LINE__) + +// +// preprocess.c +// + +char *search_include_paths(char *filename); +void init_macros(void); +void define_macro(char *name, char *buf); +void undef_macro(char *name); +Token *preprocess(Token *tok); + +// +// parse.c +// + +// Variable or function +typedef struct Obj Obj; +struct Obj { +  Obj *next; +  char *name;    // Variable name +  Type *ty;      // Type +  Token *tok;    // representative token +  bool is_local; // local or global/function +  int align;     // alignment + +  // Local variable +  int offset; + +  // Global variable or function +  bool is_function; +  bool is_definition; +  bool is_static; + +  // Global variable +  bool is_tentative; +  bool is_tls; +  char *init_data; +  Relocation *rel; + +  // Function +  bool is_inline; +  Obj *params; +  Node *body; +  Obj *locals; +  Obj *va_area; +  Obj *alloca_bottom; +  int stack_size; + +  // Static inline function +  bool is_live; +  bool is_root; +  StringArray refs; +}; + +// Global variable can be initialized either by a constant expression +// or a pointer to another global variable. This struct represents the +// latter. +typedef struct Relocation Relocation; +struct Relocation { +  Relocation *next; +  int offset; +  char **label; +  long addend; +}; + +// AST node +typedef enum { +  ND_NULL_EXPR, // Do nothing +  ND_ADD,       // + +  ND_SUB,       // - +  ND_MUL,       // * +  ND_DIV,       // / +  ND_NEG,       // unary - +  ND_MOD,       // % +  ND_BITAND,    // & +  ND_BITOR,     // | +  ND_BITXOR,    // ^ +  ND_SHL,       // << +  ND_SHR,       // >> +  ND_EQ,        // == +  ND_NE,        // != +  ND_LT,        // < +  ND_LE,        // <= +  ND_ASSIGN,    // = +  ND_COND,      // ?: +  ND_COMMA,     // , +  ND_MEMBER,    // . (struct member access) +  ND_ADDR,      // unary & +  ND_DEREF,     // unary * +  ND_NOT,       // ! +  ND_BITNOT,    // ~ +  ND_LOGAND,    // && +  ND_LOGOR,     // || +  ND_RETURN,    // "return" +  ND_IF,        // "if" +  ND_FOR,       // "for" or "while" +  ND_DO,        // "do" +  ND_SWITCH,    // "switch" +  ND_CASE,      // "case" +  ND_BLOCK,     // { ... } +  ND_GOTO,      // "goto" +  ND_GOTO_EXPR, // "goto" labels-as-values +  ND_LABEL,     // Labeled statement +  ND_LABEL_VAL, // [GNU] Labels-as-values +  ND_FUNCALL,   // Function call +  ND_EXPR_STMT, // Expression statement +  ND_STMT_EXPR, // Statement expression +  ND_VAR,       // Variable +  ND_VLA_PTR,   // VLA designator +  ND_NUM,       // Integer +  ND_CAST,      // Type cast +  ND_MEMZERO,   // Zero-clear a stack variable +  ND_ASM,       // "asm" +  ND_CAS,       // Atomic compare-and-swap +  ND_EXCH,      // Atomic exchange +} NodeKind; + +// AST node type +struct Node { +  NodeKind kind; // Node kind +  Node *next;    // Next node +  Type *ty;      // Type, e.g. int or pointer to int +  Token *tok;    // Representative token + +  Node *lhs;     // Left-hand side +  Node *rhs;     // Right-hand side + +  // "if" or "for" statement +  Node *cond; +  Node *then; +  Node *els; +  Node *init; +  Node *inc; + +  // "break" and "continue" labels +  char *brk_label; +  char *cont_label; + +  // Block or statement expression +  Node *body; + +  // Struct member access +  Member *member; + +  // Function call +  Type *func_ty; +  Node *args; +  bool pass_by_stack; +  Obj *ret_buffer; + +  // Goto or labeled statement, or labels-as-values +  char *label; +  char *unique_label; +  Node *goto_next; + +  // Switch +  Node *case_next; +  Node *default_case; + +  // Case +  long begin; +  long end; + +  // "asm" string literal +  char *asm_str; + +  // Atomic compare-and-swap +  Node *cas_addr; +  Node *cas_old; +  Node *cas_new; + +  // Atomic op= operators +  Obj *atomic_addr; +  Node *atomic_expr; + +  // Variable +  Obj *var; + +  // Numeric literal +  int64_t val; +  long double fval; +}; + +Node *new_cast(Node *expr, Type *ty); +int64_t const_expr(Token **rest, Token *tok); +Obj *parse(Token *tok); + +// +// type.c +// + +typedef enum { +  TY_VOID, +  TY_BOOL, +  TY_CHAR, +  TY_SHORT, +  TY_INT, +  TY_LONG, +  TY_FLOAT, +  TY_DOUBLE, +  TY_LDOUBLE, +  TY_ENUM, +  TY_PTR, +  TY_FUNC, +  TY_ARRAY, +  TY_VLA, // variable-length array +  TY_STRUCT, +  TY_UNION, +} TypeKind; + +struct Type { +  TypeKind kind; +  int size;           // sizeof() value +  int align;          // alignment +  bool is_unsigned;   // unsigned or signed +  bool is_atomic;     // true if _Atomic +  Type *origin;       // for type compatibility check + +  // Pointer-to or array-of type. We intentionally use the same member +  // to represent pointer/array duality in C. +  // +  // In many contexts in which a pointer is expected, we examine this +  // member instead of "kind" member to determine whether a type is a +  // pointer or not. That means in many contexts "array of T" is +  // naturally handled as if it were "pointer to T", as required by +  // the C spec. +  Type *base; + +  // Declaration +  Token *name; +  Token *name_pos; + +  // Array +  int array_len; + +  // Variable-length array +  Node *vla_len; // # of elements +  Obj *vla_size; // sizeof() value + +  // Struct +  Member *members; +  bool is_flexible; +  bool is_packed; + +  // Function type +  Type *return_ty; +  Type *params; +  bool is_variadic; +  Type *next; +}; + +// Struct member +struct Member { +  Member *next; +  Type *ty; +  Token *tok; // for error message +  Token *name; +  int idx; +  int align; +  int offset; + +  // Bitfield +  bool is_bitfield; +  int bit_offset; +  int bit_width; +}; + +extern Type *ty_void; +extern Type *ty_bool; + +extern Type *ty_char; +extern Type *ty_short; +extern Type *ty_int; +extern Type *ty_long; + +extern Type *ty_uchar; +extern Type *ty_ushort; +extern Type *ty_uint; +extern Type *ty_ulong; + +extern Type *ty_float; +extern Type *ty_double; +extern Type *ty_ldouble; + +bool is_integer(Type *ty); +bool is_flonum(Type *ty); +bool is_numeric(Type *ty); +bool is_compatible(Type *t1, Type *t2); +Type *copy_type(Type *ty); +Type *pointer_to(Type *base); +Type *func_type(Type *return_ty); +Type *array_of(Type *base, int size); +Type *vla_of(Type *base, Node *expr); +Type *enum_type(void); +Type *struct_type(void); +void add_type(Node *node); + +// +// codegen.c +// + +void codegen(Obj *prog, FILE *out); +int align_to(int n, int align); + +// +// unicode.c +// + +int encode_utf8(char *buf, uint32_t c); +uint32_t decode_utf8(char **new_pos, char *p); +bool is_ident1(uint32_t c); +bool is_ident2(uint32_t c); +int display_width(char *p, int len); + +// +// hashmap.c +// + +typedef struct { +  char *key; +  int keylen; +  void *val; +} HashEntry; + +typedef struct { +  HashEntry *buckets; +  int capacity; +  int used; +} HashMap; + +void *hashmap_get(HashMap *map, char *key); +void *hashmap_get2(HashMap *map, char *key, int keylen); +void hashmap_put(HashMap *map, char *key, void *val); +void hashmap_put2(HashMap *map, char *key, int keylen, void *val); +void hashmap_delete(HashMap *map, char *key); +void hashmap_delete2(HashMap *map, char *key, int keylen); +void hashmap_test(void); + +// +// main.c +// + +bool file_exists(char *path); + +extern StringArray include_paths; +extern bool opt_fpic; +extern bool opt_fcommon; +extern char *base_file; + +#endif diff --git a/src/3p/chibicc/codegen.c b/src/3p/chibicc/codegen.c new file mode 100644 index 0000000..da11fd7 --- /dev/null +++ b/src/3p/chibicc/codegen.c @@ -0,0 +1,1595 @@ +#include "chibicc.h" + +#define GP_MAX 6 +#define FP_MAX 8 + +static FILE *output_file; +static int depth; +static char *argreg8[] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"}; +static char *argreg16[] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"}; +static char *argreg32[] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"}; +static char *argreg64[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"}; +static Obj *current_fn; + +static void gen_expr(Node *node); +static void gen_stmt(Node *node); + +__attribute__((format(printf, 1, 2))) +static void println(char *fmt, ...) { +  va_list ap; +  va_start(ap, fmt); +  vfprintf(output_file, fmt, ap); +  va_end(ap); +  fprintf(output_file, "\n"); +} + +static int count(void) { +  static int i = 1; +  return i++; +} + +static void push(void) { +  println("  push %%rax"); +  depth++; +} + +static void pop(char *arg) { +  println("  pop %s", arg); +  depth--; +} + +static void pushf(void) { +  println("  sub $8, %%rsp"); +  println("  movsd %%xmm0, (%%rsp)"); +  depth++; +} + +static void popf(int reg) { +  println("  movsd (%%rsp), %%xmm%d", reg); +  println("  add $8, %%rsp"); +  depth--; +} + +// Round up `n` to the nearest multiple of `align`. For instance, +// align_to(5, 8) returns 8 and align_to(11, 8) returns 16. +int align_to(int n, int align) { +  return (n + align - 1) / align * align; +} + +static char *reg_dx(int sz) { +  switch (sz) { +  case 1: return "%dl"; +  case 2: return "%dx"; +  case 4: return "%edx"; +  case 8: return "%rdx"; +  } +  unreachable(); +} + +static char *reg_ax(int sz) { +  switch (sz) { +  case 1: return "%al"; +  case 2: return "%ax"; +  case 4: return "%eax"; +  case 8: return "%rax"; +  } +  unreachable(); +} + +// Compute the absolute address of a given node. +// It's an error if a given node does not reside in memory. +static void gen_addr(Node *node) { +  switch (node->kind) { +  case ND_VAR: +    // Variable-length array, which is always local. +    if (node->var->ty->kind == TY_VLA) { +      println("  mov %d(%%rbp), %%rax", node->var->offset); +      return; +    } + +    // Local variable +    if (node->var->is_local) { +      println("  lea %d(%%rbp), %%rax", node->var->offset); +      return; +    } + +    if (opt_fpic) { +      // Thread-local variable +      if (node->var->is_tls) { +        println("  data16 lea %s@tlsgd(%%rip), %%rdi", node->var->name); +        println("  .value 0x6666"); +        println("  rex64"); +        println("  call __tls_get_addr@PLT"); +        return; +      } + +      // Function or global variable +      println("  mov %s@GOTPCREL(%%rip), %%rax", node->var->name); +      return; +    } + +    // Thread-local variable +    if (node->var->is_tls) { +      println("  mov %%fs:0, %%rax"); +      println("  add $%s@tpoff, %%rax", node->var->name); +      return; +    } + +    // Here, we generate an absolute address of a function or a global +    // variable. Even though they exist at a certain address at runtime, +    // their addresses are not known at link-time for the following +    // two reasons. +    // +    //  - Address randomization: Executables are loaded to memory as a +    //    whole but it is not known what address they are loaded to. +    //    Therefore, at link-time, relative address in the same +    //    exectuable (i.e. the distance between two functions in the +    //    same executable) is known, but the absolute address is not +    //    known. +    // +    //  - Dynamic linking: Dynamic shared objects (DSOs) or .so files +    //    are loaded to memory alongside an executable at runtime and +    //    linked by the runtime loader in memory. We know nothing +    //    about addresses of global stuff that may be defined by DSOs +    //    until the runtime relocation is complete. +    // +    // In order to deal with the former case, we use RIP-relative +    // addressing, denoted by `(%rip)`. For the latter, we obtain an +    // address of a stuff that may be in a shared object file from the +    // Global Offset Table using `@GOTPCREL(%rip)` notation. + +    // Function +    if (node->ty->kind == TY_FUNC) { +      if (node->var->is_definition) +        println("  lea %s(%%rip), %%rax", node->var->name); +      else +        println("  mov %s@GOTPCREL(%%rip), %%rax", node->var->name); +      return; +    } + +    // Global variable +    println("  lea %s(%%rip), %%rax", node->var->name); +    return; +  case ND_DEREF: +    gen_expr(node->lhs); +    return; +  case ND_COMMA: +    gen_expr(node->lhs); +    gen_addr(node->rhs); +    return; +  case ND_MEMBER: +    gen_addr(node->lhs); +    println("  add $%d, %%rax", node->member->offset); +    return; +  case ND_FUNCALL: +    if (node->ret_buffer) { +      gen_expr(node); +      return; +    } +    break; +  case ND_ASSIGN: +  case ND_COND: +    if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) { +      gen_expr(node); +      return; +    } +    break; +  case ND_VLA_PTR: +    println("  lea %d(%%rbp), %%rax", node->var->offset); +    return; +  } + +  error_tok(node->tok, "not an lvalue"); +} + +// Load a value from where %rax is pointing to. +static void load(Type *ty) { +  switch (ty->kind) { +  case TY_ARRAY: +  case TY_STRUCT: +  case TY_UNION: +  case TY_FUNC: +  case TY_VLA: +    // If it is an array, do not attempt to load a value to the +    // register because in general we can't load an entire array to a +    // register. As a result, the result of an evaluation of an array +    // becomes not the array itself but the address of the array. +    // This is where "array is automatically converted to a pointer to +    // the first element of the array in C" occurs. +    return; +  case TY_FLOAT: +    println("  movss (%%rax), %%xmm0"); +    return; +  case TY_DOUBLE: +    println("  movsd (%%rax), %%xmm0"); +    return; +  case TY_LDOUBLE: +    println("  fldt (%%rax)"); +    return; +  } + +  char *insn = ty->is_unsigned ? "movz" : "movs"; + +  // When we load a char or a short value to a register, we always +  // extend them to the size of int, so we can assume the lower half of +  // a register always contains a valid value. The upper half of a +  // register for char, short and int may contain garbage. When we load +  // a long value to a register, it simply occupies the entire register. +  if (ty->size == 1) +    println("  %sbl (%%rax), %%eax", insn); +  else if (ty->size == 2) +    println("  %swl (%%rax), %%eax", insn); +  else if (ty->size == 4) +    println("  movsxd (%%rax), %%rax"); +  else +    println("  mov (%%rax), %%rax"); +} + +// Store %rax to an address that the stack top is pointing to. +static void store(Type *ty) { +  pop("%rdi"); + +  switch (ty->kind) { +  case TY_STRUCT: +  case TY_UNION: +    for (int i = 0; i < ty->size; i++) { +      println("  mov %d(%%rax), %%r8b", i); +      println("  mov %%r8b, %d(%%rdi)", i); +    } +    return; +  case TY_FLOAT: +    println("  movss %%xmm0, (%%rdi)"); +    return; +  case TY_DOUBLE: +    println("  movsd %%xmm0, (%%rdi)"); +    return; +  case TY_LDOUBLE: +    println("  fstpt (%%rdi)"); +    return; +  } + +  if (ty->size == 1) +    println("  mov %%al, (%%rdi)"); +  else if (ty->size == 2) +    println("  mov %%ax, (%%rdi)"); +  else if (ty->size == 4) +    println("  mov %%eax, (%%rdi)"); +  else +    println("  mov %%rax, (%%rdi)"); +} + +static void cmp_zero(Type *ty) { +  switch (ty->kind) { +  case TY_FLOAT: +    println("  xorps %%xmm1, %%xmm1"); +    println("  ucomiss %%xmm1, %%xmm0"); +    return; +  case TY_DOUBLE: +    println("  xorpd %%xmm1, %%xmm1"); +    println("  ucomisd %%xmm1, %%xmm0"); +    return; +  case TY_LDOUBLE: +    println("  fldz"); +    println("  fucomip"); +    println("  fstp %%st(0)"); +    return; +  } + +  if (is_integer(ty) && ty->size <= 4) +    println("  cmp $0, %%eax"); +  else +    println("  cmp $0, %%rax"); +} + +enum { I8, I16, I32, I64, U8, U16, U32, U64, F32, F64, F80 }; + +static int getTypeId(Type *ty) { +  switch (ty->kind) { +  case TY_CHAR: +    return ty->is_unsigned ? U8 : I8; +  case TY_SHORT: +    return ty->is_unsigned ? U16 : I16; +  case TY_INT: +    return ty->is_unsigned ? U32 : I32; +  case TY_LONG: +    return ty->is_unsigned ? U64 : I64; +  case TY_FLOAT: +    return F32; +  case TY_DOUBLE: +    return F64; +  case TY_LDOUBLE: +    return F80; +  } +  return U64; +} + +// The table for type casts +static char i32i8[] = "movsbl %al, %eax"; +static char i32u8[] = "movzbl %al, %eax"; +static char i32i16[] = "movswl %ax, %eax"; +static char i32u16[] = "movzwl %ax, %eax"; +static char i32f32[] = "cvtsi2ssl %eax, %xmm0"; +static char i32i64[] = "movsxd %eax, %rax"; +static char i32f64[] = "cvtsi2sdl %eax, %xmm0"; +static char i32f80[] = "mov %eax, -4(%rsp); fildl -4(%rsp)"; + +static char u32f32[] = "mov %eax, %eax; cvtsi2ssq %rax, %xmm0"; +static char u32i64[] = "mov %eax, %eax"; +static char u32f64[] = "mov %eax, %eax; cvtsi2sdq %rax, %xmm0"; +static char u32f80[] = "mov %eax, %eax; mov %rax, -8(%rsp); fildll -8(%rsp)"; + +static char i64f32[] = "cvtsi2ssq %rax, %xmm0"; +static char i64f64[] = "cvtsi2sdq %rax, %xmm0"; +static char i64f80[] = "movq %rax, -8(%rsp); fildll -8(%rsp)"; + +static char u64f32[] = "cvtsi2ssq %rax, %xmm0"; +static char u64f64[] = +  "test %rax,%rax; js 1f; pxor %xmm0,%xmm0; cvtsi2sd %rax,%xmm0; jmp 2f; " +  "1: mov %rax,%rdi; and $1,%eax; pxor %xmm0,%xmm0; shr %rdi; " +  "or %rax,%rdi; cvtsi2sd %rdi,%xmm0; addsd %xmm0,%xmm0; 2:"; +static char u64f80[] = +  "mov %rax, -8(%rsp); fildq -8(%rsp); test %rax, %rax; jns 1f;" +  "mov $1602224128, %eax; mov %eax, -4(%rsp); fadds -4(%rsp); 1:"; + +static char f32i8[] = "cvttss2sil %xmm0, %eax; movsbl %al, %eax"; +static char f32u8[] = "cvttss2sil %xmm0, %eax; movzbl %al, %eax"; +static char f32i16[] = "cvttss2sil %xmm0, %eax; movswl %ax, %eax"; +static char f32u16[] = "cvttss2sil %xmm0, %eax; movzwl %ax, %eax"; +static char f32i32[] = "cvttss2sil %xmm0, %eax"; +static char f32u32[] = "cvttss2siq %xmm0, %rax"; +static char f32i64[] = "cvttss2siq %xmm0, %rax"; +static char f32u64[] = "cvttss2siq %xmm0, %rax"; +static char f32f64[] = "cvtss2sd %xmm0, %xmm0"; +static char f32f80[] = "movss %xmm0, -4(%rsp); flds -4(%rsp)"; + +static char f64i8[] = "cvttsd2sil %xmm0, %eax; movsbl %al, %eax"; +static char f64u8[] = "cvttsd2sil %xmm0, %eax; movzbl %al, %eax"; +static char f64i16[] = "cvttsd2sil %xmm0, %eax; movswl %ax, %eax"; +static char f64u16[] = "cvttsd2sil %xmm0, %eax; movzwl %ax, %eax"; +static char f64i32[] = "cvttsd2sil %xmm0, %eax"; +static char f64u32[] = "cvttsd2siq %xmm0, %rax"; +static char f64i64[] = "cvttsd2siq %xmm0, %rax"; +static char f64u64[] = "cvttsd2siq %xmm0, %rax"; +static char f64f32[] = "cvtsd2ss %xmm0, %xmm0"; +static char f64f80[] = "movsd %xmm0, -8(%rsp); fldl -8(%rsp)"; + +#define FROM_F80_1                                           \ +  "fnstcw -10(%rsp); movzwl -10(%rsp), %eax; or $12, %ah; " \ +  "mov %ax, -12(%rsp); fldcw -12(%rsp); " + +#define FROM_F80_2 " -24(%rsp); fldcw -10(%rsp); " + +static char f80i8[] = FROM_F80_1 "fistps" FROM_F80_2 "movsbl -24(%rsp), %eax"; +static char f80u8[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +static char f80i16[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +static char f80u16[] = FROM_F80_1 "fistpl" FROM_F80_2 "movswl -24(%rsp), %eax"; +static char f80i32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +static char f80u32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +static char f80i64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +static char f80u64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +static char f80f32[] = "fstps -8(%rsp); movss -8(%rsp), %xmm0"; +static char f80f64[] = "fstpl -8(%rsp); movsd -8(%rsp), %xmm0"; + +static char *cast_table[][11] = { +  // i8   i16     i32     i64     u8     u16     u32     u64     f32     f64     f80 +  {NULL,  NULL,   NULL,   i32i64, i32u8, i32u16, NULL,   i32i64, i32f32, i32f64, i32f80}, // i8 +  {i32i8, NULL,   NULL,   i32i64, i32u8, i32u16, NULL,   i32i64, i32f32, i32f64, i32f80}, // i16 +  {i32i8, i32i16, NULL,   i32i64, i32u8, i32u16, NULL,   i32i64, i32f32, i32f64, i32f80}, // i32 +  {i32i8, i32i16, NULL,   NULL,   i32u8, i32u16, NULL,   NULL,   i64f32, i64f64, i64f80}, // i64 + +  {i32i8, NULL,   NULL,   i32i64, NULL,  NULL,   NULL,   i32i64, i32f32, i32f64, i32f80}, // u8 +  {i32i8, i32i16, NULL,   i32i64, i32u8, NULL,   NULL,   i32i64, i32f32, i32f64, i32f80}, // u16 +  {i32i8, i32i16, NULL,   u32i64, i32u8, i32u16, NULL,   u32i64, u32f32, u32f64, u32f80}, // u32 +  {i32i8, i32i16, NULL,   NULL,   i32u8, i32u16, NULL,   NULL,   u64f32, u64f64, u64f80}, // u64 + +  {f32i8, f32i16, f32i32, f32i64, f32u8, f32u16, f32u32, f32u64, NULL,   f32f64, f32f80}, // f32 +  {f64i8, f64i16, f64i32, f64i64, f64u8, f64u16, f64u32, f64u64, f64f32, NULL,   f64f80}, // f64 +  {f80i8, f80i16, f80i32, f80i64, f80u8, f80u16, f80u32, f80u64, f80f32, f80f64, NULL},   // f80 +}; + +static void cast(Type *from, Type *to) { +  if (to->kind == TY_VOID) +    return; + +  if (to->kind == TY_BOOL) { +    cmp_zero(from); +    println("  setne %%al"); +    println("  movzx %%al, %%eax"); +    return; +  } + +  int t1 = getTypeId(from); +  int t2 = getTypeId(to); +  if (cast_table[t1][t2]) +    println("  %s", cast_table[t1][t2]); +} + +// Structs or unions equal or smaller than 16 bytes are passed +// using up to two registers. +// +// If the first 8 bytes contains only floating-point type members, +// they are passed in an XMM register. Otherwise, they are passed +// in a general-purpose register. +// +// If a struct/union is larger than 8 bytes, the same rule is +// applied to the the next 8 byte chunk. +// +// This function returns true if `ty` has only floating-point +// members in its byte range [lo, hi). +static bool has_flonum(Type *ty, int lo, int hi, int offset) { +  if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) { +    for (Member *mem = ty->members; mem; mem = mem->next) +      if (!has_flonum(mem->ty, lo, hi, offset + mem->offset)) +        return false; +    return true; +  } + +  if (ty->kind == TY_ARRAY) { +    for (int i = 0; i < ty->array_len; i++) +      if (!has_flonum(ty->base, lo, hi, offset + ty->base->size * i)) +        return false; +    return true; +  } + +  return offset < lo || hi <= offset || ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE; +} + +static bool has_flonum1(Type *ty) { +  return has_flonum(ty, 0, 8, 0); +} + +static bool has_flonum2(Type *ty) { +  return has_flonum(ty, 8, 16, 0); +} + +static void push_struct(Type *ty) { +  int sz = align_to(ty->size, 8); +  println("  sub $%d, %%rsp", sz); +  depth += sz / 8; + +  for (int i = 0; i < ty->size; i++) { +    println("  mov %d(%%rax), %%r10b", i); +    println("  mov %%r10b, %d(%%rsp)", i); +  } +} + +static void push_args2(Node *args, bool first_pass) { +  if (!args) +    return; +  push_args2(args->next, first_pass); + +  if ((first_pass && !args->pass_by_stack) || (!first_pass && args->pass_by_stack)) +    return; + +  gen_expr(args); + +  switch (args->ty->kind) { +  case TY_STRUCT: +  case TY_UNION: +    push_struct(args->ty); +    break; +  case TY_FLOAT: +  case TY_DOUBLE: +    pushf(); +    break; +  case TY_LDOUBLE: +    println("  sub $16, %%rsp"); +    println("  fstpt (%%rsp)"); +    depth += 2; +    break; +  default: +    push(); +  } +} + +// Load function call arguments. Arguments are already evaluated and +// stored to the stack as local variables. What we need to do in this +// function is to load them to registers or push them to the stack as +// specified by the x86-64 psABI. Here is what the spec says: +// +// - Up to 6 arguments of integral type are passed using RDI, RSI, +//   RDX, RCX, R8 and R9. +// +// - Up to 8 arguments of floating-point type are passed using XMM0 to +//   XMM7. +// +// - If all registers of an appropriate type are already used, push an +//   argument to the stack in the right-to-left order. +// +// - Each argument passed on the stack takes 8 bytes, and the end of +//   the argument area must be aligned to a 16 byte boundary. +// +// - If a function is variadic, set the number of floating-point type +//   arguments to RAX. +static int push_args(Node *node) { +  int stack = 0, gp = 0, fp = 0; + +  // If the return type is a large struct/union, the caller passes +  // a pointer to a buffer as if it were the first argument. +  if (node->ret_buffer && node->ty->size > 16) +    gp++; + +  // Load as many arguments to the registers as possible. +  for (Node *arg = node->args; arg; arg = arg->next) { +    Type *ty = arg->ty; + +    switch (ty->kind) { +    case TY_STRUCT: +    case TY_UNION: +      if (ty->size > 16) { +        arg->pass_by_stack = true; +        stack += align_to(ty->size, 8) / 8; +      } else { +        bool fp1 = has_flonum1(ty); +        bool fp2 = has_flonum2(ty); + +        if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { +          fp = fp + fp1 + fp2; +          gp = gp + !fp1 + !fp2; +        } else { +          arg->pass_by_stack = true; +          stack += align_to(ty->size, 8) / 8; +        } +      } +      break; +    case TY_FLOAT: +    case TY_DOUBLE: +      if (fp++ >= FP_MAX) { +        arg->pass_by_stack = true; +        stack++; +      } +      break; +    case TY_LDOUBLE: +      arg->pass_by_stack = true; +      stack += 2; +      break; +    default: +      if (gp++ >= GP_MAX) { +        arg->pass_by_stack = true; +        stack++; +      } +    } +  } + +  if ((depth + stack) % 2 == 1) { +    println("  sub $8, %%rsp"); +    depth++; +    stack++; +  } + +  push_args2(node->args, true); +  push_args2(node->args, false); + +  // If the return type is a large struct/union, the caller passes +  // a pointer to a buffer as if it were the first argument. +  if (node->ret_buffer && node->ty->size > 16) { +    println("  lea %d(%%rbp), %%rax", node->ret_buffer->offset); +    push(); +  } + +  return stack; +} + +static void copy_ret_buffer(Obj *var) { +  Type *ty = var->ty; +  int gp = 0, fp = 0; + +  if (has_flonum1(ty)) { +    assert(ty->size == 4 || 8 <= ty->size); +    if (ty->size == 4) +      println("  movss %%xmm0, %d(%%rbp)", var->offset); +    else +      println("  movsd %%xmm0, %d(%%rbp)", var->offset); +    fp++; +  } else { +    for (int i = 0; i < MIN(8, ty->size); i++) { +      println("  mov %%al, %d(%%rbp)", var->offset + i); +      println("  shr $8, %%rax"); +    } +    gp++; +  } + +  if (ty->size > 8) { +    if (has_flonum2(ty)) { +      assert(ty->size == 12 || ty->size == 16); +      if (ty->size == 12) +        println("  movss %%xmm%d, %d(%%rbp)", fp, var->offset + 8); +      else +        println("  movsd %%xmm%d, %d(%%rbp)", fp, var->offset + 8); +    } else { +      char *reg1 = (gp == 0) ? "%al" : "%dl"; +      char *reg2 = (gp == 0) ? "%rax" : "%rdx"; +      for (int i = 8; i < MIN(16, ty->size); i++) { +        println("  mov %s, %d(%%rbp)", reg1, var->offset + i); +        println("  shr $8, %s", reg2); +      } +    } +  } +} + +static void copy_struct_reg(void) { +  Type *ty = current_fn->ty->return_ty; +  int gp = 0, fp = 0; + +  println("  mov %%rax, %%rdi"); + +  if (has_flonum(ty, 0, 8, 0)) { +    assert(ty->size == 4 || 8 <= ty->size); +    if (ty->size == 4) +      println("  movss (%%rdi), %%xmm0"); +    else +      println("  movsd (%%rdi), %%xmm0"); +    fp++; +  } else { +    println("  mov $0, %%rax"); +    for (int i = MIN(8, ty->size) - 1; i >= 0; i--) { +      println("  shl $8, %%rax"); +      println("  mov %d(%%rdi), %%al", i); +    } +    gp++; +  } + +  if (ty->size > 8) { +    if (has_flonum(ty, 8, 16, 0)) { +      assert(ty->size == 12 || ty->size == 16); +      if (ty->size == 4) +        println("  movss 8(%%rdi), %%xmm%d", fp); +      else +        println("  movsd 8(%%rdi), %%xmm%d", fp); +    } else { +      char *reg1 = (gp == 0) ? "%al" : "%dl"; +      char *reg2 = (gp == 0) ? "%rax" : "%rdx"; +      println("  mov $0, %s", reg2); +      for (int i = MIN(16, ty->size) - 1; i >= 8; i--) { +        println("  shl $8, %s", reg2); +        println("  mov %d(%%rdi), %s", i, reg1); +      } +    } +  } +} + +static void copy_struct_mem(void) { +  Type *ty = current_fn->ty->return_ty; +  Obj *var = current_fn->params; + +  println("  mov %d(%%rbp), %%rdi", var->offset); + +  for (int i = 0; i < ty->size; i++) { +    println("  mov %d(%%rax), %%dl", i); +    println("  mov %%dl, %d(%%rdi)", i); +  } +} + +static void builtin_alloca(void) { +  // Align size to 16 bytes. +  println("  add $15, %%rdi"); +  println("  and $0xfffffff0, %%edi"); + +  // Shift the temporary area by %rdi. +  println("  mov %d(%%rbp), %%rcx", current_fn->alloca_bottom->offset); +  println("  sub %%rsp, %%rcx"); +  println("  mov %%rsp, %%rax"); +  println("  sub %%rdi, %%rsp"); +  println("  mov %%rsp, %%rdx"); +  println("1:"); +  println("  cmp $0, %%rcx"); +  println("  je 2f"); +  println("  mov (%%rax), %%r8b"); +  println("  mov %%r8b, (%%rdx)"); +  println("  inc %%rdx"); +  println("  inc %%rax"); +  println("  dec %%rcx"); +  println("  jmp 1b"); +  println("2:"); + +  // Move alloca_bottom pointer. +  println("  mov %d(%%rbp), %%rax", current_fn->alloca_bottom->offset); +  println("  sub %%rdi, %%rax"); +  println("  mov %%rax, %d(%%rbp)", current_fn->alloca_bottom->offset); +} + +// Generate code for a given node. +static void gen_expr(Node *node) { +  println("  .loc %d %d", node->tok->file->file_no, node->tok->line_no); + +  switch (node->kind) { +  case ND_NULL_EXPR: +    return; +  case ND_NUM: { +    switch (node->ty->kind) { +    case TY_FLOAT: { +      union { float f32; uint32_t u32; } u = { node->fval }; +      println("  mov $%u, %%eax  # float %Lf", u.u32, node->fval); +      println("  movq %%rax, %%xmm0"); +      return; +    } +    case TY_DOUBLE: { +      union { double f64; uint64_t u64; } u = { node->fval }; +      println("  mov $%lu, %%rax  # double %Lf", u.u64, node->fval); +      println("  movq %%rax, %%xmm0"); +      return; +    } +    case TY_LDOUBLE: { +      union { long double f80; uint64_t u64[2]; } u; +      memset(&u, 0, sizeof(u)); +      u.f80 = node->fval; +      println("  mov $%lu, %%rax  # long double %Lf", u.u64[0], node->fval); +      println("  mov %%rax, -16(%%rsp)"); +      println("  mov $%lu, %%rax", u.u64[1]); +      println("  mov %%rax, -8(%%rsp)"); +      println("  fldt -16(%%rsp)"); +      return; +    } +    } + +    println("  mov $%ld, %%rax", node->val); +    return; +  } +  case ND_NEG: +    gen_expr(node->lhs); + +    switch (node->ty->kind) { +    case TY_FLOAT: +      println("  mov $1, %%rax"); +      println("  shl $31, %%rax"); +      println("  movq %%rax, %%xmm1"); +      println("  xorps %%xmm1, %%xmm0"); +      return; +    case TY_DOUBLE: +      println("  mov $1, %%rax"); +      println("  shl $63, %%rax"); +      println("  movq %%rax, %%xmm1"); +      println("  xorpd %%xmm1, %%xmm0"); +      return; +    case TY_LDOUBLE: +      println("  fchs"); +      return; +    } + +    println("  neg %%rax"); +    return; +  case ND_VAR: +    gen_addr(node); +    load(node->ty); +    return; +  case ND_MEMBER: { +    gen_addr(node); +    load(node->ty); + +    Member *mem = node->member; +    if (mem->is_bitfield) { +      println("  shl $%d, %%rax", 64 - mem->bit_width - mem->bit_offset); +      if (mem->ty->is_unsigned) +        println("  shr $%d, %%rax", 64 - mem->bit_width); +      else +        println("  sar $%d, %%rax", 64 - mem->bit_width); +    } +    return; +  } +  case ND_DEREF: +    gen_expr(node->lhs); +    load(node->ty); +    return; +  case ND_ADDR: +    gen_addr(node->lhs); +    return; +  case ND_ASSIGN: +    gen_addr(node->lhs); +    push(); +    gen_expr(node->rhs); + +    if (node->lhs->kind == ND_MEMBER && node->lhs->member->is_bitfield) { +      println("  mov %%rax, %%r8"); + +      // If the lhs is a bitfield, we need to read the current value +      // from memory and merge it with a new value. +      Member *mem = node->lhs->member; +      println("  mov %%rax, %%rdi"); +      println("  and $%ld, %%rdi", (1L << mem->bit_width) - 1); +      println("  shl $%d, %%rdi", mem->bit_offset); + +      println("  mov (%%rsp), %%rax"); +      load(mem->ty); + +      long mask = ((1L << mem->bit_width) - 1) << mem->bit_offset; +      println("  mov $%ld, %%r9", ~mask); +      println("  and %%r9, %%rax"); +      println("  or %%rdi, %%rax"); +      store(node->ty); +      println("  mov %%r8, %%rax"); +      return; +    } + +    store(node->ty); +    return; +  case ND_STMT_EXPR: +    for (Node *n = node->body; n; n = n->next) +      gen_stmt(n); +    return; +  case ND_COMMA: +    gen_expr(node->lhs); +    gen_expr(node->rhs); +    return; +  case ND_CAST: +    gen_expr(node->lhs); +    cast(node->lhs->ty, node->ty); +    return; +  case ND_MEMZERO: +    // `rep stosb` is equivalent to `memset(%rdi, %al, %rcx)`. +    println("  mov $%d, %%rcx", node->var->ty->size); +    println("  lea %d(%%rbp), %%rdi", node->var->offset); +    println("  mov $0, %%al"); +    println("  rep stosb"); +    return; +  case ND_COND: { +    int c = count(); +    gen_expr(node->cond); +    cmp_zero(node->cond->ty); +    println("  je .L.else.%d", c); +    gen_expr(node->then); +    println("  jmp .L.end.%d", c); +    println(".L.else.%d:", c); +    gen_expr(node->els); +    println(".L.end.%d:", c); +    return; +  } +  case ND_NOT: +    gen_expr(node->lhs); +    cmp_zero(node->lhs->ty); +    println("  sete %%al"); +    println("  movzx %%al, %%rax"); +    return; +  case ND_BITNOT: +    gen_expr(node->lhs); +    println("  not %%rax"); +    return; +  case ND_LOGAND: { +    int c = count(); +    gen_expr(node->lhs); +    cmp_zero(node->lhs->ty); +    println("  je .L.false.%d", c); +    gen_expr(node->rhs); +    cmp_zero(node->rhs->ty); +    println("  je .L.false.%d", c); +    println("  mov $1, %%rax"); +    println("  jmp .L.end.%d", c); +    println(".L.false.%d:", c); +    println("  mov $0, %%rax"); +    println(".L.end.%d:", c); +    return; +  } +  case ND_LOGOR: { +    int c = count(); +    gen_expr(node->lhs); +    cmp_zero(node->lhs->ty); +    println("  jne .L.true.%d", c); +    gen_expr(node->rhs); +    cmp_zero(node->rhs->ty); +    println("  jne .L.true.%d", c); +    println("  mov $0, %%rax"); +    println("  jmp .L.end.%d", c); +    println(".L.true.%d:", c); +    println("  mov $1, %%rax"); +    println(".L.end.%d:", c); +    return; +  } +  case ND_FUNCALL: { +    if (node->lhs->kind == ND_VAR && !strcmp(node->lhs->var->name, "alloca")) { +      gen_expr(node->args); +      println("  mov %%rax, %%rdi"); +      builtin_alloca(); +      return; +    } + +    int stack_args = push_args(node); +    gen_expr(node->lhs); + +    int gp = 0, fp = 0; + +    // If the return type is a large struct/union, the caller passes +    // a pointer to a buffer as if it were the first argument. +    if (node->ret_buffer && node->ty->size > 16) +      pop(argreg64[gp++]); + +    for (Node *arg = node->args; arg; arg = arg->next) { +      Type *ty = arg->ty; + +      switch (ty->kind) { +      case TY_STRUCT: +      case TY_UNION: +        if (ty->size > 16) +          continue; + +        bool fp1 = has_flonum1(ty); +        bool fp2 = has_flonum2(ty); + +        if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { +          if (fp1) +            popf(fp++); +          else +            pop(argreg64[gp++]); + +          if (ty->size > 8) { +            if (fp2) +              popf(fp++); +            else +              pop(argreg64[gp++]); +          } +        } +        break; +      case TY_FLOAT: +      case TY_DOUBLE: +        if (fp < FP_MAX) +          popf(fp++); +        break; +      case TY_LDOUBLE: +        break; +      default: +        if (gp < GP_MAX) +          pop(argreg64[gp++]); +      } +    } + +    println("  mov %%rax, %%r10"); +    println("  mov $%d, %%rax", fp); +    println("  call *%%r10"); +    println("  add $%d, %%rsp", stack_args * 8); + +    depth -= stack_args; + +    // It looks like the most significant 48 or 56 bits in RAX may +    // contain garbage if a function return type is short or bool/char, +    // respectively. We clear the upper bits here. +    switch (node->ty->kind) { +    case TY_BOOL: +      println("  movzx %%al, %%eax"); +      return; +    case TY_CHAR: +      if (node->ty->is_unsigned) +        println("  movzbl %%al, %%eax"); +      else +        println("  movsbl %%al, %%eax"); +      return; +    case TY_SHORT: +      if (node->ty->is_unsigned) +        println("  movzwl %%ax, %%eax"); +      else +        println("  movswl %%ax, %%eax"); +      return; +    } + +    // If the return type is a small struct, a value is returned +    // using up to two registers. +    if (node->ret_buffer && node->ty->size <= 16) { +      copy_ret_buffer(node->ret_buffer); +      println("  lea %d(%%rbp), %%rax", node->ret_buffer->offset); +    } + +    return; +  } +  case ND_LABEL_VAL: +    println("  lea %s(%%rip), %%rax", node->unique_label); +    return; +  case ND_CAS: { +    gen_expr(node->cas_addr); +    push(); +    gen_expr(node->cas_new); +    push(); +    gen_expr(node->cas_old); +    println("  mov %%rax, %%r8"); +    load(node->cas_old->ty->base); +    pop("%rdx"); // new +    pop("%rdi"); // addr + +    int sz = node->cas_addr->ty->base->size; +    println("  lock cmpxchg %s, (%%rdi)", reg_dx(sz)); +    println("  sete %%cl"); +    println("  je 1f"); +    println("  mov %s, (%%r8)", reg_ax(sz)); +    println("1:"); +    println("  movzbl %%cl, %%eax"); +    return; +  } +  case ND_EXCH: { +    gen_expr(node->lhs); +    push(); +    gen_expr(node->rhs); +    pop("%rdi"); + +    int sz = node->lhs->ty->base->size; +    println("  xchg %s, (%%rdi)", reg_ax(sz)); +    return; +  } +  } + +  switch (node->lhs->ty->kind) { +  case TY_FLOAT: +  case TY_DOUBLE: { +    gen_expr(node->rhs); +    pushf(); +    gen_expr(node->lhs); +    popf(1); + +    char *sz = (node->lhs->ty->kind == TY_FLOAT) ? "ss" : "sd"; + +    switch (node->kind) { +    case ND_ADD: +      println("  add%s %%xmm1, %%xmm0", sz); +      return; +    case ND_SUB: +      println("  sub%s %%xmm1, %%xmm0", sz); +      return; +    case ND_MUL: +      println("  mul%s %%xmm1, %%xmm0", sz); +      return; +    case ND_DIV: +      println("  div%s %%xmm1, %%xmm0", sz); +      return; +    case ND_EQ: +    case ND_NE: +    case ND_LT: +    case ND_LE: +      println("  ucomi%s %%xmm0, %%xmm1", sz); + +      if (node->kind == ND_EQ) { +        println("  sete %%al"); +        println("  setnp %%dl"); +        println("  and %%dl, %%al"); +      } else if (node->kind == ND_NE) { +        println("  setne %%al"); +        println("  setp %%dl"); +        println("  or %%dl, %%al"); +      } else if (node->kind == ND_LT) { +        println("  seta %%al"); +      } else { +        println("  setae %%al"); +      } + +      println("  and $1, %%al"); +      println("  movzb %%al, %%rax"); +      return; +    } + +    error_tok(node->tok, "invalid expression"); +  } +  case TY_LDOUBLE: { +    gen_expr(node->lhs); +    gen_expr(node->rhs); + +    switch (node->kind) { +    case ND_ADD: +      println("  faddp"); +      return; +    case ND_SUB: +      println("  fsubrp"); +      return; +    case ND_MUL: +      println("  fmulp"); +      return; +    case ND_DIV: +      println("  fdivrp"); +      return; +    case ND_EQ: +    case ND_NE: +    case ND_LT: +    case ND_LE: +      println("  fcomip"); +      println("  fstp %%st(0)"); + +      if (node->kind == ND_EQ) +        println("  sete %%al"); +      else if (node->kind == ND_NE) +        println("  setne %%al"); +      else if (node->kind == ND_LT) +        println("  seta %%al"); +      else +        println("  setae %%al"); + +      println("  movzb %%al, %%rax"); +      return; +    } + +    error_tok(node->tok, "invalid expression"); +  } +  } + +  gen_expr(node->rhs); +  push(); +  gen_expr(node->lhs); +  pop("%rdi"); + +  char *ax, *di, *dx; + +  if (node->lhs->ty->kind == TY_LONG || node->lhs->ty->base) { +    ax = "%rax"; +    di = "%rdi"; +    dx = "%rdx"; +  } else { +    ax = "%eax"; +    di = "%edi"; +    dx = "%edx"; +  } + +  switch (node->kind) { +  case ND_ADD: +    println("  add %s, %s", di, ax); +    return; +  case ND_SUB: +    println("  sub %s, %s", di, ax); +    return; +  case ND_MUL: +    println("  imul %s, %s", di, ax); +    return; +  case ND_DIV: +  case ND_MOD: +    if (node->ty->is_unsigned) { +      println("  mov $0, %s", dx); +      println("  div %s", di); +    } else { +      if (node->lhs->ty->size == 8) +        println("  cqo"); +      else +        println("  cdq"); +      println("  idiv %s", di); +    } + +    if (node->kind == ND_MOD) +      println("  mov %%rdx, %%rax"); +    return; +  case ND_BITAND: +    println("  and %s, %s", di, ax); +    return; +  case ND_BITOR: +    println("  or %s, %s", di, ax); +    return; +  case ND_BITXOR: +    println("  xor %s, %s", di, ax); +    return; +  case ND_EQ: +  case ND_NE: +  case ND_LT: +  case ND_LE: +    println("  cmp %s, %s", di, ax); + +    if (node->kind == ND_EQ) { +      println("  sete %%al"); +    } else if (node->kind == ND_NE) { +      println("  setne %%al"); +    } else if (node->kind == ND_LT) { +      if (node->lhs->ty->is_unsigned) +        println("  setb %%al"); +      else +        println("  setl %%al"); +    } else if (node->kind == ND_LE) { +      if (node->lhs->ty->is_unsigned) +        println("  setbe %%al"); +      else +        println("  setle %%al"); +    } + +    println("  movzb %%al, %%rax"); +    return; +  case ND_SHL: +    println("  mov %%rdi, %%rcx"); +    println("  shl %%cl, %s", ax); +    return; +  case ND_SHR: +    println("  mov %%rdi, %%rcx"); +    if (node->lhs->ty->is_unsigned) +      println("  shr %%cl, %s", ax); +    else +      println("  sar %%cl, %s", ax); +    return; +  } + +  error_tok(node->tok, "invalid expression"); +} + +static void gen_stmt(Node *node) { +  println("  .loc %d %d", node->tok->file->file_no, node->tok->line_no); + +  switch (node->kind) { +  case ND_IF: { +    int c = count(); +    gen_expr(node->cond); +    cmp_zero(node->cond->ty); +    println("  je  .L.else.%d", c); +    gen_stmt(node->then); +    println("  jmp .L.end.%d", c); +    println(".L.else.%d:", c); +    if (node->els) +      gen_stmt(node->els); +    println(".L.end.%d:", c); +    return; +  } +  case ND_FOR: { +    int c = count(); +    if (node->init) +      gen_stmt(node->init); +    println(".L.begin.%d:", c); +    if (node->cond) { +      gen_expr(node->cond); +      cmp_zero(node->cond->ty); +      println("  je %s", node->brk_label); +    } +    gen_stmt(node->then); +    println("%s:", node->cont_label); +    if (node->inc) +      gen_expr(node->inc); +    println("  jmp .L.begin.%d", c); +    println("%s:", node->brk_label); +    return; +  } +  case ND_DO: { +    int c = count(); +    println(".L.begin.%d:", c); +    gen_stmt(node->then); +    println("%s:", node->cont_label); +    gen_expr(node->cond); +    cmp_zero(node->cond->ty); +    println("  jne .L.begin.%d", c); +    println("%s:", node->brk_label); +    return; +  } +  case ND_SWITCH: +    gen_expr(node->cond); + +    for (Node *n = node->case_next; n; n = n->case_next) { +      char *ax = (node->cond->ty->size == 8) ? "%rax" : "%eax"; +      char *di = (node->cond->ty->size == 8) ? "%rdi" : "%edi"; + +      if (n->begin == n->end) { +        println("  cmp $%ld, %s", n->begin, ax); +        println("  je %s", n->label); +        continue; +      } + +      // [GNU] Case ranges +      println("  mov %s, %s", ax, di); +      println("  sub $%ld, %s", n->begin, di); +      println("  cmp $%ld, %s", n->end - n->begin, di); +      println("  jbe %s", n->label); +    } + +    if (node->default_case) +      println("  jmp %s", node->default_case->label); + +    println("  jmp %s", node->brk_label); +    gen_stmt(node->then); +    println("%s:", node->brk_label); +    return; +  case ND_CASE: +    println("%s:", node->label); +    gen_stmt(node->lhs); +    return; +  case ND_BLOCK: +    for (Node *n = node->body; n; n = n->next) +      gen_stmt(n); +    return; +  case ND_GOTO: +    println("  jmp %s", node->unique_label); +    return; +  case ND_GOTO_EXPR: +    gen_expr(node->lhs); +    println("  jmp *%%rax"); +    return; +  case ND_LABEL: +    println("%s:", node->unique_label); +    gen_stmt(node->lhs); +    return; +  case ND_RETURN: +    if (node->lhs) { +      gen_expr(node->lhs); +      Type *ty = node->lhs->ty; + +      switch (ty->kind) { +      case TY_STRUCT: +      case TY_UNION: +        if (ty->size <= 16) +          copy_struct_reg(); +        else +          copy_struct_mem(); +        break; +      } +    } + +    println("  jmp .L.return.%s", current_fn->name); +    return; +  case ND_EXPR_STMT: +    gen_expr(node->lhs); +    return; +  case ND_ASM: +    println("  %s", node->asm_str); +    return; +  } + +  error_tok(node->tok, "invalid statement"); +} + +// Assign offsets to local variables. +static void assign_lvar_offsets(Obj *prog) { +  for (Obj *fn = prog; fn; fn = fn->next) { +    if (!fn->is_function) +      continue; + +    // If a function has many parameters, some parameters are +    // inevitably passed by stack rather than by register. +    // The first passed-by-stack parameter resides at RBP+16. +    int top = 16; +    int bottom = 0; + +    int gp = 0, fp = 0; + +    // Assign offsets to pass-by-stack parameters. +    for (Obj *var = fn->params; var; var = var->next) { +      Type *ty = var->ty; + +      switch (ty->kind) { +      case TY_STRUCT: +      case TY_UNION: +        if (ty->size <= 16) { +          bool fp1 = has_flonum(ty, 0, 8, 0); +          bool fp2 = has_flonum(ty, 8, 16, 8); +          if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { +            fp = fp + fp1 + fp2; +            gp = gp + !fp1 + !fp2; +            continue; +          } +        } +        break; +      case TY_FLOAT: +      case TY_DOUBLE: +        if (fp++ < FP_MAX) +          continue; +        break; +      case TY_LDOUBLE: +        break; +      default: +        if (gp++ < GP_MAX) +          continue; +      } + +      top = align_to(top, 8); +      var->offset = top; +      top += var->ty->size; +    } + +    // Assign offsets to pass-by-register parameters and local variables. +    for (Obj *var = fn->locals; var; var = var->next) { +      if (var->offset) +        continue; + +      // AMD64 System V ABI has a special alignment rule for an array of +      // length at least 16 bytes. We need to align such array to at least +      // 16-byte boundaries. See p.14 of +      // https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-draft.pdf. +      int align = (var->ty->kind == TY_ARRAY && var->ty->size >= 16) +        ? MAX(16, var->align) : var->align; + +      bottom += var->ty->size; +      bottom = align_to(bottom, align); +      var->offset = -bottom; +    } + +    fn->stack_size = align_to(bottom, 16); +  } +} + +static void emit_data(Obj *prog) { +  for (Obj *var = prog; var; var = var->next) { +    if (var->is_function || !var->is_definition) +      continue; + +    if (var->is_static) +      println("  .local %s", var->name); +    else +      println("  .globl %s", var->name); + +    int align = (var->ty->kind == TY_ARRAY && var->ty->size >= 16) +      ? MAX(16, var->align) : var->align; + +    // Common symbol +    if (opt_fcommon && var->is_tentative) { +      println("  .comm %s, %d, %d", var->name, var->ty->size, align); +      continue; +    } + +    // .data or .tdata +    if (var->init_data) { +      if (var->is_tls) +        println("  .section .tdata,\"awT\",@progbits"); +      else +        println("  .data"); + +      println("  .type %s, @object", var->name); +      println("  .size %s, %d", var->name, var->ty->size); +      println("  .align %d", align); +      println("%s:", var->name); + +      Relocation *rel = var->rel; +      int pos = 0; +      while (pos < var->ty->size) { +        if (rel && rel->offset == pos) { +          println("  .quad %s%+ld", *rel->label, rel->addend); +          rel = rel->next; +          pos += 8; +        } else { +          println("  .byte %d", var->init_data[pos++]); +        } +      } +      continue; +    } + +    // .bss or .tbss +    if (var->is_tls) +      println("  .section .tbss,\"awT\",@nobits"); +    else +      println("  .bss"); + +    println("  .align %d", align); +    println("%s:", var->name); +    println("  .zero %d", var->ty->size); +  } +} + +static void store_fp(int r, int offset, int sz) { +  switch (sz) { +  case 4: +    println("  movss %%xmm%d, %d(%%rbp)", r, offset); +    return; +  case 8: +    println("  movsd %%xmm%d, %d(%%rbp)", r, offset); +    return; +  } +  unreachable(); +} + +static void store_gp(int r, int offset, int sz) { +  switch (sz) { +  case 1: +    println("  mov %s, %d(%%rbp)", argreg8[r], offset); +    return; +  case 2: +    println("  mov %s, %d(%%rbp)", argreg16[r], offset); +    return; +  case 4: +    println("  mov %s, %d(%%rbp)", argreg32[r], offset); +    return; +  case 8: +    println("  mov %s, %d(%%rbp)", argreg64[r], offset); +    return; +  default: +    for (int i = 0; i < sz; i++) { +      println("  mov %s, %d(%%rbp)", argreg8[r], offset + i); +      println("  shr $8, %s", argreg64[r]); +    } +    return; +  } +} + +static void emit_text(Obj *prog) { +  for (Obj *fn = prog; fn; fn = fn->next) { +    if (!fn->is_function || !fn->is_definition) +      continue; + +    // No code is emitted for "static inline" functions +    // if no one is referencing them. +    if (!fn->is_live) +      continue; + +    if (fn->is_static) +      println("  .local %s", fn->name); +    else +      println("  .globl %s", fn->name); + +    println("  .text"); +    println("  .type %s, @function", fn->name); +    println("%s:", fn->name); +    current_fn = fn; + +    // Prologue +    println("  push %%rbp"); +    println("  mov %%rsp, %%rbp"); +    println("  sub $%d, %%rsp", fn->stack_size); +    println("  mov %%rsp, %d(%%rbp)", fn->alloca_bottom->offset); + +    // Save arg registers if function is variadic +    if (fn->va_area) { +      int gp = 0, fp = 0; +      for (Obj *var = fn->params; var; var = var->next) { +        if (is_flonum(var->ty)) +          fp++; +        else +          gp++; +      } + +      int off = fn->va_area->offset; + +      // va_elem +      println("  movl $%d, %d(%%rbp)", gp * 8, off);          // gp_offset +      println("  movl $%d, %d(%%rbp)", fp * 8 + 48, off + 4); // fp_offset +      println("  movq %%rbp, %d(%%rbp)", off + 8);            // overflow_arg_area +      println("  addq $16, %d(%%rbp)", off + 8); +      println("  movq %%rbp, %d(%%rbp)", off + 16);           // reg_save_area +      println("  addq $%d, %d(%%rbp)", off + 24, off + 16); + +      // __reg_save_area__ +      println("  movq %%rdi, %d(%%rbp)", off + 24); +      println("  movq %%rsi, %d(%%rbp)", off + 32); +      println("  movq %%rdx, %d(%%rbp)", off + 40); +      println("  movq %%rcx, %d(%%rbp)", off + 48); +      println("  movq %%r8, %d(%%rbp)", off + 56); +      println("  movq %%r9, %d(%%rbp)", off + 64); +      println("  movsd %%xmm0, %d(%%rbp)", off + 72); +      println("  movsd %%xmm1, %d(%%rbp)", off + 80); +      println("  movsd %%xmm2, %d(%%rbp)", off + 88); +      println("  movsd %%xmm3, %d(%%rbp)", off + 96); +      println("  movsd %%xmm4, %d(%%rbp)", off + 104); +      println("  movsd %%xmm5, %d(%%rbp)", off + 112); +      println("  movsd %%xmm6, %d(%%rbp)", off + 120); +      println("  movsd %%xmm7, %d(%%rbp)", off + 128); +    } + +    // Save passed-by-register arguments to the stack +    int gp = 0, fp = 0; +    for (Obj *var = fn->params; var; var = var->next) { +      if (var->offset > 0) +        continue; + +      Type *ty = var->ty; + +      switch (ty->kind) { +      case TY_STRUCT: +      case TY_UNION: +        assert(ty->size <= 16); +        if (has_flonum(ty, 0, 8, 0)) +          store_fp(fp++, var->offset, MIN(8, ty->size)); +        else +          store_gp(gp++, var->offset, MIN(8, ty->size)); + +        if (ty->size > 8) { +          if (has_flonum(ty, 8, 16, 0)) +            store_fp(fp++, var->offset + 8, ty->size - 8); +          else +            store_gp(gp++, var->offset + 8, ty->size - 8); +        } +        break; +      case TY_FLOAT: +      case TY_DOUBLE: +        store_fp(fp++, var->offset, ty->size); +        break; +      default: +        store_gp(gp++, var->offset, ty->size); +      } +    } + +    // Emit code +    gen_stmt(fn->body); +    assert(depth == 0); + +    // [https://www.sigbus.info/n1570#5.1.2.2.3p1] The C spec defines +    // a special rule for the main function. Reaching the end of the +    // main function is equivalent to returning 0, even though the +    // behavior is undefined for the other functions. +    if (strcmp(fn->name, "main") == 0) +      println("  mov $0, %%rax"); + +    // Epilogue +    println(".L.return.%s:", fn->name); +    println("  mov %%rbp, %%rsp"); +    println("  pop %%rbp"); +    println("  ret"); +  } +} + +void codegen(Obj *prog, FILE *out) { +  output_file = out; + +  File **files = get_input_files(); +  for (int i = 0; files[i]; i++) +    println("  .file %d \"%s\"", files[i]->file_no, files[i]->name); + +  assign_lvar_offsets(prog); +  emit_data(prog); +  emit_text(prog); +} diff --git a/src/3p/chibicc/hashmap.c b/src/3p/chibicc/hashmap.c new file mode 100644 index 0000000..46539d9 --- /dev/null +++ b/src/3p/chibicc/hashmap.c @@ -0,0 +1,165 @@ +// This is an implementation of the open-addressing hash table. + +#include "chibicc.h" + +// Initial hash bucket size +#define INIT_SIZE 16 + +// Rehash if the usage exceeds 70%. +#define HIGH_WATERMARK 70 + +// We'll keep the usage below 50% after rehashing. +#define LOW_WATERMARK 50 + +// Represents a deleted hash entry +#define TOMBSTONE ((void *)-1) + +static uint64_t fnv_hash(char *s, int len) { +  uint64_t hash = 0xcbf29ce484222325; +  for (int i = 0; i < len; i++) { +    hash *= 0x100000001b3; +    hash ^= (unsigned char)s[i]; +  } +  return hash; +} + +// Make room for new entires in a given hashmap by removing +// tombstones and possibly extending the bucket size. +static void rehash(HashMap *map) { +  // Compute the size of the new hashmap. +  int nkeys = 0; +  for (int i = 0; i < map->capacity; i++) +    if (map->buckets[i].key && map->buckets[i].key != TOMBSTONE) +      nkeys++; + +  int cap = map->capacity; +  while ((nkeys * 100) / cap >= LOW_WATERMARK) +    cap = cap * 2; +  assert(cap > 0); + +  // Create a new hashmap and copy all key-values. +  HashMap map2 = {0}; +  map2.buckets = calloc(cap, sizeof(HashEntry)); +  map2.capacity = cap; + +  for (int i = 0; i < map->capacity; i++) { +    HashEntry *ent = &map->buckets[i]; +    if (ent->key && ent->key != TOMBSTONE) +      hashmap_put2(&map2, ent->key, ent->keylen, ent->val); +  } + +  assert(map2.used == nkeys); +  *map = map2; +} + +static bool match(HashEntry *ent, char *key, int keylen) { +  return ent->key && ent->key != TOMBSTONE && +         ent->keylen == keylen && memcmp(ent->key, key, keylen) == 0; +} + +static HashEntry *get_entry(HashMap *map, char *key, int keylen) { +  if (!map->buckets) +    return NULL; + +  uint64_t hash = fnv_hash(key, keylen); + +  for (int i = 0; i < map->capacity; i++) { +    HashEntry *ent = &map->buckets[(hash + i) % map->capacity]; +    if (match(ent, key, keylen)) +      return ent; +    if (ent->key == NULL) +      return NULL; +  } +  unreachable(); +} + +static HashEntry *get_or_insert_entry(HashMap *map, char *key, int keylen) { +  if (!map->buckets) { +    map->buckets = calloc(INIT_SIZE, sizeof(HashEntry)); +    map->capacity = INIT_SIZE; +  } else if ((map->used * 100) / map->capacity >= HIGH_WATERMARK) { +    rehash(map); +  } + +  uint64_t hash = fnv_hash(key, keylen); + +  for (int i = 0; i < map->capacity; i++) { +    HashEntry *ent = &map->buckets[(hash + i) % map->capacity]; + +    if (match(ent, key, keylen)) +      return ent; + +    if (ent->key == TOMBSTONE) { +      ent->key = key; +      ent->keylen = keylen; +      return ent; +    } + +    if (ent->key == NULL) { +      ent->key = key; +      ent->keylen = keylen; +      map->used++; +      return ent; +    } +  } +  unreachable(); +} + +void *hashmap_get(HashMap *map, char *key) { +  return hashmap_get2(map, key, strlen(key)); +} + +void *hashmap_get2(HashMap *map, char *key, int keylen) { +  HashEntry *ent = get_entry(map, key, keylen); +  return ent ? ent->val : NULL; +} + +void hashmap_put(HashMap *map, char *key, void *val) { +   hashmap_put2(map, key, strlen(key), val); +} + +void hashmap_put2(HashMap *map, char *key, int keylen, void *val) { +  HashEntry *ent = get_or_insert_entry(map, key, keylen); +  ent->val = val; +} + +void hashmap_delete(HashMap *map, char *key) { +  hashmap_delete2(map, key, strlen(key)); +} + +void hashmap_delete2(HashMap *map, char *key, int keylen) { +  HashEntry *ent = get_entry(map, key, keylen); +  if (ent) +    ent->key = TOMBSTONE; +} + +void hashmap_test(void) { +  HashMap *map = calloc(1, sizeof(HashMap)); + +  for (int i = 0; i < 5000; i++) +    hashmap_put(map, format("key %d", i), (void *)(size_t)i); +  for (int i = 1000; i < 2000; i++) +    hashmap_delete(map, format("key %d", i)); +  for (int i = 1500; i < 1600; i++) +    hashmap_put(map, format("key %d", i), (void *)(size_t)i); +  for (int i = 6000; i < 7000; i++) +    hashmap_put(map, format("key %d", i), (void *)(size_t)i); + +  for (int i = 0; i < 1000; i++) +    assert((size_t)hashmap_get(map, format("key %d", i)) == i); +  for (int i = 1000; i < 1500; i++) +    assert(hashmap_get(map, "no such key") == NULL); +  for (int i = 1500; i < 1600; i++) +    assert((size_t)hashmap_get(map, format("key %d", i)) == i); +  for (int i = 1600; i < 2000; i++) +    assert(hashmap_get(map, "no such key") == NULL); +  for (int i = 2000; i < 5000; i++) +    assert((size_t)hashmap_get(map, format("key %d", i)) == i); +  for (int i = 5000; i < 6000; i++) +    assert(hashmap_get(map, "no such key") == NULL); +  for (int i = 6000; i < 7000; i++) +    hashmap_put(map, format("key %d", i), (void *)(size_t)i); + +  assert(hashmap_get(map, "no such key") == NULL); +  printf("OK\n"); +} diff --git a/src/3p/chibicc/main.c b/src/3p/chibicc/main.c new file mode 100644 index 0000000..ffaabf4 --- /dev/null +++ b/src/3p/chibicc/main.c @@ -0,0 +1,791 @@ +#include "chibicc.h" + +typedef enum { +  FILE_NONE, FILE_C, FILE_ASM, FILE_OBJ, FILE_AR, FILE_DSO, +} FileType; + +StringArray include_paths; +bool opt_fcommon = true; +bool opt_fpic; + +static FileType opt_x; +static StringArray opt_include; +static bool opt_E; +static bool opt_M; +static bool opt_MD; +static bool opt_MMD; +static bool opt_MP; +static bool opt_S; +static bool opt_c; +static bool opt_cc1; +static bool opt_hash_hash_hash; +static bool opt_static; +static bool opt_shared; +static char *opt_MF; +static char *opt_MT; +static char *opt_o; + +static StringArray ld_extra_args; +static StringArray std_include_paths; + +char *base_file; +static char *output_file; + +static StringArray input_paths; +static StringArray tmpfiles; + +static void usage(int status) { +  fprintf(stderr, "chibicc [ -o <path> ] <file>\n"); +  exit(status); +} + +static bool take_arg(char *arg) { +  char *x[] = { +    "-o", "-I", "-idirafter", "-include", "-x", "-MF", "-MT", "-Xlinker", +  }; + +  for (int i = 0; i < sizeof(x) / sizeof(*x); i++) +    if (!strcmp(arg, x[i])) +      return true; +  return false; +} + +static void add_default_include_paths(char *argv0) { +  // We expect that chibicc-specific include files are installed +  // to ./include relative to argv[0]. +  strarray_push(&include_paths, format("%s/include", dirname(strdup(argv0)))); + +  // Add standard include paths. +  strarray_push(&include_paths, "/usr/local/include"); +  strarray_push(&include_paths, "/usr/include/x86_64-linux-gnu"); +  strarray_push(&include_paths, "/usr/include"); + +  // Keep a copy of the standard include paths for -MMD option. +  for (int i = 0; i < include_paths.len; i++) +    strarray_push(&std_include_paths, include_paths.data[i]); +} + +static void define(char *str) { +  char *eq = strchr(str, '='); +  if (eq) +    define_macro(strndup(str, eq - str), eq + 1); +  else +    define_macro(str, "1"); +} + +static FileType parse_opt_x(char *s) { +  if (!strcmp(s, "c")) +    return FILE_C; +  if (!strcmp(s, "assembler")) +    return FILE_ASM; +  if (!strcmp(s, "none")) +    return FILE_NONE; +  error("<command line>: unknown argument for -x: %s", s); +} + +static char *quote_makefile(char *s) { +  char *buf = calloc(1, strlen(s) * 2 + 1); + +  for (int i = 0, j = 0; s[i]; i++) { +    switch (s[i]) { +    case '$': +      buf[j++] = '$'; +      buf[j++] = '$'; +      break; +    case '#': +      buf[j++] = '\\'; +      buf[j++] = '#'; +      break; +    case ' ': +    case '\t': +      for (int k = i - 1; k >= 0 && s[k] == '\\'; k--) +        buf[j++] = '\\'; +      buf[j++] = '\\'; +      buf[j++] = s[i]; +      break; +    default: +      buf[j++] = s[i]; +      break; +    } +  } +  return buf; +} + +static void parse_args(int argc, char **argv) { +  // Make sure that all command line options that take an argument +  // have an argument. +  for (int i = 1; i < argc; i++) +    if (take_arg(argv[i])) +      if (!argv[++i]) +        usage(1); + +  StringArray idirafter = {}; + +  for (int i = 1; i < argc; i++) { +    if (!strcmp(argv[i], "-###")) { +      opt_hash_hash_hash = true; +      continue; +    } + +    if (!strcmp(argv[i], "-cc1")) { +      opt_cc1 = true; +      continue; +    } + +    if (!strcmp(argv[i], "--help")) +      usage(0); + +    if (!strcmp(argv[i], "-o")) { +      opt_o = argv[++i]; +      continue; +    } + +    if (!strncmp(argv[i], "-o", 2)) { +      opt_o = argv[i] + 2; +      continue; +    } + +    if (!strcmp(argv[i], "-S")) { +      opt_S = true; +      continue; +    } + +    if (!strcmp(argv[i], "-fcommon")) { +      opt_fcommon = true; +      continue; +    } + +    if (!strcmp(argv[i], "-fno-common")) { +      opt_fcommon = false; +      continue; +    } + +    if (!strcmp(argv[i], "-c")) { +      opt_c = true; +      continue; +    } + +    if (!strcmp(argv[i], "-E")) { +      opt_E = true; +      continue; +    } + +    if (!strncmp(argv[i], "-I", 2)) { +      strarray_push(&include_paths, argv[i] + 2); +      continue; +    } + +    if (!strcmp(argv[i], "-D")) { +      define(argv[++i]); +      continue; +    } + +    if (!strncmp(argv[i], "-D", 2)) { +      define(argv[i] + 2); +      continue; +    } + +    if (!strcmp(argv[i], "-U")) { +      undef_macro(argv[++i]); +      continue; +    } + +    if (!strncmp(argv[i], "-U", 2)) { +      undef_macro(argv[i] + 2); +      continue; +    } + +    if (!strcmp(argv[i], "-include")) { +      strarray_push(&opt_include, argv[++i]); +      continue; +    } + +    if (!strcmp(argv[i], "-x")) { +      opt_x = parse_opt_x(argv[++i]); +      continue; +    } + +    if (!strncmp(argv[i], "-x", 2)) { +      opt_x = parse_opt_x(argv[i] + 2); +      continue; +    } + +    if (!strncmp(argv[i], "-l", 2) || !strncmp(argv[i], "-Wl,", 4)) { +      strarray_push(&input_paths, argv[i]); +      continue; +    } + +    if (!strcmp(argv[i], "-Xlinker")) { +      strarray_push(&ld_extra_args, argv[++i]); +      continue; +    } + +    if (!strcmp(argv[i], "-s")) { +      strarray_push(&ld_extra_args, "-s"); +      continue; +    } + +    if (!strcmp(argv[i], "-M")) { +      opt_M = true; +      continue; +    } + +    if (!strcmp(argv[i], "-MF")) { +      opt_MF = argv[++i]; +      continue; +    } + +    if (!strcmp(argv[i], "-MP")) { +      opt_MP = true; +      continue; +    } + +    if (!strcmp(argv[i], "-MT")) { +      if (opt_MT == NULL) +        opt_MT = argv[++i]; +      else +        opt_MT = format("%s %s", opt_MT, argv[++i]); +      continue; +    } + +    if (!strcmp(argv[i], "-MD")) { +      opt_MD = true; +      continue; +    } + +    if (!strcmp(argv[i], "-MQ")) { +      if (opt_MT == NULL) +        opt_MT = quote_makefile(argv[++i]); +      else +        opt_MT = format("%s %s", opt_MT, quote_makefile(argv[++i])); +      continue; +    } + +    if (!strcmp(argv[i], "-MMD")) { +      opt_MD = opt_MMD = true; +      continue; +    } + +    if (!strcmp(argv[i], "-fpic") || !strcmp(argv[i], "-fPIC")) { +      opt_fpic = true; +      continue; +    } + +    if (!strcmp(argv[i], "-cc1-input")) { +      base_file = argv[++i]; +      continue; +    } + +    if (!strcmp(argv[i], "-cc1-output")) { +      output_file = argv[++i]; +      continue; +    } + +    if (!strcmp(argv[i], "-idirafter")) { +      strarray_push(&idirafter, argv[i++]); +      continue; +    } + +    if (!strcmp(argv[i], "-static")) { +      opt_static = true; +      strarray_push(&ld_extra_args, "-static"); +      continue; +    } + +    if (!strcmp(argv[i], "-shared")) { +      opt_shared = true; +      strarray_push(&ld_extra_args, "-shared"); +      continue; +    } + +    if (!strcmp(argv[i], "-L")) { +      strarray_push(&ld_extra_args, "-L"); +      strarray_push(&ld_extra_args, argv[++i]); +      continue; +    } + +    if (!strncmp(argv[i], "-L", 2)) { +      strarray_push(&ld_extra_args, "-L"); +      strarray_push(&ld_extra_args, argv[i] + 2); +      continue; +    } + +    if (!strcmp(argv[i], "-hashmap-test")) { +      hashmap_test(); +      exit(0); +    } + +    // These options are ignored for now. +    if (!strncmp(argv[i], "-O", 2) || +        !strncmp(argv[i], "-W", 2) || +        !strncmp(argv[i], "-g", 2) || +        !strncmp(argv[i], "-std=", 5) || +        !strcmp(argv[i], "-ffreestanding") || +        !strcmp(argv[i], "-fno-builtin") || +        !strcmp(argv[i], "-fno-omit-frame-pointer") || +        !strcmp(argv[i], "-fno-stack-protector") || +        !strcmp(argv[i], "-fno-strict-aliasing") || +        !strcmp(argv[i], "-m64") || +        !strcmp(argv[i], "-mno-red-zone") || +        !strcmp(argv[i], "-w")) +      continue; + +    if (argv[i][0] == '-' && argv[i][1] != '\0') +      error("unknown argument: %s", argv[i]); + +    strarray_push(&input_paths, argv[i]); +  } + +  for (int i = 0; i < idirafter.len; i++) +    strarray_push(&include_paths, idirafter.data[i]); + +  if (input_paths.len == 0) +    error("no input files"); + +  // -E implies that the input is the C macro language. +  if (opt_E) +    opt_x = FILE_C; +} + +static FILE *open_file(char *path) { +  if (!path || strcmp(path, "-") == 0) +    return stdout; + +  FILE *out = fopen(path, "w"); +  if (!out) +    error("cannot open output file: %s: %s", path, strerror(errno)); +  return out; +} + +static bool endswith(char *p, char *q) { +  int len1 = strlen(p); +  int len2 = strlen(q); +  return (len1 >= len2) && !strcmp(p + len1 - len2, q); +} + +// Replace file extension +static char *replace_extn(char *tmpl, char *extn) { +  char *filename = basename(strdup(tmpl)); +  char *dot = strrchr(filename, '.'); +  if (dot) +    *dot = '\0'; +  return format("%s%s", filename, extn); +} + +static void cleanup(void) { +  for (int i = 0; i < tmpfiles.len; i++) +    unlink(tmpfiles.data[i]); +} + +static char *create_tmpfile(void) { +  char *path = strdup("/tmp/chibicc-XXXXXX"); +  int fd = mkstemp(path); +  if (fd == -1) +    error("mkstemp failed: %s", strerror(errno)); +  close(fd); + +  strarray_push(&tmpfiles, path); +  return path; +} + +static void run_subprocess(char **argv) { +  // If -### is given, dump the subprocess's command line. +  if (opt_hash_hash_hash) { +    fprintf(stderr, "%s", argv[0]); +    for (int i = 1; argv[i]; i++) +      fprintf(stderr, " %s", argv[i]); +    fprintf(stderr, "\n"); +  } + +  if (fork() == 0) { +    // Child process. Run a new command. +    execvp(argv[0], argv); +    fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno)); +    _exit(1); +  } + +  // Wait for the child process to finish. +  int status; +  while (wait(&status) > 0); +  if (status != 0) +    exit(1); +} + +static void run_cc1(int argc, char **argv, char *input, char *output) { +  char **args = calloc(argc + 10, sizeof(char *)); +  memcpy(args, argv, argc * sizeof(char *)); +  args[argc++] = "-cc1"; + +  if (input) { +    args[argc++] = "-cc1-input"; +    args[argc++] = input; +  } + +  if (output) { +    args[argc++] = "-cc1-output"; +    args[argc++] = output; +  } + +  run_subprocess(args); +} + +// Print tokens to stdout. Used for -E. +static void print_tokens(Token *tok) { +  FILE *out = open_file(opt_o ? opt_o : "-"); + +  int line = 1; +  for (; tok->kind != TK_EOF; tok = tok->next) { +    if (line > 1 && tok->at_bol) +      fprintf(out, "\n"); +    if (tok->has_space && !tok->at_bol) +      fprintf(out, " "); +    fprintf(out, "%.*s", tok->len, tok->loc); +    line++; +  } +  fprintf(out, "\n"); +} + +static bool in_std_include_path(char *path) { +  for (int i = 0; i < std_include_paths.len; i++) { +    char *dir = std_include_paths.data[i]; +    int len = strlen(dir); +    if (strncmp(dir, path, len) == 0 && path[len] == '/') +      return true; +  } +  return false; +} + +// If -M options is given, the compiler write a list of input files to +// stdout in a format that "make" command can read. This feature is +// used to automate file dependency management. +static void print_dependencies(void) { +  char *path; +  if (opt_MF) +    path = opt_MF; +  else if (opt_MD) +    path = replace_extn(opt_o ? opt_o : base_file, ".d"); +  else if (opt_o) +    path = opt_o; +  else +    path = "-"; + +  FILE *out = open_file(path); +  if (opt_MT) +    fprintf(out, "%s:", opt_MT); +  else +    fprintf(out, "%s:", quote_makefile(replace_extn(base_file, ".o"))); + +  File **files = get_input_files(); + +  for (int i = 0; files[i]; i++) { +    if (opt_MMD && in_std_include_path(files[i]->name)) +      continue; +    fprintf(out, " \\\n  %s", files[i]->name); +  } + +  fprintf(out, "\n\n"); + +  if (opt_MP) { +    for (int i = 1; files[i]; i++) { +      if (opt_MMD && in_std_include_path(files[i]->name)) +        continue; +      fprintf(out, "%s:\n\n", quote_makefile(files[i]->name)); +    } +  } +} + +static Token *must_tokenize_file(char *path) { +  Token *tok = tokenize_file(path); +  if (!tok) +    error("%s: %s", path, strerror(errno)); +  return tok; +} + +static Token *append_tokens(Token *tok1, Token *tok2) { +  if (!tok1 || tok1->kind == TK_EOF) +    return tok2; + +  Token *t = tok1; +  while (t->next->kind != TK_EOF) +    t = t->next; +  t->next = tok2; +  return tok1; +} + +static void cc1(void) { +  Token *tok = NULL; + +  // Process -include option +  for (int i = 0; i < opt_include.len; i++) { +    char *incl = opt_include.data[i]; + +    char *path; +    if (file_exists(incl)) { +      path = incl; +    } else { +      path = search_include_paths(incl); +      if (!path) +        error("-include: %s: %s", incl, strerror(errno)); +    } + +    Token *tok2 = must_tokenize_file(path); +    tok = append_tokens(tok, tok2); +  } + +  // Tokenize and parse. +  Token *tok2 = must_tokenize_file(base_file); +  tok = append_tokens(tok, tok2); +  tok = preprocess(tok); + +  // If -M or -MD are given, print file dependencies. +  if (opt_M || opt_MD) { +    print_dependencies(); +    if (opt_M) +      return; +  } + +  // If -E is given, print out preprocessed C code as a result. +  if (opt_E) { +    print_tokens(tok); +    return; +  } + +  Obj *prog = parse(tok); + +  // Open a temporary output buffer. +  char *buf; +  size_t buflen; +  FILE *output_buf = open_memstream(&buf, &buflen); + +  // Traverse the AST to emit assembly. +  codegen(prog, output_buf); +  fclose(output_buf); + +  // Write the asembly text to a file. +  FILE *out = open_file(output_file); +  fwrite(buf, buflen, 1, out); +  fclose(out); +} + +static void assemble(char *input, char *output) { +  char *cmd[] = {"as", "-c", input, "-o", output, NULL}; +  run_subprocess(cmd); +} + +static char *find_file(char *pattern) { +  char *path = NULL; +  glob_t buf = {}; +  glob(pattern, 0, NULL, &buf); +  if (buf.gl_pathc > 0) +    path = strdup(buf.gl_pathv[buf.gl_pathc - 1]); +  globfree(&buf); +  return path; +} + +// Returns true if a given file exists. +bool file_exists(char *path) { +  struct stat st; +  return !stat(path, &st); +} + +static char *find_libpath(void) { +  if (file_exists("/usr/lib/x86_64-linux-gnu/crti.o")) +    return "/usr/lib/x86_64-linux-gnu"; +  if (file_exists("/usr/lib64/crti.o")) +    return "/usr/lib64"; +  error("library path is not found"); +} + +static char *find_gcc_libpath(void) { +  char *paths[] = { +    "/usr/lib/gcc/x86_64-linux-gnu/*/crtbegin.o", +    "/usr/lib/gcc/x86_64-pc-linux-gnu/*/crtbegin.o", // For Gentoo +    "/usr/lib/gcc/x86_64-redhat-linux/*/crtbegin.o", // For Fedora +  }; + +  for (int i = 0; i < sizeof(paths) / sizeof(*paths); i++) { +    char *path = find_file(paths[i]); +    if (path) +      return dirname(path); +  } + +  error("gcc library path is not found"); +} + +static void run_linker(StringArray *inputs, char *output) { +  StringArray arr = {}; + +  strarray_push(&arr, "ld"); +  strarray_push(&arr, "-o"); +  strarray_push(&arr, output); +  strarray_push(&arr, "-m"); +  strarray_push(&arr, "elf_x86_64"); + +  char *libpath = find_libpath(); +  char *gcc_libpath = find_gcc_libpath(); + +  if (opt_shared) { +    strarray_push(&arr, format("%s/crti.o", libpath)); +    strarray_push(&arr, format("%s/crtbeginS.o", gcc_libpath)); +  } else { +    strarray_push(&arr, format("%s/crt1.o", libpath)); +    strarray_push(&arr, format("%s/crti.o", libpath)); +    strarray_push(&arr, format("%s/crtbegin.o", gcc_libpath)); +  } + +  strarray_push(&arr, format("-L%s", gcc_libpath)); +  strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu"); +  strarray_push(&arr, "-L/usr/lib64"); +  strarray_push(&arr, "-L/lib64"); +  strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu"); +  strarray_push(&arr, "-L/usr/lib/x86_64-pc-linux-gnu"); +  strarray_push(&arr, "-L/usr/lib/x86_64-redhat-linux"); +  strarray_push(&arr, "-L/usr/lib"); +  strarray_push(&arr, "-L/lib"); + +  if (!opt_static) { +    strarray_push(&arr, "-dynamic-linker"); +    strarray_push(&arr, "/lib64/ld-linux-x86-64.so.2"); +  } + +  for (int i = 0; i < ld_extra_args.len; i++) +    strarray_push(&arr, ld_extra_args.data[i]); + +  for (int i = 0; i < inputs->len; i++) +    strarray_push(&arr, inputs->data[i]); + +  if (opt_static) { +    strarray_push(&arr, "--start-group"); +    strarray_push(&arr, "-lgcc"); +    strarray_push(&arr, "-lgcc_eh"); +    strarray_push(&arr, "-lc"); +    strarray_push(&arr, "--end-group"); +  } else { +    strarray_push(&arr, "-lc"); +    strarray_push(&arr, "-lgcc"); +    strarray_push(&arr, "--as-needed"); +    strarray_push(&arr, "-lgcc_s"); +    strarray_push(&arr, "--no-as-needed"); +  } + +  if (opt_shared) +    strarray_push(&arr, format("%s/crtendS.o", gcc_libpath)); +  else +    strarray_push(&arr, format("%s/crtend.o", gcc_libpath)); + +  strarray_push(&arr, format("%s/crtn.o", libpath)); +  strarray_push(&arr, NULL); + +  run_subprocess(arr.data); +} + +static FileType get_file_type(char *filename) { +  if (opt_x != FILE_NONE) +    return opt_x; + +  if (endswith(filename, ".a")) +    return FILE_AR; +  if (endswith(filename, ".so")) +    return FILE_DSO; +  if (endswith(filename, ".o")) +    return FILE_OBJ; +  if (endswith(filename, ".c")) +    return FILE_C; +  if (endswith(filename, ".s")) +    return FILE_ASM; + +  error("<command line>: unknown file extension: %s", filename); +} + +int main(int argc, char **argv) { +  atexit(cleanup); +  init_macros(); +  parse_args(argc, argv); + +  if (opt_cc1) { +    add_default_include_paths(argv[0]); +    cc1(); +    return 0; +  } + +  if (input_paths.len > 1 && opt_o && (opt_c || opt_S | opt_E)) +    error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files"); + +  StringArray ld_args = {}; + +  for (int i = 0; i < input_paths.len; i++) { +    char *input = input_paths.data[i]; + +    if (!strncmp(input, "-l", 2)) { +      strarray_push(&ld_args, input); +      continue; +    } + +    if (!strncmp(input, "-Wl,", 4)) { +      char *s = strdup(input + 4); +      char *arg = strtok(s, ","); +      while (arg) { +        strarray_push(&ld_args, arg); +        arg = strtok(NULL, ","); +      } +      continue; +    } + +    char *output; +    if (opt_o) +      output = opt_o; +    else if (opt_S) +      output = replace_extn(input, ".s"); +    else +      output = replace_extn(input, ".o"); + +    FileType type = get_file_type(input); + +    // Handle .o or .a +    if (type == FILE_OBJ || type == FILE_AR || type == FILE_DSO) { +      strarray_push(&ld_args, input); +      continue; +    } + +    // Handle .s +    if (type == FILE_ASM) { +      if (!opt_S) +        assemble(input, output); +      continue; +    } + +    assert(type == FILE_C); + +    // Just preprocess +    if (opt_E || opt_M) { +      run_cc1(argc, argv, input, NULL); +      continue; +    } + +    // Compile +    if (opt_S) { +      run_cc1(argc, argv, input, output); +      continue; +    } + +    // Compile and assemble +    if (opt_c) { +      char *tmp = create_tmpfile(); +      run_cc1(argc, argv, input, tmp); +      assemble(tmp, output); +      continue; +    } + +    // Compile, assemble and link +    char *tmp1 = create_tmpfile(); +    char *tmp2 = create_tmpfile(); +    run_cc1(argc, argv, input, tmp1); +    assemble(tmp1, tmp2); +    strarray_push(&ld_args, tmp2); +    continue; +  } + +  if (ld_args.len > 0) +    run_linker(&ld_args, opt_o ? opt_o : "a.out"); +  return 0; +} diff --git a/src/3p/chibicc/parse.c b/src/3p/chibicc/parse.c new file mode 100644 index 0000000..6acaeb8 --- /dev/null +++ b/src/3p/chibicc/parse.c @@ -0,0 +1,3368 @@ +// This file contains a recursive descent parser for C. +// +// Most functions in this file are named after the symbols they are +// supposed to read from an input token list. For example, stmt() is +// responsible for reading a statement from a token list. The function +// then construct an AST node representing a statement. +// +// Each function conceptually returns two values, an AST node and +// remaining part of the input tokens. Since C doesn't support +// multiple return values, the remaining tokens are returned to the +// caller via a pointer argument. +// +// Input tokens are represented by a linked list. Unlike many recursive +// descent parsers, we don't have the notion of the "input token stream". +// Most parsing functions don't change the global state of the parser. +// So it is very easy to lookahead arbitrary number of tokens in this +// parser. + +#include "chibicc.h" + +// Scope for local variables, global variables, typedefs +// or enum constants +typedef struct { +  Obj *var; +  Type *type_def; +  Type *enum_ty; +  int enum_val; +} VarScope; + +// Represents a block scope. +typedef struct Scope Scope; +struct Scope { +  Scope *next; + +  // C has two block scopes; one is for variables/typedefs and +  // the other is for struct/union/enum tags. +  HashMap vars; +  HashMap tags; +}; + +// Variable attributes such as typedef or extern. +typedef struct { +  bool is_typedef; +  bool is_static; +  bool is_extern; +  bool is_inline; +  bool is_tls; +  int align; +} VarAttr; + +// This struct represents a variable initializer. Since initializers +// can be nested (e.g. `int x[2][2] = {{1, 2}, {3, 4}}`), this struct +// is a tree data structure. +typedef struct Initializer Initializer; +struct Initializer { +  Initializer *next; +  Type *ty; +  Token *tok; +  bool is_flexible; + +  // If it's not an aggregate type and has an initializer, +  // `expr` has an initialization expression. +  Node *expr; + +  // If it's an initializer for an aggregate type (e.g. array or struct), +  // `children` has initializers for its children. +  Initializer **children; + +  // Only one member can be initialized for a union. +  // `mem` is used to clarify which member is initialized. +  Member *mem; +}; + +// For local variable initializer. +typedef struct InitDesg InitDesg; +struct InitDesg { +  InitDesg *next; +  int idx; +  Member *member; +  Obj *var; +}; + +// All local variable instances created during parsing are +// accumulated to this list. +static Obj *locals; + +// Likewise, global variables are accumulated to this list. +static Obj *globals; + +static Scope *scope = &(Scope){}; + +// Points to the function object the parser is currently parsing. +static Obj *current_fn; + +// Lists of all goto statements and labels in the curent function. +static Node *gotos; +static Node *labels; + +// Current "goto" and "continue" jump targets. +static char *brk_label; +static char *cont_label; + +// Points to a node representing a switch if we are parsing +// a switch statement. Otherwise, NULL. +static Node *current_switch; + +static Obj *builtin_alloca; + +static bool is_typename(Token *tok); +static Type *declspec(Token **rest, Token *tok, VarAttr *attr); +static Type *typename(Token **rest, Token *tok); +static Type *enum_specifier(Token **rest, Token *tok); +static Type *typeof_specifier(Token **rest, Token *tok); +static Type *type_suffix(Token **rest, Token *tok, Type *ty); +static Type *declarator(Token **rest, Token *tok, Type *ty); +static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr); +static void array_initializer2(Token **rest, Token *tok, Initializer *init, int i); +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem); +static void initializer2(Token **rest, Token *tok, Initializer *init); +static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty); +static Node *lvar_initializer(Token **rest, Token *tok, Obj *var); +static void gvar_initializer(Token **rest, Token *tok, Obj *var); +static Node *compound_stmt(Token **rest, Token *tok); +static Node *stmt(Token **rest, Token *tok); +static Node *expr_stmt(Token **rest, Token *tok); +static Node *expr(Token **rest, Token *tok); +static int64_t eval(Node *node); +static int64_t eval2(Node *node, char ***label); +static int64_t eval_rval(Node *node, char ***label); +static bool is_const_expr(Node *node); +static Node *assign(Token **rest, Token *tok); +static Node *logor(Token **rest, Token *tok); +static double eval_double(Node *node); +static Node *conditional(Token **rest, Token *tok); +static Node *logand(Token **rest, Token *tok); +static Node *bitor(Token **rest, Token *tok); +static Node *bitxor(Token **rest, Token *tok); +static Node *bitand(Token **rest, Token *tok); +static Node *equality(Token **rest, Token *tok); +static Node *relational(Token **rest, Token *tok); +static Node *shift(Token **rest, Token *tok); +static Node *add(Token **rest, Token *tok); +static Node *new_add(Node *lhs, Node *rhs, Token *tok); +static Node *new_sub(Node *lhs, Node *rhs, Token *tok); +static Node *mul(Token **rest, Token *tok); +static Node *cast(Token **rest, Token *tok); +static Member *get_struct_member(Type *ty, Token *tok); +static Type *struct_decl(Token **rest, Token *tok); +static Type *union_decl(Token **rest, Token *tok); +static Node *postfix(Token **rest, Token *tok); +static Node *funcall(Token **rest, Token *tok, Node *node); +static Node *unary(Token **rest, Token *tok); +static Node *primary(Token **rest, Token *tok); +static Token *parse_typedef(Token *tok, Type *basety); +static bool is_function(Token *tok); +static Token *function(Token *tok, Type *basety, VarAttr *attr); +static Token *global_variable(Token *tok, Type *basety, VarAttr *attr); + +static int align_down(int n, int align) { +  return align_to(n - align + 1, align); +} + +static void enter_scope(void) { +  Scope *sc = calloc(1, sizeof(Scope)); +  sc->next = scope; +  scope = sc; +} + +static void leave_scope(void) { +  scope = scope->next; +} + +// Find a variable by name. +static VarScope *find_var(Token *tok) { +  for (Scope *sc = scope; sc; sc = sc->next) { +    VarScope *sc2 = hashmap_get2(&sc->vars, tok->loc, tok->len); +    if (sc2) +      return sc2; +  } +  return NULL; +} + +static Type *find_tag(Token *tok) { +  for (Scope *sc = scope; sc; sc = sc->next) { +    Type *ty = hashmap_get2(&sc->tags, tok->loc, tok->len); +    if (ty) +      return ty; +  } +  return NULL; +} + +static Node *new_node(NodeKind kind, Token *tok) { +  Node *node = calloc(1, sizeof(Node)); +  node->kind = kind; +  node->tok = tok; +  return node; +} + +static Node *new_binary(NodeKind kind, Node *lhs, Node *rhs, Token *tok) { +  Node *node = new_node(kind, tok); +  node->lhs = lhs; +  node->rhs = rhs; +  return node; +} + +static Node *new_unary(NodeKind kind, Node *expr, Token *tok) { +  Node *node = new_node(kind, tok); +  node->lhs = expr; +  return node; +} + +static Node *new_num(int64_t val, Token *tok) { +  Node *node = new_node(ND_NUM, tok); +  node->val = val; +  return node; +} + +static Node *new_long(int64_t val, Token *tok) { +  Node *node = new_node(ND_NUM, tok); +  node->val = val; +  node->ty = ty_long; +  return node; +} + +static Node *new_ulong(long val, Token *tok) { +  Node *node = new_node(ND_NUM, tok); +  node->val = val; +  node->ty = ty_ulong; +  return node; +} + +static Node *new_var_node(Obj *var, Token *tok) { +  Node *node = new_node(ND_VAR, tok); +  node->var = var; +  return node; +} + +static Node *new_vla_ptr(Obj *var, Token *tok) { +  Node *node = new_node(ND_VLA_PTR, tok); +  node->var = var; +  return node; +} + +Node *new_cast(Node *expr, Type *ty) { +  add_type(expr); + +  Node *node = calloc(1, sizeof(Node)); +  node->kind = ND_CAST; +  node->tok = expr->tok; +  node->lhs = expr; +  node->ty = copy_type(ty); +  return node; +} + +static VarScope *push_scope(char *name) { +  VarScope *sc = calloc(1, sizeof(VarScope)); +  hashmap_put(&scope->vars, name, sc); +  return sc; +} + +static Initializer *new_initializer(Type *ty, bool is_flexible) { +  Initializer *init = calloc(1, sizeof(Initializer)); +  init->ty = ty; + +  if (ty->kind == TY_ARRAY) { +    if (is_flexible && ty->size < 0) { +      init->is_flexible = true; +      return init; +    } + +    init->children = calloc(ty->array_len, sizeof(Initializer *)); +    for (int i = 0; i < ty->array_len; i++) +      init->children[i] = new_initializer(ty->base, false); +    return init; +  } + +  if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) { +    // Count the number of struct members. +    int len = 0; +    for (Member *mem = ty->members; mem; mem = mem->next) +      len++; + +    init->children = calloc(len, sizeof(Initializer *)); + +    for (Member *mem = ty->members; mem; mem = mem->next) { +      if (is_flexible && ty->is_flexible && !mem->next) { +        Initializer *child = calloc(1, sizeof(Initializer)); +        child->ty = mem->ty; +        child->is_flexible = true; +        init->children[mem->idx] = child; +      } else { +        init->children[mem->idx] = new_initializer(mem->ty, false); +      } +    } +    return init; +  } + +  return init; +} + +static Obj *new_var(char *name, Type *ty) { +  Obj *var = calloc(1, sizeof(Obj)); +  var->name = name; +  var->ty = ty; +  var->align = ty->align; +  push_scope(name)->var = var; +  return var; +} + +static Obj *new_lvar(char *name, Type *ty) { +  Obj *var = new_var(name, ty); +  var->is_local = true; +  var->next = locals; +  locals = var; +  return var; +} + +static Obj *new_gvar(char *name, Type *ty) { +  Obj *var = new_var(name, ty); +  var->next = globals; +  var->is_static = true; +  var->is_definition = true; +  globals = var; +  return var; +} + +static char *new_unique_name(void) { +  static int id = 0; +  return format(".L..%d", id++); +} + +static Obj *new_anon_gvar(Type *ty) { +  return new_gvar(new_unique_name(), ty); +} + +static Obj *new_string_literal(char *p, Type *ty) { +  Obj *var = new_anon_gvar(ty); +  var->init_data = p; +  return var; +} + +static char *get_ident(Token *tok) { +  if (tok->kind != TK_IDENT) +    error_tok(tok, "expected an identifier"); +  return strndup(tok->loc, tok->len); +} + +static Type *find_typedef(Token *tok) { +  if (tok->kind == TK_IDENT) { +    VarScope *sc = find_var(tok); +    if (sc) +      return sc->type_def; +  } +  return NULL; +} + +static void push_tag_scope(Token *tok, Type *ty) { +  hashmap_put2(&scope->tags, tok->loc, tok->len, ty); +} + +// declspec = ("void" | "_Bool" | "char" | "short" | "int" | "long" +//             | "typedef" | "static" | "extern" | "inline" +//             | "_Thread_local" | "__thread" +//             | "signed" | "unsigned" +//             | struct-decl | union-decl | typedef-name +//             | enum-specifier | typeof-specifier +//             | "const" | "volatile" | "auto" | "register" | "restrict" +//             | "__restrict" | "__restrict__" | "_Noreturn")+ +// +// The order of typenames in a type-specifier doesn't matter. For +// example, `int long static` means the same as `static long int`. +// That can also be written as `static long` because you can omit +// `int` if `long` or `short` are specified. However, something like +// `char int` is not a valid type specifier. We have to accept only a +// limited combinations of the typenames. +// +// In this function, we count the number of occurrences of each typename +// while keeping the "current" type object that the typenames up +// until that point represent. When we reach a non-typename token, +// we returns the current type object. +static Type *declspec(Token **rest, Token *tok, VarAttr *attr) { +  // We use a single integer as counters for all typenames. +  // For example, bits 0 and 1 represents how many times we saw the +  // keyword "void" so far. With this, we can use a switch statement +  // as you can see below. +  enum { +    VOID     = 1 << 0, +    BOOL     = 1 << 2, +    CHAR     = 1 << 4, +    SHORT    = 1 << 6, +    INT      = 1 << 8, +    LONG     = 1 << 10, +    FLOAT    = 1 << 12, +    DOUBLE   = 1 << 14, +    OTHER    = 1 << 16, +    SIGNED   = 1 << 17, +    UNSIGNED = 1 << 18, +  }; + +  Type *ty = ty_int; +  int counter = 0; +  bool is_atomic = false; + +  while (is_typename(tok)) { +    // Handle storage class specifiers. +    if (equal(tok, "typedef") || equal(tok, "static") || equal(tok, "extern") || +        equal(tok, "inline") || equal(tok, "_Thread_local") || equal(tok, "__thread")) { +      if (!attr) +        error_tok(tok, "storage class specifier is not allowed in this context"); + +      if (equal(tok, "typedef")) +        attr->is_typedef = true; +      else if (equal(tok, "static")) +        attr->is_static = true; +      else if (equal(tok, "extern")) +        attr->is_extern = true; +      else if (equal(tok, "inline")) +        attr->is_inline = true; +      else +        attr->is_tls = true; + +      if (attr->is_typedef && +          attr->is_static + attr->is_extern + attr->is_inline + attr->is_tls > 1) +        error_tok(tok, "typedef may not be used together with static," +                  " extern, inline, __thread or _Thread_local"); +      tok = tok->next; +      continue; +    } + +    // These keywords are recognized but ignored. +    if (consume(&tok, tok, "const") || consume(&tok, tok, "volatile") || +        consume(&tok, tok, "auto") || consume(&tok, tok, "register") || +        consume(&tok, tok, "restrict") || consume(&tok, tok, "__restrict") || +        consume(&tok, tok, "__restrict__") || consume(&tok, tok, "_Noreturn")) +      continue; + +    if (equal(tok, "_Atomic")) { +      tok = tok->next; +      if (equal(tok , "(")) { +        ty = typename(&tok, tok->next); +        tok = skip(tok, ")"); +      } +      is_atomic = true; +      continue; +    } + +    if (equal(tok, "_Alignas")) { +      if (!attr) +        error_tok(tok, "_Alignas is not allowed in this context"); +      tok = skip(tok->next, "("); + +      if (is_typename(tok)) +        attr->align = typename(&tok, tok)->align; +      else +        attr->align = const_expr(&tok, tok); +      tok = skip(tok, ")"); +      continue; +    } + +    // Handle user-defined types. +    Type *ty2 = find_typedef(tok); +    if (equal(tok, "struct") || equal(tok, "union") || equal(tok, "enum") || +        equal(tok, "typeof") || ty2) { +      if (counter) +        break; + +      if (equal(tok, "struct")) { +        ty = struct_decl(&tok, tok->next); +      } else if (equal(tok, "union")) { +        ty = union_decl(&tok, tok->next); +      } else if (equal(tok, "enum")) { +        ty = enum_specifier(&tok, tok->next); +      } else if (equal(tok, "typeof")) { +        ty = typeof_specifier(&tok, tok->next); +      } else { +        ty = ty2; +        tok = tok->next; +      } + +      counter += OTHER; +      continue; +    } + +    // Handle built-in types. +    if (equal(tok, "void")) +      counter += VOID; +    else if (equal(tok, "_Bool")) +      counter += BOOL; +    else if (equal(tok, "char")) +      counter += CHAR; +    else if (equal(tok, "short")) +      counter += SHORT; +    else if (equal(tok, "int")) +      counter += INT; +    else if (equal(tok, "long")) +      counter += LONG; +    else if (equal(tok, "float")) +      counter += FLOAT; +    else if (equal(tok, "double")) +      counter += DOUBLE; +    else if (equal(tok, "signed")) +      counter |= SIGNED; +    else if (equal(tok, "unsigned")) +      counter |= UNSIGNED; +    else +      unreachable(); + +    switch (counter) { +    case VOID: +      ty = ty_void; +      break; +    case BOOL: +      ty = ty_bool; +      break; +    case CHAR: +    case SIGNED + CHAR: +      ty = ty_char; +      break; +    case UNSIGNED + CHAR: +      ty = ty_uchar; +      break; +    case SHORT: +    case SHORT + INT: +    case SIGNED + SHORT: +    case SIGNED + SHORT + INT: +      ty = ty_short; +      break; +    case UNSIGNED + SHORT: +    case UNSIGNED + SHORT + INT: +      ty = ty_ushort; +      break; +    case INT: +    case SIGNED: +    case SIGNED + INT: +      ty = ty_int; +      break; +    case UNSIGNED: +    case UNSIGNED + INT: +      ty = ty_uint; +      break; +    case LONG: +    case LONG + INT: +    case LONG + LONG: +    case LONG + LONG + INT: +    case SIGNED + LONG: +    case SIGNED + LONG + INT: +    case SIGNED + LONG + LONG: +    case SIGNED + LONG + LONG + INT: +      ty = ty_long; +      break; +    case UNSIGNED + LONG: +    case UNSIGNED + LONG + INT: +    case UNSIGNED + LONG + LONG: +    case UNSIGNED + LONG + LONG + INT: +      ty = ty_ulong; +      break; +    case FLOAT: +      ty = ty_float; +      break; +    case DOUBLE: +      ty = ty_double; +      break; +    case LONG + DOUBLE: +      ty = ty_ldouble; +      break; +    default: +      error_tok(tok, "invalid type"); +    } + +    tok = tok->next; +  } + +  if (is_atomic) { +    ty = copy_type(ty); +    ty->is_atomic = true; +  } + +  *rest = tok; +  return ty; +} + +// func-params = ("void" | param ("," param)* ("," "...")?)? ")" +// param       = declspec declarator +static Type *func_params(Token **rest, Token *tok, Type *ty) { +  if (equal(tok, "void") && equal(tok->next, ")")) { +    *rest = tok->next->next; +    return func_type(ty); +  } + +  Type head = {}; +  Type *cur = &head; +  bool is_variadic = false; + +  while (!equal(tok, ")")) { +    if (cur != &head) +      tok = skip(tok, ","); + +    if (equal(tok, "...")) { +      is_variadic = true; +      tok = tok->next; +      skip(tok, ")"); +      break; +    } + +    Type *ty2 = declspec(&tok, tok, NULL); +    ty2 = declarator(&tok, tok, ty2); + +    Token *name = ty2->name; + +    if (ty2->kind == TY_ARRAY) { +      // "array of T" is converted to "pointer to T" only in the parameter +      // context. For example, *argv[] is converted to **argv by this. +      ty2 = pointer_to(ty2->base); +      ty2->name = name; +    } else if (ty2->kind == TY_FUNC) { +      // Likewise, a function is converted to a pointer to a function +      // only in the parameter context. +      ty2 = pointer_to(ty2); +      ty2->name = name; +    } + +    cur = cur->next = copy_type(ty2); +  } + +  if (cur == &head) +    is_variadic = true; + +  ty = func_type(ty); +  ty->params = head.next; +  ty->is_variadic = is_variadic; +  *rest = tok->next; +  return ty; +} + +// array-dimensions = ("static" | "restrict")* const-expr? "]" type-suffix +static Type *array_dimensions(Token **rest, Token *tok, Type *ty) { +  while (equal(tok, "static") || equal(tok, "restrict")) +    tok = tok->next; + +  if (equal(tok, "]")) { +    ty = type_suffix(rest, tok->next, ty); +    return array_of(ty, -1); +  } + +  Node *expr = conditional(&tok, tok); +  tok = skip(tok, "]"); +  ty = type_suffix(rest, tok, ty); + +  if (ty->kind == TY_VLA || !is_const_expr(expr)) +    return vla_of(ty, expr); +  return array_of(ty, eval(expr)); +} + +// type-suffix = "(" func-params +//             | "[" array-dimensions +//             | ε +static Type *type_suffix(Token **rest, Token *tok, Type *ty) { +  if (equal(tok, "(")) +    return func_params(rest, tok->next, ty); + +  if (equal(tok, "[")) +    return array_dimensions(rest, tok->next, ty); + +  *rest = tok; +  return ty; +} + +// pointers = ("*" ("const" | "volatile" | "restrict")*)* +static Type *pointers(Token **rest, Token *tok, Type *ty) { +  while (consume(&tok, tok, "*")) { +    ty = pointer_to(ty); +    while (equal(tok, "const") || equal(tok, "volatile") || equal(tok, "restrict") || +           equal(tok, "__restrict") || equal(tok, "__restrict__")) +      tok = tok->next; +  } +  *rest = tok; +  return ty; +} + +// declarator = pointers ("(" ident ")" | "(" declarator ")" | ident) type-suffix +static Type *declarator(Token **rest, Token *tok, Type *ty) { +  ty = pointers(&tok, tok, ty); + +  if (equal(tok, "(")) { +    Token *start = tok; +    Type dummy = {}; +    declarator(&tok, start->next, &dummy); +    tok = skip(tok, ")"); +    ty = type_suffix(rest, tok, ty); +    return declarator(&tok, start->next, ty); +  } + +  Token *name = NULL; +  Token *name_pos = tok; + +  if (tok->kind == TK_IDENT) { +    name = tok; +    tok = tok->next; +  } + +  ty = type_suffix(rest, tok, ty); +  ty->name = name; +  ty->name_pos = name_pos; +  return ty; +} + +// abstract-declarator = pointers ("(" abstract-declarator ")")? type-suffix +static Type *abstract_declarator(Token **rest, Token *tok, Type *ty) { +  ty = pointers(&tok, tok, ty); + +  if (equal(tok, "(")) { +    Token *start = tok; +    Type dummy = {}; +    abstract_declarator(&tok, start->next, &dummy); +    tok = skip(tok, ")"); +    ty = type_suffix(rest, tok, ty); +    return abstract_declarator(&tok, start->next, ty); +  } + +  return type_suffix(rest, tok, ty); +} + +// type-name = declspec abstract-declarator +static Type *typename(Token **rest, Token *tok) { +  Type *ty = declspec(&tok, tok, NULL); +  return abstract_declarator(rest, tok, ty); +} + +static bool is_end(Token *tok) { +  return equal(tok, "}") || (equal(tok, ",") && equal(tok->next, "}")); +} + +static bool consume_end(Token **rest, Token *tok) { +  if (equal(tok, "}")) { +    *rest = tok->next; +    return true; +  } + +  if (equal(tok, ",") && equal(tok->next, "}")) { +    *rest = tok->next->next; +    return true; +  } + +  return false; +} + +// enum-specifier = ident? "{" enum-list? "}" +//                | ident ("{" enum-list? "}")? +// +// enum-list      = ident ("=" num)? ("," ident ("=" num)?)* ","? +static Type *enum_specifier(Token **rest, Token *tok) { +  Type *ty = enum_type(); + +  // Read a struct tag. +  Token *tag = NULL; +  if (tok->kind == TK_IDENT) { +    tag = tok; +    tok = tok->next; +  } + +  if (tag && !equal(tok, "{")) { +    Type *ty = find_tag(tag); +    if (!ty) +      error_tok(tag, "unknown enum type"); +    if (ty->kind != TY_ENUM) +      error_tok(tag, "not an enum tag"); +    *rest = tok; +    return ty; +  } + +  tok = skip(tok, "{"); + +  // Read an enum-list. +  int i = 0; +  int val = 0; +  while (!consume_end(rest, tok)) { +    if (i++ > 0) +      tok = skip(tok, ","); + +    char *name = get_ident(tok); +    tok = tok->next; + +    if (equal(tok, "=")) +      val = const_expr(&tok, tok->next); + +    VarScope *sc = push_scope(name); +    sc->enum_ty = ty; +    sc->enum_val = val++; +  } + +  if (tag) +    push_tag_scope(tag, ty); +  return ty; +} + +// typeof-specifier = "(" (expr | typename) ")" +static Type *typeof_specifier(Token **rest, Token *tok) { +  tok = skip(tok, "("); + +  Type *ty; +  if (is_typename(tok)) { +    ty = typename(&tok, tok); +  } else { +    Node *node = expr(&tok, tok); +    add_type(node); +    ty = node->ty; +  } +  *rest = skip(tok, ")"); +  return ty; +} + +// Generate code for computing a VLA size. +static Node *compute_vla_size(Type *ty, Token *tok) { +  Node *node = new_node(ND_NULL_EXPR, tok); +  if (ty->base) +    node = new_binary(ND_COMMA, node, compute_vla_size(ty->base, tok), tok); + +  if (ty->kind != TY_VLA) +    return node; + +  Node *base_sz; +  if (ty->base->kind == TY_VLA) +    base_sz = new_var_node(ty->base->vla_size, tok); +  else +    base_sz = new_num(ty->base->size, tok); + +  ty->vla_size = new_lvar("", ty_ulong); +  Node *expr = new_binary(ND_ASSIGN, new_var_node(ty->vla_size, tok), +                          new_binary(ND_MUL, ty->vla_len, base_sz, tok), +                          tok); +  return new_binary(ND_COMMA, node, expr, tok); +} + +static Node *new_alloca(Node *sz) { +  Node *node = new_unary(ND_FUNCALL, new_var_node(builtin_alloca, sz->tok), sz->tok); +  node->func_ty = builtin_alloca->ty; +  node->ty = builtin_alloca->ty->return_ty; +  node->args = sz; +  add_type(sz); +  return node; +} + +// declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";" +static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr) { +  Node head = {}; +  Node *cur = &head; +  int i = 0; + +  while (!equal(tok, ";")) { +    if (i++ > 0) +      tok = skip(tok, ","); + +    Type *ty = declarator(&tok, tok, basety); +    if (ty->kind == TY_VOID) +      error_tok(tok, "variable declared void"); +    if (!ty->name) +      error_tok(ty->name_pos, "variable name omitted"); + +    if (attr && attr->is_static) { +      // static local variable +      Obj *var = new_anon_gvar(ty); +      push_scope(get_ident(ty->name))->var = var; +      if (equal(tok, "=")) +        gvar_initializer(&tok, tok->next, var); +      continue; +    } + +    // Generate code for computing a VLA size. We need to do this +    // even if ty is not VLA because ty may be a pointer to VLA +    // (e.g. int (*foo)[n][m] where n and m are variables.) +    cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok); + +    if (ty->kind == TY_VLA) { +      if (equal(tok, "=")) +        error_tok(tok, "variable-sized object may not be initialized"); + +      // Variable length arrays (VLAs) are translated to alloca() calls. +      // For example, `int x[n+2]` is translated to `tmp = n + 2, +      // x = alloca(tmp)`. +      Obj *var = new_lvar(get_ident(ty->name), ty); +      Token *tok = ty->name; +      Node *expr = new_binary(ND_ASSIGN, new_vla_ptr(var, tok), +                              new_alloca(new_var_node(ty->vla_size, tok)), +                              tok); + +      cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok); +      continue; +    } + +    Obj *var = new_lvar(get_ident(ty->name), ty); +    if (attr && attr->align) +      var->align = attr->align; + +    if (equal(tok, "=")) { +      Node *expr = lvar_initializer(&tok, tok->next, var); +      cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok); +    } + +    if (var->ty->size < 0) +      error_tok(ty->name, "variable has incomplete type"); +    if (var->ty->kind == TY_VOID) +      error_tok(ty->name, "variable declared void"); +  } + +  Node *node = new_node(ND_BLOCK, tok); +  node->body = head.next; +  *rest = tok->next; +  return node; +} + +static Token *skip_excess_element(Token *tok) { +  if (equal(tok, "{")) { +    tok = skip_excess_element(tok->next); +    return skip(tok, "}"); +  } + +  assign(&tok, tok); +  return tok; +} + +// string-initializer = string-literal +static void string_initializer(Token **rest, Token *tok, Initializer *init) { +  if (init->is_flexible) +    *init = *new_initializer(array_of(init->ty->base, tok->ty->array_len), false); + +  int len = MIN(init->ty->array_len, tok->ty->array_len); + +  switch (init->ty->base->size) { +  case 1: { +    char *str = tok->str; +    for (int i = 0; i < len; i++) +      init->children[i]->expr = new_num(str[i], tok); +    break; +  } +  case 2: { +    uint16_t *str = (uint16_t *)tok->str; +    for (int i = 0; i < len; i++) +      init->children[i]->expr = new_num(str[i], tok); +    break; +  } +  case 4: { +    uint32_t *str = (uint32_t *)tok->str; +    for (int i = 0; i < len; i++) +      init->children[i]->expr = new_num(str[i], tok); +    break; +  } +  default: +    unreachable(); +  } + +  *rest = tok->next; +} + +// array-designator = "[" const-expr "]" +// +// C99 added the designated initializer to the language, which allows +// programmers to move the "cursor" of an initializer to any element. +// The syntax looks like this: +// +//   int x[10] = { 1, 2, [5]=3, 4, 5, 6, 7 }; +// +// `[5]` moves the cursor to the 5th element, so the 5th element of x +// is set to 3. Initialization then continues forward in order, so +// 6th, 7th, 8th and 9th elements are initialized with 4, 5, 6 and 7, +// respectively. Unspecified elements (in this case, 3rd and 4th +// elements) are initialized with zero. +// +// Nesting is allowed, so the following initializer is valid: +// +//   int x[5][10] = { [5][8]=1, 2, 3 }; +// +// It sets x[5][8], x[5][9] and x[6][0] to 1, 2 and 3, respectively. +// +// Use `.fieldname` to move the cursor for a struct initializer. E.g. +// +//   struct { int a, b, c; } x = { .c=5 }; +// +// The above initializer sets x.c to 5. +static void array_designator(Token **rest, Token *tok, Type *ty, int *begin, int *end) { +  *begin = const_expr(&tok, tok->next); +  if (*begin >= ty->array_len) +    error_tok(tok, "array designator index exceeds array bounds"); + +  if (equal(tok, "...")) { +    *end = const_expr(&tok, tok->next); +    if (*end >= ty->array_len) +      error_tok(tok, "array designator index exceeds array bounds"); +    if (*end < *begin) +      error_tok(tok, "array designator range [%d, %d] is empty", *begin, *end); +  } else { +    *end = *begin; +  } + +  *rest = skip(tok, "]"); +} + +// struct-designator = "." ident +static Member *struct_designator(Token **rest, Token *tok, Type *ty) { +  Token *start = tok; +  tok = skip(tok, "."); +  if (tok->kind != TK_IDENT) +    error_tok(tok, "expected a field designator"); + +  for (Member *mem = ty->members; mem; mem = mem->next) { +    // Anonymous struct member +    if (mem->ty->kind == TY_STRUCT && !mem->name) { +      if (get_struct_member(mem->ty, tok)) { +        *rest = start; +        return mem; +      } +      continue; +    } + +    // Regular struct member +    if (mem->name->len == tok->len && !strncmp(mem->name->loc, tok->loc, tok->len)) { +      *rest = tok->next; +      return mem; +    } +  } + +  error_tok(tok, "struct has no such member"); +} + +// designation = ("[" const-expr "]" | "." ident)* "="? initializer +static void designation(Token **rest, Token *tok, Initializer *init) { +  if (equal(tok, "[")) { +    if (init->ty->kind != TY_ARRAY) +      error_tok(tok, "array index in non-array initializer"); + +    int begin, end; +    array_designator(&tok, tok, init->ty, &begin, &end); + +    Token *tok2; +    for (int i = begin; i <= end; i++) +      designation(&tok2, tok, init->children[i]); +    array_initializer2(rest, tok2, init, begin + 1); +    return; +  } + +  if (equal(tok, ".") && init->ty->kind == TY_STRUCT) { +    Member *mem = struct_designator(&tok, tok, init->ty); +    designation(&tok, tok, init->children[mem->idx]); +    init->expr = NULL; +    struct_initializer2(rest, tok, init, mem->next); +    return; +  } + +  if (equal(tok, ".") && init->ty->kind == TY_UNION) { +    Member *mem = struct_designator(&tok, tok, init->ty); +    init->mem = mem; +    designation(rest, tok, init->children[mem->idx]); +    return; +  } + +  if (equal(tok, ".")) +    error_tok(tok, "field name not in struct or union initializer"); + +  if (equal(tok, "=")) +    tok = tok->next; +  initializer2(rest, tok, init); +} + +// An array length can be omitted if an array has an initializer +// (e.g. `int x[] = {1,2,3}`). If it's omitted, count the number +// of initializer elements. +static int count_array_init_elements(Token *tok, Type *ty) { +  bool first = true; +  Initializer *dummy = new_initializer(ty->base, true); + +  int i = 0, max = 0; + +  while (!consume_end(&tok, tok)) { +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    if (equal(tok, "[")) { +      i = const_expr(&tok, tok->next); +      if (equal(tok, "...")) +        i = const_expr(&tok, tok->next); +      tok = skip(tok, "]"); +      designation(&tok, tok, dummy); +    } else { +      initializer2(&tok, tok, dummy); +    } + +    i++; +    max = MAX(max, i); +  } +  return max; +} + +// array-initializer1 = "{" initializer ("," initializer)* ","? "}" +static void array_initializer1(Token **rest, Token *tok, Initializer *init) { +  tok = skip(tok, "{"); + +  if (init->is_flexible) { +    int len = count_array_init_elements(tok, init->ty); +    *init = *new_initializer(array_of(init->ty->base, len), false); +  } + +  bool first = true; + +  if (init->is_flexible) { +    int len = count_array_init_elements(tok, init->ty); +    *init = *new_initializer(array_of(init->ty->base, len), false); +  } + +  for (int i = 0; !consume_end(rest, tok); i++) { +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    if (equal(tok, "[")) { +      int begin, end; +      array_designator(&tok, tok, init->ty, &begin, &end); + +      Token *tok2; +      for (int j = begin; j <= end; j++) +        designation(&tok2, tok, init->children[j]); +      tok = tok2; +      i = end; +      continue; +    } + +    if (i < init->ty->array_len) +      initializer2(&tok, tok, init->children[i]); +    else +      tok = skip_excess_element(tok); +  } +} + +// array-initializer2 = initializer ("," initializer)* +static void array_initializer2(Token **rest, Token *tok, Initializer *init, int i) { +  if (init->is_flexible) { +    int len = count_array_init_elements(tok, init->ty); +    *init = *new_initializer(array_of(init->ty->base, len), false); +  } + +  for (; i < init->ty->array_len && !is_end(tok); i++) { +    Token *start = tok; +    if (i > 0) +      tok = skip(tok, ","); + +    if (equal(tok, "[") || equal(tok, ".")) { +      *rest = start; +      return; +    } + +    initializer2(&tok, tok, init->children[i]); +  } +  *rest = tok; +} + +// struct-initializer1 = "{" initializer ("," initializer)* ","? "}" +static void struct_initializer1(Token **rest, Token *tok, Initializer *init) { +  tok = skip(tok, "{"); + +  Member *mem = init->ty->members; +  bool first = true; + +  while (!consume_end(rest, tok)) { +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    if (equal(tok, ".")) { +      mem = struct_designator(&tok, tok, init->ty); +      designation(&tok, tok, init->children[mem->idx]); +      mem = mem->next; +      continue; +    } + +    if (mem) { +      initializer2(&tok, tok, init->children[mem->idx]); +      mem = mem->next; +    } else { +      tok = skip_excess_element(tok); +    } +  } +} + +// struct-initializer2 = initializer ("," initializer)* +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem) { +  bool first = true; + +  for (; mem && !is_end(tok); mem = mem->next) { +    Token *start = tok; + +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    if (equal(tok, "[") || equal(tok, ".")) { +      *rest = start; +      return; +    } + +    initializer2(&tok, tok, init->children[mem->idx]); +  } +  *rest = tok; +} + +static void union_initializer(Token **rest, Token *tok, Initializer *init) { +  // Unlike structs, union initializers take only one initializer, +  // and that initializes the first union member by default. +  // You can initialize other member using a designated initializer. +  if (equal(tok, "{") && equal(tok->next, ".")) { +    Member *mem = struct_designator(&tok, tok->next, init->ty); +    init->mem = mem; +    designation(&tok, tok, init->children[mem->idx]); +    *rest = skip(tok, "}"); +    return; +  } + +  init->mem = init->ty->members; + +  if (equal(tok, "{")) { +    initializer2(&tok, tok->next, init->children[0]); +    consume(&tok, tok, ","); +    *rest = skip(tok, "}"); +  } else { +    initializer2(rest, tok, init->children[0]); +  } +} + +// initializer = string-initializer | array-initializer +//             | struct-initializer | union-initializer +//             | assign +static void initializer2(Token **rest, Token *tok, Initializer *init) { +  if (init->ty->kind == TY_ARRAY && tok->kind == TK_STR) { +    string_initializer(rest, tok, init); +    return; +  } + +  if (init->ty->kind == TY_ARRAY) { +    if (equal(tok, "{")) +      array_initializer1(rest, tok, init); +    else +      array_initializer2(rest, tok, init, 0); +    return; +  } + +  if (init->ty->kind == TY_STRUCT) { +    if (equal(tok, "{")) { +      struct_initializer1(rest, tok, init); +      return; +    } + +    // A struct can be initialized with another struct. E.g. +    // `struct T x = y;` where y is a variable of type `struct T`. +    // Handle that case first. +    Node *expr = assign(rest, tok); +    add_type(expr); +    if (expr->ty->kind == TY_STRUCT) { +      init->expr = expr; +      return; +    } + +    struct_initializer2(rest, tok, init, init->ty->members); +    return; +  } + +  if (init->ty->kind == TY_UNION) { +    union_initializer(rest, tok, init); +    return; +  } + +  if (equal(tok, "{")) { +    // An initializer for a scalar variable can be surrounded by +    // braces. E.g. `int x = {3};`. Handle that case. +    initializer2(&tok, tok->next, init); +    *rest = skip(tok, "}"); +    return; +  } + +  init->expr = assign(rest, tok); +} + +static Type *copy_struct_type(Type *ty) { +  ty = copy_type(ty); + +  Member head = {}; +  Member *cur = &head; +  for (Member *mem = ty->members; mem; mem = mem->next) { +    Member *m = calloc(1, sizeof(Member)); +    *m = *mem; +    cur = cur->next = m; +  } + +  ty->members = head.next; +  return ty; +} + +static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty) { +  Initializer *init = new_initializer(ty, true); +  initializer2(rest, tok, init); + +  if ((ty->kind == TY_STRUCT || ty->kind == TY_UNION) && ty->is_flexible) { +    ty = copy_struct_type(ty); + +    Member *mem = ty->members; +    while (mem->next) +      mem = mem->next; +    mem->ty = init->children[mem->idx]->ty; +    ty->size += mem->ty->size; + +    *new_ty = ty; +    return init; +  } + +  *new_ty = init->ty; +  return init; +} + +static Node *init_desg_expr(InitDesg *desg, Token *tok) { +  if (desg->var) +    return new_var_node(desg->var, tok); + +  if (desg->member) { +    Node *node = new_unary(ND_MEMBER, init_desg_expr(desg->next, tok), tok); +    node->member = desg->member; +    return node; +  } + +  Node *lhs = init_desg_expr(desg->next, tok); +  Node *rhs = new_num(desg->idx, tok); +  return new_unary(ND_DEREF, new_add(lhs, rhs, tok), tok); +} + +static Node *create_lvar_init(Initializer *init, Type *ty, InitDesg *desg, Token *tok) { +  if (ty->kind == TY_ARRAY) { +    Node *node = new_node(ND_NULL_EXPR, tok); +    for (int i = 0; i < ty->array_len; i++) { +      InitDesg desg2 = {desg, i}; +      Node *rhs = create_lvar_init(init->children[i], ty->base, &desg2, tok); +      node = new_binary(ND_COMMA, node, rhs, tok); +    } +    return node; +  } + +  if (ty->kind == TY_STRUCT && !init->expr) { +    Node *node = new_node(ND_NULL_EXPR, tok); + +    for (Member *mem = ty->members; mem; mem = mem->next) { +      InitDesg desg2 = {desg, 0, mem}; +      Node *rhs = create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok); +      node = new_binary(ND_COMMA, node, rhs, tok); +    } +    return node; +  } + +  if (ty->kind == TY_UNION) { +    Member *mem = init->mem ? init->mem : ty->members; +    InitDesg desg2 = {desg, 0, mem}; +    return create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok); +  } + +  if (!init->expr) +    return new_node(ND_NULL_EXPR, tok); + +  Node *lhs = init_desg_expr(desg, tok); +  return new_binary(ND_ASSIGN, lhs, init->expr, tok); +} + +// A variable definition with an initializer is a shorthand notation +// for a variable definition followed by assignments. This function +// generates assignment expressions for an initializer. For example, +// `int x[2][2] = {{6, 7}, {8, 9}}` is converted to the following +// expressions: +// +//   x[0][0] = 6; +//   x[0][1] = 7; +//   x[1][0] = 8; +//   x[1][1] = 9; +static Node *lvar_initializer(Token **rest, Token *tok, Obj *var) { +  Initializer *init = initializer(rest, tok, var->ty, &var->ty); +  InitDesg desg = {NULL, 0, NULL, var}; + +  // If a partial initializer list is given, the standard requires +  // that unspecified elements are set to 0. Here, we simply +  // zero-initialize the entire memory region of a variable before +  // initializing it with user-supplied values. +  Node *lhs = new_node(ND_MEMZERO, tok); +  lhs->var = var; + +  Node *rhs = create_lvar_init(init, var->ty, &desg, tok); +  return new_binary(ND_COMMA, lhs, rhs, tok); +} + +static uint64_t read_buf(char *buf, int sz) { +  if (sz == 1) +    return *buf; +  if (sz == 2) +    return *(uint16_t *)buf; +  if (sz == 4) +    return *(uint32_t *)buf; +  if (sz == 8) +    return *(uint64_t *)buf; +  unreachable(); +} + +static void write_buf(char *buf, uint64_t val, int sz) { +  if (sz == 1) +    *buf = val; +  else if (sz == 2) +    *(uint16_t *)buf = val; +  else if (sz == 4) +    *(uint32_t *)buf = val; +  else if (sz == 8) +    *(uint64_t *)buf = val; +  else +    unreachable(); +} + +static Relocation * +write_gvar_data(Relocation *cur, Initializer *init, Type *ty, char *buf, int offset) { +  if (ty->kind == TY_ARRAY) { +    int sz = ty->base->size; +    for (int i = 0; i < ty->array_len; i++) +      cur = write_gvar_data(cur, init->children[i], ty->base, buf, offset + sz * i); +    return cur; +  } + +  if (ty->kind == TY_STRUCT) { +    for (Member *mem = ty->members; mem; mem = mem->next) { +      if (mem->is_bitfield) { +        Node *expr = init->children[mem->idx]->expr; +        if (!expr) +          break; + +        char *loc = buf + offset + mem->offset; +        uint64_t oldval = read_buf(loc, mem->ty->size); +        uint64_t newval = eval(expr); +        uint64_t mask = (1L << mem->bit_width) - 1; +        uint64_t combined = oldval | ((newval & mask) << mem->bit_offset); +        write_buf(loc, combined, mem->ty->size); +      } else { +        cur = write_gvar_data(cur, init->children[mem->idx], mem->ty, buf, +                              offset + mem->offset); +      } +    } +    return cur; +  } + +  if (ty->kind == TY_UNION) { +    if (!init->mem) +      return cur; +    return write_gvar_data(cur, init->children[init->mem->idx], +                           init->mem->ty, buf, offset); +  } + +  if (!init->expr) +    return cur; + +  if (ty->kind == TY_FLOAT) { +    *(float *)(buf + offset) = eval_double(init->expr); +    return cur; +  } + +  if (ty->kind == TY_DOUBLE) { +    *(double *)(buf + offset) = eval_double(init->expr); +    return cur; +  } + +  char **label = NULL; +  uint64_t val = eval2(init->expr, &label); + +  if (!label) { +    write_buf(buf + offset, val, ty->size); +    return cur; +  } + +  Relocation *rel = calloc(1, sizeof(Relocation)); +  rel->offset = offset; +  rel->label = label; +  rel->addend = val; +  cur->next = rel; +  return cur->next; +} + +// Initializers for global variables are evaluated at compile-time and +// embedded to .data section. This function serializes Initializer +// objects to a flat byte array. It is a compile error if an +// initializer list contains a non-constant expression. +static void gvar_initializer(Token **rest, Token *tok, Obj *var) { +  Initializer *init = initializer(rest, tok, var->ty, &var->ty); + +  Relocation head = {}; +  char *buf = calloc(1, var->ty->size); +  write_gvar_data(&head, init, var->ty, buf, 0); +  var->init_data = buf; +  var->rel = head.next; +} + +// Returns true if a given token represents a type. +static bool is_typename(Token *tok) { +  static HashMap map; + +  if (map.capacity == 0) { +    static char *kw[] = { +      "void", "_Bool", "char", "short", "int", "long", "struct", "union", +      "typedef", "enum", "static", "extern", "_Alignas", "signed", "unsigned", +      "const", "volatile", "auto", "register", "restrict", "__restrict", +      "__restrict__", "_Noreturn", "float", "double", "typeof", "inline", +      "_Thread_local", "__thread", "_Atomic", +    }; + +    for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) +      hashmap_put(&map, kw[i], (void *)1); +  } + +  return hashmap_get2(&map, tok->loc, tok->len) || find_typedef(tok); +} + +// asm-stmt = "asm" ("volatile" | "inline")* "(" string-literal ")" +static Node *asm_stmt(Token **rest, Token *tok) { +  Node *node = new_node(ND_ASM, tok); +  tok = tok->next; + +  while (equal(tok, "volatile") || equal(tok, "inline")) +    tok = tok->next; + +  tok = skip(tok, "("); +  if (tok->kind != TK_STR || tok->ty->base->kind != TY_CHAR) +    error_tok(tok, "expected string literal"); +  node->asm_str = tok->str; +  *rest = skip(tok->next, ")"); +  return node; +} + +// stmt = "return" expr? ";" +//      | "if" "(" expr ")" stmt ("else" stmt)? +//      | "switch" "(" expr ")" stmt +//      | "case" const-expr ("..." const-expr)? ":" stmt +//      | "default" ":" stmt +//      | "for" "(" expr-stmt expr? ";" expr? ")" stmt +//      | "while" "(" expr ")" stmt +//      | "do" stmt "while" "(" expr ")" ";" +//      | "asm" asm-stmt +//      | "goto" (ident | "*" expr) ";" +//      | "break" ";" +//      | "continue" ";" +//      | ident ":" stmt +//      | "{" compound-stmt +//      | expr-stmt +static Node *stmt(Token **rest, Token *tok) { +  if (equal(tok, "return")) { +    Node *node = new_node(ND_RETURN, tok); +    if (consume(rest, tok->next, ";")) +      return node; + +    Node *exp = expr(&tok, tok->next); +    *rest = skip(tok, ";"); + +    add_type(exp); +    Type *ty = current_fn->ty->return_ty; +    if (ty->kind != TY_STRUCT && ty->kind != TY_UNION) +      exp = new_cast(exp, current_fn->ty->return_ty); + +    node->lhs = exp; +    return node; +  } + +  if (equal(tok, "if")) { +    Node *node = new_node(ND_IF, tok); +    tok = skip(tok->next, "("); +    node->cond = expr(&tok, tok); +    tok = skip(tok, ")"); +    node->then = stmt(&tok, tok); +    if (equal(tok, "else")) +      node->els = stmt(&tok, tok->next); +    *rest = tok; +    return node; +  } + +  if (equal(tok, "switch")) { +    Node *node = new_node(ND_SWITCH, tok); +    tok = skip(tok->next, "("); +    node->cond = expr(&tok, tok); +    tok = skip(tok, ")"); + +    Node *sw = current_switch; +    current_switch = node; + +    char *brk = brk_label; +    brk_label = node->brk_label = new_unique_name(); + +    node->then = stmt(rest, tok); + +    current_switch = sw; +    brk_label = brk; +    return node; +  } + +  if (equal(tok, "case")) { +    if (!current_switch) +      error_tok(tok, "stray case"); + +    Node *node = new_node(ND_CASE, tok); +    int begin = const_expr(&tok, tok->next); +    int end; + +    if (equal(tok, "...")) { +      // [GNU] Case ranges, e.g. "case 1 ... 5:" +      end = const_expr(&tok, tok->next); +      if (end < begin) +        error_tok(tok, "empty case range specified"); +    } else { +      end = begin; +    } + +    tok = skip(tok, ":"); +    node->label = new_unique_name(); +    node->lhs = stmt(rest, tok); +    node->begin = begin; +    node->end = end; +    node->case_next = current_switch->case_next; +    current_switch->case_next = node; +    return node; +  } + +  if (equal(tok, "default")) { +    if (!current_switch) +      error_tok(tok, "stray default"); + +    Node *node = new_node(ND_CASE, tok); +    tok = skip(tok->next, ":"); +    node->label = new_unique_name(); +    node->lhs = stmt(rest, tok); +    current_switch->default_case = node; +    return node; +  } + +  if (equal(tok, "for")) { +    Node *node = new_node(ND_FOR, tok); +    tok = skip(tok->next, "("); + +    enter_scope(); + +    char *brk = brk_label; +    char *cont = cont_label; +    brk_label = node->brk_label = new_unique_name(); +    cont_label = node->cont_label = new_unique_name(); + +    if (is_typename(tok)) { +      Type *basety = declspec(&tok, tok, NULL); +      node->init = declaration(&tok, tok, basety, NULL); +    } else { +      node->init = expr_stmt(&tok, tok); +    } + +    if (!equal(tok, ";")) +      node->cond = expr(&tok, tok); +    tok = skip(tok, ";"); + +    if (!equal(tok, ")")) +      node->inc = expr(&tok, tok); +    tok = skip(tok, ")"); + +    node->then = stmt(rest, tok); + +    leave_scope(); +    brk_label = brk; +    cont_label = cont; +    return node; +  } + +  if (equal(tok, "while")) { +    Node *node = new_node(ND_FOR, tok); +    tok = skip(tok->next, "("); +    node->cond = expr(&tok, tok); +    tok = skip(tok, ")"); + +    char *brk = brk_label; +    char *cont = cont_label; +    brk_label = node->brk_label = new_unique_name(); +    cont_label = node->cont_label = new_unique_name(); + +    node->then = stmt(rest, tok); + +    brk_label = brk; +    cont_label = cont; +    return node; +  } + +  if (equal(tok, "do")) { +    Node *node = new_node(ND_DO, tok); + +    char *brk = brk_label; +    char *cont = cont_label; +    brk_label = node->brk_label = new_unique_name(); +    cont_label = node->cont_label = new_unique_name(); + +    node->then = stmt(&tok, tok->next); + +    brk_label = brk; +    cont_label = cont; + +    tok = skip(tok, "while"); +    tok = skip(tok, "("); +    node->cond = expr(&tok, tok); +    tok = skip(tok, ")"); +    *rest = skip(tok, ";"); +    return node; +  } + +  if (equal(tok, "asm")) +    return asm_stmt(rest, tok); + +  if (equal(tok, "goto")) { +    if (equal(tok->next, "*")) { +      // [GNU] `goto *ptr` jumps to the address specified by `ptr`. +      Node *node = new_node(ND_GOTO_EXPR, tok); +      node->lhs = expr(&tok, tok->next->next); +      *rest = skip(tok, ";"); +      return node; +    } + +    Node *node = new_node(ND_GOTO, tok); +    node->label = get_ident(tok->next); +    node->goto_next = gotos; +    gotos = node; +    *rest = skip(tok->next->next, ";"); +    return node; +  } + +  if (equal(tok, "break")) { +    if (!brk_label) +      error_tok(tok, "stray break"); +    Node *node = new_node(ND_GOTO, tok); +    node->unique_label = brk_label; +    *rest = skip(tok->next, ";"); +    return node; +  } + +  if (equal(tok, "continue")) { +    if (!cont_label) +      error_tok(tok, "stray continue"); +    Node *node = new_node(ND_GOTO, tok); +    node->unique_label = cont_label; +    *rest = skip(tok->next, ";"); +    return node; +  } + +  if (tok->kind == TK_IDENT && equal(tok->next, ":")) { +    Node *node = new_node(ND_LABEL, tok); +    node->label = strndup(tok->loc, tok->len); +    node->unique_label = new_unique_name(); +    node->lhs = stmt(rest, tok->next->next); +    node->goto_next = labels; +    labels = node; +    return node; +  } + +  if (equal(tok, "{")) +    return compound_stmt(rest, tok->next); + +  return expr_stmt(rest, tok); +} + +// compound-stmt = (typedef | declaration | stmt)* "}" +static Node *compound_stmt(Token **rest, Token *tok) { +  Node *node = new_node(ND_BLOCK, tok); +  Node head = {}; +  Node *cur = &head; + +  enter_scope(); + +  while (!equal(tok, "}")) { +    if (is_typename(tok) && !equal(tok->next, ":")) { +      VarAttr attr = {}; +      Type *basety = declspec(&tok, tok, &attr); + +      if (attr.is_typedef) { +        tok = parse_typedef(tok, basety); +        continue; +      } + +      if (is_function(tok)) { +        tok = function(tok, basety, &attr); +        continue; +      } + +      if (attr.is_extern) { +        tok = global_variable(tok, basety, &attr); +        continue; +      } + +      cur = cur->next = declaration(&tok, tok, basety, &attr); +    } else { +      cur = cur->next = stmt(&tok, tok); +    } +    add_type(cur); +  } + +  leave_scope(); + +  node->body = head.next; +  *rest = tok->next; +  return node; +} + +// expr-stmt = expr? ";" +static Node *expr_stmt(Token **rest, Token *tok) { +  if (equal(tok, ";")) { +    *rest = tok->next; +    return new_node(ND_BLOCK, tok); +  } + +  Node *node = new_node(ND_EXPR_STMT, tok); +  node->lhs = expr(&tok, tok); +  *rest = skip(tok, ";"); +  return node; +} + +// expr = assign ("," expr)? +static Node *expr(Token **rest, Token *tok) { +  Node *node = assign(&tok, tok); + +  if (equal(tok, ",")) +    return new_binary(ND_COMMA, node, expr(rest, tok->next), tok); + +  *rest = tok; +  return node; +} + +static int64_t eval(Node *node) { +  return eval2(node, NULL); +} + +// Evaluate a given node as a constant expression. +// +// A constant expression is either just a number or ptr+n where ptr +// is a pointer to a global variable and n is a postiive/negative +// number. The latter form is accepted only as an initialization +// expression for a global variable. +static int64_t eval2(Node *node, char ***label) { +  add_type(node); + +  if (is_flonum(node->ty)) +    return eval_double(node); + +  switch (node->kind) { +  case ND_ADD: +    return eval2(node->lhs, label) + eval(node->rhs); +  case ND_SUB: +    return eval2(node->lhs, label) - eval(node->rhs); +  case ND_MUL: +    return eval(node->lhs) * eval(node->rhs); +  case ND_DIV: +    if (node->ty->is_unsigned) +      return (uint64_t)eval(node->lhs) / eval(node->rhs); +    return eval(node->lhs) / eval(node->rhs); +  case ND_NEG: +    return -eval(node->lhs); +  case ND_MOD: +    if (node->ty->is_unsigned) +      return (uint64_t)eval(node->lhs) % eval(node->rhs); +    return eval(node->lhs) % eval(node->rhs); +  case ND_BITAND: +    return eval(node->lhs) & eval(node->rhs); +  case ND_BITOR: +    return eval(node->lhs) | eval(node->rhs); +  case ND_BITXOR: +    return eval(node->lhs) ^ eval(node->rhs); +  case ND_SHL: +    return eval(node->lhs) << eval(node->rhs); +  case ND_SHR: +    if (node->ty->is_unsigned && node->ty->size == 8) +      return (uint64_t)eval(node->lhs) >> eval(node->rhs); +    return eval(node->lhs) >> eval(node->rhs); +  case ND_EQ: +    return eval(node->lhs) == eval(node->rhs); +  case ND_NE: +    return eval(node->lhs) != eval(node->rhs); +  case ND_LT: +    if (node->lhs->ty->is_unsigned) +      return (uint64_t)eval(node->lhs) < eval(node->rhs); +    return eval(node->lhs) < eval(node->rhs); +  case ND_LE: +    if (node->lhs->ty->is_unsigned) +      return (uint64_t)eval(node->lhs) <= eval(node->rhs); +    return eval(node->lhs) <= eval(node->rhs); +  case ND_COND: +    return eval(node->cond) ? eval2(node->then, label) : eval2(node->els, label); +  case ND_COMMA: +    return eval2(node->rhs, label); +  case ND_NOT: +    return !eval(node->lhs); +  case ND_BITNOT: +    return ~eval(node->lhs); +  case ND_LOGAND: +    return eval(node->lhs) && eval(node->rhs); +  case ND_LOGOR: +    return eval(node->lhs) || eval(node->rhs); +  case ND_CAST: { +    int64_t val = eval2(node->lhs, label); +    if (is_integer(node->ty)) { +      switch (node->ty->size) { +      case 1: return node->ty->is_unsigned ? (uint8_t)val : (int8_t)val; +      case 2: return node->ty->is_unsigned ? (uint16_t)val : (int16_t)val; +      case 4: return node->ty->is_unsigned ? (uint32_t)val : (int32_t)val; +      } +    } +    return val; +  } +  case ND_ADDR: +    return eval_rval(node->lhs, label); +  case ND_LABEL_VAL: +    *label = &node->unique_label; +    return 0; +  case ND_MEMBER: +    if (!label) +      error_tok(node->tok, "not a compile-time constant"); +    if (node->ty->kind != TY_ARRAY) +      error_tok(node->tok, "invalid initializer"); +    return eval_rval(node->lhs, label) + node->member->offset; +  case ND_VAR: +    if (!label) +      error_tok(node->tok, "not a compile-time constant"); +    if (node->var->ty->kind != TY_ARRAY && node->var->ty->kind != TY_FUNC) +      error_tok(node->tok, "invalid initializer"); +    *label = &node->var->name; +    return 0; +  case ND_NUM: +    return node->val; +  } + +  error_tok(node->tok, "not a compile-time constant"); +} + +static int64_t eval_rval(Node *node, char ***label) { +  switch (node->kind) { +  case ND_VAR: +    if (node->var->is_local) +      error_tok(node->tok, "not a compile-time constant"); +    *label = &node->var->name; +    return 0; +  case ND_DEREF: +    return eval2(node->lhs, label); +  case ND_MEMBER: +    return eval_rval(node->lhs, label) + node->member->offset; +  } + +  error_tok(node->tok, "invalid initializer"); +} + +static bool is_const_expr(Node *node) { +  add_type(node); + +  switch (node->kind) { +  case ND_ADD: +  case ND_SUB: +  case ND_MUL: +  case ND_DIV: +  case ND_BITAND: +  case ND_BITOR: +  case ND_BITXOR: +  case ND_SHL: +  case ND_SHR: +  case ND_EQ: +  case ND_NE: +  case ND_LT: +  case ND_LE: +  case ND_LOGAND: +  case ND_LOGOR: +    return is_const_expr(node->lhs) && is_const_expr(node->rhs); +  case ND_COND: +    if (!is_const_expr(node->cond)) +      return false; +    return is_const_expr(eval(node->cond) ? node->then : node->els); +  case ND_COMMA: +    return is_const_expr(node->rhs); +  case ND_NEG: +  case ND_NOT: +  case ND_BITNOT: +  case ND_CAST: +    return is_const_expr(node->lhs); +  case ND_NUM: +    return true; +  } + +  return false; +} + +int64_t const_expr(Token **rest, Token *tok) { +  Node *node = conditional(rest, tok); +  return eval(node); +} + +static double eval_double(Node *node) { +  add_type(node); + +  if (is_integer(node->ty)) { +    if (node->ty->is_unsigned) +      return (unsigned long)eval(node); +    return eval(node); +  } + +  switch (node->kind) { +  case ND_ADD: +    return eval_double(node->lhs) + eval_double(node->rhs); +  case ND_SUB: +    return eval_double(node->lhs) - eval_double(node->rhs); +  case ND_MUL: +    return eval_double(node->lhs) * eval_double(node->rhs); +  case ND_DIV: +    return eval_double(node->lhs) / eval_double(node->rhs); +  case ND_NEG: +    return -eval_double(node->lhs); +  case ND_COND: +    return eval_double(node->cond) ? eval_double(node->then) : eval_double(node->els); +  case ND_COMMA: +    return eval_double(node->rhs); +  case ND_CAST: +    if (is_flonum(node->lhs->ty)) +      return eval_double(node->lhs); +    return eval(node->lhs); +  case ND_NUM: +    return node->fval; +  } + +  error_tok(node->tok, "not a compile-time constant"); +} + +// Convert op= operators to expressions containing an assignment. +// +// In general, `A op= C` is converted to ``tmp = &A, *tmp = *tmp op B`. +// However, if a given expression is of form `A.x op= C`, the input is +// converted to `tmp = &A, (*tmp).x = (*tmp).x op C` to handle assignments +// to bitfields. +static Node *to_assign(Node *binary) { +  add_type(binary->lhs); +  add_type(binary->rhs); +  Token *tok = binary->tok; + +  // Convert `A.x op= C` to `tmp = &A, (*tmp).x = (*tmp).x op C`. +  if (binary->lhs->kind == ND_MEMBER) { +    Obj *var = new_lvar("", pointer_to(binary->lhs->lhs->ty)); + +    Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok), +                             new_unary(ND_ADDR, binary->lhs->lhs, tok), tok); + +    Node *expr2 = new_unary(ND_MEMBER, +                            new_unary(ND_DEREF, new_var_node(var, tok), tok), +                            tok); +    expr2->member = binary->lhs->member; + +    Node *expr3 = new_unary(ND_MEMBER, +                            new_unary(ND_DEREF, new_var_node(var, tok), tok), +                            tok); +    expr3->member = binary->lhs->member; + +    Node *expr4 = new_binary(ND_ASSIGN, expr2, +                             new_binary(binary->kind, expr3, binary->rhs, tok), +                             tok); + +    return new_binary(ND_COMMA, expr1, expr4, tok); +  } + +  // If A is an atomic type, Convert `A op= B` to +  // +  // ({ +  //   T1 *addr = &A; T2 val = (B); T1 old = *addr; T1 new; +  //   do { +  //    new = old op val; +  //   } while (!atomic_compare_exchange_strong(addr, &old, new)); +  //   new; +  // }) +  if (binary->lhs->ty->is_atomic) { +    Node head = {}; +    Node *cur = &head; + +    Obj *addr = new_lvar("", pointer_to(binary->lhs->ty)); +    Obj *val = new_lvar("", binary->rhs->ty); +    Obj *old = new_lvar("", binary->lhs->ty); +    Obj *new = new_lvar("", binary->lhs->ty); + +    cur = cur->next = +      new_unary(ND_EXPR_STMT, +                new_binary(ND_ASSIGN, new_var_node(addr, tok), +                           new_unary(ND_ADDR, binary->lhs, tok), tok), +                tok); + +    cur = cur->next = +      new_unary(ND_EXPR_STMT, +                new_binary(ND_ASSIGN, new_var_node(val, tok), binary->rhs, tok), +                tok); + +    cur = cur->next = +      new_unary(ND_EXPR_STMT, +                new_binary(ND_ASSIGN, new_var_node(old, tok), +                           new_unary(ND_DEREF, new_var_node(addr, tok), tok), tok), +                tok); + +    Node *loop = new_node(ND_DO, tok); +    loop->brk_label = new_unique_name(); +    loop->cont_label = new_unique_name(); + +    Node *body = new_binary(ND_ASSIGN, +                            new_var_node(new, tok), +                            new_binary(binary->kind, new_var_node(old, tok), +                                       new_var_node(val, tok), tok), +                            tok); + +    loop->then = new_node(ND_BLOCK, tok); +    loop->then->body = new_unary(ND_EXPR_STMT, body, tok); + +    Node *cas = new_node(ND_CAS, tok); +    cas->cas_addr = new_var_node(addr, tok); +    cas->cas_old = new_unary(ND_ADDR, new_var_node(old, tok), tok); +    cas->cas_new = new_var_node(new, tok); +    loop->cond = new_unary(ND_NOT, cas, tok); + +    cur = cur->next = loop; +    cur = cur->next = new_unary(ND_EXPR_STMT, new_var_node(new, tok), tok); + +    Node *node = new_node(ND_STMT_EXPR, tok); +    node->body = head.next; +    return node; +  } + +  // Convert `A op= B` to ``tmp = &A, *tmp = *tmp op B`. +  Obj *var = new_lvar("", pointer_to(binary->lhs->ty)); + +  Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok), +                           new_unary(ND_ADDR, binary->lhs, tok), tok); + +  Node *expr2 = +    new_binary(ND_ASSIGN, +               new_unary(ND_DEREF, new_var_node(var, tok), tok), +               new_binary(binary->kind, +                          new_unary(ND_DEREF, new_var_node(var, tok), tok), +                          binary->rhs, +                          tok), +               tok); + +  return new_binary(ND_COMMA, expr1, expr2, tok); +} + +// assign    = conditional (assign-op assign)? +// assign-op = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" +//           | "<<=" | ">>=" +static Node *assign(Token **rest, Token *tok) { +  Node *node = conditional(&tok, tok); + +  if (equal(tok, "=")) +    return new_binary(ND_ASSIGN, node, assign(rest, tok->next), tok); + +  if (equal(tok, "+=")) +    return to_assign(new_add(node, assign(rest, tok->next), tok)); + +  if (equal(tok, "-=")) +    return to_assign(new_sub(node, assign(rest, tok->next), tok)); + +  if (equal(tok, "*=")) +    return to_assign(new_binary(ND_MUL, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "/=")) +    return to_assign(new_binary(ND_DIV, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "%=")) +    return to_assign(new_binary(ND_MOD, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "&=")) +    return to_assign(new_binary(ND_BITAND, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "|=")) +    return to_assign(new_binary(ND_BITOR, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "^=")) +    return to_assign(new_binary(ND_BITXOR, node, assign(rest, tok->next), tok)); + +  if (equal(tok, "<<=")) +    return to_assign(new_binary(ND_SHL, node, assign(rest, tok->next), tok)); + +  if (equal(tok, ">>=")) +    return to_assign(new_binary(ND_SHR, node, assign(rest, tok->next), tok)); + +  *rest = tok; +  return node; +} + +// conditional = logor ("?" expr? ":" conditional)? +static Node *conditional(Token **rest, Token *tok) { +  Node *cond = logor(&tok, tok); + +  if (!equal(tok, "?")) { +    *rest = tok; +    return cond; +  } + +  if (equal(tok->next, ":")) { +    // [GNU] Compile `a ?: b` as `tmp = a, tmp ? tmp : b`. +    add_type(cond); +    Obj *var = new_lvar("", cond->ty); +    Node *lhs = new_binary(ND_ASSIGN, new_var_node(var, tok), cond, tok); +    Node *rhs = new_node(ND_COND, tok); +    rhs->cond = new_var_node(var, tok); +    rhs->then = new_var_node(var, tok); +    rhs->els = conditional(rest, tok->next->next); +    return new_binary(ND_COMMA, lhs, rhs, tok); +  } + +  Node *node = new_node(ND_COND, tok); +  node->cond = cond; +  node->then = expr(&tok, tok->next); +  tok = skip(tok, ":"); +  node->els = conditional(rest, tok); +  return node; +} + +// logor = logand ("||" logand)* +static Node *logor(Token **rest, Token *tok) { +  Node *node = logand(&tok, tok); +  while (equal(tok, "||")) { +    Token *start = tok; +    node = new_binary(ND_LOGOR, node, logand(&tok, tok->next), start); +  } +  *rest = tok; +  return node; +} + +// logand = bitor ("&&" bitor)* +static Node *logand(Token **rest, Token *tok) { +  Node *node = bitor(&tok, tok); +  while (equal(tok, "&&")) { +    Token *start = tok; +    node = new_binary(ND_LOGAND, node, bitor(&tok, tok->next), start); +  } +  *rest = tok; +  return node; +} + +// bitor = bitxor ("|" bitxor)* +static Node *bitor(Token **rest, Token *tok) { +  Node *node = bitxor(&tok, tok); +  while (equal(tok, "|")) { +    Token *start = tok; +    node = new_binary(ND_BITOR, node, bitxor(&tok, tok->next), start); +  } +  *rest = tok; +  return node; +} + +// bitxor = bitand ("^" bitand)* +static Node *bitxor(Token **rest, Token *tok) { +  Node *node = bitand(&tok, tok); +  while (equal(tok, "^")) { +    Token *start = tok; +    node = new_binary(ND_BITXOR, node, bitand(&tok, tok->next), start); +  } +  *rest = tok; +  return node; +} + +// bitand = equality ("&" equality)* +static Node *bitand(Token **rest, Token *tok) { +  Node *node = equality(&tok, tok); +  while (equal(tok, "&")) { +    Token *start = tok; +    node = new_binary(ND_BITAND, node, equality(&tok, tok->next), start); +  } +  *rest = tok; +  return node; +} + +// equality = relational ("==" relational | "!=" relational)* +static Node *equality(Token **rest, Token *tok) { +  Node *node = relational(&tok, tok); + +  for (;;) { +    Token *start = tok; + +    if (equal(tok, "==")) { +      node = new_binary(ND_EQ, node, relational(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, "!=")) { +      node = new_binary(ND_NE, node, relational(&tok, tok->next), start); +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)* +static Node *relational(Token **rest, Token *tok) { +  Node *node = shift(&tok, tok); + +  for (;;) { +    Token *start = tok; + +    if (equal(tok, "<")) { +      node = new_binary(ND_LT, node, shift(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, "<=")) { +      node = new_binary(ND_LE, node, shift(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, ">")) { +      node = new_binary(ND_LT, shift(&tok, tok->next), node, start); +      continue; +    } + +    if (equal(tok, ">=")) { +      node = new_binary(ND_LE, shift(&tok, tok->next), node, start); +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// shift = add ("<<" add | ">>" add)* +static Node *shift(Token **rest, Token *tok) { +  Node *node = add(&tok, tok); + +  for (;;) { +    Token *start = tok; + +    if (equal(tok, "<<")) { +      node = new_binary(ND_SHL, node, add(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, ">>")) { +      node = new_binary(ND_SHR, node, add(&tok, tok->next), start); +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// In C, `+` operator is overloaded to perform the pointer arithmetic. +// If p is a pointer, p+n adds not n but sizeof(*p)*n to the value of p, +// so that p+n points to the location n elements (not bytes) ahead of p. +// In other words, we need to scale an integer value before adding to a +// pointer value. This function takes care of the scaling. +static Node *new_add(Node *lhs, Node *rhs, Token *tok) { +  add_type(lhs); +  add_type(rhs); + +  // num + num +  if (is_numeric(lhs->ty) && is_numeric(rhs->ty)) +    return new_binary(ND_ADD, lhs, rhs, tok); + +  if (lhs->ty->base && rhs->ty->base) +    error_tok(tok, "invalid operands"); + +  // Canonicalize `num + ptr` to `ptr + num`. +  if (!lhs->ty->base && rhs->ty->base) { +    Node *tmp = lhs; +    lhs = rhs; +    rhs = tmp; +  } + +  // VLA + num +  if (lhs->ty->base->kind == TY_VLA) { +    rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), tok); +    return new_binary(ND_ADD, lhs, rhs, tok); +  } + +  // ptr + num +  rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok); +  return new_binary(ND_ADD, lhs, rhs, tok); +} + +// Like `+`, `-` is overloaded for the pointer type. +static Node *new_sub(Node *lhs, Node *rhs, Token *tok) { +  add_type(lhs); +  add_type(rhs); + +  // num - num +  if (is_numeric(lhs->ty) && is_numeric(rhs->ty)) +    return new_binary(ND_SUB, lhs, rhs, tok); + +  // VLA + num +  if (lhs->ty->base->kind == TY_VLA) { +    rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), tok); +    add_type(rhs); +    Node *node = new_binary(ND_SUB, lhs, rhs, tok); +    node->ty = lhs->ty; +    return node; +  } + +  // ptr - num +  if (lhs->ty->base && is_integer(rhs->ty)) { +    rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok); +    add_type(rhs); +    Node *node = new_binary(ND_SUB, lhs, rhs, tok); +    node->ty = lhs->ty; +    return node; +  } + +  // ptr - ptr, which returns how many elements are between the two. +  if (lhs->ty->base && rhs->ty->base) { +    Node *node = new_binary(ND_SUB, lhs, rhs, tok); +    node->ty = ty_long; +    return new_binary(ND_DIV, node, new_num(lhs->ty->base->size, tok), tok); +  } + +  error_tok(tok, "invalid operands"); +} + +// add = mul ("+" mul | "-" mul)* +static Node *add(Token **rest, Token *tok) { +  Node *node = mul(&tok, tok); + +  for (;;) { +    Token *start = tok; + +    if (equal(tok, "+")) { +      node = new_add(node, mul(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, "-")) { +      node = new_sub(node, mul(&tok, tok->next), start); +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// mul = cast ("*" cast | "/" cast | "%" cast)* +static Node *mul(Token **rest, Token *tok) { +  Node *node = cast(&tok, tok); + +  for (;;) { +    Token *start = tok; + +    if (equal(tok, "*")) { +      node = new_binary(ND_MUL, node, cast(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, "/")) { +      node = new_binary(ND_DIV, node, cast(&tok, tok->next), start); +      continue; +    } + +    if (equal(tok, "%")) { +      node = new_binary(ND_MOD, node, cast(&tok, tok->next), start); +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// cast = "(" type-name ")" cast | unary +static Node *cast(Token **rest, Token *tok) { +  if (equal(tok, "(") && is_typename(tok->next)) { +    Token *start = tok; +    Type *ty = typename(&tok, tok->next); +    tok = skip(tok, ")"); + +    // compound literal +    if (equal(tok, "{")) +      return unary(rest, start); + +    // type cast +    Node *node = new_cast(cast(rest, tok), ty); +    node->tok = start; +    return node; +  } + +  return unary(rest, tok); +} + +// unary = ("+" | "-" | "*" | "&" | "!" | "~") cast +//       | ("++" | "--") unary +//       | "&&" ident +//       | postfix +static Node *unary(Token **rest, Token *tok) { +  if (equal(tok, "+")) +    return cast(rest, tok->next); + +  if (equal(tok, "-")) +    return new_unary(ND_NEG, cast(rest, tok->next), tok); + +  if (equal(tok, "&")) { +    Node *lhs = cast(rest, tok->next); +    add_type(lhs); +    if (lhs->kind == ND_MEMBER && lhs->member->is_bitfield) +      error_tok(tok, "cannot take address of bitfield"); +    return new_unary(ND_ADDR, lhs, tok); +  } + +  if (equal(tok, "*")) { +    // [https://www.sigbus.info/n1570#6.5.3.2p4] This is an oddity +    // in the C spec, but dereferencing a function shouldn't do +    // anything. If foo is a function, `*foo`, `**foo` or `*****foo` +    // are all equivalent to just `foo`. +    Node *node = cast(rest, tok->next); +    add_type(node); +    if (node->ty->kind == TY_FUNC) +      return node; +    return new_unary(ND_DEREF, node, tok); +  } + +  if (equal(tok, "!")) +    return new_unary(ND_NOT, cast(rest, tok->next), tok); + +  if (equal(tok, "~")) +    return new_unary(ND_BITNOT, cast(rest, tok->next), tok); + +  // Read ++i as i+=1 +  if (equal(tok, "++")) +    return to_assign(new_add(unary(rest, tok->next), new_num(1, tok), tok)); + +  // Read --i as i-=1 +  if (equal(tok, "--")) +    return to_assign(new_sub(unary(rest, tok->next), new_num(1, tok), tok)); + +  // [GNU] labels-as-values +  if (equal(tok, "&&")) { +    Node *node = new_node(ND_LABEL_VAL, tok); +    node->label = get_ident(tok->next); +    node->goto_next = gotos; +    gotos = node; +    *rest = tok->next->next; +    return node; +  } + +  return postfix(rest, tok); +} + +// struct-members = (declspec declarator (","  declarator)* ";")* +static void struct_members(Token **rest, Token *tok, Type *ty) { +  Member head = {}; +  Member *cur = &head; +  int idx = 0; + +  while (!equal(tok, "}")) { +    VarAttr attr = {}; +    Type *basety = declspec(&tok, tok, &attr); +    bool first = true; + +    // Anonymous struct member +    if ((basety->kind == TY_STRUCT || basety->kind == TY_UNION) && +        consume(&tok, tok, ";")) { +      Member *mem = calloc(1, sizeof(Member)); +      mem->ty = basety; +      mem->idx = idx++; +      mem->align = attr.align ? attr.align : mem->ty->align; +      cur = cur->next = mem; +      continue; +    } + +    // Regular struct members +    while (!consume(&tok, tok, ";")) { +      if (!first) +        tok = skip(tok, ","); +      first = false; + +      Member *mem = calloc(1, sizeof(Member)); +      mem->ty = declarator(&tok, tok, basety); +      mem->name = mem->ty->name; +      mem->idx = idx++; +      mem->align = attr.align ? attr.align : mem->ty->align; + +      if (consume(&tok, tok, ":")) { +        mem->is_bitfield = true; +        mem->bit_width = const_expr(&tok, tok); +      } + +      cur = cur->next = mem; +    } +  } + +  // If the last element is an array of incomplete type, it's +  // called a "flexible array member". It should behave as if +  // if were a zero-sized array. +  if (cur != &head && cur->ty->kind == TY_ARRAY && cur->ty->array_len < 0) { +    cur->ty = array_of(cur->ty->base, 0); +    ty->is_flexible = true; +  } + +  *rest = tok->next; +  ty->members = head.next; +} + +// attribute = ("__attribute__" "(" "(" "packed" ")" ")")* +static Token *attribute_list(Token *tok, Type *ty) { +  while (consume(&tok, tok, "__attribute__")) { +    tok = skip(tok, "("); +    tok = skip(tok, "("); + +    bool first = true; + +    while (!consume(&tok, tok, ")")) { +      if (!first) +        tok = skip(tok, ","); +      first = false; + +      if (consume(&tok, tok, "packed")) { +        ty->is_packed = true; +        continue; +      } + +      if (consume(&tok, tok, "aligned")) { +        tok = skip(tok, "("); +        ty->align = const_expr(&tok, tok); +        tok = skip(tok, ")"); +        continue; +      } + +      error_tok(tok, "unknown attribute"); +    } + +    tok = skip(tok, ")"); +  } + +  return tok; +} + +// struct-union-decl = attribute? ident? ("{" struct-members)? +static Type *struct_union_decl(Token **rest, Token *tok) { +  Type *ty = struct_type(); +  tok = attribute_list(tok, ty); + +  // Read a tag. +  Token *tag = NULL; +  if (tok->kind == TK_IDENT) { +    tag = tok; +    tok = tok->next; +  } + +  if (tag && !equal(tok, "{")) { +    *rest = tok; + +    Type *ty2 = find_tag(tag); +    if (ty2) +      return ty2; + +    ty->size = -1; +    push_tag_scope(tag, ty); +    return ty; +  } + +  tok = skip(tok, "{"); + +  // Construct a struct object. +  struct_members(&tok, tok, ty); +  *rest = attribute_list(tok, ty); + +  if (tag) { +    // If this is a redefinition, overwrite a previous type. +    // Otherwise, register the struct type. +    Type *ty2 = hashmap_get2(&scope->tags, tag->loc, tag->len); +    if (ty2) { +      *ty2 = *ty; +      return ty2; +    } + +    push_tag_scope(tag, ty); +  } + +  return ty; +} + +// struct-decl = struct-union-decl +static Type *struct_decl(Token **rest, Token *tok) { +  Type *ty = struct_union_decl(rest, tok); +  ty->kind = TY_STRUCT; + +  if (ty->size < 0) +    return ty; + +  // Assign offsets within the struct to members. +  int bits = 0; + +  for (Member *mem = ty->members; mem; mem = mem->next) { +    if (mem->is_bitfield && mem->bit_width == 0) { +      // Zero-width anonymous bitfield has a special meaning. +      // It affects only alignment. +      bits = align_to(bits, mem->ty->size * 8); +    } else if (mem->is_bitfield) { +      int sz = mem->ty->size; +      if (bits / (sz * 8) != (bits + mem->bit_width - 1) / (sz * 8)) +        bits = align_to(bits, sz * 8); + +      mem->offset = align_down(bits / 8, sz); +      mem->bit_offset = bits % (sz * 8); +      bits += mem->bit_width; +    } else { +      if (!ty->is_packed) +        bits = align_to(bits, mem->align * 8); +      mem->offset = bits / 8; +      bits += mem->ty->size * 8; +    } + +    if (!ty->is_packed && ty->align < mem->align) +      ty->align = mem->align; +  } + +  ty->size = align_to(bits, ty->align * 8) / 8; +  return ty; +} + +// union-decl = struct-union-decl +static Type *union_decl(Token **rest, Token *tok) { +  Type *ty = struct_union_decl(rest, tok); +  ty->kind = TY_UNION; + +  if (ty->size < 0) +    return ty; + +  // If union, we don't have to assign offsets because they +  // are already initialized to zero. We need to compute the +  // alignment and the size though. +  for (Member *mem = ty->members; mem; mem = mem->next) { +    if (ty->align < mem->align) +      ty->align = mem->align; +    if (ty->size < mem->ty->size) +      ty->size = mem->ty->size; +  } +  ty->size = align_to(ty->size, ty->align); +  return ty; +} + +// Find a struct member by name. +static Member *get_struct_member(Type *ty, Token *tok) { +  for (Member *mem = ty->members; mem; mem = mem->next) { +    // Anonymous struct member +    if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) && +        !mem->name) { +      if (get_struct_member(mem->ty, tok)) +        return mem; +      continue; +    } + +    // Regular struct member +    if (mem->name->len == tok->len && +        !strncmp(mem->name->loc, tok->loc, tok->len)) +      return mem; +  } +  return NULL; +} + +// Create a node representing a struct member access, such as foo.bar +// where foo is a struct and bar is a member name. +// +// C has a feature called "anonymous struct" which allows a struct to +// have another unnamed struct as a member like this: +// +//   struct { struct { int a; }; int b; } x; +// +// The members of an anonymous struct belong to the outer struct's +// member namespace. Therefore, in the above example, you can access +// member "a" of the anonymous struct as "x.a". +// +// This function takes care of anonymous structs. +static Node *struct_ref(Node *node, Token *tok) { +  add_type(node); +  if (node->ty->kind != TY_STRUCT && node->ty->kind != TY_UNION) +    error_tok(node->tok, "not a struct nor a union"); + +  Type *ty = node->ty; + +  for (;;) { +    Member *mem = get_struct_member(ty, tok); +    if (!mem) +      error_tok(tok, "no such member"); +    node = new_unary(ND_MEMBER, node, tok); +    node->member = mem; +    if (mem->name) +      break; +    ty = mem->ty; +  } +  return node; +} + +// Convert A++ to `(typeof A)((A += 1) - 1)` +static Node *new_inc_dec(Node *node, Token *tok, int addend) { +  add_type(node); +  return new_cast(new_add(to_assign(new_add(node, new_num(addend, tok), tok)), +                          new_num(-addend, tok), tok), +                  node->ty); +} + +// postfix = "(" type-name ")" "{" initializer-list "}" +//         = ident "(" func-args ")" postfix-tail* +//         | primary postfix-tail* +// +// postfix-tail = "[" expr "]" +//              | "(" func-args ")" +//              | "." ident +//              | "->" ident +//              | "++" +//              | "--" +static Node *postfix(Token **rest, Token *tok) { +  if (equal(tok, "(") && is_typename(tok->next)) { +    // Compound literal +    Token *start = tok; +    Type *ty = typename(&tok, tok->next); +    tok = skip(tok, ")"); + +    if (scope->next == NULL) { +      Obj *var = new_anon_gvar(ty); +      gvar_initializer(rest, tok, var); +      return new_var_node(var, start); +    } + +    Obj *var = new_lvar("", ty); +    Node *lhs = lvar_initializer(rest, tok, var); +    Node *rhs = new_var_node(var, tok); +    return new_binary(ND_COMMA, lhs, rhs, start); +  } + +  Node *node = primary(&tok, tok); + +  for (;;) { +    if (equal(tok, "(")) { +      node = funcall(&tok, tok->next, node); +      continue; +    } + +    if (equal(tok, "[")) { +      // x[y] is short for *(x+y) +      Token *start = tok; +      Node *idx = expr(&tok, tok->next); +      tok = skip(tok, "]"); +      node = new_unary(ND_DEREF, new_add(node, idx, start), start); +      continue; +    } + +    if (equal(tok, ".")) { +      node = struct_ref(node, tok->next); +      tok = tok->next->next; +      continue; +    } + +    if (equal(tok, "->")) { +      // x->y is short for (*x).y +      node = new_unary(ND_DEREF, node, tok); +      node = struct_ref(node, tok->next); +      tok = tok->next->next; +      continue; +    } + +    if (equal(tok, "++")) { +      node = new_inc_dec(node, tok, 1); +      tok = tok->next; +      continue; +    } + +    if (equal(tok, "--")) { +      node = new_inc_dec(node, tok, -1); +      tok = tok->next; +      continue; +    } + +    *rest = tok; +    return node; +  } +} + +// funcall = (assign ("," assign)*)? ")" +static Node *funcall(Token **rest, Token *tok, Node *fn) { +  add_type(fn); + +  if (fn->ty->kind != TY_FUNC && +      (fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) +    error_tok(fn->tok, "not a function"); + +  Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base; +  Type *param_ty = ty->params; + +  Node head = {}; +  Node *cur = &head; + +  while (!equal(tok, ")")) { +    if (cur != &head) +      tok = skip(tok, ","); + +    Node *arg = assign(&tok, tok); +    add_type(arg); + +    if (!param_ty && !ty->is_variadic) +      error_tok(tok, "too many arguments"); + +    if (param_ty) { +      if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) +        arg = new_cast(arg, param_ty); +      param_ty = param_ty->next; +    } else if (arg->ty->kind == TY_FLOAT) { +      // If parameter type is omitted (e.g. in "..."), float +      // arguments are promoted to double. +      arg = new_cast(arg, ty_double); +    } + +    cur = cur->next = arg; +  } + +  if (param_ty) +    error_tok(tok, "too few arguments"); + +  *rest = skip(tok, ")"); + +  Node *node = new_unary(ND_FUNCALL, fn, tok); +  node->func_ty = ty; +  node->ty = ty->return_ty; +  node->args = head.next; + +  // If a function returns a struct, it is caller's responsibility +  // to allocate a space for the return value. +  if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) +    node->ret_buffer = new_lvar("", node->ty); +  return node; +} + +// generic-selection = "(" assign "," generic-assoc ("," generic-assoc)* ")" +// +// generic-assoc = type-name ":" assign +//               | "default" ":" assign +static Node *generic_selection(Token **rest, Token *tok) { +  Token *start = tok; +  tok = skip(tok, "("); + +  Node *ctrl = assign(&tok, tok); +  add_type(ctrl); + +  Type *t1 = ctrl->ty; +  if (t1->kind == TY_FUNC) +    t1 = pointer_to(t1); +  else if (t1->kind == TY_ARRAY) +    t1 = pointer_to(t1->base); + +  Node *ret = NULL; + +  while (!consume(rest, tok, ")")) { +    tok = skip(tok, ","); + +    if (equal(tok, "default")) { +      tok = skip(tok->next, ":"); +      Node *node = assign(&tok, tok); +      if (!ret) +        ret = node; +      continue; +    } + +    Type *t2 = typename(&tok, tok); +    tok = skip(tok, ":"); +    Node *node = assign(&tok, tok); +    if (is_compatible(t1, t2)) +      ret = node; +  } + +  if (!ret) +    error_tok(start, "controlling expression type not compatible with" +              " any generic association type"); +  return ret; +} + +// primary = "(" "{" stmt+ "}" ")" +//         | "(" expr ")" +//         | "sizeof" "(" type-name ")" +//         | "sizeof" unary +//         | "_Alignof" "(" type-name ")" +//         | "_Alignof" unary +//         | "_Generic" generic-selection +//         | "__builtin_types_compatible_p" "(" type-name, type-name, ")" +//         | "__builtin_reg_class" "(" type-name ")" +//         | ident +//         | str +//         | num +static Node *primary(Token **rest, Token *tok) { +  Token *start = tok; + +  if (equal(tok, "(") && equal(tok->next, "{")) { +    // This is a GNU statement expresssion. +    Node *node = new_node(ND_STMT_EXPR, tok); +    node->body = compound_stmt(&tok, tok->next->next)->body; +    *rest = skip(tok, ")"); +    return node; +  } + +  if (equal(tok, "(")) { +    Node *node = expr(&tok, tok->next); +    *rest = skip(tok, ")"); +    return node; +  } + +  if (equal(tok, "sizeof") && equal(tok->next, "(") && is_typename(tok->next->next)) { +    Type *ty = typename(&tok, tok->next->next); +    *rest = skip(tok, ")"); + +    if (ty->kind == TY_VLA) { +      if (ty->vla_size) +        return new_var_node(ty->vla_size, tok); + +      Node *lhs = compute_vla_size(ty, tok); +      Node *rhs = new_var_node(ty->vla_size, tok); +      return new_binary(ND_COMMA, lhs, rhs, tok); +    } + +    return new_ulong(ty->size, start); +  } + +  if (equal(tok, "sizeof")) { +    Node *node = unary(rest, tok->next); +    add_type(node); +    if (node->ty->kind == TY_VLA) +      return new_var_node(node->ty->vla_size, tok); +    return new_ulong(node->ty->size, tok); +  } + +  if (equal(tok, "_Alignof") && equal(tok->next, "(") && is_typename(tok->next->next)) { +    Type *ty = typename(&tok, tok->next->next); +    *rest = skip(tok, ")"); +    return new_ulong(ty->align, tok); +  } + +  if (equal(tok, "_Alignof")) { +    Node *node = unary(rest, tok->next); +    add_type(node); +    return new_ulong(node->ty->align, tok); +  } + +  if (equal(tok, "_Generic")) +    return generic_selection(rest, tok->next); + +  if (equal(tok, "__builtin_types_compatible_p")) { +    tok = skip(tok->next, "("); +    Type *t1 = typename(&tok, tok); +    tok = skip(tok, ","); +    Type *t2 = typename(&tok, tok); +    *rest = skip(tok, ")"); +    return new_num(is_compatible(t1, t2), start); +  } + +  if (equal(tok, "__builtin_reg_class")) { +    tok = skip(tok->next, "("); +    Type *ty = typename(&tok, tok); +    *rest = skip(tok, ")"); + +    if (is_integer(ty) || ty->kind == TY_PTR) +      return new_num(0, start); +    if (is_flonum(ty)) +      return new_num(1, start); +    return new_num(2, start); +  } + +  if (equal(tok, "__builtin_compare_and_swap")) { +    Node *node = new_node(ND_CAS, tok); +    tok = skip(tok->next, "("); +    node->cas_addr = assign(&tok, tok); +    tok = skip(tok, ","); +    node->cas_old = assign(&tok, tok); +    tok = skip(tok, ","); +    node->cas_new = assign(&tok, tok); +    *rest = skip(tok, ")"); +    return node; +  } + +  if (equal(tok, "__builtin_atomic_exchange")) { +    Node *node = new_node(ND_EXCH, tok); +    tok = skip(tok->next, "("); +    node->lhs = assign(&tok, tok); +    tok = skip(tok, ","); +    node->rhs = assign(&tok, tok); +    *rest = skip(tok, ")"); +    return node; +  } + +  if (tok->kind == TK_IDENT) { +    // Variable or enum constant +    VarScope *sc = find_var(tok); +    *rest = tok->next; + +    // For "static inline" function +    if (sc && sc->var && sc->var->is_function) { +      if (current_fn) +        strarray_push(¤t_fn->refs, sc->var->name); +      else +        sc->var->is_root = true; +    } + +    if (sc) { +      if (sc->var) +        return new_var_node(sc->var, tok); +      if (sc->enum_ty) +        return new_num(sc->enum_val, tok); +    } + +    if (equal(tok->next, "(")) +      error_tok(tok, "implicit declaration of a function"); +    error_tok(tok, "undefined variable"); +  } + +  if (tok->kind == TK_STR) { +    Obj *var = new_string_literal(tok->str, tok->ty); +    *rest = tok->next; +    return new_var_node(var, tok); +  } + +  if (tok->kind == TK_NUM) { +    Node *node; +    if (is_flonum(tok->ty)) { +      node = new_node(ND_NUM, tok); +      node->fval = tok->fval; +    } else { +      node = new_num(tok->val, tok); +    } + +    node->ty = tok->ty; +    *rest = tok->next; +    return node; +  } + +  error_tok(tok, "expected an expression"); +} + +static Token *parse_typedef(Token *tok, Type *basety) { +  bool first = true; + +  while (!consume(&tok, tok, ";")) { +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    Type *ty = declarator(&tok, tok, basety); +    if (!ty->name) +      error_tok(ty->name_pos, "typedef name omitted"); +    push_scope(get_ident(ty->name))->type_def = ty; +  } +  return tok; +} + +static void create_param_lvars(Type *param) { +  if (param) { +    create_param_lvars(param->next); +    if (!param->name) +      error_tok(param->name_pos, "parameter name omitted"); +    new_lvar(get_ident(param->name), param); +  } +} + +// This function matches gotos or labels-as-values with labels. +// +// We cannot resolve gotos as we parse a function because gotos +// can refer a label that appears later in the function. +// So, we need to do this after we parse the entire function. +static void resolve_goto_labels(void) { +  for (Node *x = gotos; x; x = x->goto_next) { +    for (Node *y = labels; y; y = y->goto_next) { +      if (!strcmp(x->label, y->label)) { +        x->unique_label = y->unique_label; +        break; +      } +    } + +    if (x->unique_label == NULL) +      error_tok(x->tok->next, "use of undeclared label"); +  } + +  gotos = labels = NULL; +} + +static Obj *find_func(char *name) { +  Scope *sc = scope; +  while (sc->next) +    sc = sc->next; + +  VarScope *sc2 = hashmap_get(&sc->vars, name); +  if (sc2 && sc2->var && sc2->var->is_function) +    return sc2->var; +  return NULL; +} + +static void mark_live(Obj *var) { +  if (!var->is_function || var->is_live) +    return; +  var->is_live = true; + +  for (int i = 0; i < var->refs.len; i++) { +    Obj *fn = find_func(var->refs.data[i]); +    if (fn) +      mark_live(fn); +  } +} + +static Token *function(Token *tok, Type *basety, VarAttr *attr) { +  Type *ty = declarator(&tok, tok, basety); +  if (!ty->name) +    error_tok(ty->name_pos, "function name omitted"); +  char *name_str = get_ident(ty->name); + +  Obj *fn = find_func(name_str); +  if (fn) { +    // Redeclaration +    if (!fn->is_function) +      error_tok(tok, "redeclared as a different kind of symbol"); +    if (fn->is_definition && equal(tok, "{")) +      error_tok(tok, "redefinition of %s", name_str); +    if (!fn->is_static && attr->is_static) +      error_tok(tok, "static declaration follows a non-static declaration"); +    fn->is_definition = fn->is_definition || equal(tok, "{"); +  } else { +    fn = new_gvar(name_str, ty); +    fn->is_function = true; +    fn->is_definition = equal(tok, "{"); +    fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern); +    fn->is_inline = attr->is_inline; +  } + +  fn->is_root = !(fn->is_static && fn->is_inline); + +  if (consume(&tok, tok, ";")) +    return tok; + +  current_fn = fn; +  locals = NULL; +  enter_scope(); +  create_param_lvars(ty->params); + +  // A buffer for a struct/union return value is passed +  // as the hidden first parameter. +  Type *rty = ty->return_ty; +  if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) +    new_lvar("", pointer_to(rty)); + +  fn->params = locals; + +  if (ty->is_variadic) +    fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136)); +  fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char)); + +  tok = skip(tok, "{"); + +  // [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is +  // automatically defined as a local variable containing the +  // current function name. +  push_scope("__func__")->var = +    new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1)); + +  // [GNU] __FUNCTION__ is yet another name of __func__. +  push_scope("__FUNCTION__")->var = +    new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1)); + +  fn->body = compound_stmt(&tok, tok); +  fn->locals = locals; +  leave_scope(); +  resolve_goto_labels(); +  return tok; +} + +static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) { +  bool first = true; + +  while (!consume(&tok, tok, ";")) { +    if (!first) +      tok = skip(tok, ","); +    first = false; + +    Type *ty = declarator(&tok, tok, basety); +    if (!ty->name) +      error_tok(ty->name_pos, "variable name omitted"); + +    Obj *var = new_gvar(get_ident(ty->name), ty); +    var->is_definition = !attr->is_extern; +    var->is_static = attr->is_static; +    var->is_tls = attr->is_tls; +    if (attr->align) +      var->align = attr->align; + +    if (equal(tok, "=")) +      gvar_initializer(&tok, tok->next, var); +    else if (!attr->is_extern && !attr->is_tls) +      var->is_tentative = true; +  } +  return tok; +} + +// Lookahead tokens and returns true if a given token is a start +// of a function definition or declaration. +static bool is_function(Token *tok) { +  if (equal(tok, ";")) +    return false; + +  Type dummy = {}; +  Type *ty = declarator(&tok, tok, &dummy); +  return ty->kind == TY_FUNC; +} + +// Remove redundant tentative definitions. +static void scan_globals(void) { +  Obj head; +  Obj *cur = &head; + +  for (Obj *var = globals; var; var = var->next) { +    if (!var->is_tentative) { +      cur = cur->next = var; +      continue; +    } + +    // Find another definition of the same identifier. +    Obj *var2 = globals; +    for (; var2; var2 = var2->next) +      if (var != var2 && var2->is_definition && !strcmp(var->name, var2->name)) +        break; + +    // If there's another definition, the tentative definition +    // is redundant +    if (!var2) +      cur = cur->next = var; +  } + +  cur->next = NULL; +  globals = head.next; +} + +static void declare_builtin_functions(void) { +  Type *ty = func_type(pointer_to(ty_void)); +  ty->params = copy_type(ty_int); +  builtin_alloca = new_gvar("alloca", ty); +  builtin_alloca->is_definition = false; +} + +// program = (typedef | function-definition | global-variable)* +Obj *parse(Token *tok) { +  declare_builtin_functions(); +  globals = NULL; + +  while (tok->kind != TK_EOF) { +    VarAttr attr = {}; +    Type *basety = declspec(&tok, tok, &attr); + +    // Typedef +    if (attr.is_typedef) { +      tok = parse_typedef(tok, basety); +      continue; +    } + +    // Function +    if (is_function(tok)) { +      tok = function(tok, basety, &attr); +      continue; +    } + +    // Global variable +    tok = global_variable(tok, basety, &attr); +  } + +  for (Obj *var = globals; var; var = var->next) +    if (var->is_root) +      mark_live(var); + +  // Remove redundant tentative definitions. +  scan_globals(); +  return globals; +} diff --git a/src/3p/chibicc/preprocess.c b/src/3p/chibicc/preprocess.c new file mode 100644 index 0000000..cd8d1d8 --- /dev/null +++ b/src/3p/chibicc/preprocess.c @@ -0,0 +1,1208 @@ +// This file implements the C preprocessor. +// +// The preprocessor takes a list of tokens as an input and returns a +// new list of tokens as an output. +// +// The preprocessing language is designed in such a way that that's +// guaranteed to stop even if there is a recursive macro. +// Informally speaking, a macro is applied only once for each token. +// That is, if a macro token T appears in a result of direct or +// indirect macro expansion of T, T won't be expanded any further. +// For example, if T is defined as U, and U is defined as T, then +// token T is expanded to U and then to T and the macro expansion +// stops at that point. +// +// To achieve the above behavior, we attach for each token a set of +// macro names from which the token is expanded. The set is called +// "hideset". Hideset is initially empty, and every time we expand a +// macro, the macro name is added to the resulting tokens' hidesets. +// +// The above macro expansion algorithm is explained in this document +// written by Dave Prossor, which is used as a basis for the +// standard's wording: +// https://github.com/rui314/chibicc/wiki/cpp.algo.pdf + +#include "chibicc.h" + +typedef struct MacroParam MacroParam; +struct MacroParam { +  MacroParam *next; +  char *name; +}; + +typedef struct MacroArg MacroArg; +struct MacroArg { +  MacroArg *next; +  char *name; +  bool is_va_args; +  Token *tok; +}; + +typedef Token *macro_handler_fn(Token *); + +typedef struct Macro Macro; +struct Macro { +  char *name; +  bool is_objlike; // Object-like or function-like +  MacroParam *params; +  char *va_args_name; +  Token *body; +  macro_handler_fn *handler; +}; + +// `#if` can be nested, so we use a stack to manage nested `#if`s. +typedef struct CondIncl CondIncl; +struct CondIncl { +  CondIncl *next; +  enum { IN_THEN, IN_ELIF, IN_ELSE } ctx; +  Token *tok; +  bool included; +}; + +typedef struct Hideset Hideset; +struct Hideset { +  Hideset *next; +  char *name; +}; + +static HashMap macros; +static CondIncl *cond_incl; +static HashMap pragma_once; +static int include_next_idx; + +static Token *preprocess2(Token *tok); +static Macro *find_macro(Token *tok); + +static bool is_hash(Token *tok) { +  return tok->at_bol && equal(tok, "#"); +} + +// Some preprocessor directives such as #include allow extraneous +// tokens before newline. This function skips such tokens. +static Token *skip_line(Token *tok) { +  if (tok->at_bol) +    return tok; +  warn_tok(tok, "extra token"); +  while (!tok->at_bol) +    tok = tok->next; +  return tok; +} + +static Token *copy_token(Token *tok) { +  Token *t = calloc(1, sizeof(Token)); +  *t = *tok; +  t->next = NULL; +  return t; +} + +static Token *new_eof(Token *tok) { +  Token *t = copy_token(tok); +  t->kind = TK_EOF; +  t->len = 0; +  return t; +} + +static Hideset *new_hideset(char *name) { +  Hideset *hs = calloc(1, sizeof(Hideset)); +  hs->name = name; +  return hs; +} + +static Hideset *hideset_union(Hideset *hs1, Hideset *hs2) { +  Hideset head = {}; +  Hideset *cur = &head; + +  for (; hs1; hs1 = hs1->next) +    cur = cur->next = new_hideset(hs1->name); +  cur->next = hs2; +  return head.next; +} + +static bool hideset_contains(Hideset *hs, char *s, int len) { +  for (; hs; hs = hs->next) +    if (strlen(hs->name) == len && !strncmp(hs->name, s, len)) +      return true; +  return false; +} + +static Hideset *hideset_intersection(Hideset *hs1, Hideset *hs2) { +  Hideset head = {}; +  Hideset *cur = &head; + +  for (; hs1; hs1 = hs1->next) +    if (hideset_contains(hs2, hs1->name, strlen(hs1->name))) +      cur = cur->next = new_hideset(hs1->name); +  return head.next; +} + +static Token *add_hideset(Token *tok, Hideset *hs) { +  Token head = {}; +  Token *cur = &head; + +  for (; tok; tok = tok->next) { +    Token *t = copy_token(tok); +    t->hideset = hideset_union(t->hideset, hs); +    cur = cur->next = t; +  } +  return head.next; +} + +// Append tok2 to the end of tok1. +static Token *append(Token *tok1, Token *tok2) { +  if (tok1->kind == TK_EOF) +    return tok2; + +  Token head = {}; +  Token *cur = &head; + +  for (; tok1->kind != TK_EOF; tok1 = tok1->next) +    cur = cur->next = copy_token(tok1); +  cur->next = tok2; +  return head.next; +} + +static Token *skip_cond_incl2(Token *tok) { +  while (tok->kind != TK_EOF) { +    if (is_hash(tok) && +        (equal(tok->next, "if") || equal(tok->next, "ifdef") || +         equal(tok->next, "ifndef"))) { +      tok = skip_cond_incl2(tok->next->next); +      continue; +    } +    if (is_hash(tok) && equal(tok->next, "endif")) +      return tok->next->next; +    tok = tok->next; +  } +  return tok; +} + +// Skip until next `#else`, `#elif` or `#endif`. +// Nested `#if` and `#endif` are skipped. +static Token *skip_cond_incl(Token *tok) { +  while (tok->kind != TK_EOF) { +    if (is_hash(tok) && +        (equal(tok->next, "if") || equal(tok->next, "ifdef") || +         equal(tok->next, "ifndef"))) { +      tok = skip_cond_incl2(tok->next->next); +      continue; +    } + +    if (is_hash(tok) && +        (equal(tok->next, "elif") || equal(tok->next, "else") || +         equal(tok->next, "endif"))) +      break; +    tok = tok->next; +  } +  return tok; +} + +// Double-quote a given string and returns it. +static char *quote_string(char *str) { +  int bufsize = 3; +  for (int i = 0; str[i]; i++) { +    if (str[i] == '\\' || str[i] == '"') +      bufsize++; +    bufsize++; +  } + +  char *buf = calloc(1, bufsize); +  char *p = buf; +  *p++ = '"'; +  for (int i = 0; str[i]; i++) { +    if (str[i] == '\\' || str[i] == '"') +      *p++ = '\\'; +    *p++ = str[i]; +  } +  *p++ = '"'; +  *p++ = '\0'; +  return buf; +} + +static Token *new_str_token(char *str, Token *tmpl) { +  char *buf = quote_string(str); +  return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); +} + +// Copy all tokens until the next newline, terminate them with +// an EOF token and then returns them. This function is used to +// create a new list of tokens for `#if` arguments. +static Token *copy_line(Token **rest, Token *tok) { +  Token head = {}; +  Token *cur = &head; + +  for (; !tok->at_bol; tok = tok->next) +    cur = cur->next = copy_token(tok); + +  cur->next = new_eof(tok); +  *rest = tok; +  return head.next; +} + +static Token *new_num_token(int val, Token *tmpl) { +  char *buf = format("%d\n", val); +  return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); +} + +static Token *read_const_expr(Token **rest, Token *tok) { +  tok = copy_line(rest, tok); + +  Token head = {}; +  Token *cur = &head; + +  while (tok->kind != TK_EOF) { +    // "defined(foo)" or "defined foo" becomes "1" if macro "foo" +    // is defined. Otherwise "0". +    if (equal(tok, "defined")) { +      Token *start = tok; +      bool has_paren = consume(&tok, tok->next, "("); + +      if (tok->kind != TK_IDENT) +        error_tok(start, "macro name must be an identifier"); +      Macro *m = find_macro(tok); +      tok = tok->next; + +      if (has_paren) +        tok = skip(tok, ")"); + +      cur = cur->next = new_num_token(m ? 1 : 0, start); +      continue; +    } + +    cur = cur->next = tok; +    tok = tok->next; +  } + +  cur->next = tok; +  return head.next; +} + +// Read and evaluate a constant expression. +static long eval_const_expr(Token **rest, Token *tok) { +  Token *start = tok; +  Token *expr = read_const_expr(rest, tok->next); +  expr = preprocess2(expr); + +  if (expr->kind == TK_EOF) +    error_tok(start, "no expression"); + +  // [https://www.sigbus.info/n1570#6.10.1p4] The standard requires +  // we replace remaining non-macro identifiers with "0" before +  // evaluating a constant expression. For example, `#if foo` is +  // equivalent to `#if 0` if foo is not defined. +  for (Token *t = expr; t->kind != TK_EOF; t = t->next) { +    if (t->kind == TK_IDENT) { +      Token *next = t->next; +      *t = *new_num_token(0, t); +      t->next = next; +    } +  } + +  // Convert pp-numbers to regular numbers +  convert_pp_tokens(expr); + +  Token *rest2; +  long val = const_expr(&rest2, expr); +  if (rest2->kind != TK_EOF) +    error_tok(rest2, "extra token"); +  return val; +} + +static CondIncl *push_cond_incl(Token *tok, bool included) { +  CondIncl *ci = calloc(1, sizeof(CondIncl)); +  ci->next = cond_incl; +  ci->ctx = IN_THEN; +  ci->tok = tok; +  ci->included = included; +  cond_incl = ci; +  return ci; +} + +static Macro *find_macro(Token *tok) { +  if (tok->kind != TK_IDENT) +    return NULL; +  return hashmap_get2(¯os, tok->loc, tok->len); +} + +static Macro *add_macro(char *name, bool is_objlike, Token *body) { +  Macro *m = calloc(1, sizeof(Macro)); +  m->name = name; +  m->is_objlike = is_objlike; +  m->body = body; +  hashmap_put(¯os, name, m); +  return m; +} + +static MacroParam *read_macro_params(Token **rest, Token *tok, char **va_args_name) { +  MacroParam head = {}; +  MacroParam *cur = &head; + +  while (!equal(tok, ")")) { +    if (cur != &head) +      tok = skip(tok, ","); + +    if (equal(tok, "...")) { +      *va_args_name = "__VA_ARGS__"; +      *rest = skip(tok->next, ")"); +      return head.next; +    } + +    if (tok->kind != TK_IDENT) +      error_tok(tok, "expected an identifier"); + +    if (equal(tok->next, "...")) { +      *va_args_name = strndup(tok->loc, tok->len); +      *rest = skip(tok->next->next, ")"); +      return head.next; +    } + +    MacroParam *m = calloc(1, sizeof(MacroParam)); +    m->name = strndup(tok->loc, tok->len); +    cur = cur->next = m; +    tok = tok->next; +  } + +  *rest = tok->next; +  return head.next; +} + +static void read_macro_definition(Token **rest, Token *tok) { +  if (tok->kind != TK_IDENT) +    error_tok(tok, "macro name must be an identifier"); +  char *name = strndup(tok->loc, tok->len); +  tok = tok->next; + +  if (!tok->has_space && equal(tok, "(")) { +    // Function-like macro +    char *va_args_name = NULL; +    MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name); + +    Macro *m = add_macro(name, false, copy_line(rest, tok)); +    m->params = params; +    m->va_args_name = va_args_name; +  } else { +    // Object-like macro +    add_macro(name, true, copy_line(rest, tok)); +  } +} + +static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) { +  Token head = {}; +  Token *cur = &head; +  int level = 0; + +  for (;;) { +    if (level == 0 && equal(tok, ")")) +      break; +    if (level == 0 && !read_rest && equal(tok, ",")) +      break; + +    if (tok->kind == TK_EOF) +      error_tok(tok, "premature end of input"); + +    if (equal(tok, "(")) +      level++; +    else if (equal(tok, ")")) +      level--; + +    cur = cur->next = copy_token(tok); +    tok = tok->next; +  } + +  cur->next = new_eof(tok); + +  MacroArg *arg = calloc(1, sizeof(MacroArg)); +  arg->tok = head.next; +  *rest = tok; +  return arg; +} + +static MacroArg * +read_macro_args(Token **rest, Token *tok, MacroParam *params, char *va_args_name) { +  Token *start = tok; +  tok = tok->next->next; + +  MacroArg head = {}; +  MacroArg *cur = &head; + +  MacroParam *pp = params; +  for (; pp; pp = pp->next) { +    if (cur != &head) +      tok = skip(tok, ","); +    cur = cur->next = read_macro_arg_one(&tok, tok, false); +    cur->name = pp->name; +  } + +  if (va_args_name) { +    MacroArg *arg; +    if (equal(tok, ")")) { +      arg = calloc(1, sizeof(MacroArg)); +      arg->tok = new_eof(tok); +    } else { +      if (pp != params) +        tok = skip(tok, ","); +      arg = read_macro_arg_one(&tok, tok, true); +    } +    arg->name = va_args_name;; +    arg->is_va_args = true; +    cur = cur->next = arg; +  } else if (pp) { +    error_tok(start, "too many arguments"); +  } + +  skip(tok, ")"); +  *rest = tok; +  return head.next; +} + +static MacroArg *find_arg(MacroArg *args, Token *tok) { +  for (MacroArg *ap = args; ap; ap = ap->next) +    if (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len)) +      return ap; +  return NULL; +} + +// Concatenates all tokens in `tok` and returns a new string. +static char *join_tokens(Token *tok, Token *end) { +  // Compute the length of the resulting token. +  int len = 1; +  for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { +    if (t != tok && t->has_space) +      len++; +    len += t->len; +  } + +  char *buf = calloc(1, len); + +  // Copy token texts. +  int pos = 0; +  for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { +    if (t != tok && t->has_space) +      buf[pos++] = ' '; +    strncpy(buf + pos, t->loc, t->len); +    pos += t->len; +  } +  buf[pos] = '\0'; +  return buf; +} + +// Concatenates all tokens in `arg` and returns a new string token. +// This function is used for the stringizing operator (#). +static Token *stringize(Token *hash, Token *arg) { +  // Create a new string token. We need to set some value to its +  // source location for error reporting function, so we use a macro +  // name token as a template. +  char *s = join_tokens(arg, NULL); +  return new_str_token(s, hash); +} + +// Concatenate two tokens to create a new token. +static Token *paste(Token *lhs, Token *rhs) { +  // Paste the two tokens. +  char *buf = format("%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc); + +  // Tokenize the resulting string. +  Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf)); +  if (tok->next->kind != TK_EOF) +    error_tok(lhs, "pasting forms '%s', an invalid token", buf); +  return tok; +} + +static bool has_varargs(MacroArg *args) { +  for (MacroArg *ap = args; ap; ap = ap->next) +    if (!strcmp(ap->name, "__VA_ARGS__")) +      return ap->tok->kind != TK_EOF; +  return false; +} + +// Replace func-like macro parameters with given arguments. +static Token *subst(Token *tok, MacroArg *args) { +  Token head = {}; +  Token *cur = &head; + +  while (tok->kind != TK_EOF) { +    // "#" followed by a parameter is replaced with stringized actuals. +    if (equal(tok, "#")) { +      MacroArg *arg = find_arg(args, tok->next); +      if (!arg) +        error_tok(tok->next, "'#' is not followed by a macro parameter"); +      cur = cur->next = stringize(tok, arg->tok); +      tok = tok->next->next; +      continue; +    } + +    // [GNU] If __VA_ARG__ is empty, `,##__VA_ARGS__` is expanded +    // to the empty token list. Otherwise, its expaned to `,` and +    // __VA_ARGS__. +    if (equal(tok, ",") && equal(tok->next, "##")) { +      MacroArg *arg = find_arg(args, tok->next->next); +      if (arg && arg->is_va_args) { +        if (arg->tok->kind == TK_EOF) { +          tok = tok->next->next->next; +        } else { +          cur = cur->next = copy_token(tok); +          tok = tok->next->next; +        } +        continue; +      } +    } + +    if (equal(tok, "##")) { +      if (cur == &head) +        error_tok(tok, "'##' cannot appear at start of macro expansion"); + +      if (tok->next->kind == TK_EOF) +        error_tok(tok, "'##' cannot appear at end of macro expansion"); + +      MacroArg *arg = find_arg(args, tok->next); +      if (arg) { +        if (arg->tok->kind != TK_EOF) { +          *cur = *paste(cur, arg->tok); +          for (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next) +            cur = cur->next = copy_token(t); +        } +        tok = tok->next->next; +        continue; +      } + +      *cur = *paste(cur, tok->next); +      tok = tok->next->next; +      continue; +    } + +    MacroArg *arg = find_arg(args, tok); + +    if (arg && equal(tok->next, "##")) { +      Token *rhs = tok->next->next; + +      if (arg->tok->kind == TK_EOF) { +        MacroArg *arg2 = find_arg(args, rhs); +        if (arg2) { +          for (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next) +            cur = cur->next = copy_token(t); +        } else { +          cur = cur->next = copy_token(rhs); +        } +        tok = rhs->next; +        continue; +      } + +      for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) +        cur = cur->next = copy_token(t); +      tok = tok->next; +      continue; +    } + +    // If __VA_ARG__ is empty, __VA_OPT__(x) is expanded to the +    // empty token list. Otherwise, __VA_OPT__(x) is expanded to x. +    if (equal(tok, "__VA_OPT__") && equal(tok->next, "(")) { +      MacroArg *arg = read_macro_arg_one(&tok, tok->next->next, true); +      if (has_varargs(args)) +        for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) +          cur = cur->next = t; +      tok = skip(tok, ")"); +      continue; +    } + +    // Handle a macro token. Macro arguments are completely macro-expanded +    // before they are substituted into a macro body. +    if (arg) { +      Token *t = preprocess2(arg->tok); +      t->at_bol = tok->at_bol; +      t->has_space = tok->has_space; +      for (; t->kind != TK_EOF; t = t->next) +        cur = cur->next = copy_token(t); +      tok = tok->next; +      continue; +    } + +    // Handle a non-macro token. +    cur = cur->next = copy_token(tok); +    tok = tok->next; +    continue; +  } + +  cur->next = tok; +  return head.next; +} + +// If tok is a macro, expand it and return true. +// Otherwise, do nothing and return false. +static bool expand_macro(Token **rest, Token *tok) { +  if (hideset_contains(tok->hideset, tok->loc, tok->len)) +    return false; + +  Macro *m = find_macro(tok); +  if (!m) +    return false; + +  // Built-in dynamic macro application such as __LINE__ +  if (m->handler) { +    *rest = m->handler(tok); +    (*rest)->next = tok->next; +    return true; +  } + +  // Object-like macro application +  if (m->is_objlike) { +    Hideset *hs = hideset_union(tok->hideset, new_hideset(m->name)); +    Token *body = add_hideset(m->body, hs); +    for (Token *t = body; t->kind != TK_EOF; t = t->next) +      t->origin = tok; +    *rest = append(body, tok->next); +    (*rest)->at_bol = tok->at_bol; +    (*rest)->has_space = tok->has_space; +    return true; +  } + +  // If a funclike macro token is not followed by an argument list, +  // treat it as a normal identifier. +  if (!equal(tok->next, "(")) +    return false; + +  // Function-like macro application +  Token *macro_token = tok; +  MacroArg *args = read_macro_args(&tok, tok, m->params, m->va_args_name); +  Token *rparen = tok; + +  // Tokens that consist a func-like macro invocation may have different +  // hidesets, and if that's the case, it's not clear what the hideset +  // for the new tokens should be. We take the interesection of the +  // macro token and the closing parenthesis and use it as a new hideset +  // as explained in the Dave Prossor's algorithm. +  Hideset *hs = hideset_intersection(macro_token->hideset, rparen->hideset); +  hs = hideset_union(hs, new_hideset(m->name)); + +  Token *body = subst(m->body, args); +  body = add_hideset(body, hs); +  for (Token *t = body; t->kind != TK_EOF; t = t->next) +    t->origin = macro_token; +  *rest = append(body, tok->next); +  (*rest)->at_bol = macro_token->at_bol; +  (*rest)->has_space = macro_token->has_space; +  return true; +} + +char *search_include_paths(char *filename) { +  if (filename[0] == '/') +    return filename; + +  static HashMap cache; +  char *cached = hashmap_get(&cache, filename); +  if (cached) +    return cached; + +  // Search a file from the include paths. +  for (int i = 0; i < include_paths.len; i++) { +    char *path = format("%s/%s", include_paths.data[i], filename); +    if (!file_exists(path)) +      continue; +    hashmap_put(&cache, filename, path); +    include_next_idx = i + 1; +    return path; +  } +  return NULL; +} + +static char *search_include_next(char *filename) { +  for (; include_next_idx < include_paths.len; include_next_idx++) { +    char *path = format("%s/%s", include_paths.data[include_next_idx], filename); +    if (file_exists(path)) +      return path; +  } +  return NULL; +} + +// Read an #include argument. +static char *read_include_filename(Token **rest, Token *tok, bool *is_dquote) { +  // Pattern 1: #include "foo.h" +  if (tok->kind == TK_STR) { +    // A double-quoted filename for #include is a special kind of +    // token, and we don't want to interpret any escape sequences in it. +    // For example, "\f" in "C:\foo" is not a formfeed character but +    // just two non-control characters, backslash and f. +    // So we don't want to use token->str. +    *is_dquote = true; +    *rest = skip_line(tok->next); +    return strndup(tok->loc + 1, tok->len - 2); +  } + +  // Pattern 2: #include <foo.h> +  if (equal(tok, "<")) { +    // Reconstruct a filename from a sequence of tokens between +    // "<" and ">". +    Token *start = tok; + +    // Find closing ">". +    for (; !equal(tok, ">"); tok = tok->next) +      if (tok->at_bol || tok->kind == TK_EOF) +        error_tok(tok, "expected '>'"); + +    *is_dquote = false; +    *rest = skip_line(tok->next); +    return join_tokens(start->next, tok); +  } + +  // Pattern 3: #include FOO +  // In this case FOO must be macro-expanded to either +  // a single string token or a sequence of "<" ... ">". +  if (tok->kind == TK_IDENT) { +    Token *tok2 = preprocess2(copy_line(rest, tok)); +    return read_include_filename(&tok2, tok2, is_dquote); +  } + +  error_tok(tok, "expected a filename"); +} + +// Detect the following "include guard" pattern. +// +//   #ifndef FOO_H +//   #define FOO_H +//   ... +//   #endif +static char *detect_include_guard(Token *tok) { +  // Detect the first two lines. +  if (!is_hash(tok) || !equal(tok->next, "ifndef")) +    return NULL; +  tok = tok->next->next; + +  if (tok->kind != TK_IDENT) +    return NULL; + +  char *macro = strndup(tok->loc, tok->len); +  tok = tok->next; + +  if (!is_hash(tok) || !equal(tok->next, "define") || !equal(tok->next->next, macro)) +    return NULL; + +  // Read until the end of the file. +  while (tok->kind != TK_EOF) { +    if (!is_hash(tok)) { +      tok = tok->next; +      continue; +    } + +    if (equal(tok->next, "endif") && tok->next->next->kind == TK_EOF) +      return macro; + +    if (equal(tok, "if") || equal(tok, "ifdef") || equal(tok, "ifndef")) +      tok = skip_cond_incl(tok->next); +    else +      tok = tok->next; +  } +  return NULL; +} + +static Token *include_file(Token *tok, char *path, Token *filename_tok) { +  // Check for "#pragma once" +  if (hashmap_get(&pragma_once, path)) +    return tok; + +  // If we read the same file before, and if the file was guarded +  // by the usual #ifndef ... #endif pattern, we may be able to +  // skip the file without opening it. +  static HashMap include_guards; +  char *guard_name = hashmap_get(&include_guards, path); +  if (guard_name && hashmap_get(¯os, guard_name)) +    return tok; + +  Token *tok2 = tokenize_file(path); +  if (!tok2) +    error_tok(filename_tok, "%s: cannot open file: %s", path, strerror(errno)); + +  guard_name = detect_include_guard(tok2); +  if (guard_name) +    hashmap_put(&include_guards, path, guard_name); + +  return append(tok2, tok); +} + +// Read #line arguments +static void read_line_marker(Token **rest, Token *tok) { +  Token *start = tok; +  tok = preprocess(copy_line(rest, tok)); + +  if (tok->kind != TK_NUM || tok->ty->kind != TY_INT) +    error_tok(tok, "invalid line marker"); +  start->file->line_delta = tok->val - start->line_no; + +  tok = tok->next; +  if (tok->kind == TK_EOF) +    return; + +  if (tok->kind != TK_STR) +    error_tok(tok, "filename expected"); +  start->file->display_name = tok->str; +} + +// Visit all tokens in `tok` while evaluating preprocessing +// macros and directives. +static Token *preprocess2(Token *tok) { +  Token head = {}; +  Token *cur = &head; + +  while (tok->kind != TK_EOF) { +    // If it is a macro, expand it. +    if (expand_macro(&tok, tok)) +      continue; + +    // Pass through if it is not a "#". +    if (!is_hash(tok)) { +      tok->line_delta = tok->file->line_delta; +      tok->filename = tok->file->display_name; +      cur = cur->next = tok; +      tok = tok->next; +      continue; +    } + +    Token *start = tok; +    tok = tok->next; + +    if (equal(tok, "include")) { +      bool is_dquote; +      char *filename = read_include_filename(&tok, tok->next, &is_dquote); + +      if (filename[0] != '/' && is_dquote) { +        char *path = format("%s/%s", dirname(strdup(start->file->name)), filename); +        if (file_exists(path)) { +          tok = include_file(tok, path, start->next->next); +          continue; +        } +      } + +      char *path = search_include_paths(filename); +      tok = include_file(tok, path ? path : filename, start->next->next); +      continue; +    } + +    if (equal(tok, "include_next")) { +      bool ignore; +      char *filename = read_include_filename(&tok, tok->next, &ignore); +      char *path = search_include_next(filename); +      tok = include_file(tok, path ? path : filename, start->next->next); +      continue; +    } + +    if (equal(tok, "define")) { +      read_macro_definition(&tok, tok->next); +      continue; +    } + +    if (equal(tok, "undef")) { +      tok = tok->next; +      if (tok->kind != TK_IDENT) +        error_tok(tok, "macro name must be an identifier"); +      undef_macro(strndup(tok->loc, tok->len)); +      tok = skip_line(tok->next); +      continue; +    } + +    if (equal(tok, "if")) { +      long val = eval_const_expr(&tok, tok); +      push_cond_incl(start, val); +      if (!val) +        tok = skip_cond_incl(tok); +      continue; +    } + +    if (equal(tok, "ifdef")) { +      bool defined = find_macro(tok->next); +      push_cond_incl(tok, defined); +      tok = skip_line(tok->next->next); +      if (!defined) +        tok = skip_cond_incl(tok); +      continue; +    } + +    if (equal(tok, "ifndef")) { +      bool defined = find_macro(tok->next); +      push_cond_incl(tok, !defined); +      tok = skip_line(tok->next->next); +      if (defined) +        tok = skip_cond_incl(tok); +      continue; +    } + +    if (equal(tok, "elif")) { +      if (!cond_incl || cond_incl->ctx == IN_ELSE) +        error_tok(start, "stray #elif"); +      cond_incl->ctx = IN_ELIF; + +      if (!cond_incl->included && eval_const_expr(&tok, tok)) +        cond_incl->included = true; +      else +        tok = skip_cond_incl(tok); +      continue; +    } + +    if (equal(tok, "else")) { +      if (!cond_incl || cond_incl->ctx == IN_ELSE) +        error_tok(start, "stray #else"); +      cond_incl->ctx = IN_ELSE; +      tok = skip_line(tok->next); + +      if (cond_incl->included) +        tok = skip_cond_incl(tok); +      continue; +    } + +    if (equal(tok, "endif")) { +      if (!cond_incl) +        error_tok(start, "stray #endif"); +      cond_incl = cond_incl->next; +      tok = skip_line(tok->next); +      continue; +    } + +    if (equal(tok, "line")) { +      read_line_marker(&tok, tok->next); +      continue; +    } + +    if (tok->kind == TK_PP_NUM) { +      read_line_marker(&tok, tok); +      continue; +    } + +    if (equal(tok, "pragma") && equal(tok->next, "once")) { +      hashmap_put(&pragma_once, tok->file->name, (void *)1); +      tok = skip_line(tok->next->next); +      continue; +    } + +    if (equal(tok, "pragma")) { +      do { +        tok = tok->next; +      } while (!tok->at_bol); +      continue; +    } + +    if (equal(tok, "error")) +      error_tok(tok, "error"); + +    // `#`-only line is legal. It's called a null directive. +    if (tok->at_bol) +      continue; + +    error_tok(tok, "invalid preprocessor directive"); +  } + +  cur->next = tok; +  return head.next; +} + +void define_macro(char *name, char *buf) { +  Token *tok = tokenize(new_file("<built-in>", 1, buf)); +  add_macro(name, true, tok); +} + +void undef_macro(char *name) { +  hashmap_delete(¯os, name); +} + +static Macro *add_builtin(char *name, macro_handler_fn *fn) { +  Macro *m = add_macro(name, true, NULL); +  m->handler = fn; +  return m; +} + +static Token *file_macro(Token *tmpl) { +  while (tmpl->origin) +    tmpl = tmpl->origin; +  return new_str_token(tmpl->file->display_name, tmpl); +} + +static Token *line_macro(Token *tmpl) { +  while (tmpl->origin) +    tmpl = tmpl->origin; +  int i = tmpl->line_no + tmpl->file->line_delta; +  return new_num_token(i, tmpl); +} + +// __COUNTER__ is expanded to serial values starting from 0. +static Token *counter_macro(Token *tmpl) { +  static int i = 0; +  return new_num_token(i++, tmpl); +} + +// __TIMESTAMP__ is expanded to a string describing the last +// modification time of the current file. E.g. +// "Fri Jul 24 01:32:50 2020" +static Token *timestamp_macro(Token *tmpl) { +  struct stat st; +  if (stat(tmpl->file->name, &st) != 0) +    return new_str_token("??? ??? ?? ??:??:?? ????", tmpl); + +  char buf[30]; +  ctime_r(&st.st_mtime, buf); +  buf[24] = '\0'; +  return new_str_token(buf, tmpl); +} + +static Token *base_file_macro(Token *tmpl) { +  return new_str_token(base_file, tmpl); +} + +// __DATE__ is expanded to the current date, e.g. "May 17 2020". +static char *format_date(struct tm *tm) { +  static char mon[][4] = { +    "Jan", "Feb", "Mar", "Apr", "May", "Jun", +    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +  }; + +  return format("\"%s %2d %d\"", mon[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900); +} + +// __TIME__ is expanded to the current time, e.g. "13:34:03". +static char *format_time(struct tm *tm) { +  return format("\"%02d:%02d:%02d\"", tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +void init_macros(void) { +  // Define predefined macros +  define_macro("_LP64", "1"); +  define_macro("__C99_MACRO_WITH_VA_ARGS", "1"); +  define_macro("__ELF__", "1"); +  define_macro("__LP64__", "1"); +  define_macro("__SIZEOF_DOUBLE__", "8"); +  define_macro("__SIZEOF_FLOAT__", "4"); +  define_macro("__SIZEOF_INT__", "4"); +  define_macro("__SIZEOF_LONG_DOUBLE__", "8"); +  define_macro("__SIZEOF_LONG_LONG__", "8"); +  define_macro("__SIZEOF_LONG__", "8"); +  define_macro("__SIZEOF_POINTER__", "8"); +  define_macro("__SIZEOF_PTRDIFF_T__", "8"); +  define_macro("__SIZEOF_SHORT__", "2"); +  define_macro("__SIZEOF_SIZE_T__", "8"); +  define_macro("__SIZE_TYPE__", "unsigned long"); +  define_macro("__STDC_HOSTED__", "1"); +  define_macro("__STDC_NO_COMPLEX__", "1"); +  define_macro("__STDC_UTF_16__", "1"); +  define_macro("__STDC_UTF_32__", "1"); +  define_macro("__STDC_VERSION__", "201112L"); +  define_macro("__STDC__", "1"); +  define_macro("__USER_LABEL_PREFIX__", ""); +  define_macro("__alignof__", "_Alignof"); +  define_macro("__amd64", "1"); +  define_macro("__amd64__", "1"); +  define_macro("__chibicc__", "1"); +  define_macro("__const__", "const"); +  define_macro("__gnu_linux__", "1"); +  define_macro("__inline__", "inline"); +  define_macro("__linux", "1"); +  define_macro("__linux__", "1"); +  define_macro("__signed__", "signed"); +  define_macro("__typeof__", "typeof"); +  define_macro("__unix", "1"); +  define_macro("__unix__", "1"); +  define_macro("__volatile__", "volatile"); +  define_macro("__x86_64", "1"); +  define_macro("__x86_64__", "1"); +  define_macro("linux", "1"); +  define_macro("unix", "1"); + +  add_builtin("__FILE__", file_macro); +  add_builtin("__LINE__", line_macro); +  add_builtin("__COUNTER__", counter_macro); +  add_builtin("__TIMESTAMP__", timestamp_macro); +  add_builtin("__BASE_FILE__", base_file_macro); + +  time_t now = time(NULL); +  struct tm *tm = localtime(&now); +  define_macro("__DATE__", format_date(tm)); +  define_macro("__TIME__", format_time(tm)); +} + +typedef enum { +  STR_NONE, STR_UTF8, STR_UTF16, STR_UTF32, STR_WIDE, +} StringKind; + +static StringKind getStringKind(Token *tok) { +  if (!strcmp(tok->loc, "u8")) +    return STR_UTF8; + +  switch (tok->loc[0]) { +  case '"': return STR_NONE; +  case 'u': return STR_UTF16; +  case 'U': return STR_UTF32; +  case 'L': return STR_WIDE; +  } +  unreachable(); +} + +// Concatenate adjacent string literals into a single string literal +// as per the C spec. +static void join_adjacent_string_literals(Token *tok) { +  // First pass: If regular string literals are adjacent to wide +  // string literals, regular string literals are converted to a wide +  // type before concatenation. In this pass, we do the conversion. +  for (Token *tok1 = tok; tok1->kind != TK_EOF;) { +    if (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { +      tok1 = tok1->next; +      continue; +    } + +    StringKind kind = getStringKind(tok1); +    Type *basety = tok1->ty->base; + +    for (Token *t = tok1->next; t->kind == TK_STR; t = t->next) { +      StringKind k = getStringKind(t); +      if (kind == STR_NONE) { +        kind = k; +        basety = t->ty->base; +      } else if (k != STR_NONE && kind != k) { +        error_tok(t, "unsupported non-standard concatenation of string literals"); +      } +    } + +    if (basety->size > 1) +      for (Token *t = tok1; t->kind == TK_STR; t = t->next) +        if (t->ty->base->size == 1) +          *t = *tokenize_string_literal(t, basety); + +    while (tok1->kind == TK_STR) +      tok1 = tok1->next; +  } + +  // Second pass: concatenate adjacent string literals. +  for (Token *tok1 = tok; tok1->kind != TK_EOF;) { +    if (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { +      tok1 = tok1->next; +      continue; +    } + +    Token *tok2 = tok1->next; +    while (tok2->kind == TK_STR) +      tok2 = tok2->next; + +    int len = tok1->ty->array_len; +    for (Token *t = tok1->next; t != tok2; t = t->next) +      len = len + t->ty->array_len - 1; + +    char *buf = calloc(tok1->ty->base->size, len); + +    int i = 0; +    for (Token *t = tok1; t != tok2; t = t->next) { +      memcpy(buf + i, t->str, t->ty->size); +      i = i + t->ty->size - t->ty->base->size; +    } + +    *tok1 = *copy_token(tok1); +    tok1->ty = array_of(tok1->ty->base, len); +    tok1->str = buf; +    tok1->next = tok2; +    tok1 = tok2; +  } +} + +// Entry point function of the preprocessor. +Token *preprocess(Token *tok) { +  tok = preprocess2(tok); +  if (cond_incl) +    error_tok(cond_incl->tok, "unterminated conditional directive"); +  convert_pp_tokens(tok); +  join_adjacent_string_literals(tok); + +  for (Token *t = tok; t; t = t->next) +    t->line_no += t->line_delta; +  return tok; +} diff --git a/src/3p/chibicc/strings.c b/src/3p/chibicc/strings.c new file mode 100644 index 0000000..0538fef --- /dev/null +++ b/src/3p/chibicc/strings.c @@ -0,0 +1,17 @@ +#include "chibicc.h" + +void strarray_push(StringArray *arr, char *s) { +  if (!arr->data) { +    arr->data = calloc(8, sizeof(char *)); +    arr->capacity = 8; +  } + +  if (arr->capacity == arr->len) { +    arr->data = realloc(arr->data, sizeof(char *) * arr->capacity * 2); +    arr->capacity *= 2; +    for (int i = arr->len; i < arr->capacity; i++) +      arr->data[i] = NULL; +  } + +  arr->data[arr->len++] = s; +} diff --git a/src/3p/chibicc/tokenize.c b/src/3p/chibicc/tokenize.c new file mode 100644 index 0000000..8ed414e --- /dev/null +++ b/src/3p/chibicc/tokenize.c @@ -0,0 +1,799 @@ +#include "chibicc.h" + +// Input file +static File *current_file; + +// A list of all input files. +static File **input_files; + +// True if the current position is at the beginning of a line +static bool at_bol; + +// True if the current position follows a space character +static bool has_space; + +// Reports an error and exit. +void error(char *fmt, ...) { +  va_list ap; +  va_start(ap, fmt); +  fprintf(stderr, "cmeta: chibicc: "); +  vfprintf(stderr, fmt, ap); +  fprintf(stderr, "\n"); +  exit(1); +} + +// Reports an error message in the following format. +// +// foo.c:10: x = y + 1; +//               ^ <error message here> +static void verror_at(char *filename, char *input, int line_no, +                      char *loc, char *fmt, va_list ap) { +  // Find a line containing `loc`. +  char *line = loc; +  while (input < line && line[-1] != '\n') +    line--; + +  char *end = loc; +  while (*end && *end != '\n') +    end++; + +  // Print out the line. +  int indent = fprintf(stderr, "%s:%d: ", filename, line_no); +  fprintf(stderr, "%.*s\n", (int)(end - line), line); + +  // Show the error message. +  int pos = display_width(line, loc - line) + indent; + +  fprintf(stderr, "%*s", pos, ""); // print pos spaces. +  fprintf(stderr, "^ "); +  vfprintf(stderr, fmt, ap); +  fprintf(stderr, "\n"); +} + +void error_at(char *loc, char *fmt, ...) { +  int line_no = 1; +  for (char *p = current_file->contents; p < loc; p++) +    if (*p == '\n') +      line_no++; + +  va_list ap; +  va_start(ap, fmt); +  verror_at(current_file->name, current_file->contents, line_no, loc, fmt, ap); +  exit(1); +} + +void error_tok(Token *tok, char *fmt, ...) { +  va_list ap; +  va_start(ap, fmt); +  verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap); +  exit(1); +} + +void warn_tok(Token *tok, char *fmt, ...) { +  va_list ap; +  va_start(ap, fmt); +  verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap); +  va_end(ap); +} + +// Consumes the current token if it matches `op`. +bool equal(Token *tok, char *op) { +  return memcmp(tok->loc, op, tok->len) == 0 && op[tok->len] == '\0'; +} + +// Ensure that the current token is `op`. +Token *skip(Token *tok, char *op) { +  if (!equal(tok, op)) +    error_tok(tok, "expected '%s'", op); +  return tok->next; +} + +bool consume(Token **rest, Token *tok, char *str) { +  if (equal(tok, str)) { +    *rest = tok->next; +    return true; +  } +  *rest = tok; +  return false; +} + +// Create a new token. +static Token *new_token(TokenKind kind, char *start, char *end) { +  Token *tok = calloc(1, sizeof(Token)); +  tok->kind = kind; +  tok->loc = start; +  tok->len = end - start; +  tok->file = current_file; +  tok->filename = current_file->display_name; +  tok->at_bol = at_bol; +  tok->has_space = has_space; + +  at_bol = has_space = false; +  return tok; +} + +static bool startswith(char *p, char *q) { +  return strncmp(p, q, strlen(q)) == 0; +} + +// Read an identifier and returns the length of it. +// If p does not point to a valid identifier, 0 is returned. +static int read_ident(char *start) { +  char *p = start; +  uint32_t c = decode_utf8(&p, p); +  if (!is_ident1(c)) +    return 0; + +  for (;;) { +    char *q; +    c = decode_utf8(&q, p); +    if (!is_ident2(c)) +      return p - start; +    p = q; +  } +} + +static int from_hex(char c) { +  if ('0' <= c && c <= '9') +    return c - '0'; +  if ('a' <= c && c <= 'f') +    return c - 'a' + 10; +  return c - 'A' + 10; +} + +// Read a punctuator token from p and returns its length. +static int read_punct(char *p) { +  static char *kw[] = { +    "<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=", +    "-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&", +    "||", "<<", ">>", "##", +  }; + +  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) +    if (startswith(p, kw[i])) +      return strlen(kw[i]); + +  return ispunct(*p) ? 1 : 0; +} + +static bool is_keyword(Token *tok) { +  static HashMap map; + +  if (map.capacity == 0) { +    static char *kw[] = { +      "return", "if", "else", "for", "while", "int", "sizeof", "char", +      "struct", "union", "short", "long", "void", "typedef", "_Bool", +      "enum", "static", "goto", "break", "continue", "switch", "case", +      "default", "extern", "_Alignof", "_Alignas", "do", "signed", +      "unsigned", "const", "volatile", "auto", "register", "restrict", +      "__restrict", "__restrict__", "_Noreturn", "float", "double", +      "typeof", "asm", "_Thread_local", "__thread", "_Atomic", +      "__attribute__", +    }; + +    for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) +      hashmap_put(&map, kw[i], (void *)1); +  } + +  return hashmap_get2(&map, tok->loc, tok->len); +} + +static int read_escaped_char(char **new_pos, char *p) { +  if ('0' <= *p && *p <= '7') { +    // Read an octal number. +    int c = *p++ - '0'; +    if ('0' <= *p && *p <= '7') { +      c = (c << 3) + (*p++ - '0'); +      if ('0' <= *p && *p <= '7') +        c = (c << 3) + (*p++ - '0'); +    } +    *new_pos = p; +    return c; +  } + +  if (*p == 'x') { +    // Read a hexadecimal number. +    p++; +    if (!isxdigit(*p)) +      error_at(p, "invalid hex escape sequence"); + +    int c = 0; +    for (; isxdigit(*p); p++) +      c = (c << 4) + from_hex(*p); +    *new_pos = p; +    return c; +  } + +  *new_pos = p + 1; + +  // Escape sequences are defined using themselves here. E.g. +  // '\n' is implemented using '\n'. This tautological definition +  // works because the compiler that compiles our compiler knows +  // what '\n' actually is. In other words, we "inherit" the ASCII +  // code of '\n' from the compiler that compiles our compiler, +  // so we don't have to teach the actual code here. +  // +  // This fact has huge implications not only for the correctness +  // of the compiler but also for the security of the generated code. +  // For more info, read "Reflections on Trusting Trust" by Ken Thompson. +  // https://github.com/rui314/chibicc/wiki/thompson1984.pdf +  switch (*p) { +  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'; +  // [GNU] \e for the ASCII escape character is a GNU C extension. +  case 'e': return 27; +  default: return *p; +  } +} + +// Find a closing double-quote. +static char *string_literal_end(char *p) { +  char *start = p; +  for (; *p != '"'; p++) { +    if (*p == '\n' || *p == '\0') +      error_at(start, "unclosed string literal"); +    if (*p == '\\') +      p++; +  } +  return p; +} + +static Token *read_string_literal(char *start, char *quote) { +  char *end = string_literal_end(quote + 1); +  char *buf = calloc(1, end - quote); +  int len = 0; + +  for (char *p = quote + 1; p < end;) { +    if (*p == '\\') +      buf[len++] = read_escaped_char(&p, p + 1); +    else +      buf[len++] = *p++; +  } + +  Token *tok = new_token(TK_STR, start, end + 1); +  tok->ty = array_of(ty_char, len + 1); +  tok->str = buf; +  return tok; +} + +// Read a UTF-8-encoded string literal and transcode it in UTF-16. +// +// UTF-16 is yet another variable-width encoding for Unicode. Code +// points smaller than U+10000 are encoded in 2 bytes. Code points +// equal to or larger than that are encoded in 4 bytes. Each 2 bytes +// in the 4 byte sequence is called "surrogate", and a 4 byte sequence +// is called a "surrogate pair". +static Token *read_utf16_string_literal(char *start, char *quote) { +  char *end = string_literal_end(quote + 1); +  uint16_t *buf = calloc(2, end - start); +  int len = 0; + +  for (char *p = quote + 1; p < end;) { +    if (*p == '\\') { +      buf[len++] = read_escaped_char(&p, p + 1); +      continue; +    } + +    uint32_t c = decode_utf8(&p, p); +    if (c < 0x10000) { +      // Encode a code point in 2 bytes. +      buf[len++] = c; +    } else { +      // Encode a code point in 4 bytes. +      c -= 0x10000; +      buf[len++] = 0xd800 + ((c >> 10) & 0x3ff); +      buf[len++] = 0xdc00 + (c & 0x3ff); +    } +  } + +  Token *tok = new_token(TK_STR, start, end + 1); +  tok->ty = array_of(ty_ushort, len + 1); +  tok->str = (char *)buf; +  return tok; +} + +// Read a UTF-8-encoded string literal and transcode it in UTF-32. +// +// UTF-32 is a fixed-width encoding for Unicode. Each code point is +// encoded in 4 bytes. +static Token *read_utf32_string_literal(char *start, char *quote, Type *ty) { +  char *end = string_literal_end(quote + 1); +  uint32_t *buf = calloc(4, end - quote); +  int len = 0; + +  for (char *p = quote + 1; p < end;) { +    if (*p == '\\') +      buf[len++] = read_escaped_char(&p, p + 1); +    else +      buf[len++] = decode_utf8(&p, p); +  } + +  Token *tok = new_token(TK_STR, start, end + 1); +  tok->ty = array_of(ty, len + 1); +  tok->str = (char *)buf; +  return tok; +} + +static Token *read_char_literal(char *start, char *quote, Type *ty) { +  char *p = quote + 1; +  if (*p == '\0') +    error_at(start, "unclosed char literal"); + +  int c; +  if (*p == '\\') +    c = read_escaped_char(&p, p + 1); +  else +    c = decode_utf8(&p, p); + +  char *end = strchr(p, '\''); +  if (!end) +    error_at(p, "unclosed char literal"); + +  Token *tok = new_token(TK_NUM, start, end + 1); +  tok->val = c; +  tok->ty = ty; +  return tok; +} + +static bool convert_pp_int(Token *tok) { +  char *p = tok->loc; + +  // Read a binary, octal, decimal or hexadecimal number. +  int base = 10; +  if (!strncasecmp(p, "0x", 2) && isxdigit(p[2])) { +    p += 2; +    base = 16; +  } else if (!strncasecmp(p, "0b", 2) && (p[2] == '0' || p[2] == '1')) { +    p += 2; +    base = 2; +  } else if (*p == '0') { +    base = 8; +  } + +  int64_t val = strtoul(p, &p, base); + +  // Read U, L or LL suffixes. +  bool l = false; +  bool u = false; + +  if (startswith(p, "LLU") || startswith(p, "LLu") || +      startswith(p, "llU") || startswith(p, "llu") || +      startswith(p, "ULL") || startswith(p, "Ull") || +      startswith(p, "uLL") || startswith(p, "ull")) { +    p += 3; +    l = u = true; +  } else if (!strncasecmp(p, "lu", 2) || !strncasecmp(p, "ul", 2)) { +    p += 2; +    l = u = true; +  } else if (startswith(p, "LL") || startswith(p, "ll")) { +    p += 2; +    l = true; +  } else if (*p == 'L' || *p == 'l') { +    p++; +    l = true; +  } else if (*p == 'U' || *p == 'u') { +    p++; +    u = true; +  } + +  if (p != tok->loc + tok->len) +    return false; + +  // Infer a type. +  Type *ty; +  if (base == 10) { +    if (l && u) +      ty = ty_ulong; +    else if (l) +      ty = ty_long; +    else if (u) +      ty = (val >> 32) ? ty_ulong : ty_uint; +    else +      ty = (val >> 31) ? ty_long : ty_int; +  } else { +    if (l && u) +      ty = ty_ulong; +    else if (l) +      ty = (val >> 63) ? ty_ulong : ty_long; +    else if (u) +      ty = (val >> 32) ? ty_ulong : ty_uint; +    else if (val >> 63) +      ty = ty_ulong; +    else if (val >> 32) +      ty = ty_long; +    else if (val >> 31) +      ty = ty_uint; +    else +      ty = ty_int; +  } + +  tok->kind = TK_NUM; +  tok->val = val; +  tok->ty = ty; +  return true; +} + +// The definition of the numeric literal at the preprocessing stage +// is more relaxed than the definition of that at the later stages. +// In order to handle that, a numeric literal is tokenized as a +// "pp-number" token first and then converted to a regular number +// token after preprocessing. +// +// This function converts a pp-number token to a regular number token. +static void convert_pp_number(Token *tok) { +  // Try to parse as an integer constant. +  if (convert_pp_int(tok)) +    return; + +  // If it's not an integer, it must be a floating point constant. +  char *end; +  long double val = strtold(tok->loc, &end); + +  Type *ty; +  if (*end == 'f' || *end == 'F') { +    ty = ty_float; +    end++; +  } else if (*end == 'l' || *end == 'L') { +    ty = ty_ldouble; +    end++; +  } else { +    ty = ty_double; +  } + +  if (tok->loc + tok->len != end) +    error_tok(tok, "invalid numeric constant"); + +  tok->kind = TK_NUM; +  tok->fval = val; +  tok->ty = ty; +} + +void convert_pp_tokens(Token *tok) { +  for (Token *t = tok; t->kind != TK_EOF; t = t->next) { +    if (is_keyword(t)) +      t->kind = TK_KEYWORD; +    else if (t->kind == TK_PP_NUM) +      convert_pp_number(t); +  } +} + +// Initialize line info for all tokens. +static void add_line_numbers(Token *tok) { +  char *p = current_file->contents; +  int n = 1; + +  do { +    if (p == tok->loc) { +      tok->line_no = n; +      tok = tok->next; +    } +    if (*p == '\n') +      n++; +  } while (*p++); +} + +Token *tokenize_string_literal(Token *tok, Type *basety) { +  Token *t; +  if (basety->size == 2) +    t = read_utf16_string_literal(tok->loc, tok->loc); +  else +    t = read_utf32_string_literal(tok->loc, tok->loc, basety); +  t->next = tok->next; +  return t; +} + +// Tokenize a given string and returns new tokens. +Token *tokenize(File *file) { +  current_file = file; + +  char *p = file->contents; +  Token head = {0}; +  Token *cur = &head; + +  at_bol = true; +  has_space = false; + +  while (*p) { +    // Skip line comments. +    if (startswith(p, "//")) { +      p += 2; +      while (*p != '\n') +        p++; +      has_space = true; +      continue; +    } + +    // Skip block comments. +    if (startswith(p, "/*")) { +      char *q = strstr(p + 2, "*/"); +      if (!q) +        error_at(p, "unclosed block comment"); +      p = q + 2; +      has_space = true; +      continue; +    } + +    // Skip newline. +    if (*p == '\n') { +      p++; +      at_bol = true; +      has_space = false; +      continue; +    } + +    // Skip whitespace characters. +    if (isspace(*p)) { +      p++; +      has_space = true; +      continue; +    } + +    // Numeric literal +    if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) { +      char *q = p++; +      for (;;) { +        if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1])) +          p += 2; +        else if (isalnum(*p) || *p == '.') +          p++; +        else +          break; +      } +      cur = cur->next = new_token(TK_PP_NUM, q, p); +      continue; +    } + +    // String literal +    if (*p == '"') { +      cur = cur->next = read_string_literal(p, p); +      p += cur->len; +      continue; +    } + +    // UTF-8 string literal +    if (startswith(p, "u8\"")) { +      cur = cur->next = read_string_literal(p, p + 2); +      p += cur->len; +      continue; +    } + +    // UTF-16 string literal +    if (startswith(p, "u\"")) { +      cur = cur->next = read_utf16_string_literal(p, p + 1); +      p += cur->len; +      continue; +    } + +    // Wide string literal +    if (startswith(p, "L\"")) { +      cur = cur->next = read_utf32_string_literal(p, p + 1, ty_int); +      p += cur->len; +      continue; +    } + +    // UTF-32 string literal +    if (startswith(p, "U\"")) { +      cur = cur->next = read_utf32_string_literal(p, p + 1, ty_uint); +      p += cur->len; +      continue; +    } + +    // Character literal +    if (*p == '\'') { +      cur = cur->next = read_char_literal(p, p, ty_int); +      cur->val = (char)cur->val; +      p += cur->len; +      continue; +    } + +    // UTF-16 character literal +    if (startswith(p, "u'")) { +      cur = cur->next = read_char_literal(p, p + 1, ty_ushort); +      cur->val &= 0xffff; +      p += cur->len; +      continue; +    } + +    // Wide character literal +    if (startswith(p, "L'")) { +      cur = cur->next = read_char_literal(p, p + 1, ty_int); +      p += cur->len; +      continue; +    } + +    // UTF-32 character literal +    if (startswith(p, "U'")) { +      cur = cur->next = read_char_literal(p, p + 1, ty_uint); +      p += cur->len; +      continue; +    } + +    // Identifier or keyword +    int ident_len = read_ident(p); +    if (ident_len) { +      cur = cur->next = new_token(TK_IDENT, p, p + ident_len); +      p += cur->len; +      continue; +    } + +    // Punctuators +    int punct_len = read_punct(p); +    if (punct_len) { +      cur = cur->next = new_token(TK_PUNCT, p, p + punct_len); +      p += cur->len; +      continue; +    } + +    error_at(p, "invalid token"); +  } + +  cur = cur->next = new_token(TK_EOF, p, p); +  add_line_numbers(head.next); +  return head.next; +} + +// Returns the contents of a given file. +/* this function is a bit wonkily implemented, and relies on a non-windows + * thing, so we have our own in src/cmeta.c. +static char *read_file(char *path) { +  FILE *fp; + +  if (strcmp(path, "-") == 0) { +    // By convention, read from stdin if a given filename is "-". +    fp = stdin; +  } else { +    fp = fopen(path, "r"); +    if (!fp) +      return NULL; +  } + +  char *buf; +  size_t buflen; +  FILE *out = open_memstream(&buf, &buflen); + +  // Read the entire file. +  for (;;) { +    char buf2[4096]; +    int n = fread(buf2, 1, sizeof(buf2), fp); +    if (n == 0) +      break; +    fwrite(buf2, 1, n, out); +  } + +  if (fp != stdin) +    fclose(fp); + +  // Make sure that the last line is properly terminated with '\n'. +  fflush(out); +  if (buflen == 0 || buf[buflen - 1] != '\n') +    fputc('\n', out); +  fputc('\0', out); +  fclose(out); +  return buf; +} +*/ + +File **get_input_files(void) { +  return input_files; +} + +File *new_file(char *name, int file_no, char *contents) { +  File *file = calloc(1, sizeof(File)); +  file->name = name; +  file->display_name = name; +  file->file_no = file_no; +  file->contents = contents; +  return file; +} + +// Replaces \r or \r\n with \n. +static void canonicalize_newline(char *p) { +  int i = 0, j = 0; + +  while (p[i]) { +    if (p[i] == '\r' && p[i + 1] == '\n') { +      i += 2; +      p[j++] = '\n'; +    } else if (p[i] == '\r') { +      i++; +      p[j++] = '\n'; +    } else { +      p[j++] = p[i++]; +    } +  } + +  p[j] = '\0'; +} + +// Removes backslashes followed by a newline. +static void remove_backslash_newline(char *p) { +  int i = 0, j = 0; + +  // We want to keep the number of newline characters so that +  // the logical line number matches the physical one. +  // This counter maintain the number of newlines we have removed. +  int n = 0; + +  while (p[i]) { +    if (p[i] == '\\' && p[i + 1] == '\n') { +      i += 2; +      n++; +    } else if (p[i] == '\n') { +      p[j++] = p[i++]; +      for (; n > 0; n--) +        p[j++] = '\n'; +    } else { +      p[j++] = p[i++]; +    } +  } + +  for (; n > 0; n--) +    p[j++] = '\n'; +  p[j] = '\0'; +} + +static uint32_t read_universal_char(char *p, int len) { +  uint32_t c = 0; +  for (int i = 0; i < len; i++) { +    if (!isxdigit(p[i])) +      return 0; +    c = (c << 4) | from_hex(p[i]); +  } +  return c; +} + +// Replace \u or \U escape sequences with corresponding UTF-8 bytes. +static void convert_universal_chars(char *p) { +  char *q = p; + +  while (*p) { +    if (startswith(p, "\\u")) { +      uint32_t c = read_universal_char(p + 2, 4); +      if (c) { +        p += 6; +        q += encode_utf8(q, c); +      } else { +        *q++ = *p++; +      } +    } else if (startswith(p, "\\U")) { +      uint32_t c = read_universal_char(p + 2, 8); +      if (c) { +        p += 10; +        q += encode_utf8(q, c); +      } else { +        *q++ = *p++; +      } +    } else if (p[0] == '\\') { +      *q++ = *p++; +      *q++ = *p++; +    } else { +      *q++ = *p++; +    } +  } + +  *q = '\0'; +} + +// NOTE modified API from upstream +Token *tokenize_buf(const char *name, char *p) { +  canonicalize_newline(p); +  remove_backslash_newline(p); +  convert_universal_chars(p); + +  // Save the filename for assembler .file directive. +  static int file_no; +  File *file = new_file((char *)name, file_no + 1, p); + +  // Save the filename for assembler .file directive. +  input_files = realloc(input_files, sizeof(char *) * (file_no + 2)); +  input_files[file_no] = file; +  input_files[file_no + 1] = NULL; +  file_no++; + +  return tokenize(file); +} diff --git a/src/3p/chibicc/type.c b/src/3p/chibicc/type.c new file mode 100644 index 0000000..02ade59 --- /dev/null +++ b/src/3p/chibicc/type.c @@ -0,0 +1,307 @@ +#include "chibicc.h" + +Type *ty_void = &(Type){TY_VOID, 1, 1}; +Type *ty_bool = &(Type){TY_BOOL, 1, 1}; + +Type *ty_char = &(Type){TY_CHAR, 1, 1}; +Type *ty_short = &(Type){TY_SHORT, 2, 2}; +Type *ty_int = &(Type){TY_INT, 4, 4}; +Type *ty_long = &(Type){TY_LONG, 8, 8}; + +Type *ty_uchar = &(Type){TY_CHAR, 1, 1, true}; +Type *ty_ushort = &(Type){TY_SHORT, 2, 2, true}; +Type *ty_uint = &(Type){TY_INT, 4, 4, true}; +Type *ty_ulong = &(Type){TY_LONG, 8, 8, true}; + +Type *ty_float = &(Type){TY_FLOAT, 4, 4}; +Type *ty_double = &(Type){TY_DOUBLE, 8, 8}; +Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16}; + +static Type *new_type(TypeKind kind, int size, int align) { +  Type *ty = calloc(1, sizeof(Type)); +  ty->kind = kind; +  ty->size = size; +  ty->align = align; +  return ty; +} + +bool is_integer(Type *ty) { +  TypeKind k = ty->kind; +  return k == TY_BOOL || k == TY_CHAR || k == TY_SHORT || +         k == TY_INT  || k == TY_LONG || k == TY_ENUM; +} + +bool is_flonum(Type *ty) { +  return ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE || +         ty->kind == TY_LDOUBLE; +} + +bool is_numeric(Type *ty) { +  return is_integer(ty) || is_flonum(ty); +} + +bool is_compatible(Type *t1, Type *t2) { +  if (t1 == t2) +    return true; + +  if (t1->origin) +    return is_compatible(t1->origin, t2); + +  if (t2->origin) +    return is_compatible(t1, t2->origin); + +  if (t1->kind != t2->kind) +    return false; + +  switch (t1->kind) { +  case TY_CHAR: +  case TY_SHORT: +  case TY_INT: +  case TY_LONG: +    return t1->is_unsigned == t2->is_unsigned; +  case TY_FLOAT: +  case TY_DOUBLE: +  case TY_LDOUBLE: +    return true; +  case TY_PTR: +    return is_compatible(t1->base, t2->base); +  case TY_FUNC: { +    if (!is_compatible(t1->return_ty, t2->return_ty)) +      return false; +    if (t1->is_variadic != t2->is_variadic) +      return false; + +    Type *p1 = t1->params; +    Type *p2 = t2->params; +    for (; p1 && p2; p1 = p1->next, p2 = p2->next) +      if (!is_compatible(p1, p2)) +        return false; +    return p1 == NULL && p2 == NULL; +  } +  case TY_ARRAY: +    if (!is_compatible(t1->base, t2->base)) +      return false; +    return t1->array_len < 0 && t2->array_len < 0 && +           t1->array_len == t2->array_len; +  } +  return false; +} + +Type *copy_type(Type *ty) { +  Type *ret = calloc(1, sizeof(Type)); +  *ret = *ty; +  ret->origin = ty; +  return ret; +} + +Type *pointer_to(Type *base) { +  Type *ty = new_type(TY_PTR, 8, 8); +  ty->base = base; +  ty->is_unsigned = true; +  return ty; +} + +Type *func_type(Type *return_ty) { +  // The C spec disallows sizeof(<function type>), but +  // GCC allows that and the expression is evaluated to 1. +  Type *ty = new_type(TY_FUNC, 1, 1); +  ty->return_ty = return_ty; +  return ty; +} + +Type *array_of(Type *base, int len) { +  Type *ty = new_type(TY_ARRAY, base->size * len, base->align); +  ty->base = base; +  ty->array_len = len; +  return ty; +} + +Type *vla_of(Type *base, Node *len) { +  Type *ty = new_type(TY_VLA, 8, 8); +  ty->base = base; +  ty->vla_len = len; +  return ty; +} + +Type *enum_type(void) { +  return new_type(TY_ENUM, 4, 4); +} + +Type *struct_type(void) { +  return new_type(TY_STRUCT, 0, 1); +} + +static Type *get_common_type(Type *ty1, Type *ty2) { +  if (ty1->base) +    return pointer_to(ty1->base); + +  if (ty1->kind == TY_FUNC) +    return pointer_to(ty1); +  if (ty2->kind == TY_FUNC) +    return pointer_to(ty2); + +  if (ty1->kind == TY_LDOUBLE || ty2->kind == TY_LDOUBLE) +    return ty_ldouble; +  if (ty1->kind == TY_DOUBLE || ty2->kind == TY_DOUBLE) +    return ty_double; +  if (ty1->kind == TY_FLOAT || ty2->kind == TY_FLOAT) +    return ty_float; + +  if (ty1->size < 4) +    ty1 = ty_int; +  if (ty2->size < 4) +    ty2 = ty_int; + +  if (ty1->size != ty2->size) +    return (ty1->size < ty2->size) ? ty2 : ty1; + +  if (ty2->is_unsigned) +    return ty2; +  return ty1; +} + +// For many binary operators, we implicitly promote operands so that +// both operands have the same type. Any integral type smaller than +// int is always promoted to int. If the type of one operand is larger +// than the other's (e.g. "long" vs. "int"), the smaller operand will +// be promoted to match with the other. +// +// This operation is called the "usual arithmetic conversion". +static void usual_arith_conv(Node **lhs, Node **rhs) { +  Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty); +  *lhs = new_cast(*lhs, ty); +  *rhs = new_cast(*rhs, ty); +} + +void add_type(Node *node) { +  if (!node || node->ty) +    return; + +  add_type(node->lhs); +  add_type(node->rhs); +  add_type(node->cond); +  add_type(node->then); +  add_type(node->els); +  add_type(node->init); +  add_type(node->inc); + +  for (Node *n = node->body; n; n = n->next) +    add_type(n); +  for (Node *n = node->args; n; n = n->next) +    add_type(n); + +  switch (node->kind) { +  case ND_NUM: +    node->ty = ty_int; +    return; +  case ND_ADD: +  case ND_SUB: +  case ND_MUL: +  case ND_DIV: +  case ND_MOD: +  case ND_BITAND: +  case ND_BITOR: +  case ND_BITXOR: +    usual_arith_conv(&node->lhs, &node->rhs); +    node->ty = node->lhs->ty; +    return; +  case ND_NEG: { +    Type *ty = get_common_type(ty_int, node->lhs->ty); +    node->lhs = new_cast(node->lhs, ty); +    node->ty = ty; +    return; +  } +  case ND_ASSIGN: +    if (node->lhs->ty->kind == TY_ARRAY) +      error_tok(node->lhs->tok, "not an lvalue"); +    if (node->lhs->ty->kind != TY_STRUCT) +      node->rhs = new_cast(node->rhs, node->lhs->ty); +    node->ty = node->lhs->ty; +    return; +  case ND_EQ: +  case ND_NE: +  case ND_LT: +  case ND_LE: +    usual_arith_conv(&node->lhs, &node->rhs); +    node->ty = ty_int; +    return; +  case ND_FUNCALL: +    node->ty = node->func_ty->return_ty; +    return; +  case ND_NOT: +  case ND_LOGOR: +  case ND_LOGAND: +    node->ty = ty_int; +    return; +  case ND_BITNOT: +  case ND_SHL: +  case ND_SHR: +    node->ty = node->lhs->ty; +    return; +  case ND_VAR: +  case ND_VLA_PTR: +    node->ty = node->var->ty; +    return; +  case ND_COND: +    if (node->then->ty->kind == TY_VOID || node->els->ty->kind == TY_VOID) { +      node->ty = ty_void; +    } else { +      usual_arith_conv(&node->then, &node->els); +      node->ty = node->then->ty; +    } +    return; +  case ND_COMMA: +    node->ty = node->rhs->ty; +    return; +  case ND_MEMBER: +    node->ty = node->member->ty; +    return; +  case ND_ADDR: { +    Type *ty = node->lhs->ty; +    if (ty->kind == TY_ARRAY) +      node->ty = pointer_to(ty->base); +    else +      node->ty = pointer_to(ty); +    return; +  } +  case ND_DEREF: +    if (!node->lhs->ty->base) +      error_tok(node->tok, "invalid pointer dereference"); +    if (node->lhs->ty->base->kind == TY_VOID) +      error_tok(node->tok, "dereferencing a void pointer"); + +    node->ty = node->lhs->ty->base; +    return; +  case ND_STMT_EXPR: +    if (node->body) { +      Node *stmt = node->body; +      while (stmt->next) +        stmt = stmt->next; +      if (stmt->kind == ND_EXPR_STMT) { +        node->ty = stmt->lhs->ty; +        return; +      } +    } +    error_tok(node->tok, "statement expression returning void is not supported"); +    return; +  case ND_LABEL_VAL: +    node->ty = pointer_to(ty_void); +    return; +  case ND_CAS: +    add_type(node->cas_addr); +    add_type(node->cas_old); +    add_type(node->cas_new); +    node->ty = ty_bool; + +    if (node->cas_addr->ty->kind != TY_PTR) +      error_tok(node->cas_addr->tok, "pointer expected"); +    if (node->cas_old->ty->kind != TY_PTR) +      error_tok(node->cas_old->tok, "pointer expected"); +    return; +  case ND_EXCH: +    if (node->lhs->ty->kind != TY_PTR) +      error_tok(node->cas_addr->tok, "pointer expected"); +    node->ty = node->lhs->ty->base; +    return; +  } +} diff --git a/src/3p/chibicc/unicode.c b/src/3p/chibicc/unicode.c new file mode 100644 index 0000000..6db1ad7 --- /dev/null +++ b/src/3p/chibicc/unicode.c @@ -0,0 +1,189 @@ +#include "chibicc.h" + +// Encode a given character in UTF-8. +int encode_utf8(char *buf, uint32_t c) { +  if (c <= 0x7F) { +    buf[0] = c; +    return 1; +  } + +  if (c <= 0x7FF) { +    buf[0] = 0xC0 | (c >> 6); +    buf[1] = 0x80 | ((c >> 6) & 0x3F); +    return 2; +  } + +  if (c <= 0xFFFF) { +    buf[0] = 0xE0 | (c >> 12); +    buf[1] = 0x80 | ((c >> 6) & 0x3F); +    buf[2] = 0x80 | (c & 0x3F); +    return 3; +  } + +  buf[0] = 0xF0 | (c >> 18); +  buf[1] = 0x80 | ((c >> 12) & 0x3F); +  buf[2] = 0x80 | ((c >> 6) & 0x3F); +  buf[3] = 0x80 | (c & 0x3F); +  return 4; +} + +// Read a UTF-8-encoded Unicode code point from a source file. +// We assume that source files are always in UTF-8. +// +// UTF-8 is a variable-width encoding in which one code point is +// encoded in one to four bytes. One byte UTF-8 code points are +// identical to ASCII. Non-ASCII characters are encoded using more +// than one byte. +uint32_t decode_utf8(char **new_pos, char *p) { +  if ((unsigned char)*p < 128) { +    *new_pos = p + 1; +    return *p; +  } + +  char *start = p; +  int len; +  uint32_t c; + +  if ((unsigned char)*p >= 0xF0) { +    len = 4; +    c = *p & 7; +  } else if ((unsigned char)*p >= 0xE0) { +    len = 3; +    c = *p & 15; +  } else if ((unsigned char)*p >= 0xC0) { +    len = 2; +    c = *p & 31; +  } else { +    error_at(start, "invalid UTF-8 sequence"); +  } + +  for (int i = 1; i < len; i++) { +    if ((unsigned char)p[i] >> 6 != 2) +      error_at(start, "invalid UTF-8 sequence"); +    c = (c << 6) | (p[i] & 63); +  } + +  *new_pos = p + len; +  return c; +} + +static bool in_range(uint32_t *range, uint32_t c) { +  for (int i = 0; range[i] != -1; i += 2) +    if (range[i] <= c && c <= range[i + 1]) +      return true; +  return false; +} + +// [https://www.sigbus.info/n1570#D] C11 allows not only ASCII but +// some multibyte characters in certan Unicode ranges to be used in an +// identifier. +// +// This function returns true if a given character is acceptable as +// the first character of an identifier. +// +// For example, ¾ (U+00BE) is a valid identifier because characters in +// 0x00BE-0x00C0 are allowed, while neither ⟘ (U+27D8) nor ' ' +// (U+3000, full-width space) are allowed because they are out of range. +bool is_ident1(uint32_t c) { +  static uint32_t range[] = { +    '_', '_', 'a', 'z', 'A', 'Z', '$', '$', +    0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF, +    0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6, +    0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F, +    0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D, +    0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F, +    0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793, +    0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F, +    0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF, +    0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD, +    0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD, +    0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD, +    0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD, +    0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD, -1, +  }; + +  return in_range(range, c); +} + +// Returns true if a given character is acceptable as a non-first +// character of an identifier. +bool is_ident2(uint32_t c) { +  static uint32_t range[] = { +    '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, +    0xFE20, 0xFE2F, -1, +  }; + +  return is_ident1(c) || in_range(range, c); +} + +// Returns the number of columns needed to display a given +// character in a fixed-width font. +// +// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c +static int char_width(uint32_t c) { +  static uint32_t range1[] = { +    0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486, +    0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2, +    0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615, +    0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8, +    0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A, +    0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C, +    0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963, +    0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD, +    0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42, +    0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82, +    0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD, +    0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F, +    0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82, +    0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48, +    0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF, +    0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43, +    0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6, +    0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1, +    0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19, +    0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E, +    0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC, +    0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037, +    0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F, +    0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773, +    0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3, +    0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922, +    0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18, +    0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C, +    0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF, +    0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F, +    0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806, +    0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F, +    0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03, +    0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F, +    0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD, +    0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF, +    -1, +  }; + +  if (in_range(range1, c)) +    return 0; + +  static uint32_t range2[] = { +    0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E, +    0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19, +    0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644, +    0x20000, 0x2FFFD, 0x30000, 0x3FFFD, -1, +  }; + +  if (in_range(range2, c)) +    return 2; +  return 1; +} + +// Returns the number of columns needed to display a given +// string in a fixed-width font. +int display_width(char *p, int len) { +  char *start = p; +  int w = 0; +  while (p - start < len) { +    uint32_t c = decode_utf8(&p, p); +    w += char_width(c); +  } +  return w; +} diff --git a/src/3p/openbsd/asprintf.c b/src/3p/openbsd/asprintf.c new file mode 100644 index 0000000..195efce --- /dev/null +++ b/src/3p/openbsd/asprintf.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004 Darren Tucker. + * + * Based originally on asprintf.c from OpenBSD: + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and 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 <errno.h> +#include <limits.h> /* for INT_MAX */ +#include <stdarg.h> +#include <stdio.h> /* for vsnprintf */ +#include <stdlib.h> + +#define INIT_SZ	128 + +int +vasprintf(char **str, const char *fmt, va_list ap) +{ +	int ret; +	va_list ap2; +	char *string, *newstr; +	size_t len; + +	if ((string = malloc(INIT_SZ)) == NULL) +		goto fail; + +	va_copy(ap2, ap); +	ret = vsnprintf(string, INIT_SZ, fmt, ap2); +	va_end(ap2); +	if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ +		*str = string; +	} else if (ret == INT_MAX || ret < 0) { /* Bad length */ +		free(string); +		goto fail; +	} else {	/* bigger than initial, realloc allowing for nul */ +		len = (size_t)ret + 1; +		if ((newstr = realloc(string, len)) == NULL) { +			free(string); +			goto fail; +		} +		va_copy(ap2, ap); +		ret = vsnprintf(newstr, len, fmt, ap2); +		va_end(ap2); +		if (ret < 0 || (size_t)ret >= len) { /* failed with realloc'ed string */ +			free(newstr); +			goto fail; +		} +		*str = newstr; +	} +	return (ret); + +fail: +	*str = NULL; +	errno = ENOMEM; +	return (-1); +} + +int asprintf(char **str, const char *fmt, ...) +{ +	va_list ap; +	int ret; +	 +	*str = NULL; +	va_start(ap, fmt); +	ret = vasprintf(str, fmt, ap); +	va_end(ap); + +	return ret; +} diff --git a/src/3p/udis86/decode.c b/src/3p/udis86/decode.c new file mode 100644 index 0000000..036b9ed --- /dev/null +++ b/src/3p/udis86/decode.c @@ -0,0 +1,1266 @@ +/* udis86 - libudis86/decode.c + *  + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "udint.h" +#include "types.h" +#include "extern.h" +#include "decode.h" + +#ifndef __UD_STANDALONE__ +# include <string.h> +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES    15 + +/* rex prefix bits */ +#define REX_W(r)        ( ( 0xF & ( r ) )  >> 3 ) +#define REX_R(r)        ( ( 0x7 & ( r ) )  >> 2 ) +#define REX_X(r)        ( ( 0x3 & ( r ) )  >> 1 ) +#define REX_B(r)        ( ( 0x1 & ( r ) )  >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ +                          ( P_REXR(n) << 2 ) | \ +                          ( P_REXX(n) << 1 ) | \ +                          ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b)        ( ( b ) >> 6 ) +#define SIB_I(b)        ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b)        ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b)    ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b)    ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b)    ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b)     ( ( b ) & 7 ) + +static int decode_ext(struct ud *u, uint16_t ptr); +static int decode_opcode(struct ud *u); + +enum reg_class { /* register classes */ +  REGCLASS_GPR, +  REGCLASS_MMX, +  REGCLASS_CR, +  REGCLASS_DB, +  REGCLASS_SEG, +  REGCLASS_XMM +}; + + /*  + * inp_start + *    Should be called before each de-code operation. + */ +static void +inp_start(struct ud *u) +{ +  u->inp_ctr = 0; +} + +static uint8_t +inp_peek(struct ud *u) +{ +  if (u->inp_end == 0) { +    if (u->inp_buf != NULL) { +      if (u->inp_buf_index < u->inp_buf_size) { +        return u->inp_buf[u->inp_buf_index]; +      } +    } else if (u->inp_peek != UD_EOI) { +      return u->inp_peek; +    } else { +      int c; +      if ((c = u->inp_hook(u)) != UD_EOI) { +        u->inp_peek = c; +        return u->inp_peek; +      } +    } +  } +  u->inp_end = 1; +  UDERR(u, "byte expected, eoi received\n"); +  return 0; +} +    +static uint8_t +inp_next(struct ud *u) +{ +  if (u->inp_end == 0) { +    if (u->inp_buf != NULL) { +      if (u->inp_buf_index < u->inp_buf_size) { +        u->inp_ctr++; +        return (u->inp_curr = u->inp_buf[u->inp_buf_index++]); +      } +    } else { +      int c = u->inp_peek; +      if (c != UD_EOI || (c = u->inp_hook(u)) != UD_EOI) { +        u->inp_peek = UD_EOI; +        u->inp_curr = c; +        u->inp_sess[u->inp_ctr++] = u->inp_curr; +        return u->inp_curr; +      } +    } +  } +  u->inp_end = 1; +  UDERR(u, "byte expected, eoi received\n"); +  return 0; +} + +static uint8_t +inp_curr(struct ud *u) +{ +  return u->inp_curr; +} + + +/* + * inp_uint8 + * int_uint16 + * int_uint32 + * int_uint64 + *    Load little-endian values from input + */ +static uint8_t  +inp_uint8(struct ud* u) +{ +  return inp_next(u); +} + +static uint16_t  +inp_uint16(struct ud* u) +{ +  uint16_t r, ret; + +  ret = inp_next(u); +  r = inp_next(u); +  return ret | (r << 8); +} + +static uint32_t  +inp_uint32(struct ud* u) +{ +  uint32_t r, ret; + +  ret = inp_next(u); +  r = inp_next(u); +  ret = ret | (r << 8); +  r = inp_next(u); +  ret = ret | (r << 16); +  r = inp_next(u); +  return ret | (r << 24); +} + +static uint64_t  +inp_uint64(struct ud* u) +{ +  uint64_t r, ret; + +  ret = inp_next(u); +  r = inp_next(u); +  ret = ret | (r << 8); +  r = inp_next(u); +  ret = ret | (r << 16); +  r = inp_next(u); +  ret = ret | (r << 24); +  r = inp_next(u); +  ret = ret | (r << 32); +  r = inp_next(u); +  ret = ret | (r << 40); +  r = inp_next(u); +  ret = ret | (r << 48); +  r = inp_next(u); +  return ret | (r << 56); +} + + +static UD_INLINE int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ +  if (dis_mode == 64) { +    return rex_w ? 64 : (pfx_opr ? 16 : 32); +  } else if (dis_mode == 32) { +    return pfx_opr ? 16 : 32; +  } else { +    UD_ASSERT(dis_mode == 16); +    return pfx_opr ? 32 : 16; +  } +} + + +static UD_INLINE int +eff_adr_mode(int dis_mode, int pfx_adr) +{ +  if (dis_mode == 64) { +    return pfx_adr ? 32 : 64; +  } else if (dis_mode == 32) { +    return pfx_adr ? 16 : 32; +  } else { +    UD_ASSERT(dis_mode == 16); +    return pfx_adr ? 32 : 16; +  } +} + + +/*  + * decode_prefixes + * + *  Extracts instruction prefixes. + */ +static int  +decode_prefixes(struct ud *u) +{ +  int done = 0; +  uint8_t curr = 0, last = 0; +  UD_RETURN_ON_ERROR(u); + +  do { +    last = curr; +    curr = inp_next(u);  +    UD_RETURN_ON_ERROR(u); +    if (u->inp_ctr == MAX_INSN_LENGTH) { +      UD_RETURN_WITH_ERROR(u, "max instruction length"); +    } +    +    switch (curr)   +    { +    case 0x2E:  +      u->pfx_seg = UD_R_CS;  +      break; +    case 0x36:      +      u->pfx_seg = UD_R_SS;  +      break; +    case 0x3E:  +      u->pfx_seg = UD_R_DS;  +      break; +    case 0x26:  +      u->pfx_seg = UD_R_ES;  +      break; +    case 0x64:  +      u->pfx_seg = UD_R_FS;  +      break; +    case 0x65:  +      u->pfx_seg = UD_R_GS;  +      break; +    case 0x67: /* adress-size override prefix */  +      u->pfx_adr = 0x67; +      break; +    case 0xF0:  +      u->pfx_lock = 0xF0; +      break; +    case 0x66:  +      u->pfx_opr = 0x66; +      break; +    case 0xF2: +      u->pfx_str = 0xf2; +      break; +    case 0xF3: +      u->pfx_str = 0xf3; +      break; +    default: +      /* consume if rex */ +      done = (u->dis_mode == 64 && (curr & 0xF0) == 0x40) ? 0 : 1; +      break; +    } +  } while (!done); +  /* rex prefixes in 64bit mode, must be the last prefix */ +  if (u->dis_mode == 64 && (last & 0xF0) == 0x40) { +    u->pfx_rex = last;   +  } +  return 0; +} + + +/* + * vex_l, vex_w + *  Return the vex.L and vex.W bits + */ +static UD_INLINE uint8_t +vex_l(const struct ud *u) +{ +  UD_ASSERT(u->vex_op != 0); +  return ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 2) & 1; +} + +static UD_INLINE uint8_t +vex_w(const struct ud *u) +{ +  UD_ASSERT(u->vex_op != 0); +  return u->vex_op == 0xc4 ? ((u->vex_b2 >> 7) & 1) : 0; +} + + +static UD_INLINE uint8_t +modrm(struct ud * u) +{ +    if ( !u->have_modrm ) { +        u->modrm = inp_next( u ); +        u->modrm_offset = (uint8_t) (u->inp_ctr - 1); +        u->have_modrm = 1; +    } +    return u->modrm; +} + + +static unsigned int +resolve_operand_size(const struct ud* u, ud_operand_size_t osize) +{ +  switch (osize) { +  case SZ_V: +    return u->opr_mode; +  case SZ_Z:   +    return u->opr_mode == 16 ? 16 : 32; +  case SZ_Y: +    return u->opr_mode == 16 ? 32 : u->opr_mode; +  case SZ_RDQ: +    return u->dis_mode == 64 ? 64 : 32; +  case SZ_X: +    UD_ASSERT(u->vex_op != 0); +    return (P_VEXL(u->itab_entry->prefix) && vex_l(u)) ?  SZ_QQ : SZ_DQ; +  default: +    return osize; +  } +} + + +static int resolve_mnemonic( struct ud* u ) +{ +  /* resolve 3dnow weirdness. */ +  if ( u->mnemonic == UD_I3dnow ) { +    u->mnemonic = ud_itab[ u->le->table[ inp_curr( u )  ] ].mnemonic; +  } +  /* SWAPGS is only valid in 64bits mode */ +  if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { +    UDERR(u, "swapgs invalid in 64bits mode\n"); +    return -1; +  } + +  if (u->mnemonic == UD_Ixchg) { +    if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX  && +         u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || +        (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && +         u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { +      u->operand[0].type = UD_NONE; +      u->operand[1].type = UD_NONE; +      u->mnemonic = UD_Inop; +    } +  } + +  if (u->mnemonic == UD_Inop && u->pfx_repe) { +    u->pfx_repe = 0; +    u->mnemonic = UD_Ipause; +  } +  return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void  +decode_a(struct ud* u, struct ud_operand *op) +{ +  if (u->opr_mode == 16) {   +    /* seg16:off16 */ +    op->type = UD_OP_PTR; +    op->size = 32; +    op->lval.ptr.off = inp_uint16(u); +    op->lval.ptr.seg = inp_uint16(u); +  } else { +    /* seg16:off32 */ +    op->type = UD_OP_PTR; +    op->size = 48; +    op->lval.ptr.off = inp_uint32(u); +    op->lval.ptr.seg = inp_uint16(u); +  } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register  + * ----------------------------------------------------------------------------- + */ +static enum ud_type  +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ +  switch (s) { +    case 64: +        return UD_R_RAX + rm; +    case 32: +        return UD_R_EAX + rm; +    case 16: +        return UD_R_AX  + rm; +    case  8: +        if (u->dis_mode == 64 && u->pfx_rex) { +            if (rm >= 4) +                return UD_R_SPL + (rm-4); +            return UD_R_AL + rm; +        } else return UD_R_AL + rm; +    case 0: +        /* invalid size in case of a decode error */ +        UD_ASSERT(u->error); +        return UD_NONE; +    default: +        UD_ASSERT(!"invalid operand size"); +        return UD_NONE; +  } +} + +static void +decode_reg(struct ud *u,  +           struct ud_operand *opr, +           int type, +           int num, +           int size) +{ +  int reg; +  size = resolve_operand_size(u, size); +  switch (type) { +    case REGCLASS_GPR : reg = decode_gpr(u, size, num); break; +    case REGCLASS_MMX : reg = UD_R_MM0  + (num & 7); break; +    case REGCLASS_XMM : +      reg = num + (size == SZ_QQ ? UD_R_YMM0 : UD_R_XMM0); +      break; +    case REGCLASS_CR : reg = UD_R_CR0  + num; break; +    case REGCLASS_DB : reg = UD_R_DR0  + num; break; +    case REGCLASS_SEG : { +      /* +       * Only 6 segment registers, anything else is an error. +       */ +      if ((num & 7) > 5) { +        UDERR(u, "invalid segment register value\n"); +        return; +      } else { +        reg = UD_R_ES + (num & 7); +      } +      break; +    } +    default: +      UD_ASSERT(!"invalid register type"); +      return; +  } +  opr->type = UD_OP_REG; +  opr->base = reg; +  opr->size = size; +} + + +/* + * decode_imm  + * + *    Decode Immediate values. + */ +static void  +decode_imm(struct ud* u, unsigned int size, struct ud_operand *op) +{ +  op->size = resolve_operand_size(u, size); +  op->type = UD_OP_IMM; + +  switch (op->size) { +  case  8: op->lval.sbyte = inp_uint8(u);   break; +  case 16: op->lval.uword = inp_uint16(u);  break; +  case 32: op->lval.udword = inp_uint32(u); break; +  case 64: op->lval.uqword = inp_uint64(u); break; +  default: return; +  } +} + + +/*  + * decode_mem_disp + * + *    Decode mem address displacement. + */ +static void  +decode_mem_disp(struct ud* u, unsigned int size, struct ud_operand *op) +{ +  switch (size) { +  case 8: +    op->offset = 8;  +    op->lval.ubyte  = inp_uint8(u); +    break; +  case 16: +    op->offset = 16;  +    op->lval.uword  = inp_uint16(u);  +    break; +  case 32: +    op->offset = 32;  +    op->lval.udword = inp_uint32(u);  +    break; +  case 64: +    op->offset = 64;  +    op->lval.uqword = inp_uint64(u);  +    break; +  default: +      return; +  } +} + + +/* + * decode_modrm_reg + * + *    Decodes reg field of mod/rm byte + *  + */ +static UD_INLINE void +decode_modrm_reg(struct ud         *u,  +                 struct ud_operand *operand, +                 unsigned int       type, +                 unsigned int       size) +{ +  uint8_t reg = (REX_R(u->_rex) << 3) | MODRM_REG(modrm(u)); +  decode_reg(u, operand, type, reg, size); +} + + +/* + * decode_modrm_rm + * + *    Decodes rm field of mod/rm byte + *  + */ +static void  +decode_modrm_rm(struct ud         *u,  +                struct ud_operand *op, +                unsigned char      type,    /* register type */ +                unsigned int       size)    /* operand size */ + +{ +  size_t offset = 0; +  unsigned char mod, rm; + +  /* get mod, r/m and reg fields */ +  mod = MODRM_MOD(modrm(u)); +  rm  = (REX_B(u->_rex) << 3) | MODRM_RM(modrm(u)); + +  /*  +   * If mod is 11b, then the modrm.rm specifies a register. +   * +   */ +  if (mod == 3) { +    decode_reg(u, op, type, rm, size); +    return; +  } + +  /*  +   * !11b => Memory Address +   */   +  op->type = UD_OP_MEM; +  op->size = resolve_operand_size(u, size); + +  if (u->adr_mode == 64) { +    op->base = UD_R_RAX + rm; +    if (mod == 1) { +      offset = 8; +    } else if (mod == 2) { +      offset = 32; +    } else if (mod == 0 && (rm & 7) == 5) {            +      op->base = UD_R_RIP; +      offset = 32; +    } else { +      offset = 0; +    } +    /*  +     * Scale-Index-Base (SIB)  +     */ +    if ((rm & 7) == 4) { +      inp_next(u); +       +      op->base  = UD_R_RAX + (SIB_B(inp_curr(u)) | (REX_B(u->_rex) << 3)); +      op->index = UD_R_RAX + (SIB_I(inp_curr(u)) | (REX_X(u->_rex) << 3)); +      /* special conditions for base reference */ +      if (op->index == UD_R_RSP) { +        op->index = UD_NONE; +        op->scale = UD_NONE; +      } else { +        op->scale = (1 << SIB_S(inp_curr(u))) & ~1; +      } + +      if (op->base == UD_R_RBP || op->base == UD_R_R13) { +        if (mod == 0) { +          op->base = UD_NONE; +        }  +        if (mod == 1) { +          offset = 8; +        } else { +          offset = 32; +        } +      } +    } else { +        op->scale = UD_NONE; +        op->index = UD_NONE; +    } +  } else if (u->adr_mode == 32) { +    op->base = UD_R_EAX + rm; +    if (mod == 1) { +      offset = 8; +    } else if (mod == 2) { +      offset = 32; +    } else if (mod == 0 && rm == 5) { +      op->base = UD_NONE; +      offset = 32; +    } else { +      offset = 0; +    } + +    /* Scale-Index-Base (SIB) */ +    if ((rm & 7) == 4) { +      inp_next(u); + +      op->scale = (1 << SIB_S(inp_curr(u))) & ~1; +      op->index = UD_R_EAX + (SIB_I(inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); +      op->base  = UD_R_EAX + (SIB_B(inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + +      if (op->index == UD_R_ESP) { +        op->index = UD_NONE; +        op->scale = UD_NONE; +      } + +      /* special condition for base reference */ +      if (op->base == UD_R_EBP) { +        if (mod == 0) { +          op->base = UD_NONE; +        }  +        if (mod == 1) { +          offset = 8; +        } else { +          offset = 32; +        } +      } +    } else { +      op->scale = UD_NONE; +      op->index = UD_NONE; +    } +  } else { +    const unsigned int bases[]   = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, +                                     UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; +    const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, +                                     UD_NONE, UD_NONE, UD_NONE, UD_NONE }; +    op->base  = bases[rm & 7]; +    op->index = indices[rm & 7]; +    op->scale = UD_NONE; +    if (mod == 0 && rm == 6) { +      offset = 16; +      op->base = UD_NONE; +    } else if (mod == 1) { +      offset = 8; +    } else if (mod == 2) {  +      offset = 16; +    } +  } + +  if (offset) { +    decode_mem_disp(u, offset, op); +  } else { +    op->offset = 0; +  } +} + + +/*  + * decode_moffset + *    Decode offset-only memory operand + */ +static void +decode_moffset(struct ud *u, unsigned int size, struct ud_operand *opr) +{ +  opr->type  = UD_OP_MEM; +  opr->base  = UD_NONE; +  opr->index = UD_NONE; +  opr->scale = UD_NONE; +  opr->size  = resolve_operand_size(u, size); +  decode_mem_disp(u, u->adr_mode, opr); +} + + +static void +decode_vex_vvvv(struct ud *u, struct ud_operand *opr, unsigned size) +{ +  uint8_t vvvv; +  UD_ASSERT(u->vex_op != 0); +  vvvv = ((u->vex_op == 0xc4 ? u->vex_b2 : u->vex_b1) >> 3) & 0xf; +  decode_reg(u, opr, REGCLASS_XMM, (0xf & ~vvvv), size); +} + + +/*  + * decode_vex_immreg + *    Decode source operand encoded in immediate byte [7:4] + */ +static int +decode_vex_immreg(struct ud *u, struct ud_operand *opr, unsigned size) +{ +  uint8_t imm  = inp_next(u); +  uint8_t mask = u->dis_mode == 64 ? 0xf : 0x7; +  UD_RETURN_ON_ERROR(u); +  UD_ASSERT(u->vex_op != 0); +  decode_reg(u, opr, REGCLASS_XMM, mask & (imm >> 4), size); +  return 0; +} + + +/*  + * decode_operand + * + *      Decodes a single operand. + *      Returns the type of the operand (UD_NONE if none) + */ +static int +decode_operand(struct ud           *u,  +               struct ud_operand   *operand, +               enum ud_operand_code type, +               unsigned int         size) +{ +  operand->type = UD_NONE; +  operand->_oprcode = type; + +  switch (type) { +    case OP_A : +      decode_a(u, operand); +      break; +    case OP_MR: +      decode_modrm_rm(u, operand, REGCLASS_GPR,  +                      MODRM_MOD(modrm(u)) == 3 ?  +                        Mx_reg_size(size) : Mx_mem_size(size)); +      break; +    case OP_F: +      u->br_far  = 1; +      /* intended fall through */ +    case OP_M: +      if (MODRM_MOD(modrm(u)) == 3) { +        UDERR(u, "expected modrm.mod != 3\n"); +      } +      /* intended fall through */ +    case OP_E: +      decode_modrm_rm(u, operand, REGCLASS_GPR, size); +      break; +    case OP_G: +      decode_modrm_reg(u, operand, REGCLASS_GPR, size); +      break; +    case OP_sI: +    case OP_I: +      decode_imm(u, size, operand); +      break; +    case OP_I1: +      operand->type = UD_OP_CONST; +      operand->lval.udword = 1; +      break; +    case OP_N: +      if (MODRM_MOD(modrm(u)) != 3) { +        UDERR(u, "expected modrm.mod == 3\n"); +      } +      /* intended fall through */ +    case OP_Q: +      decode_modrm_rm(u, operand, REGCLASS_MMX, size); +      break; +    case OP_P: +      decode_modrm_reg(u, operand, REGCLASS_MMX, size); +      break; +    case OP_U: +      if (MODRM_MOD(modrm(u)) != 3) { +        UDERR(u, "expected modrm.mod == 3\n"); +      } +      /* intended fall through */ +    case OP_W: +      decode_modrm_rm(u, operand, REGCLASS_XMM, size); +      break; +    case OP_V: +      decode_modrm_reg(u, operand, REGCLASS_XMM, size); +      break; +    case OP_H: +      decode_vex_vvvv(u, operand, size); +      break; +    case OP_MU: +      decode_modrm_rm(u, operand, REGCLASS_XMM,  +                      MODRM_MOD(modrm(u)) == 3 ?  +                        Mx_reg_size(size) : Mx_mem_size(size)); +      break; +    case OP_S: +      decode_modrm_reg(u, operand, REGCLASS_SEG, size); +      break; +    case OP_O: +      decode_moffset(u, size, operand); +      break; +    case OP_R0:  +    case OP_R1:  +    case OP_R2:  +    case OP_R3:  +    case OP_R4:  +    case OP_R5:  +    case OP_R6:  +    case OP_R7: +      decode_reg(u, operand, REGCLASS_GPR,  +                 (REX_B(u->_rex) << 3) | (type - OP_R0), size); +      break; +    case OP_AL: +    case OP_AX: +    case OP_eAX: +    case OP_rAX: +      decode_reg(u, operand, REGCLASS_GPR, 0, size); +      break; +    case OP_CL: +    case OP_CX: +    case OP_eCX: +      decode_reg(u, operand, REGCLASS_GPR, 1, size); +      break; +    case OP_DL: +    case OP_DX: +    case OP_eDX: +      decode_reg(u, operand, REGCLASS_GPR, 2, size); +      break; +    case OP_ES:  +    case OP_CS:  +    case OP_DS: +    case OP_SS:  +    case OP_FS:  +    case OP_GS: +      /* in 64bits mode, only fs and gs are allowed */ +      if (u->dis_mode == 64) { +        if (type != OP_FS && type != OP_GS) { +          UDERR(u, "invalid segment register in 64bits\n"); +        } +      } +      operand->type = UD_OP_REG; +      operand->base = (type - OP_ES) + UD_R_ES; +      operand->size = 16; +      break; +    case OP_J : +      decode_imm(u, size, operand); +      operand->type = UD_OP_JIMM; +      break ; +    case OP_R : +      if (MODRM_MOD(modrm(u)) != 3) { +        UDERR(u, "expected modrm.mod == 3\n"); +      } +      decode_modrm_rm(u, operand, REGCLASS_GPR, size); +      break; +    case OP_C: +      decode_modrm_reg(u, operand, REGCLASS_CR, size); +      break; +    case OP_D: +      decode_modrm_reg(u, operand, REGCLASS_DB, size); +      break; +    case OP_I3 : +      operand->type = UD_OP_CONST; +      operand->lval.sbyte = 3; +      break; +    case OP_ST0:  +    case OP_ST1:  +    case OP_ST2:  +    case OP_ST3: +    case OP_ST4: +    case OP_ST5:  +    case OP_ST6:  +    case OP_ST7: +      operand->type = UD_OP_REG; +      operand->base = (type - OP_ST0) + UD_R_ST0; +      operand->size = 80; +      break; +    case OP_L: +      decode_vex_immreg(u, operand, size); +      break; +    default : +      operand->type = UD_NONE; +      break; +  } +  return operand->type; +} + + +/*  + * decode_operands + * + *    Disassemble upto 3 operands of the current instruction being + *    disassembled. By the end of the function, the operand fields + *    of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ +  decode_operand(u, &u->operand[0], +                    u->itab_entry->operand1.type, +                    u->itab_entry->operand1.size); +  if (u->operand[0].type != UD_NONE) { +      decode_operand(u, &u->operand[1], +                        u->itab_entry->operand2.type, +                        u->itab_entry->operand2.size); +  } +  if (u->operand[1].type != UD_NONE) { +      decode_operand(u, &u->operand[2], +                        u->itab_entry->operand3.type, +                        u->itab_entry->operand3.size); +  } +  if (u->operand[2].type != UD_NONE) { +      decode_operand(u, &u->operand[3], +                        u->itab_entry->operand4.type, +                        u->itab_entry->operand4.size); +  } +  return 0; +} +     +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ +  u->error     = 0; +  u->pfx_seg   = 0; +  u->pfx_opr   = 0; +  u->pfx_adr   = 0; +  u->pfx_lock  = 0; +  u->pfx_repne = 0; +  u->pfx_rep   = 0; +  u->pfx_repe  = 0; +  u->pfx_rex   = 0; +  u->pfx_str   = 0; +  u->mnemonic  = UD_Inone; +  u->itab_entry = NULL; +  u->have_modrm = 0; +  u->br_far    = 0; +  u->vex_op    = 0; +  u->_rex      = 0; +  u->operand[0].type = UD_NONE; +  u->operand[1].type = UD_NONE; +  u->operand[2].type = UD_NONE; +  u->operand[3].type = UD_NONE; +} + + +static UD_INLINE int +resolve_pfx_str(struct ud* u) +{ +  if (u->pfx_str == 0xf3) { +    if (P_STR(u->itab_entry->prefix)) { +        u->pfx_rep  = 0xf3; +    } else { +        u->pfx_repe = 0xf3; +    } +  } else if (u->pfx_str == 0xf2) { +    u->pfx_repne = 0xf3; +  } +  return 0; +} + + +static int +resolve_mode( struct ud* u ) +{ +  int default64; +  /* if in error state, bail out */ +  if ( u->error ) return -1;  + +  /* propagate prefix effects */ +  if ( u->dis_mode == 64 ) {  /* set 64bit-mode flags */ + +    /* Check validity of  instruction m64 */ +    if ( P_INV64( u->itab_entry->prefix ) ) { +      UDERR(u, "instruction invalid in 64bits\n"); +      return -1; +    } + +    /* compute effective rex based on, +     *  - vex prefix (if any) +     *  - rex prefix (if any, and not vex) +     *  - allowed prefixes specified by the opcode map +     */ +    if (u->vex_op == 0xc4) { +        /* vex has rex.rxb in 1's complement */ +        u->_rex = ((~(u->vex_b1 >> 5) & 0x7) /* rex.0rxb */ |  +                   ((u->vex_b2  >> 4) & 0x8) /* rex.w000 */); +    } else if (u->vex_op == 0xc5) { +        /* vex has rex.r in 1's complement */ +        u->_rex = (~(u->vex_b1 >> 5)) & 4; +    } else { +        UD_ASSERT(u->vex_op == 0); +        u->_rex = u->pfx_rex; +    } +    u->_rex &= REX_PFX_MASK(u->itab_entry->prefix); + +    /* whether this instruction has a default operand size of  +     * 64bit, also hardcoded into the opcode map. +     */ +    default64 = P_DEF64( u->itab_entry->prefix );  +    /* calculate effective operand size */ +    if (REX_W(u->_rex)) { +        u->opr_mode = 64; +    } else if ( u->pfx_opr ) { +        u->opr_mode = 16; +    } else { +        /* unless the default opr size of instruction is 64, +         * the effective operand size in the absence of rex.w +         * prefix is 32. +         */ +        u->opr_mode = default64 ? 64 : 32; +    } + +    /* calculate effective address size */ +    u->adr_mode = (u->pfx_adr) ? 32 : 64; +  } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ +    u->opr_mode = ( u->pfx_opr ) ? 16 : 32; +    u->adr_mode = ( u->pfx_adr ) ? 16 : 32; +  } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ +    u->opr_mode = ( u->pfx_opr ) ? 32 : 16; +    u->adr_mode = ( u->pfx_adr ) ? 32 : 16; +  } + +  return 0; +} + + +static UD_INLINE int +decode_insn(struct ud *u, uint16_t ptr) +{ +  UD_ASSERT((ptr & 0x8000) == 0); +  u->itab_entry = &ud_itab[ ptr ]; +  u->mnemonic = u->itab_entry->mnemonic; +  return (resolve_pfx_str(u)  == 0 && +          resolve_mode(u)     == 0 && +          decode_operands(u)  == 0 && +          resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + *    Decoding 3dnow is a little tricky because of its strange opcode + *    structure. The final opcode disambiguation depends on the last + *    byte that comes after the operands have been decoded. Fortunately, + *    all 3dnow instructions have the same set of operand types. So we + *    go ahead and decode the instruction by picking an arbitrarily chosen + *    valid entry in the table, decode the operands, and read the final + *    byte to resolve the menmonic. + */ +static UD_INLINE int +decode_3dnow(struct ud* u) +{ +  uint16_t ptr; +  UD_ASSERT(u->le->type == UD_TAB__OPC_3DNOW); +  UD_ASSERT(u->le->table[0xc] != 0); +  decode_insn(u, u->le->table[0xc]); +  inp_next(u);  +  if (u->error) { +    return -1; +  } +  ptr = u->le->table[inp_curr(u)];  +  UD_ASSERT((ptr & 0x8000) == 0); +  u->mnemonic = ud_itab[ptr].mnemonic; +  return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ +  uint8_t idx; +  uint8_t pfx; +  +  /* +   * String prefixes (f2, f3) take precedence over operand +   * size prefix (66). +   */ +  pfx = u->pfx_str; +  if (pfx == 0) { +    pfx = u->pfx_opr; +  } +  idx = ((pfx & 0xf) + 1) / 2; +  if (u->le->table[idx] == 0) { +    idx = 0; +  } +  if (idx && u->le->table[idx] != 0) { +    /* +     * "Consume" the prefix as a part of the opcode, so it is no +     * longer exported as an instruction prefix. +     */ +    u->pfx_str = 0; +    if (pfx == 0x66) { +        /*  +         * consume "66" only if it was used for decoding, leaving +         * it to be used as an operands size override for some +         * simd instructions. +         */ +        u->pfx_opr = 0; +    } +  } +  return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_vex(struct ud *u) +{ +  uint8_t index; +  if (u->dis_mode != 64 && MODRM_MOD(inp_peek(u)) != 0x3) { +    index = 0; +  } else { +    u->vex_op = inp_curr(u); +    u->vex_b1 = inp_next(u); +    if (u->vex_op == 0xc4) { +      uint8_t pp, m; +      /* 3-byte vex */ +      u->vex_b2 = inp_next(u); +      UD_RETURN_ON_ERROR(u); +      m  = u->vex_b1 & 0x1f; +      if (m == 0 || m > 3) { +        UD_RETURN_WITH_ERROR(u, "reserved vex.m-mmmm value"); +      } +      pp = u->vex_b2 & 0x3; +      index = (pp << 2) | m; +    } else { +      /* 2-byte vex */ +      UD_ASSERT(u->vex_op == 0xc5); +      index = 0x1 | ((u->vex_b1 & 0x3) << 2); +    } +  } +  return decode_ext(u, u->le->table[index]);  +} + + +/* + * decode_ext() + * + *    Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ +  uint8_t idx = 0; +  if ((ptr & 0x8000) == 0) { +    return decode_insn(u, ptr);  +  } +  u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; +  if (u->le->type == UD_TAB__OPC_3DNOW) { +    return decode_3dnow(u); +  } + +  switch (u->le->type) { +    case UD_TAB__OPC_MOD: +      /* !11 = 0, 11 = 1 */ +      idx = (MODRM_MOD(modrm(u)) + 1) / 4; +      break; +      /* disassembly mode/operand size/address size based tables. +       * 16 = 0,, 32 = 1, 64 = 2 +       */ +    case UD_TAB__OPC_MODE: +      idx = u->dis_mode != 64 ? 0 : 1; +      break; +    case UD_TAB__OPC_OSIZE: +      idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; +      break; +    case UD_TAB__OPC_ASIZE: +      idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; +      break; +    case UD_TAB__OPC_X87: +      idx = modrm(u) - 0xC0; +      break; +    case UD_TAB__OPC_VENDOR: +      if (u->vendor == UD_VENDOR_ANY) { +        /* choose a valid entry */ +        idx = (u->le->table[idx] != 0) ? 0 : 1; +      } else if (u->vendor == UD_VENDOR_AMD) { +        idx = 0; +      } else { +        idx = 1; +      } +      break; +    case UD_TAB__OPC_RM: +      idx = MODRM_RM(modrm(u)); +      break; +    case UD_TAB__OPC_REG: +      idx = MODRM_REG(modrm(u)); +      break; +    case UD_TAB__OPC_SSE: +      return decode_ssepfx(u); +    case UD_TAB__OPC_VEX: +      return decode_vex(u); +    case UD_TAB__OPC_VEX_W: +      idx = vex_w(u); +      break; +    case UD_TAB__OPC_VEX_L: +      idx = vex_l(u); +      break; +    case UD_TAB__OPC_TABLE: +      inp_next(u); +      return decode_opcode(u); +    default: +      UD_ASSERT(!"not reached"); +      break; +  } + +  return decode_ext(u, u->le->table[idx]); +} + + +static int +decode_opcode(struct ud *u) +{ +  uint16_t ptr; +  UD_ASSERT(u->le->type == UD_TAB__OPC_TABLE); +  UD_RETURN_ON_ERROR(u); +  ptr = u->le->table[inp_curr(u)]; +  return decode_ext(u, ptr); +} + +  +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ +  inp_start(u); +  clear_insn(u); +  u->le = &ud_lookup_table_list[0]; +  u->error = decode_prefixes(u) == -1 ||  +             decode_opcode(u)   == -1 || +             u->error; +  /* Handle decode error. */ +  if (u->error) { +    /* clear out the decode data. */ +    clear_insn(u); +    /* mark the sequence of bytes as invalid. */ +    u->itab_entry = &ud_itab[0]; /* entry 0 is invalid */ +    u->mnemonic = u->itab_entry->mnemonic; +  }  + +    /* maybe this stray segment override byte +     * should be spewed out? +     */ +    if ( !P_SEG( u->itab_entry->prefix ) &&  +            u->operand[0].type != UD_OP_MEM && +            u->operand[1].type != UD_OP_MEM ) +        u->pfx_seg = 0; + +  u->insn_offset = u->pc; /* set offset of instruction */ +  u->asm_buf_fill = 0;   /* set translation buffer index to 0 */ +  u->pc += u->inp_ctr;    /* move program counter by bytes decoded */ + +  /* return number of bytes disassembled. */ +  return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/decode.h b/src/3p/udis86/decode.h new file mode 100644 index 0000000..3949c4e --- /dev/null +++ b/src/3p/udis86/decode.h @@ -0,0 +1,197 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "types.h" +#include "udint.h" +#include "itab.h" + +#define MAX_INSN_LENGTH 15 + +/* itab prefix bits */ +#define P_none          ( 0 ) + +#define P_inv64         ( 1 << 0 ) +#define P_INV64(n)      ( ( n >> 0 ) & 1 ) +#define P_def64         ( 1 << 1 ) +#define P_DEF64(n)      ( ( n >> 1 ) & 1 ) + +#define P_oso           ( 1 << 2 ) +#define P_OSO(n)        ( ( n >> 2 ) & 1 ) +#define P_aso           ( 1 << 3 ) +#define P_ASO(n)        ( ( n >> 3 ) & 1 ) + +#define P_rexb          ( 1 << 4 ) +#define P_REXB(n)       ( ( n >> 4 ) & 1 ) +#define P_rexw          ( 1 << 5 ) +#define P_REXW(n)       ( ( n >> 5 ) & 1 ) +#define P_rexr          ( 1 << 6 ) +#define P_REXR(n)       ( ( n >> 6 ) & 1 ) +#define P_rexx          ( 1 << 7 ) +#define P_REXX(n)       ( ( n >> 7 ) & 1 ) + +#define P_seg           ( 1 << 8 ) +#define P_SEG(n)        ( ( n >> 8 ) & 1 ) + +#define P_vexl          ( 1 << 9 ) +#define P_VEXL(n)       ( ( n >> 9 ) & 1 ) +#define P_vexw          ( 1 << 10 ) +#define P_VEXW(n)       ( ( n >> 10 ) & 1 ) + +#define P_str           ( 1 << 11 ) +#define P_STR(n)        ( ( n >> 11 ) & 1 ) +#define P_strz          ( 1 << 12 ) +#define P_STR_ZF(n)     ( ( n >> 12 ) & 1 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { +    OP_NONE, + +    OP_A,      OP_E,      OP_M,       OP_G,        +    OP_I,      OP_F, + +    OP_R0,     OP_R1,     OP_R2,      OP_R3, +    OP_R4,     OP_R5,     OP_R6,      OP_R7, + +    OP_AL,     OP_CL,     OP_DL, +    OP_AX,     OP_CX,     OP_DX, +    OP_eAX,    OP_eCX,    OP_eDX, +    OP_rAX,    OP_rCX,    OP_rDX, + +    OP_ES,     OP_CS,     OP_SS,      OP_DS,   +    OP_FS,     OP_GS, + +    OP_ST0,    OP_ST1,    OP_ST2,     OP_ST3, +    OP_ST4,    OP_ST5,    OP_ST6,     OP_ST7, + +    OP_J,      OP_S,      OP_O,           +    OP_I1,     OP_I3,     OP_sI, + +    OP_V,      OP_W,      OP_Q,       OP_P,  +    OP_U,      OP_N,      OP_MU,      OP_H, +    OP_L, + +    OP_R,      OP_C,      OP_D,        + +    OP_MR +} UD_ATTR_PACKED; + + +/* + * Operand size constants + * + *  Symbolic constants for various operand sizes. Some of these constants + *  are given a value equal to the width of the data (SZ_B == 8), such + *  that they maybe used interchangeably in the internals. Modifying them + *  will most certainly break things! + */ +typedef uint16_t ud_operand_size_t; + +#define SZ_NA  0 +#define SZ_Z   1 +#define SZ_V   2 +#define SZ_Y   3 +#define SZ_X   4 +#define SZ_RDQ 7 +#define SZ_B   8 +#define SZ_W   16 +#define SZ_D   32 +#define SZ_Q   64 +#define SZ_T   80 +#define SZ_O   12 +#define SZ_DQ  128 /* double quad */ +#define SZ_QQ  256 /* quad quad */ + +/* + * Complex size types; that encode sizes for operands of type MR (memory or + * register); for internal use only. Id space above 256. + */ +#define SZ_BD  ((SZ_B << 8) | SZ_D) +#define SZ_BV  ((SZ_B << 8) | SZ_V) +#define SZ_WD  ((SZ_W << 8) | SZ_D) +#define SZ_WV  ((SZ_W << 8) | SZ_V) +#define SZ_WY  ((SZ_W << 8) | SZ_Y) +#define SZ_DY  ((SZ_D << 8) | SZ_Y) +#define SZ_WO  ((SZ_W << 8) | SZ_O) +#define SZ_DO  ((SZ_D << 8) | SZ_O) +#define SZ_QO  ((SZ_Q << 8) | SZ_O) + + +/* resolve complex size type. + */ +static UD_INLINE ud_operand_size_t +Mx_mem_size(ud_operand_size_t size) +{ +  return (size >> 8) & 0xff; +} + +static UD_INLINE ud_operand_size_t +Mx_reg_size(ud_operand_size_t size) +{ +  return size & 0xff; +} + +/* A single operand of an entry in the instruction table.  + * (internal use only) + */ +struct ud_itab_entry_operand  +{ +  enum ud_operand_code type; +  ud_operand_size_t size; +}; + + +/* A single entry in an instruction table.  + *(internal use only) + */ +struct ud_itab_entry  +{ +  enum ud_mnemonic_code         mnemonic; +  struct ud_itab_entry_operand  operand1; +  struct ud_itab_entry_operand  operand2; +  struct ud_itab_entry_operand  operand3; +  struct ud_itab_entry_operand  operand4; +  uint32_t                      prefix; +}; + +struct ud_lookup_table_list_entry { +    const uint16_t *table; +    enum ud_table_type type; +    const char *meta; +}; +      +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/src/3p/udis86/extern.h b/src/3p/udis86/extern.h new file mode 100644 index 0000000..71a01fd --- /dev/null +++ b/src/3p/udis86/extern.h @@ -0,0 +1,113 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009, 2013 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "types.h" + +#if defined(_MSC_VER) && defined(_USRDLL) +# ifdef LIBUDIS86_EXPORTS +#  define LIBUDIS86_DLLEXTERN __declspec(dllexport) +# else  +#  define LIBUDIS86_DLLEXTERN __declspec(dllimport) +# endif +#else +# define LIBUDIS86_DLLEXTERN  +#endif + +/* ============================= PUBLIC API ================================= */ + +extern LIBUDIS86_DLLEXTERN void ud_init(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_mode(struct ud*, uint8_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_pc(struct ud*, uint64_t); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_set_input_buffer(struct ud*, const uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern LIBUDIS86_DLLEXTERN void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern LIBUDIS86_DLLEXTERN void ud_set_vendor(struct ud*, unsigned); + +extern LIBUDIS86_DLLEXTERN void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern LIBUDIS86_DLLEXTERN void ud_input_skip(struct ud*, size_t); + +extern LIBUDIS86_DLLEXTERN int ud_input_end(const struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_decode(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_disassemble(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_intel(struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_translate_att(struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_asm(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const uint8_t* ud_insn_ptr(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN uint64_t ud_insn_off(const struct ud*); + +extern LIBUDIS86_DLLEXTERN const char* ud_insn_hex(struct ud*); + +extern LIBUDIS86_DLLEXTERN unsigned int ud_insn_len(const struct ud* u); + +extern LIBUDIS86_DLLEXTERN const struct ud_operand* ud_insn_opr(const struct ud *u, unsigned int n); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_sreg(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN int ud_opr_is_gpr(const struct ud_operand *opr); + +extern LIBUDIS86_DLLEXTERN enum ud_mnemonic_code ud_insn_mnemonic(const struct ud *u); + +extern LIBUDIS86_DLLEXTERN const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern LIBUDIS86_DLLEXTERN void ud_set_user_opaque_data(struct ud*, void*); + +extern LIBUDIS86_DLLEXTERN void* ud_get_user_opaque_data(const struct ud*); + +extern LIBUDIS86_DLLEXTERN void ud_set_asm_buffer(struct ud *u, char *buf, size_t size); + +extern LIBUDIS86_DLLEXTERN void ud_set_sym_resolver(struct ud *u,  +                                const char* (*resolver)(struct ud*,  +                                                        uint64_t addr, +                                                        int64_t *offset)); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif /* UD_EXTERN_H */ diff --git a/src/3p/udis86/itab.c b/src/3p/udis86/itab.c new file mode 100644 index 0000000..c774223 --- /dev/null +++ b/src/3p/udis86/itab.c @@ -0,0 +1,5945 @@ +/* itab.c -- generated by udis86:scripts/ud_itab.py, do no edit */ +#include "decode.h" + +#define GROUP(n) (0x8000 | (n)) +#define INVALID  0 + +const uint16_t ud_itab__0[] = { +  /*  0 */          15,          16,          17,          18, +  /*  4 */          19,          20,    GROUP(1),    GROUP(2), +  /*  8 */         964,         965,         966,         967, +  /*  c */         968,         969,    GROUP(3),    GROUP(4), +  /* 10 */           5,           6,           7,           8, +  /* 14 */           9,          10,  GROUP(284),  GROUP(285), +  /* 18 */        1336,        1337,        1338,        1339, +  /* 1c */        1340,        1341,  GROUP(286),  GROUP(287), +  /* 20 */          49,          50,          51,          52, +  /* 24 */          53,          54,     INVALID,  GROUP(288), +  /* 28 */        1407,        1408,        1409,        1410, +  /* 2c */        1411,        1412,     INVALID,  GROUP(289), +  /* 30 */        1487,        1488,        1489,        1490, +  /* 34 */        1491,        1492,     INVALID,  GROUP(290), +  /* 38 */         100,         101,         102,         103, +  /* 3c */         104,         105,     INVALID,  GROUP(291), +  /* 40 */         699,         700,         701,         702, +  /* 44 */         703,         704,         705,         706, +  /* 48 */         175,         176,         177,         178, +  /* 4c */         179,         180,         181,         182, +  /* 50 */        1246,        1247,        1248,        1249, +  /* 54 */        1250,        1251,        1252,        1253, +  /* 58 */        1101,        1102,        1103,        1104, +  /* 5c */        1105,        1106,        1107,        1108, +  /* 60 */  GROUP(292),  GROUP(295),  GROUP(298),  GROUP(299), +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */        1254,         697,        1256,         698, +  /* 6c */         709,  GROUP(300),         982,  GROUP(301), +  /* 70 */         726,         728,         730,         732, +  /* 74 */         734,         736,         738,         740, +  /* 78 */         742,         744,         746,         748, +  /* 7c */         750,         752,         754,         756, +  /* 80 */  GROUP(302),  GROUP(303),  GROUP(304),  GROUP(313), +  /* 84 */        1433,        1434,        1475,        1476, +  /* 88 */         828,         829,         830,         831, +  /* 8c */         832,         770,         833,  GROUP(314), +  /* 90 */        1477,        1478,        1479,        1480, +  /* 94 */        1481,        1482,        1483,        1484, +  /* 98 */  GROUP(315),  GROUP(316),  GROUP(317),        1470, +  /* 9c */  GROUP(318),  GROUP(322),        1310,         766, +  /* a0 */         834,         835,         836,         837, +  /* a4 */         922,  GROUP(326),         114,  GROUP(327), +  /* a8 */        1435,        1436,        1402,  GROUP(328), +  /* ac */         790,  GROUP(329),        1346,  GROUP(330), +  /* b0 */         838,         839,         840,         841, +  /* b4 */         842,         843,         844,         845, +  /* b8 */         846,         847,         848,         849, +  /* bc */         850,         851,         852,         853, +  /* c0 */  GROUP(331),  GROUP(332),        1301,        1302, +  /* c4 */  GROUP(333),  GROUP(403),  GROUP(405),  GROUP(406), +  /* c8 */         200,         776,        1303,        1304, +  /* cc */         713,         714,  GROUP(407),  GROUP(408), +  /* d0 */  GROUP(409),  GROUP(410),  GROUP(411),  GROUP(412), +  /* d4 */  GROUP(413),  GROUP(414),  GROUP(415),        1486, +  /* d8 */  GROUP(416),  GROUP(419),  GROUP(422),  GROUP(425), +  /* dc */  GROUP(428),  GROUP(431),  GROUP(434),  GROUP(437), +  /* e0 */         794,         795,         796,  GROUP(440), +  /* e4 */         690,         691,         978,         979, +  /* e8 */          72,         763,  GROUP(441),         765, +  /* ec */         692,         693,         980,         981, +  /* f0 */         789,         712,        1299,        1300, +  /* f4 */         687,          83,  GROUP(442),  GROUP(443), +  /* f8 */          77,        1395,          81,        1398, +  /* fc */          78,        1396,  GROUP(444),  GROUP(445), +}; + +static const uint16_t ud_itab__1[] = { +  /*  0 */        1240,     INVALID, +}; + +static const uint16_t ud_itab__2[] = { +  /*  0 */        1096,     INVALID, +}; + +static const uint16_t ud_itab__3[] = { +  /*  0 */        1241,     INVALID, +}; + +static const uint16_t ud_itab__4[] = { +  /*  0 */    GROUP(5),    GROUP(6),         767,         797, +  /*  4 */     INVALID,        1426,          82,        1431, +  /*  8 */         716,        1471,     INVALID,        1444, +  /*  c */     INVALID,   GROUP(27),         430,   GROUP(28), +  /* 10 */   GROUP(29),   GROUP(30),   GROUP(31),   GROUP(34), +  /* 14 */   GROUP(35),   GROUP(36),   GROUP(37),   GROUP(40), +  /* 18 */   GROUP(41),         955,         956,         957, +  /* 1c */         958,         959,         960,         961, +  /* 20 */         854,         855,         856,         857, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */   GROUP(42),   GROUP(43),   GROUP(44),   GROUP(45), +  /* 2c */   GROUP(46),   GROUP(47),   GROUP(48),   GROUP(49), +  /* 30 */        1472,        1297,        1295,        1296, +  /* 34 */   GROUP(50),   GROUP(52),     INVALID,        1514, +  /* 38 */   GROUP(54),     INVALID,  GROUP(116),     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */          84,          85,          86,          87, +  /* 44 */          88,          89,          90,          91, +  /* 48 */          92,          93,          94,          95, +  /* 4c */          96,          97,          98,          99, +  /* 50 */  GROUP(143),  GROUP(144),  GROUP(145),  GROUP(146), +  /* 54 */  GROUP(147),  GROUP(148),  GROUP(149),  GROUP(150), +  /* 58 */  GROUP(151),  GROUP(152),  GROUP(153),  GROUP(154), +  /* 5c */  GROUP(155),  GROUP(156),  GROUP(157),  GROUP(158), +  /* 60 */  GROUP(159),  GROUP(160),  GROUP(161),  GROUP(162), +  /* 64 */  GROUP(163),  GROUP(164),  GROUP(165),  GROUP(166), +  /* 68 */  GROUP(167),  GROUP(168),  GROUP(169),  GROUP(170), +  /* 6c */  GROUP(171),  GROUP(172),  GROUP(173),  GROUP(176), +  /* 70 */  GROUP(177),  GROUP(178),  GROUP(182),  GROUP(186), +  /* 74 */  GROUP(191),  GROUP(192),  GROUP(193),         199, +  /* 78 */  GROUP(194),  GROUP(195),     INVALID,     INVALID, +  /* 7c */  GROUP(196),  GROUP(197),  GROUP(198),  GROUP(201), +  /* 80 */         727,         729,         731,         733, +  /* 84 */         735,         737,         739,         741, +  /* 88 */         743,         745,         747,         749, +  /* 8c */         751,         753,         755,         757, +  /* 90 */        1350,        1351,        1352,        1353, +  /* 94 */        1354,        1355,        1356,        1357, +  /* 98 */        1358,        1359,        1360,        1361, +  /* 9c */        1362,        1363,        1364,        1365, +  /* a0 */        1245,        1100,         131,        1670, +  /* a4 */        1375,        1376,  GROUP(202),  GROUP(207), +  /* a8 */        1244,        1099,        1305,        1675, +  /* ac */        1377,        1378,  GROUP(215),         694, +  /* b0 */         122,         123,         775,        1673, +  /* b4 */         772,         773,         940,         941, +  /* b8 */  GROUP(221),     INVALID,  GROUP(222),        1671, +  /* bc */        1659,        1660,         930,         931, +  /* c0 */        1473,        1474,  GROUP(223),         904, +  /* c4 */  GROUP(224),  GROUP(225),  GROUP(226),  GROUP(227), +  /* c8 */        1661,        1662,        1663,        1664, +  /* cc */        1665,        1666,        1667,        1668, +  /* d0 */  GROUP(236),  GROUP(237),  GROUP(238),  GROUP(239), +  /* d4 */  GROUP(240),  GROUP(241),  GROUP(242),  GROUP(243), +  /* d8 */  GROUP(244),  GROUP(245),  GROUP(246),  GROUP(247), +  /* dc */  GROUP(248),  GROUP(249),  GROUP(250),  GROUP(251), +  /* e0 */  GROUP(252),  GROUP(253),  GROUP(254),  GROUP(255), +  /* e4 */  GROUP(256),  GROUP(257),  GROUP(258),  GROUP(259), +  /* e8 */  GROUP(260),  GROUP(261),  GROUP(262),  GROUP(263), +  /* ec */  GROUP(264),  GROUP(265),  GROUP(266),  GROUP(267), +  /* f0 */  GROUP(268),  GROUP(269),  GROUP(270),  GROUP(271), +  /* f4 */  GROUP(272),  GROUP(273),  GROUP(274),  GROUP(275), +  /* f8 */  GROUP(277),  GROUP(278),  GROUP(279),  GROUP(280), +  /* fc */  GROUP(281),  GROUP(282),  GROUP(283),     INVALID, +}; + +static const uint16_t ud_itab__5[] = { +  /*  0 */        1384,        1406,         786,         798, +  /*  4 */        1453,        1454,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__6[] = { +  /*  0 */    GROUP(7),    GROUP(8), +}; + +static const uint16_t ud_itab__7[] = { +  /*  0 */        1374,        1383,         785,         774, +  /*  4 */        1385,     INVALID,         787,         719, +}; + +static const uint16_t ud_itab__8[] = { +  /*  0 */    GROUP(9),   GROUP(14),   GROUP(15),   GROUP(16), +  /*  4 */        1386,     INVALID,         788,   GROUP(25), +}; + +static const uint16_t ud_itab__9[] = { +  /*  0 */     INVALID,   GROUP(10),   GROUP(11),   GROUP(12), +  /*  4 */   GROUP(13),     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__10[] = { +  /*  0 */     INVALID,        1455,     INVALID, +}; + +static const uint16_t ud_itab__11[] = { +  /*  0 */     INVALID,        1461,     INVALID, +}; + +static const uint16_t ud_itab__12[] = { +  /*  0 */     INVALID,        1462,     INVALID, +}; + +static const uint16_t ud_itab__13[] = { +  /*  0 */     INVALID,        1463,     INVALID, +}; + +static const uint16_t ud_itab__14[] = { +  /*  0 */         824,         952,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__15[] = { +  /*  0 */        1485,        1508,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__16[] = { +  /*  0 */   GROUP(17),   GROUP(18),   GROUP(19),   GROUP(20), +  /*  4 */   GROUP(21),   GROUP(22),   GROUP(23),   GROUP(24), +}; + +static const uint16_t ud_itab__17[] = { +  /*  0 */        1466,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__18[] = { +  /*  0 */        1467,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__19[] = { +  /*  0 */        1468,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__20[] = { +  /*  0 */        1469,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__21[] = { +  /*  0 */        1397,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__22[] = { +  /*  0 */          80,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__23[] = { +  /*  0 */        1399,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__24[] = { +  /*  0 */         720,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__25[] = { +  /*  0 */        1425,   GROUP(26),     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__26[] = { +  /*  0 */        1298,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__27[] = { +  /*  0 */        1119,        1120,        1121,        1122, +  /*  4 */        1123,        1124,        1125,        1126, +}; + +static const uint16_t ud_itab__28[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  c */        1216,        1217,     INVALID,     INVALID, +  /* 10 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 14 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */        1218,        1219,     INVALID,     INVALID, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 2c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 5c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,        1220,     INVALID, +  /* 8c */     INVALID,     INVALID,        1221,     INVALID, +  /* 90 */        1222,     INVALID,     INVALID,     INVALID, +  /* 94 */        1223,     INVALID,        1224,        1225, +  /* 98 */     INVALID,     INVALID,        1226,     INVALID, +  /* 9c */     INVALID,     INVALID,        1227,     INVALID, +  /* a0 */        1228,     INVALID,     INVALID,     INVALID, +  /* a4 */        1229,     INVALID,        1230,        1231, +  /* a8 */     INVALID,     INVALID,        1232,     INVALID, +  /* ac */     INVALID,     INVALID,        1233,     INVALID, +  /* b0 */        1234,     INVALID,     INVALID,     INVALID, +  /* b4 */        1235,     INVALID,        1236,        1237, +  /* b8 */     INVALID,     INVALID,     INVALID,        1238, +  /* bc */     INVALID,     INVALID,     INVALID,        1239, +  /* c0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__29[] = { +  /*  0 */         936,         925,         928,         932, +}; + +static const uint16_t ud_itab__30[] = { +  /*  0 */         938,         926,         929,         934, +}; + +static const uint16_t ud_itab__31[] = { +  /*  0 */   GROUP(32),   GROUP(33), +}; + +static const uint16_t ud_itab__32[] = { +  /*  0 */         892,        1563,        1571,         888, +}; + +static const uint16_t ud_itab__33[] = { +  /*  0 */         896,        1561,        1569,     INVALID, +}; + +static const uint16_t ud_itab__34[] = { +  /*  0 */         894,     INVALID,     INVALID,         890, +}; + +static const uint16_t ud_itab__35[] = { +  /*  0 */        1449,     INVALID,     INVALID,        1451, +}; + +static const uint16_t ud_itab__36[] = { +  /*  0 */        1447,     INVALID,     INVALID,        1445, +}; + +static const uint16_t ud_itab__37[] = { +  /*  0 */   GROUP(38),   GROUP(39), +}; + +static const uint16_t ud_itab__38[] = { +  /*  0 */         882,     INVALID,        1567,         878, +}; + +static const uint16_t ud_itab__39[] = { +  /*  0 */         886,     INVALID,        1565,     INVALID, +}; + +static const uint16_t ud_itab__40[] = { +  /*  0 */         884,     INVALID,     INVALID,         880, +}; + +static const uint16_t ud_itab__41[] = { +  /*  0 */        1127,        1128,        1129,        1130, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__42[] = { +  /*  0 */         862,     INVALID,     INVALID,         858, +}; + +static const uint16_t ud_itab__43[] = { +  /*  0 */         864,     INVALID,     INVALID,         860, +}; + +static const uint16_t ud_itab__44[] = { +  /*  0 */         141,         152,         154,         142, +}; + +static const uint16_t ud_itab__45[] = { +  /*  0 */         907,     INVALID,     INVALID,         905, +}; + +static const uint16_t ud_itab__46[] = { +  /*  0 */         165,         166,         168,         162, +}; + +static const uint16_t ud_itab__47[] = { +  /*  0 */         147,         148,         158,         138, +}; + +static const uint16_t ud_itab__48[] = { +  /*  0 */        1442,     INVALID,     INVALID,        1440, +}; + +static const uint16_t ud_itab__49[] = { +  /*  0 */         129,     INVALID,     INVALID,         127, +}; + +static const uint16_t ud_itab__50[] = { +  /*  0 */        1427,   GROUP(51), +}; + +static const uint16_t ud_itab__51[] = { +  /*  0 */     INVALID,        1428,     INVALID, +}; + +static const uint16_t ud_itab__52[] = { +  /*  0 */        1429,   GROUP(53), +}; + +static const uint16_t ud_itab__53[] = { +  /*  0 */     INVALID,        1430,     INVALID, +}; + +static const uint16_t ud_itab__54[] = { +  /*  0 */   GROUP(67),   GROUP(68),   GROUP(63),   GROUP(64), +  /*  4 */   GROUP(65),   GROUP(66),   GROUP(86),   GROUP(90), +  /*  8 */   GROUP(69),   GROUP(70),   GROUP(71),   GROUP(72), +  /*  c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 10 */   GROUP(73),     INVALID,     INVALID,     INVALID, +  /* 14 */   GROUP(75),   GROUP(76),     INVALID,   GROUP(77), +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */   GROUP(78),   GROUP(79),   GROUP(80),     INVALID, +  /* 20 */   GROUP(81),   GROUP(82),   GROUP(83),   GROUP(84), +  /* 24 */   GROUP(85),  GROUP(108),     INVALID,     INVALID, +  /* 28 */   GROUP(87),   GROUP(88),   GROUP(89),   GROUP(74), +  /* 2c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 30 */   GROUP(91),   GROUP(92),   GROUP(93),   GROUP(94), +  /* 34 */   GROUP(95),   GROUP(96),     INVALID,   GROUP(97), +  /* 38 */   GROUP(98),   GROUP(99),  GROUP(100),  GROUP(101), +  /* 3c */  GROUP(102),  GROUP(103),  GROUP(104),  GROUP(105), +  /* 40 */  GROUP(106),  GROUP(107),     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 5c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */   GROUP(55),   GROUP(59),     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,  GROUP(109), +  /* dc */  GROUP(110),  GROUP(111),  GROUP(112),  GROUP(113), +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */  GROUP(114),  GROUP(115),     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__55[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,   GROUP(56), +}; + +static const uint16_t ud_itab__56[] = { +  /*  0 */   GROUP(57),   GROUP(58), +}; + +static const uint16_t ud_itab__57[] = { +  /*  0 */     INVALID,         717,     INVALID, +}; + +static const uint16_t ud_itab__58[] = { +  /*  0 */     INVALID,         718,     INVALID, +}; + +static const uint16_t ud_itab__59[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,   GROUP(60), +}; + +static const uint16_t ud_itab__60[] = { +  /*  0 */   GROUP(61),   GROUP(62), +}; + +static const uint16_t ud_itab__61[] = { +  /*  0 */     INVALID,         721,     INVALID, +}; + +static const uint16_t ud_itab__62[] = { +  /*  0 */     INVALID,         722,     INVALID, +}; + +static const uint16_t ud_itab__63[] = { +  /*  0 */        1588,     INVALID,     INVALID,        1589, +}; + +static const uint16_t ud_itab__64[] = { +  /*  0 */        1591,     INVALID,     INVALID,        1592, +}; + +static const uint16_t ud_itab__65[] = { +  /*  0 */        1594,     INVALID,     INVALID,        1595, +}; + +static const uint16_t ud_itab__66[] = { +  /*  0 */        1597,     INVALID,     INVALID,        1598, +}; + +static const uint16_t ud_itab__67[] = { +  /*  0 */        1582,     INVALID,     INVALID,        1583, +}; + +static const uint16_t ud_itab__68[] = { +  /*  0 */        1585,     INVALID,     INVALID,        1586, +}; + +static const uint16_t ud_itab__69[] = { +  /*  0 */        1606,     INVALID,     INVALID,        1607, +}; + +static const uint16_t ud_itab__70[] = { +  /*  0 */        1612,     INVALID,     INVALID,        1613, +}; + +static const uint16_t ud_itab__71[] = { +  /*  0 */        1609,     INVALID,     INVALID,        1610, +}; + +static const uint16_t ud_itab__72[] = { +  /*  0 */        1615,     INVALID,     INVALID,        1616, +}; + +static const uint16_t ud_itab__73[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1621, +}; + +static const uint16_t ud_itab__74[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1683, +}; + +static const uint16_t ud_itab__75[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1657, +}; + +static const uint16_t ud_itab__76[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1656, +}; + +static const uint16_t ud_itab__77[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1711, +}; + +static const uint16_t ud_itab__78[] = { +  /*  0 */        1573,     INVALID,     INVALID,        1574, +}; + +static const uint16_t ud_itab__79[] = { +  /*  0 */        1576,     INVALID,     INVALID,        1577, +}; + +static const uint16_t ud_itab__80[] = { +  /*  0 */        1579,     INVALID,     INVALID,        1580, +}; + +static const uint16_t ud_itab__81[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1685, +}; + +static const uint16_t ud_itab__82[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1687, +}; + +static const uint16_t ud_itab__83[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1689, +}; + +static const uint16_t ud_itab__84[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1691, +}; + +static const uint16_t ud_itab__85[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1693, +}; + +static const uint16_t ud_itab__86[] = { +  /*  0 */        1600,     INVALID,     INVALID,        1601, +}; + +static const uint16_t ud_itab__87[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1622, +}; + +static const uint16_t ud_itab__88[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1708, +}; + +static const uint16_t ud_itab__89[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1681, +}; + +static const uint16_t ud_itab__90[] = { +  /*  0 */        1603,     INVALID,     INVALID,        1604, +}; + +static const uint16_t ud_itab__91[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1696, +}; + +static const uint16_t ud_itab__92[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1698, +}; + +static const uint16_t ud_itab__93[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1700, +}; + +static const uint16_t ud_itab__94[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1702, +}; + +static const uint16_t ud_itab__95[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1704, +}; + +static const uint16_t ud_itab__96[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1706, +}; + +static const uint16_t ud_itab__97[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1717, +}; + +static const uint16_t ud_itab__98[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1624, +}; + +static const uint16_t ud_itab__99[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1626, +}; + +static const uint16_t ud_itab__100[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1628, +}; + +static const uint16_t ud_itab__101[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1630, +}; + +static const uint16_t ud_itab__102[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1632, +}; + +static const uint16_t ud_itab__103[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1634, +}; + +static const uint16_t ud_itab__104[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1638, +}; + +static const uint16_t ud_itab__105[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1636, +}; + +static const uint16_t ud_itab__106[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1640, +}; + +static const uint16_t ud_itab__107[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1642, +}; + +static const uint16_t ud_itab__108[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1695, +}; + +static const uint16_t ud_itab__109[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          45, +}; + +static const uint16_t ud_itab__110[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          41, +}; + +static const uint16_t ud_itab__111[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          43, +}; + +static const uint16_t ud_itab__112[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          37, +}; + +static const uint16_t ud_itab__113[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          39, +}; + +static const uint16_t ud_itab__114[] = { +  /*  0 */        1723,        1725,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__115[] = { +  /*  0 */        1724,        1726,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__116[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */  GROUP(117),  GROUP(118),  GROUP(119),  GROUP(120), +  /*  c */  GROUP(121),  GROUP(122),  GROUP(123),  GROUP(124), +  /* 10 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 14 */  GROUP(125),  GROUP(126),  GROUP(127),  GROUP(129), +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */  GROUP(130),  GROUP(131),  GROUP(132),     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 2c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */  GROUP(134),  GROUP(135),  GROUP(136),     INVALID, +  /* 44 */  GROUP(137),     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 5c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 60 */  GROUP(139),  GROUP(140),  GROUP(141),  GROUP(142), +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,  GROUP(138), +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__117[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1644, +}; + +static const uint16_t ud_itab__118[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1646, +}; + +static const uint16_t ud_itab__119[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1648, +}; + +static const uint16_t ud_itab__120[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1650, +}; + +static const uint16_t ud_itab__121[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1654, +}; + +static const uint16_t ud_itab__122[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1652, +}; + +static const uint16_t ud_itab__123[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1677, +}; + +static const uint16_t ud_itab__124[] = { +  /*  0 */        1618,     INVALID,     INVALID,        1619, +}; + +static const uint16_t ud_itab__125[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1045, +}; + +static const uint16_t ud_itab__126[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1056, +}; + +static const uint16_t ud_itab__127[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,  GROUP(128), +}; + +static const uint16_t ud_itab__128[] = { +  /*  0 */        1047,        1049,        1051, +}; + +static const uint16_t ud_itab__129[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,         201, +}; + +static const uint16_t ud_itab__130[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1058, +}; + +static const uint16_t ud_itab__131[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1557, +}; + +static const uint16_t ud_itab__132[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,  GROUP(133), +}; + +static const uint16_t ud_itab__133[] = { +  /*  0 */        1062,        1063,        1064, +}; + +static const uint16_t ud_itab__134[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,         197, +}; + +static const uint16_t ud_itab__135[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,         195, +}; + +static const uint16_t ud_itab__136[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1679, +}; + +static const uint16_t ud_itab__137[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1512, +}; + +static const uint16_t ud_itab__138[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,          47, +}; + +static const uint16_t ud_itab__139[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1715, +}; + +static const uint16_t ud_itab__140[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1713, +}; + +static const uint16_t ud_itab__141[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1721, +}; + +static const uint16_t ud_itab__142[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1719, +}; + +static const uint16_t ud_itab__143[] = { +  /*  0 */         900,     INVALID,     INVALID,         898, +}; + +static const uint16_t ud_itab__144[] = { +  /*  0 */        1387,        1391,        1393,        1389, +}; + +static const uint16_t ud_itab__145[] = { +  /*  0 */        1306,     INVALID,        1308,     INVALID, +}; + +static const uint16_t ud_itab__146[] = { +  /*  0 */        1291,     INVALID,        1293,     INVALID, +}; + +static const uint16_t ud_itab__147[] = { +  /*  0 */          61,     INVALID,     INVALID,          59, +}; + +static const uint16_t ud_itab__148[] = { +  /*  0 */          65,     INVALID,     INVALID,          63, +}; + +static const uint16_t ud_itab__149[] = { +  /*  0 */         976,     INVALID,     INVALID,         974, +}; + +static const uint16_t ud_itab__150[] = { +  /*  0 */        1499,     INVALID,     INVALID,        1497, +}; + +static const uint16_t ud_itab__151[] = { +  /*  0 */          27,          29,          31,          25, +}; + +static const uint16_t ud_itab__152[] = { +  /*  0 */         946,         948,         950,         944, +}; + +static const uint16_t ud_itab__153[] = { +  /*  0 */         145,         150,         156,         139, +}; + +static const uint16_t ud_itab__154[] = { +  /*  0 */         134,     INVALID,         163,         143, +}; + +static const uint16_t ud_itab__155[] = { +  /*  0 */        1419,        1421,        1423,        1417, +}; + +static const uint16_t ud_itab__156[] = { +  /*  0 */         818,         820,         822,         816, +}; + +static const uint16_t ud_itab__157[] = { +  /*  0 */         189,         191,         193,         187, +}; + +static const uint16_t ud_itab__158[] = { +  /*  0 */         802,         804,         806,         800, +}; + +static const uint16_t ud_itab__159[] = { +  /*  0 */        1209,     INVALID,     INVALID,        1207, +}; + +static const uint16_t ud_itab__160[] = { +  /*  0 */        1212,     INVALID,     INVALID,        1210, +}; + +static const uint16_t ud_itab__161[] = { +  /*  0 */        1215,     INVALID,     INVALID,        1213, +}; + +static const uint16_t ud_itab__162[] = { +  /*  0 */         987,     INVALID,     INVALID,         985, +}; + +static const uint16_t ud_itab__163[] = { +  /*  0 */        1038,     INVALID,     INVALID,        1036, +}; + +static const uint16_t ud_itab__164[] = { +  /*  0 */        1041,     INVALID,     INVALID,        1039, +}; + +static const uint16_t ud_itab__165[] = { +  /*  0 */        1044,     INVALID,     INVALID,        1042, +}; + +static const uint16_t ud_itab__166[] = { +  /*  0 */         993,     INVALID,     INVALID,         991, +}; + +static const uint16_t ud_itab__167[] = { +  /*  0 */        1200,     INVALID,     INVALID,        1198, +}; + +static const uint16_t ud_itab__168[] = { +  /*  0 */        1203,     INVALID,     INVALID,        1201, +}; + +static const uint16_t ud_itab__169[] = { +  /*  0 */        1206,     INVALID,     INVALID,        1204, +}; + +static const uint16_t ud_itab__170[] = { +  /*  0 */         990,     INVALID,     INVALID,         988, +}; + +static const uint16_t ud_itab__171[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1547, +}; + +static const uint16_t ud_itab__172[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1545, +}; + +static const uint16_t ud_itab__173[] = { +  /*  0 */  GROUP(174),     INVALID,     INVALID,  GROUP(175), +}; + +static const uint16_t ud_itab__174[] = { +  /*  0 */         866,         867,         910, +}; + +static const uint16_t ud_itab__175[] = { +  /*  0 */         868,         870,         911, +}; + +static const uint16_t ud_itab__176[] = { +  /*  0 */         920,     INVALID,        1522,        1517, +}; + +static const uint16_t ud_itab__177[] = { +  /*  0 */        1134,        1537,        1535,        1539, +}; + +static const uint16_t ud_itab__178[] = { +  /*  0 */     INVALID,     INVALID,  GROUP(179),     INVALID, +  /*  4 */  GROUP(180),     INVALID,  GROUP(181),     INVALID, +}; + +static const uint16_t ud_itab__179[] = { +  /*  0 */        1159,     INVALID,     INVALID,        1163, +}; + +static const uint16_t ud_itab__180[] = { +  /*  0 */        1152,     INVALID,     INVALID,        1150, +}; + +static const uint16_t ud_itab__181[] = { +  /*  0 */        1138,     INVALID,     INVALID,        1137, +}; + +static const uint16_t ud_itab__182[] = { +  /*  0 */     INVALID,     INVALID,  GROUP(183),     INVALID, +  /*  4 */  GROUP(184),     INVALID,  GROUP(185),     INVALID, +}; + +static const uint16_t ud_itab__183[] = { +  /*  0 */        1165,     INVALID,     INVALID,        1169, +}; + +static const uint16_t ud_itab__184[] = { +  /*  0 */        1153,     INVALID,     INVALID,        1157, +}; + +static const uint16_t ud_itab__185[] = { +  /*  0 */        1142,     INVALID,     INVALID,        1141, +}; + +static const uint16_t ud_itab__186[] = { +  /*  0 */     INVALID,     INVALID,  GROUP(187),  GROUP(188), +  /*  4 */     INVALID,     INVALID,  GROUP(189),  GROUP(190), +}; + +static const uint16_t ud_itab__187[] = { +  /*  0 */        1171,     INVALID,     INVALID,        1175, +}; + +static const uint16_t ud_itab__188[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1543, +}; + +static const uint16_t ud_itab__189[] = { +  /*  0 */        1146,     INVALID,     INVALID,        1145, +}; + +static const uint16_t ud_itab__190[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1541, +}; + +static const uint16_t ud_itab__191[] = { +  /*  0 */        1027,     INVALID,     INVALID,        1028, +}; + +static const uint16_t ud_itab__192[] = { +  /*  0 */        1030,     INVALID,     INVALID,        1031, +}; + +static const uint16_t ud_itab__193[] = { +  /*  0 */        1033,     INVALID,     INVALID,        1034, +}; + +static const uint16_t ud_itab__194[] = { +  /*  0 */     INVALID,        1464,     INVALID, +}; + +static const uint16_t ud_itab__195[] = { +  /*  0 */     INVALID,        1465,     INVALID, +}; + +static const uint16_t ud_itab__196[] = { +  /*  0 */     INVALID,        1551,     INVALID,        1549, +}; + +static const uint16_t ud_itab__197[] = { +  /*  0 */     INVALID,        1555,     INVALID,        1553, +}; + +static const uint16_t ud_itab__198[] = { +  /*  0 */  GROUP(199),     INVALID,         916,  GROUP(200), +}; + +static const uint16_t ud_itab__199[] = { +  /*  0 */         872,         873,         913, +}; + +static const uint16_t ud_itab__200[] = { +  /*  0 */         874,         876,         914, +}; + +static const uint16_t ud_itab__201[] = { +  /*  0 */         921,     INVALID,        1524,        1515, +}; + +static const uint16_t ud_itab__202[] = { +  /*  0 */     INVALID,  GROUP(203), +}; + +static const uint16_t ud_itab__203[] = { +  /*  0 */  GROUP(204),  GROUP(205),  GROUP(206),     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__204[] = { +  /*  0 */         825,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__205[] = { +  /*  0 */        1509,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__206[] = { +  /*  0 */        1510,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__207[] = { +  /*  0 */     INVALID,  GROUP(208), +}; + +static const uint16_t ud_itab__208[] = { +  /*  0 */  GROUP(209),  GROUP(210),  GROUP(211),  GROUP(212), +  /*  4 */  GROUP(213),  GROUP(214),     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__209[] = { +  /*  0 */        1511,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__210[] = { +  /*  0 */        1501,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__211[] = { +  /*  0 */        1502,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__212[] = { +  /*  0 */        1503,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__213[] = { +  /*  0 */        1504,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__214[] = { +  /*  0 */        1505,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__215[] = { +  /*  0 */  GROUP(216),  GROUP(217), +}; + +static const uint16_t ud_itab__216[] = { +  /*  0 */         683,         682,         768,        1400, +  /*  4 */        1507,        1506,     INVALID,          79, +}; + +static const uint16_t ud_itab__217[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,  GROUP(218),  GROUP(219),  GROUP(220), +}; + +static const uint16_t ud_itab__218[] = { +  /*  0 */         777,         778,         779,         780, +  /*  4 */         781,         782,         783,         784, +}; + +static const uint16_t ud_itab__219[] = { +  /*  0 */         808,         809,         810,         811, +  /*  4 */         812,         813,         814,         815, +}; + +static const uint16_t ud_itab__220[] = { +  /*  0 */        1366,        1367,        1368,        1369, +  /*  4 */        1370,        1371,        1372,        1373, +}; + +static const uint16_t ud_itab__221[] = { +  /*  0 */     INVALID,     INVALID,        1710,     INVALID, +}; + +static const uint16_t ud_itab__222[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */        1669,        1676,        1674,        1672, +}; + +static const uint16_t ud_itab__223[] = { +  /*  0 */         112,         117,         120,         110, +}; + +static const uint16_t ud_itab__224[] = { +  /*  0 */        1059,     INVALID,     INVALID,        1060, +}; + +static const uint16_t ud_itab__225[] = { +  /*  0 */        1055,     INVALID,     INVALID,        1053, +}; + +static const uint16_t ud_itab__226[] = { +  /*  0 */        1381,     INVALID,     INVALID,        1379, +}; + +static const uint16_t ud_itab__227[] = { +  /*  0 */  GROUP(228),  GROUP(235), +}; + +static const uint16_t ud_itab__228[] = { +  /*  0 */     INVALID,  GROUP(229),     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,  GROUP(230),  GROUP(234), +}; + +static const uint16_t ud_itab__229[] = { +  /*  0 */         124,         125,         126, +}; + +static const uint16_t ud_itab__230[] = { +  /*  0 */  GROUP(231),     INVALID,  GROUP(232),  GROUP(233), +}; + +static const uint16_t ud_itab__231[] = { +  /*  0 */     INVALID,        1459,     INVALID, +}; + +static const uint16_t ud_itab__232[] = { +  /*  0 */     INVALID,        1458,     INVALID, +}; + +static const uint16_t ud_itab__233[] = { +  /*  0 */     INVALID,        1457,     INVALID, +}; + +static const uint16_t ud_itab__234[] = { +  /*  0 */     INVALID,        1460,     INVALID, +}; + +static const uint16_t ud_itab__235[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,        1456,     INVALID, +}; + +static const uint16_t ud_itab__236[] = { +  /*  0 */     INVALID,          35,     INVALID,          33, +}; + +static const uint16_t ud_itab__237[] = { +  /*  0 */        1160,     INVALID,     INVALID,        1161, +}; + +static const uint16_t ud_itab__238[] = { +  /*  0 */        1166,     INVALID,     INVALID,        1167, +}; + +static const uint16_t ud_itab__239[] = { +  /*  0 */        1172,     INVALID,     INVALID,        1173, +}; + +static const uint16_t ud_itab__240[] = { +  /*  0 */        1527,     INVALID,     INVALID,        1528, +}; + +static const uint16_t ud_itab__241[] = { +  /*  0 */        1093,     INVALID,     INVALID,        1094, +}; + +static const uint16_t ud_itab__242[] = { +  /*  0 */     INVALID,        1521,        1526,         918, +}; + +static const uint16_t ud_itab__243[] = { +  /*  0 */        1086,     INVALID,     INVALID,        1084, +}; + +static const uint16_t ud_itab__244[] = { +  /*  0 */        1192,     INVALID,     INVALID,        1193, +}; + +static const uint16_t ud_itab__245[] = { +  /*  0 */        1195,     INVALID,     INVALID,        1196, +}; + +static const uint16_t ud_itab__246[] = { +  /*  0 */        1083,     INVALID,     INVALID,        1081, +}; + +static const uint16_t ud_itab__247[] = { +  /*  0 */        1017,     INVALID,     INVALID,        1015, +}; + +static const uint16_t ud_itab__248[] = { +  /*  0 */        1009,     INVALID,     INVALID,        1010, +}; + +static const uint16_t ud_itab__249[] = { +  /*  0 */        1012,     INVALID,     INVALID,        1013, +}; + +static const uint16_t ud_itab__250[] = { +  /*  0 */        1075,     INVALID,     INVALID,        1076, +}; + +static const uint16_t ud_itab__251[] = { +  /*  0 */        1020,     INVALID,     INVALID,        1018, +}; + +static const uint16_t ud_itab__252[] = { +  /*  0 */        1023,     INVALID,     INVALID,        1021, +}; + +static const uint16_t ud_itab__253[] = { +  /*  0 */        1147,     INVALID,     INVALID,        1148, +}; + +static const uint16_t ud_itab__254[] = { +  /*  0 */        1156,     INVALID,     INVALID,        1154, +}; + +static const uint16_t ud_itab__255[] = { +  /*  0 */        1026,     INVALID,     INVALID,        1024, +}; + +static const uint16_t ud_itab__256[] = { +  /*  0 */        1087,     INVALID,     INVALID,        1088, +}; + +static const uint16_t ud_itab__257[] = { +  /*  0 */        1092,     INVALID,     INVALID,        1090, +}; + +static const uint16_t ud_itab__258[] = { +  /*  0 */     INVALID,         136,         132,         160, +}; + +static const uint16_t ud_itab__259[] = { +  /*  0 */         909,     INVALID,     INVALID,         902, +}; + +static const uint16_t ud_itab__260[] = { +  /*  0 */        1186,     INVALID,     INVALID,        1187, +}; + +static const uint16_t ud_itab__261[] = { +  /*  0 */        1189,     INVALID,     INVALID,        1190, +}; + +static const uint16_t ud_itab__262[] = { +  /*  0 */        1080,     INVALID,     INVALID,        1078, +}; + +static const uint16_t ud_itab__263[] = { +  /*  0 */        1118,     INVALID,     INVALID,        1116, +}; + +static const uint16_t ud_itab__264[] = { +  /*  0 */        1003,     INVALID,     INVALID,        1004, +}; + +static const uint16_t ud_itab__265[] = { +  /*  0 */        1006,     INVALID,     INVALID,        1007, +}; + +static const uint16_t ud_itab__266[] = { +  /*  0 */        1074,     INVALID,     INVALID,        1072, +}; + +static const uint16_t ud_itab__267[] = { +  /*  0 */        1266,     INVALID,     INVALID,        1264, +}; + +static const uint16_t ud_itab__268[] = { +  /*  0 */     INVALID,        1559,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__269[] = { +  /*  0 */        1136,     INVALID,     INVALID,        1135, +}; + +static const uint16_t ud_itab__270[] = { +  /*  0 */        1140,     INVALID,     INVALID,        1139, +}; + +static const uint16_t ud_itab__271[] = { +  /*  0 */        1144,     INVALID,     INVALID,        1143, +}; + +static const uint16_t ud_itab__272[] = { +  /*  0 */        1533,     INVALID,     INVALID,        1534, +}; + +static const uint16_t ud_itab__273[] = { +  /*  0 */        1069,     INVALID,     INVALID,        1070, +}; + +static const uint16_t ud_itab__274[] = { +  /*  0 */        1133,     INVALID,     INVALID,        1131, +}; + +static const uint16_t ud_itab__275[] = { +  /*  0 */     INVALID,  GROUP(276), +}; + +static const uint16_t ud_itab__276[] = { +  /*  0 */         799,     INVALID,     INVALID,        1519, +}; + +static const uint16_t ud_itab__277[] = { +  /*  0 */        1179,     INVALID,     INVALID,        1177, +}; + +static const uint16_t ud_itab__278[] = { +  /*  0 */        1182,     INVALID,     INVALID,        1180, +}; + +static const uint16_t ud_itab__279[] = { +  /*  0 */        1183,     INVALID,     INVALID,        1184, +}; + +static const uint16_t ud_itab__280[] = { +  /*  0 */        1532,     INVALID,     INVALID,        1530, +}; + +static const uint16_t ud_itab__281[] = { +  /*  0 */         996,     INVALID,     INVALID,         994, +}; + +static const uint16_t ud_itab__282[] = { +  /*  0 */         997,     INVALID,     INVALID,         998, +}; + +static const uint16_t ud_itab__283[] = { +  /*  0 */        1000,     INVALID,     INVALID,        1001, +}; + +static const uint16_t ud_itab__284[] = { +  /*  0 */        1242,     INVALID, +}; + +static const uint16_t ud_itab__285[] = { +  /*  0 */        1097,     INVALID, +}; + +static const uint16_t ud_itab__286[] = { +  /*  0 */        1243,     INVALID, +}; + +static const uint16_t ud_itab__287[] = { +  /*  0 */        1098,     INVALID, +}; + +static const uint16_t ud_itab__288[] = { +  /*  0 */         173,     INVALID, +}; + +static const uint16_t ud_itab__289[] = { +  /*  0 */         174,     INVALID, +}; + +static const uint16_t ud_itab__290[] = { +  /*  0 */           1,     INVALID, +}; + +static const uint16_t ud_itab__291[] = { +  /*  0 */           4,     INVALID, +}; + +static const uint16_t ud_itab__292[] = { +  /*  0 */  GROUP(293),  GROUP(294),     INVALID, +}; + +static const uint16_t ud_itab__293[] = { +  /*  0 */        1257,     INVALID, +}; + +static const uint16_t ud_itab__294[] = { +  /*  0 */        1258,     INVALID, +}; + +static const uint16_t ud_itab__295[] = { +  /*  0 */  GROUP(296),  GROUP(297),     INVALID, +}; + +static const uint16_t ud_itab__296[] = { +  /*  0 */        1110,     INVALID, +}; + +static const uint16_t ud_itab__297[] = { +  /*  0 */        1111,     INVALID, +}; + +static const uint16_t ud_itab__298[] = { +  /*  0 */        1658,     INVALID, +}; + +static const uint16_t ud_itab__299[] = { +  /*  0 */          67,          68, +}; + +static const uint16_t ud_itab__300[] = { +  /*  0 */         710,         711,     INVALID, +}; + +static const uint16_t ud_itab__301[] = { +  /*  0 */         983,         984,     INVALID, +}; + +static const uint16_t ud_itab__302[] = { +  /*  0 */          21,         970,          11,        1342, +  /*  4 */          55,        1413,        1493,         106, +}; + +static const uint16_t ud_itab__303[] = { +  /*  0 */          23,         971,          13,        1343, +  /*  4 */          57,        1414,        1494,         108, +}; + +static const uint16_t ud_itab__304[] = { +  /*  0 */  GROUP(305),  GROUP(306),  GROUP(307),  GROUP(308), +  /*  4 */  GROUP(309),  GROUP(310),  GROUP(311),  GROUP(312), +}; + +static const uint16_t ud_itab__305[] = { +  /*  0 */          22,     INVALID, +}; + +static const uint16_t ud_itab__306[] = { +  /*  0 */         972,     INVALID, +}; + +static const uint16_t ud_itab__307[] = { +  /*  0 */          12,     INVALID, +}; + +static const uint16_t ud_itab__308[] = { +  /*  0 */        1344,     INVALID, +}; + +static const uint16_t ud_itab__309[] = { +  /*  0 */          56,     INVALID, +}; + +static const uint16_t ud_itab__310[] = { +  /*  0 */        1415,     INVALID, +}; + +static const uint16_t ud_itab__311[] = { +  /*  0 */        1495,     INVALID, +}; + +static const uint16_t ud_itab__312[] = { +  /*  0 */         107,     INVALID, +}; + +static const uint16_t ud_itab__313[] = { +  /*  0 */          24,         973,          14,        1345, +  /*  4 */          58,        1416,        1496,         109, +}; + +static const uint16_t ud_itab__314[] = { +  /*  0 */        1109,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__315[] = { +  /*  0 */          74,          75,          76, +}; + +static const uint16_t ud_itab__316[] = { +  /*  0 */         170,         171,         172, +}; + +static const uint16_t ud_itab__317[] = { +  /*  0 */          73,     INVALID, +}; + +static const uint16_t ud_itab__318[] = { +  /*  0 */  GROUP(319),  GROUP(320),  GROUP(321), +}; + +static const uint16_t ud_itab__319[] = { +  /*  0 */        1259,        1260, +}; + +static const uint16_t ud_itab__320[] = { +  /*  0 */        1261,        1262, +}; + +static const uint16_t ud_itab__321[] = { +  /*  0 */     INVALID,        1263, +}; + +static const uint16_t ud_itab__322[] = { +  /*  0 */  GROUP(323),  GROUP(324),  GROUP(325), +}; + +static const uint16_t ud_itab__323[] = { +  /*  0 */        1112,     INVALID, +}; + +static const uint16_t ud_itab__324[] = { +  /*  0 */        1113,        1114, +}; + +static const uint16_t ud_itab__325[] = { +  /*  0 */     INVALID,        1115, +}; + +static const uint16_t ud_itab__326[] = { +  /*  0 */         923,         924,         927, +}; + +static const uint16_t ud_itab__327[] = { +  /*  0 */         115,         116,         119, +}; + +static const uint16_t ud_itab__328[] = { +  /*  0 */        1403,        1404,        1405, +}; + +static const uint16_t ud_itab__329[] = { +  /*  0 */         791,         792,         793, +}; + +static const uint16_t ud_itab__330[] = { +  /*  0 */        1347,        1348,        1349, +}; + +static const uint16_t ud_itab__331[] = { +  /*  0 */        1279,        1286,        1267,        1275, +  /*  4 */        1327,        1334,        1318,        1313, +}; + +static const uint16_t ud_itab__332[] = { +  /*  0 */        1284,        1287,        1268,        1274, +  /*  4 */        1323,        1330,        1319,        1315, +}; + +static const uint16_t ud_itab__333[] = { +  /*  0 */  GROUP(334),  GROUP(335),     INVALID,     INVALID, +  /*  4 */     INVALID,  GROUP(341),  GROUP(357),  GROUP(369), +  /*  8 */     INVALID,  GROUP(394),     INVALID,     INVALID, +  /*  c */     INVALID,  GROUP(399),     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__334[] = { +  /*  0 */         771,     INVALID, +}; + +static const uint16_t ud_itab__335[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 10 */         937,         939,  GROUP(336),         895, +  /* 14 */        1450,        1448,  GROUP(337),         885, +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */         863,         865,     INVALID,         908, +  /* 2c */     INVALID,     INVALID,        1443,         130, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */         901,        1388,        1307,        1292, +  /* 54 */          62,          66,         977,        1500, +  /* 58 */          28,         947,         146,         135, +  /* 5c */        1420,         819,         190,         803, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,  GROUP(340), +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,  GROUP(338),     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,         113,     INVALID, +  /* c4 */     INVALID,     INVALID,        1382,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__336[] = { +  /*  0 */         893,         897, +}; + +static const uint16_t ud_itab__337[] = { +  /*  0 */         883,         887, +}; + +static const uint16_t ud_itab__338[] = { +  /*  0 */  GROUP(339),     INVALID, +}; + +static const uint16_t ud_itab__339[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,        1401, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__340[] = { +  /*  0 */        1742,        1743, +}; + +static const uint16_t ud_itab__341[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 10 */         933,         935,  GROUP(342),         891, +  /* 14 */        1452,        1446,  GROUP(343),         881, +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */         859,         861,     INVALID,         906, +  /* 2c */     INVALID,     INVALID,        1441,         128, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */         899,        1390,     INVALID,     INVALID, +  /* 54 */          60,          64,         975,        1498, +  /* 58 */          26,         945,         140,         144, +  /* 5c */        1418,         817,         188,         801, +  /* 60 */        1208,        1211,        1214,         986, +  /* 64 */        1037,        1040,        1043,         992, +  /* 68 */        1199,        1202,        1205,         989, +  /* 6c */        1548,        1546,  GROUP(344),        1518, +  /* 70 */        1540,  GROUP(345),  GROUP(347),  GROUP(349), +  /* 74 */        1029,        1032,        1035,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */        1550,        1554,  GROUP(351),        1516, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,         111,     INVALID, +  /* c4 */        1061,        1054,        1380,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */          34,        1162,        1168,        1174, +  /* d4 */        1529,        1095,         919,  GROUP(352), +  /* d8 */        1194,        1197,        1082,        1016, +  /* dc */        1011,        1014,        1077,        1019, +  /* e0 */        1022,        1149,        1155,        1025, +  /* e4 */        1089,        1091,         161,         903, +  /* e8 */        1188,        1191,        1079,        1117, +  /* ec */        1005,        1008,        1073,        1265, +  /* f0 */     INVALID,  GROUP(353),  GROUP(354),  GROUP(355), +  /* f4 */     INVALID,        1071,        1132,  GROUP(356), +  /* f8 */        1178,        1181,        1185,        1531, +  /* fc */         995,         999,        1002,     INVALID, +}; + +static const uint16_t ud_itab__342[] = { +  /*  0 */         889,     INVALID, +}; + +static const uint16_t ud_itab__343[] = { +  /*  0 */         879,     INVALID, +}; + +static const uint16_t ud_itab__344[] = { +  /*  0 */         869,         871,         912, +}; + +static const uint16_t ud_itab__345[] = { +  /*  0 */     INVALID,     INVALID,        1164,     INVALID, +  /*  4 */        1151,     INVALID,  GROUP(346),     INVALID, +}; + +static const uint16_t ud_itab__346[] = { +  /*  0 */        1756,     INVALID, +}; + +static const uint16_t ud_itab__347[] = { +  /*  0 */     INVALID,     INVALID,        1170,     INVALID, +  /*  4 */        1158,     INVALID,  GROUP(348),     INVALID, +}; + +static const uint16_t ud_itab__348[] = { +  /*  0 */        1758,     INVALID, +}; + +static const uint16_t ud_itab__349[] = { +  /*  0 */     INVALID,     INVALID,        1176,        1544, +  /*  4 */     INVALID,     INVALID,  GROUP(350),        1542, +}; + +static const uint16_t ud_itab__350[] = { +  /*  0 */        1760,     INVALID, +}; + +static const uint16_t ud_itab__351[] = { +  /*  0 */         875,         877,         915, +}; + +static const uint16_t ud_itab__352[] = { +  /*  0 */        1085,     INVALID, +}; + +static const uint16_t ud_itab__353[] = { +  /*  0 */        1755,     INVALID, +}; + +static const uint16_t ud_itab__354[] = { +  /*  0 */        1757,     INVALID, +}; + +static const uint16_t ud_itab__355[] = { +  /*  0 */        1759,     INVALID, +}; + +static const uint16_t ud_itab__356[] = { +  /*  0 */     INVALID,        1520, +}; + +static const uint16_t ud_itab__357[] = { +  /*  0 */        1584,        1587,        1590,        1593, +  /*  4 */        1596,        1599,        1602,        1605, +  /*  8 */        1608,        1614,        1611,        1617, +  /*  c */  GROUP(358),  GROUP(359),  GROUP(360),  GROUP(361), +  /* 10 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 14 */     INVALID,     INVALID,     INVALID,        1712, +  /* 18 */  GROUP(362),  GROUP(363),     INVALID,     INVALID, +  /* 1c */        1575,        1578,        1581,     INVALID, +  /* 20 */        1686,        1688,        1690,        1692, +  /* 24 */        1694,     INVALID,     INVALID,     INVALID, +  /* 28 */        1623,        1709,        1682,        1684, +  /* 2c */  GROUP(365),  GROUP(366),  GROUP(367),  GROUP(368), +  /* 30 */        1697,        1699,        1701,        1703, +  /* 34 */        1705,        1707,     INVALID,        1718, +  /* 38 */        1625,        1627,        1629,        1631, +  /* 3c */        1633,        1635,        1639,        1637, +  /* 40 */        1641,        1643,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 5c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,          46, +  /* dc */          42,          44,          38,          40, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__358[] = { +  /*  0 */        1737,     INVALID, +}; + +static const uint16_t ud_itab__359[] = { +  /*  0 */        1735,     INVALID, +}; + +static const uint16_t ud_itab__360[] = { +  /*  0 */        1740,     INVALID, +}; + +static const uint16_t ud_itab__361[] = { +  /*  0 */        1741,     INVALID, +}; + +static const uint16_t ud_itab__362[] = { +  /*  0 */        1727,     INVALID, +}; + +static const uint16_t ud_itab__363[] = { +  /*  0 */  GROUP(364),     INVALID, +}; + +static const uint16_t ud_itab__364[] = { +  /*  0 */     INVALID,        1728, +}; + +static const uint16_t ud_itab__365[] = { +  /*  0 */        1731,     INVALID, +}; + +static const uint16_t ud_itab__366[] = { +  /*  0 */        1733,     INVALID, +}; + +static const uint16_t ud_itab__367[] = { +  /*  0 */        1732,     INVALID, +}; + +static const uint16_t ud_itab__368[] = { +  /*  0 */        1734,     INVALID, +}; + +static const uint16_t ud_itab__369[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */  GROUP(370),  GROUP(371),  GROUP(372),     INVALID, +  /*  8 */        1645,        1647,        1649,        1651, +  /*  c */        1655,        1653,        1678,        1620, +  /* 10 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 14 */  GROUP(374),        1057,  GROUP(375),         202, +  /* 18 */  GROUP(379),  GROUP(381),     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */  GROUP(383),        1558,  GROUP(385),     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 2c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */         198,         196,        1680,     INVALID, +  /* 44 */        1513,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,  GROUP(391),  GROUP(392), +  /* 4c */  GROUP(393),     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 5c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 60 */        1716,        1714,        1722,        1720, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,          48, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__370[] = { +  /*  0 */        1738,     INVALID, +}; + +static const uint16_t ud_itab__371[] = { +  /*  0 */        1736,     INVALID, +}; + +static const uint16_t ud_itab__372[] = { +  /*  0 */  GROUP(373),     INVALID, +}; + +static const uint16_t ud_itab__373[] = { +  /*  0 */     INVALID,        1739, +}; + +static const uint16_t ud_itab__374[] = { +  /*  0 */        1046,     INVALID, +}; + +static const uint16_t ud_itab__375[] = { +  /*  0 */  GROUP(376),  GROUP(377),  GROUP(378), +}; + +static const uint16_t ud_itab__376[] = { +  /*  0 */        1048,     INVALID, +}; + +static const uint16_t ud_itab__377[] = { +  /*  0 */        1050,     INVALID, +}; + +static const uint16_t ud_itab__378[] = { +  /*  0 */     INVALID,        1052, +}; + +static const uint16_t ud_itab__379[] = { +  /*  0 */  GROUP(380),     INVALID, +}; + +static const uint16_t ud_itab__380[] = { +  /*  0 */     INVALID,        1730, +}; + +static const uint16_t ud_itab__381[] = { +  /*  0 */  GROUP(382),     INVALID, +}; + +static const uint16_t ud_itab__382[] = { +  /*  0 */     INVALID,        1729, +}; + +static const uint16_t ud_itab__383[] = { +  /*  0 */  GROUP(384),     INVALID, +}; + +static const uint16_t ud_itab__384[] = { +  /*  0 */        1065,     INVALID, +}; + +static const uint16_t ud_itab__385[] = { +  /*  0 */  GROUP(386),  GROUP(388), +}; + +static const uint16_t ud_itab__386[] = { +  /*  0 */  GROUP(387),     INVALID, +}; + +static const uint16_t ud_itab__387[] = { +  /*  0 */        1066,     INVALID, +}; + +static const uint16_t ud_itab__388[] = { +  /*  0 */  GROUP(389),  GROUP(390), +}; + +static const uint16_t ud_itab__389[] = { +  /*  0 */        1067,     INVALID, +}; + +static const uint16_t ud_itab__390[] = { +  /*  0 */        1068,     INVALID, +}; + +static const uint16_t ud_itab__391[] = { +  /*  0 */        1745,     INVALID, +}; + +static const uint16_t ud_itab__392[] = { +  /*  0 */        1744,     INVALID, +}; + +static const uint16_t ud_itab__393[] = { +  /*  0 */        1754,     INVALID, +}; + +static const uint16_t ud_itab__394[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 10 */  GROUP(395),  GROUP(396),  GROUP(397),     INVALID, +  /* 14 */     INVALID,     INVALID,  GROUP(398),     INVALID, +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,     INVALID,         155,     INVALID, +  /* 2c */         169,         159,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,        1394,        1309,        1294, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */          32,         951,         157,         164, +  /* 5c */        1424,         823,         194,         807, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,        1523, +  /* 70 */        1536,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */     INVALID,     INVALID,         917,        1525, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,         121,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,         133,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__395[] = { +  /*  0 */        1751,        1750, +}; + +static const uint16_t ud_itab__396[] = { +  /*  0 */        1753,        1752, +}; + +static const uint16_t ud_itab__397[] = { +  /*  0 */        1572,        1570, +}; + +static const uint16_t ud_itab__398[] = { +  /*  0 */        1568,        1566, +}; + +static const uint16_t ud_itab__399[] = { +  /*  0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /*  c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 10 */  GROUP(402),  GROUP(400),  GROUP(401),     INVALID, +  /* 14 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 18 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,     INVALID,         153,     INVALID, +  /* 2c */         167,         149,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 40 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 44 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 48 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 4c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 50 */     INVALID,        1392,     INVALID,     INVALID, +  /* 54 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 58 */          30,         949,         151,     INVALID, +  /* 5c */        1422,         821,         192,         805, +  /* 60 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 64 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 68 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 6c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 70 */        1538,     INVALID,     INVALID,     INVALID, +  /* 74 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 78 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 7c */        1552,        1556,     INVALID,     INVALID, +  /* 80 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 84 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 88 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 8c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 90 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 94 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 98 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 9c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* a8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ac */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* b8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* bc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c0 */     INVALID,     INVALID,         118,     INVALID, +  /* c4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* c8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* cc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d0 */          36,     INVALID,     INVALID,     INVALID, +  /* d4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* d8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* dc */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e0 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* e4 */     INVALID,     INVALID,         137,     INVALID, +  /* e8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* ec */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f0 */        1560,     INVALID,     INVALID,     INVALID, +  /* f4 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* f8 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* fc */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__400[] = { +  /*  0 */        1749,        1748, +}; + +static const uint16_t ud_itab__401[] = { +  /*  0 */        1564,        1562, +}; + +static const uint16_t ud_itab__402[] = { +  /*  0 */        1747,        1746, +}; + +static const uint16_t ud_itab__403[] = { +  /*  0 */  GROUP(404),  GROUP(335),     INVALID,     INVALID, +  /*  4 */     INVALID,  GROUP(341),  GROUP(357),  GROUP(369), +  /*  8 */     INVALID,  GROUP(394),     INVALID,     INVALID, +  /*  c */     INVALID,  GROUP(399),     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__404[] = { +  /*  0 */         769,     INVALID, +}; + +static const uint16_t ud_itab__405[] = { +  /*  0 */         826,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__406[] = { +  /*  0 */         827,     INVALID,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__407[] = { +  /*  0 */         715,     INVALID, +}; + +static const uint16_t ud_itab__408[] = { +  /*  0 */         723,         724,         725, +}; + +static const uint16_t ud_itab__409[] = { +  /*  0 */        1280,        1285,        1269,        1273, +  /*  4 */        1326,        1333,        1320,        1314, +}; + +static const uint16_t ud_itab__410[] = { +  /*  0 */        1281,        1288,        1272,        1276, +  /*  4 */        1325,        1332,        1329,        1312, +}; + +static const uint16_t ud_itab__411[] = { +  /*  0 */        1282,        1289,        1270,        1277, +  /*  4 */        1324,        1331,        1321,        1316, +}; + +static const uint16_t ud_itab__412[] = { +  /*  0 */        1283,        1290,        1271,        1278, +  /*  4 */        1328,        1335,        1322,        1317, +}; + +static const uint16_t ud_itab__413[] = { +  /*  0 */           3,     INVALID, +}; + +static const uint16_t ud_itab__414[] = { +  /*  0 */           2,     INVALID, +}; + +static const uint16_t ud_itab__415[] = { +  /*  0 */        1311,     INVALID, +}; + +static const uint16_t ud_itab__416[] = { +  /*  0 */  GROUP(417),  GROUP(418), +}; + +static const uint16_t ud_itab__417[] = { +  /*  0 */         206,         503,         307,         357, +  /*  4 */         587,         630,         387,         413, +}; + +static const uint16_t ud_itab__418[] = { +  /*  0 */         215,         216,         217,         218, +  /*  4 */         219,         220,         221,         222, +  /*  8 */         504,         505,         506,         507, +  /*  c */         508,         509,         510,         511, +  /* 10 */         309,         310,         311,         312, +  /* 14 */         313,         314,         315,         316, +  /* 18 */         359,         360,         361,         362, +  /* 1c */         363,         364,         365,         366, +  /* 20 */         589,         590,         591,         592, +  /* 24 */         593,         594,         595,         596, +  /* 28 */         614,         615,         616,         617, +  /* 2c */         618,         619,         620,         621, +  /* 30 */         388,         389,         390,         391, +  /* 34 */         392,         393,         394,         395, +  /* 38 */         414,         415,         416,         417, +  /* 3c */         418,         419,         420,         421, +}; + +static const uint16_t ud_itab__419[] = { +  /*  0 */  GROUP(420),  GROUP(421), +}; + +static const uint16_t ud_itab__420[] = { +  /*  0 */         476,     INVALID,         573,         540, +  /*  4 */         493,         492,         584,         583, +}; + +static const uint16_t ud_itab__421[] = { +  /*  0 */         477,         478,         479,         480, +  /*  4 */         481,         482,         483,         484, +  /*  8 */         658,         659,         660,         661, +  /*  c */         662,         663,         664,         665, +  /* 10 */         522,     INVALID,     INVALID,     INVALID, +  /* 14 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 18 */         549,         550,         551,         552, +  /* 1c */         553,         554,         555,         556, +  /* 20 */         233,         204,     INVALID,     INVALID, +  /* 24 */         639,         657,     INVALID,     INVALID, +  /* 28 */         485,         486,         487,         488, +  /* 2c */         489,         490,         491,     INVALID, +  /* 30 */         203,         685,         529,         526, +  /* 34 */         684,         528,         377,         454, +  /* 38 */         527,         686,         537,         536, +  /* 3c */         530,         534,         535,         376, +}; + +static const uint16_t ud_itab__422[] = { +  /*  0 */  GROUP(423),  GROUP(424), +}; + +static const uint16_t ud_itab__423[] = { +  /*  0 */         456,         520,         448,         450, +  /*  4 */         462,         464,         460,         458, +}; + +static const uint16_t ud_itab__424[] = { +  /*  0 */         235,         236,         237,         238, +  /*  4 */         239,         240,         241,         242, +  /*  8 */         243,         244,         245,         246, +  /*  c */         247,         248,         249,         250, +  /* 10 */         251,         252,         253,         254, +  /* 14 */         255,         256,         257,         258, +  /* 18 */         259,         260,         261,         262, +  /* 1c */         263,         264,         265,         266, +  /* 20 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */     INVALID,         656,     INVALID,     INVALID, +  /* 2c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__425[] = { +  /*  0 */  GROUP(426),  GROUP(427), +}; + +static const uint16_t ud_itab__426[] = { +  /*  0 */         453,         471,         467,         470, +  /*  4 */     INVALID,         474,     INVALID,         538, +}; + +static const uint16_t ud_itab__427[] = { +  /*  0 */         267,         268,         269,         270, +  /*  4 */         271,         272,         273,         274, +  /*  8 */         275,         276,         277,         278, +  /*  c */         279,         280,         281,         282, +  /* 10 */         283,         284,         285,         286, +  /* 14 */         287,         288,         289,         290, +  /* 18 */         291,         292,         293,         294, +  /* 1c */         295,         296,         297,         298, +  /* 20 */         524,         523,         234,         455, +  /* 24 */         525,         532,     INVALID,     INVALID, +  /* 28 */         299,         300,         301,         302, +  /* 2c */         303,         304,         305,         306, +  /* 30 */         333,         334,         335,         336, +  /* 34 */         337,         338,         339,         340, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__428[] = { +  /*  0 */  GROUP(429),  GROUP(430), +}; + +static const uint16_t ud_itab__429[] = { +  /*  0 */         205,         494,         308,         358, +  /*  4 */         588,         613,         378,         404, +}; + +static const uint16_t ud_itab__430[] = { +  /*  0 */         207,         208,         209,         210, +  /*  4 */         211,         212,         213,         214, +  /*  8 */         495,         496,         497,         498, +  /*  c */         499,         500,         501,         502, +  /* 10 */         317,         318,         319,         320, +  /* 14 */         321,         322,         323,         324, +  /* 18 */         325,         326,         327,         328, +  /* 1c */         329,         330,         331,         332, +  /* 20 */         622,         623,         624,         625, +  /* 24 */         626,         627,         628,         629, +  /* 28 */         597,         598,         599,         600, +  /* 2c */         601,         602,         603,         604, +  /* 30 */         405,         406,         407,         408, +  /* 34 */         409,         410,         411,         412, +  /* 38 */         379,         380,         381,         382, +  /* 3c */         383,         384,         385,         386, +}; + +static const uint16_t ud_itab__431[] = { +  /*  0 */  GROUP(432),  GROUP(433), +}; + +static const uint16_t ud_itab__432[] = { +  /*  0 */         475,         472,         574,         539, +  /*  4 */         531,     INVALID,         533,         585, +}; + +static const uint16_t ud_itab__433[] = { +  /*  0 */         431,         432,         433,         434, +  /*  4 */         435,         436,         437,         438, +  /*  8 */         666,         667,         668,         669, +  /*  c */         670,         671,         672,         673, +  /* 10 */         575,         576,         577,         578, +  /* 14 */         579,         580,         581,         582, +  /* 18 */         541,         542,         543,         544, +  /* 1c */         545,         546,         547,         548, +  /* 20 */         640,         641,         642,         643, +  /* 24 */         644,         645,         646,         647, +  /* 28 */         648,         649,         650,         651, +  /* 2c */         652,         653,         654,         655, +  /* 30 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 34 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__434[] = { +  /*  0 */  GROUP(435),  GROUP(436), +}; + +static const uint16_t ud_itab__435[] = { +  /*  0 */         457,         521,         447,         449, +  /*  4 */         463,         465,         461,         459, +}; + +static const uint16_t ud_itab__436[] = { +  /*  0 */         223,         224,         225,         226, +  /*  4 */         227,         228,         229,         230, +  /*  8 */         512,         513,         514,         515, +  /*  c */         516,         517,         518,         519, +  /* 10 */         367,         368,         369,         370, +  /* 14 */         371,         372,         373,         374, +  /* 18 */     INVALID,         375,     INVALID,     INVALID, +  /* 1c */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 20 */         631,         632,         633,         634, +  /* 24 */         635,         636,         637,         638, +  /* 28 */         605,         606,         607,         608, +  /* 2c */         609,         610,         611,         612, +  /* 30 */         422,         423,         424,         425, +  /* 34 */         426,         427,         428,         429, +  /* 38 */         396,         397,         398,         399, +  /* 3c */         400,         401,         402,         403, +}; + +static const uint16_t ud_itab__437[] = { +  /*  0 */  GROUP(438),  GROUP(439), +}; + +static const uint16_t ud_itab__438[] = { +  /*  0 */         451,         473,         466,         468, +  /*  4 */         231,         452,         232,         469, +}; + +static const uint16_t ud_itab__439[] = { +  /*  0 */         439,         440,         441,         442, +  /*  4 */         443,         444,         445,         446, +  /*  8 */         674,         675,         676,         677, +  /*  c */         678,         679,         680,         681, +  /* 10 */         557,         558,         559,         560, +  /* 14 */         561,         562,         563,         564, +  /* 18 */         565,         566,         567,         568, +  /* 1c */         569,         570,         571,         572, +  /* 20 */         586,     INVALID,     INVALID,     INVALID, +  /* 24 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 28 */         341,         342,         343,         344, +  /* 2c */         345,         346,         347,         348, +  /* 30 */         349,         350,         351,         352, +  /* 34 */         353,         354,         355,         356, +  /* 38 */     INVALID,     INVALID,     INVALID,     INVALID, +  /* 3c */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__440[] = { +  /*  0 */         758,         759,         760, +}; + +static const uint16_t ud_itab__441[] = { +  /*  0 */         764,     INVALID, +}; + +static const uint16_t ud_itab__442[] = { +  /*  0 */        1432,        1437,         962,         953, +  /*  4 */         942,         695,         186,         689, +}; + +static const uint16_t ud_itab__443[] = { +  /*  0 */        1438,        1439,         963,         954, +  /*  4 */         943,         696,         185,         688, +}; + +static const uint16_t ud_itab__444[] = { +  /*  0 */         708,         183,     INVALID,     INVALID, +  /*  4 */     INVALID,     INVALID,     INVALID,     INVALID, +}; + +static const uint16_t ud_itab__445[] = { +  /*  0 */         707,         184,  GROUP(446),          71, +  /*  4 */         761,         762,        1255,     INVALID, +}; + +static const uint16_t ud_itab__446[] = { +  /*  0 */          69,          70, +}; + + +struct ud_lookup_table_list_entry ud_lookup_table_list[] = { +    /* 000 */ { ud_itab__0, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 001 */ { ud_itab__1, UD_TAB__OPC_MODE, "/m" }, +    /* 002 */ { ud_itab__2, UD_TAB__OPC_MODE, "/m" }, +    /* 003 */ { ud_itab__3, UD_TAB__OPC_MODE, "/m" }, +    /* 004 */ { ud_itab__4, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 005 */ { ud_itab__5, UD_TAB__OPC_REG, "/reg" }, +    /* 006 */ { ud_itab__6, UD_TAB__OPC_MOD, "/mod" }, +    /* 007 */ { ud_itab__7, UD_TAB__OPC_REG, "/reg" }, +    /* 008 */ { ud_itab__8, UD_TAB__OPC_REG, "/reg" }, +    /* 009 */ { ud_itab__9, UD_TAB__OPC_RM, "/rm" }, +    /* 010 */ { ud_itab__10, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 011 */ { ud_itab__11, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 012 */ { ud_itab__12, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 013 */ { ud_itab__13, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 014 */ { ud_itab__14, UD_TAB__OPC_RM, "/rm" }, +    /* 015 */ { ud_itab__15, UD_TAB__OPC_RM, "/rm" }, +    /* 016 */ { ud_itab__16, UD_TAB__OPC_RM, "/rm" }, +    /* 017 */ { ud_itab__17, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 018 */ { ud_itab__18, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 019 */ { ud_itab__19, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 020 */ { ud_itab__20, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 021 */ { ud_itab__21, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 022 */ { ud_itab__22, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 023 */ { ud_itab__23, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 024 */ { ud_itab__24, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 025 */ { ud_itab__25, UD_TAB__OPC_RM, "/rm" }, +    /* 026 */ { ud_itab__26, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 027 */ { ud_itab__27, UD_TAB__OPC_REG, "/reg" }, +    /* 028 */ { ud_itab__28, UD_TAB__OPC_3DNOW, "/3dnow" }, +    /* 029 */ { ud_itab__29, UD_TAB__OPC_SSE, "/sse" }, +    /* 030 */ { ud_itab__30, UD_TAB__OPC_SSE, "/sse" }, +    /* 031 */ { ud_itab__31, UD_TAB__OPC_MOD, "/mod" }, +    /* 032 */ { ud_itab__32, UD_TAB__OPC_SSE, "/sse" }, +    /* 033 */ { ud_itab__33, UD_TAB__OPC_SSE, "/sse" }, +    /* 034 */ { ud_itab__34, UD_TAB__OPC_SSE, "/sse" }, +    /* 035 */ { ud_itab__35, UD_TAB__OPC_SSE, "/sse" }, +    /* 036 */ { ud_itab__36, UD_TAB__OPC_SSE, "/sse" }, +    /* 037 */ { ud_itab__37, UD_TAB__OPC_MOD, "/mod" }, +    /* 038 */ { ud_itab__38, UD_TAB__OPC_SSE, "/sse" }, +    /* 039 */ { ud_itab__39, UD_TAB__OPC_SSE, "/sse" }, +    /* 040 */ { ud_itab__40, UD_TAB__OPC_SSE, "/sse" }, +    /* 041 */ { ud_itab__41, UD_TAB__OPC_REG, "/reg" }, +    /* 042 */ { ud_itab__42, UD_TAB__OPC_SSE, "/sse" }, +    /* 043 */ { ud_itab__43, UD_TAB__OPC_SSE, "/sse" }, +    /* 044 */ { ud_itab__44, UD_TAB__OPC_SSE, "/sse" }, +    /* 045 */ { ud_itab__45, UD_TAB__OPC_SSE, "/sse" }, +    /* 046 */ { ud_itab__46, UD_TAB__OPC_SSE, "/sse" }, +    /* 047 */ { ud_itab__47, UD_TAB__OPC_SSE, "/sse" }, +    /* 048 */ { ud_itab__48, UD_TAB__OPC_SSE, "/sse" }, +    /* 049 */ { ud_itab__49, UD_TAB__OPC_SSE, "/sse" }, +    /* 050 */ { ud_itab__50, UD_TAB__OPC_MODE, "/m" }, +    /* 051 */ { ud_itab__51, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 052 */ { ud_itab__52, UD_TAB__OPC_MODE, "/m" }, +    /* 053 */ { ud_itab__53, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 054 */ { ud_itab__54, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 055 */ { ud_itab__55, UD_TAB__OPC_SSE, "/sse" }, +    /* 056 */ { ud_itab__56, UD_TAB__OPC_MODE, "/m" }, +    /* 057 */ { ud_itab__57, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 058 */ { ud_itab__58, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 059 */ { ud_itab__59, UD_TAB__OPC_SSE, "/sse" }, +    /* 060 */ { ud_itab__60, UD_TAB__OPC_MODE, "/m" }, +    /* 061 */ { ud_itab__61, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 062 */ { ud_itab__62, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 063 */ { ud_itab__63, UD_TAB__OPC_SSE, "/sse" }, +    /* 064 */ { ud_itab__64, UD_TAB__OPC_SSE, "/sse" }, +    /* 065 */ { ud_itab__65, UD_TAB__OPC_SSE, "/sse" }, +    /* 066 */ { ud_itab__66, UD_TAB__OPC_SSE, "/sse" }, +    /* 067 */ { ud_itab__67, UD_TAB__OPC_SSE, "/sse" }, +    /* 068 */ { ud_itab__68, UD_TAB__OPC_SSE, "/sse" }, +    /* 069 */ { ud_itab__69, UD_TAB__OPC_SSE, "/sse" }, +    /* 070 */ { ud_itab__70, UD_TAB__OPC_SSE, "/sse" }, +    /* 071 */ { ud_itab__71, UD_TAB__OPC_SSE, "/sse" }, +    /* 072 */ { ud_itab__72, UD_TAB__OPC_SSE, "/sse" }, +    /* 073 */ { ud_itab__73, UD_TAB__OPC_SSE, "/sse" }, +    /* 074 */ { ud_itab__74, UD_TAB__OPC_SSE, "/sse" }, +    /* 075 */ { ud_itab__75, UD_TAB__OPC_SSE, "/sse" }, +    /* 076 */ { ud_itab__76, UD_TAB__OPC_SSE, "/sse" }, +    /* 077 */ { ud_itab__77, UD_TAB__OPC_SSE, "/sse" }, +    /* 078 */ { ud_itab__78, UD_TAB__OPC_SSE, "/sse" }, +    /* 079 */ { ud_itab__79, UD_TAB__OPC_SSE, "/sse" }, +    /* 080 */ { ud_itab__80, UD_TAB__OPC_SSE, "/sse" }, +    /* 081 */ { ud_itab__81, UD_TAB__OPC_SSE, "/sse" }, +    /* 082 */ { ud_itab__82, UD_TAB__OPC_SSE, "/sse" }, +    /* 083 */ { ud_itab__83, UD_TAB__OPC_SSE, "/sse" }, +    /* 084 */ { ud_itab__84, UD_TAB__OPC_SSE, "/sse" }, +    /* 085 */ { ud_itab__85, UD_TAB__OPC_SSE, "/sse" }, +    /* 086 */ { ud_itab__86, UD_TAB__OPC_SSE, "/sse" }, +    /* 087 */ { ud_itab__87, UD_TAB__OPC_SSE, "/sse" }, +    /* 088 */ { ud_itab__88, UD_TAB__OPC_SSE, "/sse" }, +    /* 089 */ { ud_itab__89, UD_TAB__OPC_SSE, "/sse" }, +    /* 090 */ { ud_itab__90, UD_TAB__OPC_SSE, "/sse" }, +    /* 091 */ { ud_itab__91, UD_TAB__OPC_SSE, "/sse" }, +    /* 092 */ { ud_itab__92, UD_TAB__OPC_SSE, "/sse" }, +    /* 093 */ { ud_itab__93, UD_TAB__OPC_SSE, "/sse" }, +    /* 094 */ { ud_itab__94, UD_TAB__OPC_SSE, "/sse" }, +    /* 095 */ { ud_itab__95, UD_TAB__OPC_SSE, "/sse" }, +    /* 096 */ { ud_itab__96, UD_TAB__OPC_SSE, "/sse" }, +    /* 097 */ { ud_itab__97, UD_TAB__OPC_SSE, "/sse" }, +    /* 098 */ { ud_itab__98, UD_TAB__OPC_SSE, "/sse" }, +    /* 099 */ { ud_itab__99, UD_TAB__OPC_SSE, "/sse" }, +    /* 100 */ { ud_itab__100, UD_TAB__OPC_SSE, "/sse" }, +    /* 101 */ { ud_itab__101, UD_TAB__OPC_SSE, "/sse" }, +    /* 102 */ { ud_itab__102, UD_TAB__OPC_SSE, "/sse" }, +    /* 103 */ { ud_itab__103, UD_TAB__OPC_SSE, "/sse" }, +    /* 104 */ { ud_itab__104, UD_TAB__OPC_SSE, "/sse" }, +    /* 105 */ { ud_itab__105, UD_TAB__OPC_SSE, "/sse" }, +    /* 106 */ { ud_itab__106, UD_TAB__OPC_SSE, "/sse" }, +    /* 107 */ { ud_itab__107, UD_TAB__OPC_SSE, "/sse" }, +    /* 108 */ { ud_itab__108, UD_TAB__OPC_SSE, "/sse" }, +    /* 109 */ { ud_itab__109, UD_TAB__OPC_SSE, "/sse" }, +    /* 110 */ { ud_itab__110, UD_TAB__OPC_SSE, "/sse" }, +    /* 111 */ { ud_itab__111, UD_TAB__OPC_SSE, "/sse" }, +    /* 112 */ { ud_itab__112, UD_TAB__OPC_SSE, "/sse" }, +    /* 113 */ { ud_itab__113, UD_TAB__OPC_SSE, "/sse" }, +    /* 114 */ { ud_itab__114, UD_TAB__OPC_SSE, "/sse" }, +    /* 115 */ { ud_itab__115, UD_TAB__OPC_SSE, "/sse" }, +    /* 116 */ { ud_itab__116, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 117 */ { ud_itab__117, UD_TAB__OPC_SSE, "/sse" }, +    /* 118 */ { ud_itab__118, UD_TAB__OPC_SSE, "/sse" }, +    /* 119 */ { ud_itab__119, UD_TAB__OPC_SSE, "/sse" }, +    /* 120 */ { ud_itab__120, UD_TAB__OPC_SSE, "/sse" }, +    /* 121 */ { ud_itab__121, UD_TAB__OPC_SSE, "/sse" }, +    /* 122 */ { ud_itab__122, UD_TAB__OPC_SSE, "/sse" }, +    /* 123 */ { ud_itab__123, UD_TAB__OPC_SSE, "/sse" }, +    /* 124 */ { ud_itab__124, UD_TAB__OPC_SSE, "/sse" }, +    /* 125 */ { ud_itab__125, UD_TAB__OPC_SSE, "/sse" }, +    /* 126 */ { ud_itab__126, UD_TAB__OPC_SSE, "/sse" }, +    /* 127 */ { ud_itab__127, UD_TAB__OPC_SSE, "/sse" }, +    /* 128 */ { ud_itab__128, UD_TAB__OPC_OSIZE, "/o" }, +    /* 129 */ { ud_itab__129, UD_TAB__OPC_SSE, "/sse" }, +    /* 130 */ { ud_itab__130, UD_TAB__OPC_SSE, "/sse" }, +    /* 131 */ { ud_itab__131, UD_TAB__OPC_SSE, "/sse" }, +    /* 132 */ { ud_itab__132, UD_TAB__OPC_SSE, "/sse" }, +    /* 133 */ { ud_itab__133, UD_TAB__OPC_OSIZE, "/o" }, +    /* 134 */ { ud_itab__134, UD_TAB__OPC_SSE, "/sse" }, +    /* 135 */ { ud_itab__135, UD_TAB__OPC_SSE, "/sse" }, +    /* 136 */ { ud_itab__136, UD_TAB__OPC_SSE, "/sse" }, +    /* 137 */ { ud_itab__137, UD_TAB__OPC_SSE, "/sse" }, +    /* 138 */ { ud_itab__138, UD_TAB__OPC_SSE, "/sse" }, +    /* 139 */ { ud_itab__139, UD_TAB__OPC_SSE, "/sse" }, +    /* 140 */ { ud_itab__140, UD_TAB__OPC_SSE, "/sse" }, +    /* 141 */ { ud_itab__141, UD_TAB__OPC_SSE, "/sse" }, +    /* 142 */ { ud_itab__142, UD_TAB__OPC_SSE, "/sse" }, +    /* 143 */ { ud_itab__143, UD_TAB__OPC_SSE, "/sse" }, +    /* 144 */ { ud_itab__144, UD_TAB__OPC_SSE, "/sse" }, +    /* 145 */ { ud_itab__145, UD_TAB__OPC_SSE, "/sse" }, +    /* 146 */ { ud_itab__146, UD_TAB__OPC_SSE, "/sse" }, +    /* 147 */ { ud_itab__147, UD_TAB__OPC_SSE, "/sse" }, +    /* 148 */ { ud_itab__148, UD_TAB__OPC_SSE, "/sse" }, +    /* 149 */ { ud_itab__149, UD_TAB__OPC_SSE, "/sse" }, +    /* 150 */ { ud_itab__150, UD_TAB__OPC_SSE, "/sse" }, +    /* 151 */ { ud_itab__151, UD_TAB__OPC_SSE, "/sse" }, +    /* 152 */ { ud_itab__152, UD_TAB__OPC_SSE, "/sse" }, +    /* 153 */ { ud_itab__153, UD_TAB__OPC_SSE, "/sse" }, +    /* 154 */ { ud_itab__154, UD_TAB__OPC_SSE, "/sse" }, +    /* 155 */ { ud_itab__155, UD_TAB__OPC_SSE, "/sse" }, +    /* 156 */ { ud_itab__156, UD_TAB__OPC_SSE, "/sse" }, +    /* 157 */ { ud_itab__157, UD_TAB__OPC_SSE, "/sse" }, +    /* 158 */ { ud_itab__158, UD_TAB__OPC_SSE, "/sse" }, +    /* 159 */ { ud_itab__159, UD_TAB__OPC_SSE, "/sse" }, +    /* 160 */ { ud_itab__160, UD_TAB__OPC_SSE, "/sse" }, +    /* 161 */ { ud_itab__161, UD_TAB__OPC_SSE, "/sse" }, +    /* 162 */ { ud_itab__162, UD_TAB__OPC_SSE, "/sse" }, +    /* 163 */ { ud_itab__163, UD_TAB__OPC_SSE, "/sse" }, +    /* 164 */ { ud_itab__164, UD_TAB__OPC_SSE, "/sse" }, +    /* 165 */ { ud_itab__165, UD_TAB__OPC_SSE, "/sse" }, +    /* 166 */ { ud_itab__166, UD_TAB__OPC_SSE, "/sse" }, +    /* 167 */ { ud_itab__167, UD_TAB__OPC_SSE, "/sse" }, +    /* 168 */ { ud_itab__168, UD_TAB__OPC_SSE, "/sse" }, +    /* 169 */ { ud_itab__169, UD_TAB__OPC_SSE, "/sse" }, +    /* 170 */ { ud_itab__170, UD_TAB__OPC_SSE, "/sse" }, +    /* 171 */ { ud_itab__171, UD_TAB__OPC_SSE, "/sse" }, +    /* 172 */ { ud_itab__172, UD_TAB__OPC_SSE, "/sse" }, +    /* 173 */ { ud_itab__173, UD_TAB__OPC_SSE, "/sse" }, +    /* 174 */ { ud_itab__174, UD_TAB__OPC_OSIZE, "/o" }, +    /* 175 */ { ud_itab__175, UD_TAB__OPC_OSIZE, "/o" }, +    /* 176 */ { ud_itab__176, UD_TAB__OPC_SSE, "/sse" }, +    /* 177 */ { ud_itab__177, UD_TAB__OPC_SSE, "/sse" }, +    /* 178 */ { ud_itab__178, UD_TAB__OPC_REG, "/reg" }, +    /* 179 */ { ud_itab__179, UD_TAB__OPC_SSE, "/sse" }, +    /* 180 */ { ud_itab__180, UD_TAB__OPC_SSE, "/sse" }, +    /* 181 */ { ud_itab__181, UD_TAB__OPC_SSE, "/sse" }, +    /* 182 */ { ud_itab__182, UD_TAB__OPC_REG, "/reg" }, +    /* 183 */ { ud_itab__183, UD_TAB__OPC_SSE, "/sse" }, +    /* 184 */ { ud_itab__184, UD_TAB__OPC_SSE, "/sse" }, +    /* 185 */ { ud_itab__185, UD_TAB__OPC_SSE, "/sse" }, +    /* 186 */ { ud_itab__186, UD_TAB__OPC_REG, "/reg" }, +    /* 187 */ { ud_itab__187, UD_TAB__OPC_SSE, "/sse" }, +    /* 188 */ { ud_itab__188, UD_TAB__OPC_SSE, "/sse" }, +    /* 189 */ { ud_itab__189, UD_TAB__OPC_SSE, "/sse" }, +    /* 190 */ { ud_itab__190, UD_TAB__OPC_SSE, "/sse" }, +    /* 191 */ { ud_itab__191, UD_TAB__OPC_SSE, "/sse" }, +    /* 192 */ { ud_itab__192, UD_TAB__OPC_SSE, "/sse" }, +    /* 193 */ { ud_itab__193, UD_TAB__OPC_SSE, "/sse" }, +    /* 194 */ { ud_itab__194, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 195 */ { ud_itab__195, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 196 */ { ud_itab__196, UD_TAB__OPC_SSE, "/sse" }, +    /* 197 */ { ud_itab__197, UD_TAB__OPC_SSE, "/sse" }, +    /* 198 */ { ud_itab__198, UD_TAB__OPC_SSE, "/sse" }, +    /* 199 */ { ud_itab__199, UD_TAB__OPC_OSIZE, "/o" }, +    /* 200 */ { ud_itab__200, UD_TAB__OPC_OSIZE, "/o" }, +    /* 201 */ { ud_itab__201, UD_TAB__OPC_SSE, "/sse" }, +    /* 202 */ { ud_itab__202, UD_TAB__OPC_MOD, "/mod" }, +    /* 203 */ { ud_itab__203, UD_TAB__OPC_REG, "/reg" }, +    /* 204 */ { ud_itab__204, UD_TAB__OPC_RM, "/rm" }, +    /* 205 */ { ud_itab__205, UD_TAB__OPC_RM, "/rm" }, +    /* 206 */ { ud_itab__206, UD_TAB__OPC_RM, "/rm" }, +    /* 207 */ { ud_itab__207, UD_TAB__OPC_MOD, "/mod" }, +    /* 208 */ { ud_itab__208, UD_TAB__OPC_REG, "/reg" }, +    /* 209 */ { ud_itab__209, UD_TAB__OPC_RM, "/rm" }, +    /* 210 */ { ud_itab__210, UD_TAB__OPC_RM, "/rm" }, +    /* 211 */ { ud_itab__211, UD_TAB__OPC_RM, "/rm" }, +    /* 212 */ { ud_itab__212, UD_TAB__OPC_RM, "/rm" }, +    /* 213 */ { ud_itab__213, UD_TAB__OPC_RM, "/rm" }, +    /* 214 */ { ud_itab__214, UD_TAB__OPC_RM, "/rm" }, +    /* 215 */ { ud_itab__215, UD_TAB__OPC_MOD, "/mod" }, +    /* 216 */ { ud_itab__216, UD_TAB__OPC_REG, "/reg" }, +    /* 217 */ { ud_itab__217, UD_TAB__OPC_REG, "/reg" }, +    /* 218 */ { ud_itab__218, UD_TAB__OPC_RM, "/rm" }, +    /* 219 */ { ud_itab__219, UD_TAB__OPC_RM, "/rm" }, +    /* 220 */ { ud_itab__220, UD_TAB__OPC_RM, "/rm" }, +    /* 221 */ { ud_itab__221, UD_TAB__OPC_SSE, "/sse" }, +    /* 222 */ { ud_itab__222, UD_TAB__OPC_REG, "/reg" }, +    /* 223 */ { ud_itab__223, UD_TAB__OPC_SSE, "/sse" }, +    /* 224 */ { ud_itab__224, UD_TAB__OPC_SSE, "/sse" }, +    /* 225 */ { ud_itab__225, UD_TAB__OPC_SSE, "/sse" }, +    /* 226 */ { ud_itab__226, UD_TAB__OPC_SSE, "/sse" }, +    /* 227 */ { ud_itab__227, UD_TAB__OPC_MOD, "/mod" }, +    /* 228 */ { ud_itab__228, UD_TAB__OPC_REG, "/reg" }, +    /* 229 */ { ud_itab__229, UD_TAB__OPC_OSIZE, "/o" }, +    /* 230 */ { ud_itab__230, UD_TAB__OPC_SSE, "/sse" }, +    /* 231 */ { ud_itab__231, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 232 */ { ud_itab__232, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 233 */ { ud_itab__233, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 234 */ { ud_itab__234, UD_TAB__OPC_VENDOR, "/vendor" }, +    /* 235 */ { ud_itab__235, UD_TAB__OPC_REG, "/reg" }, +    /* 236 */ { ud_itab__236, UD_TAB__OPC_SSE, "/sse" }, +    /* 237 */ { ud_itab__237, UD_TAB__OPC_SSE, "/sse" }, +    /* 238 */ { ud_itab__238, UD_TAB__OPC_SSE, "/sse" }, +    /* 239 */ { ud_itab__239, UD_TAB__OPC_SSE, "/sse" }, +    /* 240 */ { ud_itab__240, UD_TAB__OPC_SSE, "/sse" }, +    /* 241 */ { ud_itab__241, UD_TAB__OPC_SSE, "/sse" }, +    /* 242 */ { ud_itab__242, UD_TAB__OPC_SSE, "/sse" }, +    /* 243 */ { ud_itab__243, UD_TAB__OPC_SSE, "/sse" }, +    /* 244 */ { ud_itab__244, UD_TAB__OPC_SSE, "/sse" }, +    /* 245 */ { ud_itab__245, UD_TAB__OPC_SSE, "/sse" }, +    /* 246 */ { ud_itab__246, UD_TAB__OPC_SSE, "/sse" }, +    /* 247 */ { ud_itab__247, UD_TAB__OPC_SSE, "/sse" }, +    /* 248 */ { ud_itab__248, UD_TAB__OPC_SSE, "/sse" }, +    /* 249 */ { ud_itab__249, UD_TAB__OPC_SSE, "/sse" }, +    /* 250 */ { ud_itab__250, UD_TAB__OPC_SSE, "/sse" }, +    /* 251 */ { ud_itab__251, UD_TAB__OPC_SSE, "/sse" }, +    /* 252 */ { ud_itab__252, UD_TAB__OPC_SSE, "/sse" }, +    /* 253 */ { ud_itab__253, UD_TAB__OPC_SSE, "/sse" }, +    /* 254 */ { ud_itab__254, UD_TAB__OPC_SSE, "/sse" }, +    /* 255 */ { ud_itab__255, UD_TAB__OPC_SSE, "/sse" }, +    /* 256 */ { ud_itab__256, UD_TAB__OPC_SSE, "/sse" }, +    /* 257 */ { ud_itab__257, UD_TAB__OPC_SSE, "/sse" }, +    /* 258 */ { ud_itab__258, UD_TAB__OPC_SSE, "/sse" }, +    /* 259 */ { ud_itab__259, UD_TAB__OPC_SSE, "/sse" }, +    /* 260 */ { ud_itab__260, UD_TAB__OPC_SSE, "/sse" }, +    /* 261 */ { ud_itab__261, UD_TAB__OPC_SSE, "/sse" }, +    /* 262 */ { ud_itab__262, UD_TAB__OPC_SSE, "/sse" }, +    /* 263 */ { ud_itab__263, UD_TAB__OPC_SSE, "/sse" }, +    /* 264 */ { ud_itab__264, UD_TAB__OPC_SSE, "/sse" }, +    /* 265 */ { ud_itab__265, UD_TAB__OPC_SSE, "/sse" }, +    /* 266 */ { ud_itab__266, UD_TAB__OPC_SSE, "/sse" }, +    /* 267 */ { ud_itab__267, UD_TAB__OPC_SSE, "/sse" }, +    /* 268 */ { ud_itab__268, UD_TAB__OPC_SSE, "/sse" }, +    /* 269 */ { ud_itab__269, UD_TAB__OPC_SSE, "/sse" }, +    /* 270 */ { ud_itab__270, UD_TAB__OPC_SSE, "/sse" }, +    /* 271 */ { ud_itab__271, UD_TAB__OPC_SSE, "/sse" }, +    /* 272 */ { ud_itab__272, UD_TAB__OPC_SSE, "/sse" }, +    /* 273 */ { ud_itab__273, UD_TAB__OPC_SSE, "/sse" }, +    /* 274 */ { ud_itab__274, UD_TAB__OPC_SSE, "/sse" }, +    /* 275 */ { ud_itab__275, UD_TAB__OPC_MOD, "/mod" }, +    /* 276 */ { ud_itab__276, UD_TAB__OPC_SSE, "/sse" }, +    /* 277 */ { ud_itab__277, UD_TAB__OPC_SSE, "/sse" }, +    /* 278 */ { ud_itab__278, UD_TAB__OPC_SSE, "/sse" }, +    /* 279 */ { ud_itab__279, UD_TAB__OPC_SSE, "/sse" }, +    /* 280 */ { ud_itab__280, UD_TAB__OPC_SSE, "/sse" }, +    /* 281 */ { ud_itab__281, UD_TAB__OPC_SSE, "/sse" }, +    /* 282 */ { ud_itab__282, UD_TAB__OPC_SSE, "/sse" }, +    /* 283 */ { ud_itab__283, UD_TAB__OPC_SSE, "/sse" }, +    /* 284 */ { ud_itab__284, UD_TAB__OPC_MODE, "/m" }, +    /* 285 */ { ud_itab__285, UD_TAB__OPC_MODE, "/m" }, +    /* 286 */ { ud_itab__286, UD_TAB__OPC_MODE, "/m" }, +    /* 287 */ { ud_itab__287, UD_TAB__OPC_MODE, "/m" }, +    /* 288 */ { ud_itab__288, UD_TAB__OPC_MODE, "/m" }, +    /* 289 */ { ud_itab__289, UD_TAB__OPC_MODE, "/m" }, +    /* 290 */ { ud_itab__290, UD_TAB__OPC_MODE, "/m" }, +    /* 291 */ { ud_itab__291, UD_TAB__OPC_MODE, "/m" }, +    /* 292 */ { ud_itab__292, UD_TAB__OPC_OSIZE, "/o" }, +    /* 293 */ { ud_itab__293, UD_TAB__OPC_MODE, "/m" }, +    /* 294 */ { ud_itab__294, UD_TAB__OPC_MODE, "/m" }, +    /* 295 */ { ud_itab__295, UD_TAB__OPC_OSIZE, "/o" }, +    /* 296 */ { ud_itab__296, UD_TAB__OPC_MODE, "/m" }, +    /* 297 */ { ud_itab__297, UD_TAB__OPC_MODE, "/m" }, +    /* 298 */ { ud_itab__298, UD_TAB__OPC_MODE, "/m" }, +    /* 299 */ { ud_itab__299, UD_TAB__OPC_MODE, "/m" }, +    /* 300 */ { ud_itab__300, UD_TAB__OPC_OSIZE, "/o" }, +    /* 301 */ { ud_itab__301, UD_TAB__OPC_OSIZE, "/o" }, +    /* 302 */ { ud_itab__302, UD_TAB__OPC_REG, "/reg" }, +    /* 303 */ { ud_itab__303, UD_TAB__OPC_REG, "/reg" }, +    /* 304 */ { ud_itab__304, UD_TAB__OPC_REG, "/reg" }, +    /* 305 */ { ud_itab__305, UD_TAB__OPC_MODE, "/m" }, +    /* 306 */ { ud_itab__306, UD_TAB__OPC_MODE, "/m" }, +    /* 307 */ { ud_itab__307, UD_TAB__OPC_MODE, "/m" }, +    /* 308 */ { ud_itab__308, UD_TAB__OPC_MODE, "/m" }, +    /* 309 */ { ud_itab__309, UD_TAB__OPC_MODE, "/m" }, +    /* 310 */ { ud_itab__310, UD_TAB__OPC_MODE, "/m" }, +    /* 311 */ { ud_itab__311, UD_TAB__OPC_MODE, "/m" }, +    /* 312 */ { ud_itab__312, UD_TAB__OPC_MODE, "/m" }, +    /* 313 */ { ud_itab__313, UD_TAB__OPC_REG, "/reg" }, +    /* 314 */ { ud_itab__314, UD_TAB__OPC_REG, "/reg" }, +    /* 315 */ { ud_itab__315, UD_TAB__OPC_OSIZE, "/o" }, +    /* 316 */ { ud_itab__316, UD_TAB__OPC_OSIZE, "/o" }, +    /* 317 */ { ud_itab__317, UD_TAB__OPC_MODE, "/m" }, +    /* 318 */ { ud_itab__318, UD_TAB__OPC_OSIZE, "/o" }, +    /* 319 */ { ud_itab__319, UD_TAB__OPC_MODE, "/m" }, +    /* 320 */ { ud_itab__320, UD_TAB__OPC_MODE, "/m" }, +    /* 321 */ { ud_itab__321, UD_TAB__OPC_MODE, "/m" }, +    /* 322 */ { ud_itab__322, UD_TAB__OPC_OSIZE, "/o" }, +    /* 323 */ { ud_itab__323, UD_TAB__OPC_MODE, "/m" }, +    /* 324 */ { ud_itab__324, UD_TAB__OPC_MODE, "/m" }, +    /* 325 */ { ud_itab__325, UD_TAB__OPC_MODE, "/m" }, +    /* 326 */ { ud_itab__326, UD_TAB__OPC_OSIZE, "/o" }, +    /* 327 */ { ud_itab__327, UD_TAB__OPC_OSIZE, "/o" }, +    /* 328 */ { ud_itab__328, UD_TAB__OPC_OSIZE, "/o" }, +    /* 329 */ { ud_itab__329, UD_TAB__OPC_OSIZE, "/o" }, +    /* 330 */ { ud_itab__330, UD_TAB__OPC_OSIZE, "/o" }, +    /* 331 */ { ud_itab__331, UD_TAB__OPC_REG, "/reg" }, +    /* 332 */ { ud_itab__332, UD_TAB__OPC_REG, "/reg" }, +    /* 333 */ { ud_itab__333, UD_TAB__OPC_VEX, "/vex" }, +    /* 334 */ { ud_itab__334, UD_TAB__OPC_MODE, "/m" }, +    /* 335 */ { ud_itab__335, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 336 */ { ud_itab__336, UD_TAB__OPC_MOD, "/mod" }, +    /* 337 */ { ud_itab__337, UD_TAB__OPC_MOD, "/mod" }, +    /* 338 */ { ud_itab__338, UD_TAB__OPC_MOD, "/mod" }, +    /* 339 */ { ud_itab__339, UD_TAB__OPC_REG, "/reg" }, +    /* 340 */ { ud_itab__340, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 341 */ { ud_itab__341, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 342 */ { ud_itab__342, UD_TAB__OPC_MOD, "/mod" }, +    /* 343 */ { ud_itab__343, UD_TAB__OPC_MOD, "/mod" }, +    /* 344 */ { ud_itab__344, UD_TAB__OPC_OSIZE, "/o" }, +    /* 345 */ { ud_itab__345, UD_TAB__OPC_REG, "/reg" }, +    /* 346 */ { ud_itab__346, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 347 */ { ud_itab__347, UD_TAB__OPC_REG, "/reg" }, +    /* 348 */ { ud_itab__348, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 349 */ { ud_itab__349, UD_TAB__OPC_REG, "/reg" }, +    /* 350 */ { ud_itab__350, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 351 */ { ud_itab__351, UD_TAB__OPC_OSIZE, "/o" }, +    /* 352 */ { ud_itab__352, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 353 */ { ud_itab__353, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 354 */ { ud_itab__354, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 355 */ { ud_itab__355, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 356 */ { ud_itab__356, UD_TAB__OPC_MOD, "/mod" }, +    /* 357 */ { ud_itab__357, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 358 */ { ud_itab__358, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 359 */ { ud_itab__359, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 360 */ { ud_itab__360, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 361 */ { ud_itab__361, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 362 */ { ud_itab__362, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 363 */ { ud_itab__363, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 364 */ { ud_itab__364, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 365 */ { ud_itab__365, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 366 */ { ud_itab__366, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 367 */ { ud_itab__367, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 368 */ { ud_itab__368, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 369 */ { ud_itab__369, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 370 */ { ud_itab__370, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 371 */ { ud_itab__371, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 372 */ { ud_itab__372, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 373 */ { ud_itab__373, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 374 */ { ud_itab__374, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 375 */ { ud_itab__375, UD_TAB__OPC_OSIZE, "/o" }, +    /* 376 */ { ud_itab__376, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 377 */ { ud_itab__377, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 378 */ { ud_itab__378, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 379 */ { ud_itab__379, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 380 */ { ud_itab__380, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 381 */ { ud_itab__381, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 382 */ { ud_itab__382, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 383 */ { ud_itab__383, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 384 */ { ud_itab__384, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 385 */ { ud_itab__385, UD_TAB__OPC_MODE, "/m" }, +    /* 386 */ { ud_itab__386, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 387 */ { ud_itab__387, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 388 */ { ud_itab__388, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 389 */ { ud_itab__389, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 390 */ { ud_itab__390, UD_TAB__OPC_VEX_L, "/vexl" }, +    /* 391 */ { ud_itab__391, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 392 */ { ud_itab__392, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 393 */ { ud_itab__393, UD_TAB__OPC_VEX_W, "/vexw" }, +    /* 394 */ { ud_itab__394, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 395 */ { ud_itab__395, UD_TAB__OPC_MOD, "/mod" }, +    /* 396 */ { ud_itab__396, UD_TAB__OPC_MOD, "/mod" }, +    /* 397 */ { ud_itab__397, UD_TAB__OPC_MOD, "/mod" }, +    /* 398 */ { ud_itab__398, UD_TAB__OPC_MOD, "/mod" }, +    /* 399 */ { ud_itab__399, UD_TAB__OPC_TABLE, "opctbl" }, +    /* 400 */ { ud_itab__400, UD_TAB__OPC_MOD, "/mod" }, +    /* 401 */ { ud_itab__401, UD_TAB__OPC_MOD, "/mod" }, +    /* 402 */ { ud_itab__402, UD_TAB__OPC_MOD, "/mod" }, +    /* 403 */ { ud_itab__403, UD_TAB__OPC_VEX, "/vex" }, +    /* 404 */ { ud_itab__404, UD_TAB__OPC_MODE, "/m" }, +    /* 405 */ { ud_itab__405, UD_TAB__OPC_REG, "/reg" }, +    /* 406 */ { ud_itab__406, UD_TAB__OPC_REG, "/reg" }, +    /* 407 */ { ud_itab__407, UD_TAB__OPC_MODE, "/m" }, +    /* 408 */ { ud_itab__408, UD_TAB__OPC_OSIZE, "/o" }, +    /* 409 */ { ud_itab__409, UD_TAB__OPC_REG, "/reg" }, +    /* 410 */ { ud_itab__410, UD_TAB__OPC_REG, "/reg" }, +    /* 411 */ { ud_itab__411, UD_TAB__OPC_REG, "/reg" }, +    /* 412 */ { ud_itab__412, UD_TAB__OPC_REG, "/reg" }, +    /* 413 */ { ud_itab__413, UD_TAB__OPC_MODE, "/m" }, +    /* 414 */ { ud_itab__414, UD_TAB__OPC_MODE, "/m" }, +    /* 415 */ { ud_itab__415, UD_TAB__OPC_MODE, "/m" }, +    /* 416 */ { ud_itab__416, UD_TAB__OPC_MOD, "/mod" }, +    /* 417 */ { ud_itab__417, UD_TAB__OPC_REG, "/reg" }, +    /* 418 */ { ud_itab__418, UD_TAB__OPC_X87, "/x87" }, +    /* 419 */ { ud_itab__419, UD_TAB__OPC_MOD, "/mod" }, +    /* 420 */ { ud_itab__420, UD_TAB__OPC_REG, "/reg" }, +    /* 421 */ { ud_itab__421, UD_TAB__OPC_X87, "/x87" }, +    /* 422 */ { ud_itab__422, UD_TAB__OPC_MOD, "/mod" }, +    /* 423 */ { ud_itab__423, UD_TAB__OPC_REG, "/reg" }, +    /* 424 */ { ud_itab__424, UD_TAB__OPC_X87, "/x87" }, +    /* 425 */ { ud_itab__425, UD_TAB__OPC_MOD, "/mod" }, +    /* 426 */ { ud_itab__426, UD_TAB__OPC_REG, "/reg" }, +    /* 427 */ { ud_itab__427, UD_TAB__OPC_X87, "/x87" }, +    /* 428 */ { ud_itab__428, UD_TAB__OPC_MOD, "/mod" }, +    /* 429 */ { ud_itab__429, UD_TAB__OPC_REG, "/reg" }, +    /* 430 */ { ud_itab__430, UD_TAB__OPC_X87, "/x87" }, +    /* 431 */ { ud_itab__431, UD_TAB__OPC_MOD, "/mod" }, +    /* 432 */ { ud_itab__432, UD_TAB__OPC_REG, "/reg" }, +    /* 433 */ { ud_itab__433, UD_TAB__OPC_X87, "/x87" }, +    /* 434 */ { ud_itab__434, UD_TAB__OPC_MOD, "/mod" }, +    /* 435 */ { ud_itab__435, UD_TAB__OPC_REG, "/reg" }, +    /* 436 */ { ud_itab__436, UD_TAB__OPC_X87, "/x87" }, +    /* 437 */ { ud_itab__437, UD_TAB__OPC_MOD, "/mod" }, +    /* 438 */ { ud_itab__438, UD_TAB__OPC_REG, "/reg" }, +    /* 439 */ { ud_itab__439, UD_TAB__OPC_X87, "/x87" }, +    /* 440 */ { ud_itab__440, UD_TAB__OPC_ASIZE, "/a" }, +    /* 441 */ { ud_itab__441, UD_TAB__OPC_MODE, "/m" }, +    /* 442 */ { ud_itab__442, UD_TAB__OPC_REG, "/reg" }, +    /* 443 */ { ud_itab__443, UD_TAB__OPC_REG, "/reg" }, +    /* 444 */ { ud_itab__444, UD_TAB__OPC_REG, "/reg" }, +    /* 445 */ { ud_itab__445, UD_TAB__OPC_REG, "/reg" }, +    /* 446 */ { ud_itab__446, UD_TAB__OPC_MODE, "/m" }, +}; + +/* itab entry operand definitions (for readability) */ +#define O_AL      { OP_AL,       SZ_B     } +#define O_AX      { OP_AX,       SZ_W     } +#define O_Av      { OP_A,        SZ_V     } +#define O_C       { OP_C,        SZ_NA    } +#define O_CL      { OP_CL,       SZ_B     } +#define O_CS      { OP_CS,       SZ_NA    } +#define O_CX      { OP_CX,       SZ_W     } +#define O_D       { OP_D,        SZ_NA    } +#define O_DL      { OP_DL,       SZ_B     } +#define O_DS      { OP_DS,       SZ_NA    } +#define O_DX      { OP_DX,       SZ_W     } +#define O_E       { OP_E,        SZ_NA    } +#define O_ES      { OP_ES,       SZ_NA    } +#define O_Eb      { OP_E,        SZ_B     } +#define O_Ed      { OP_E,        SZ_D     } +#define O_Eq      { OP_E,        SZ_Q     } +#define O_Ev      { OP_E,        SZ_V     } +#define O_Ew      { OP_E,        SZ_W     } +#define O_Ey      { OP_E,        SZ_Y     } +#define O_Ez      { OP_E,        SZ_Z     } +#define O_FS      { OP_FS,       SZ_NA    } +#define O_Fv      { OP_F,        SZ_V     } +#define O_G       { OP_G,        SZ_NA    } +#define O_GS      { OP_GS,       SZ_NA    } +#define O_Gb      { OP_G,        SZ_B     } +#define O_Gd      { OP_G,        SZ_D     } +#define O_Gq      { OP_G,        SZ_Q     } +#define O_Gv      { OP_G,        SZ_V     } +#define O_Gw      { OP_G,        SZ_W     } +#define O_Gy      { OP_G,        SZ_Y     } +#define O_Gz      { OP_G,        SZ_Z     } +#define O_H       { OP_H,        SZ_X     } +#define O_Hqq     { OP_H,        SZ_QQ    } +#define O_Hx      { OP_H,        SZ_X     } +#define O_I1      { OP_I1,       SZ_NA    } +#define O_I3      { OP_I3,       SZ_NA    } +#define O_Ib      { OP_I,        SZ_B     } +#define O_Iv      { OP_I,        SZ_V     } +#define O_Iw      { OP_I,        SZ_W     } +#define O_Iz      { OP_I,        SZ_Z     } +#define O_Jb      { OP_J,        SZ_B     } +#define O_Jv      { OP_J,        SZ_V     } +#define O_Jz      { OP_J,        SZ_Z     } +#define O_L       { OP_L,        SZ_O     } +#define O_Lx      { OP_L,        SZ_X     } +#define O_M       { OP_M,        SZ_NA    } +#define O_Mb      { OP_M,        SZ_B     } +#define O_MbRd    { OP_MR,       SZ_BD    } +#define O_MbRv    { OP_MR,       SZ_BV    } +#define O_Md      { OP_M,        SZ_D     } +#define O_MdRy    { OP_MR,       SZ_DY    } +#define O_MdU     { OP_MU,       SZ_DO    } +#define O_Mdq     { OP_M,        SZ_DQ    } +#define O_Mo      { OP_M,        SZ_O     } +#define O_Mq      { OP_M,        SZ_Q     } +#define O_MqU     { OP_MU,       SZ_QO    } +#define O_Ms      { OP_M,        SZ_W     } +#define O_Mt      { OP_M,        SZ_T     } +#define O_Mv      { OP_M,        SZ_V     } +#define O_Mw      { OP_M,        SZ_W     } +#define O_MwRd    { OP_MR,       SZ_WD    } +#define O_MwRv    { OP_MR,       SZ_WV    } +#define O_MwRy    { OP_MR,       SZ_WY    } +#define O_MwU     { OP_MU,       SZ_WO    } +#define O_N       { OP_N,        SZ_Q     } +#define O_NONE    { OP_NONE,     SZ_NA    } +#define O_Ob      { OP_O,        SZ_B     } +#define O_Ov      { OP_O,        SZ_V     } +#define O_Ow      { OP_O,        SZ_W     } +#define O_P       { OP_P,        SZ_Q     } +#define O_Q       { OP_Q,        SZ_Q     } +#define O_R       { OP_R,        SZ_RDQ   } +#define O_R0b     { OP_R0,       SZ_B     } +#define O_R0v     { OP_R0,       SZ_V     } +#define O_R0w     { OP_R0,       SZ_W     } +#define O_R0y     { OP_R0,       SZ_Y     } +#define O_R0z     { OP_R0,       SZ_Z     } +#define O_R1b     { OP_R1,       SZ_B     } +#define O_R1v     { OP_R1,       SZ_V     } +#define O_R1w     { OP_R1,       SZ_W     } +#define O_R1y     { OP_R1,       SZ_Y     } +#define O_R1z     { OP_R1,       SZ_Z     } +#define O_R2b     { OP_R2,       SZ_B     } +#define O_R2v     { OP_R2,       SZ_V     } +#define O_R2w     { OP_R2,       SZ_W     } +#define O_R2y     { OP_R2,       SZ_Y     } +#define O_R2z     { OP_R2,       SZ_Z     } +#define O_R3b     { OP_R3,       SZ_B     } +#define O_R3v     { OP_R3,       SZ_V     } +#define O_R3w     { OP_R3,       SZ_W     } +#define O_R3y     { OP_R3,       SZ_Y     } +#define O_R3z     { OP_R3,       SZ_Z     } +#define O_R4b     { OP_R4,       SZ_B     } +#define O_R4v     { OP_R4,       SZ_V     } +#define O_R4w     { OP_R4,       SZ_W     } +#define O_R4y     { OP_R4,       SZ_Y     } +#define O_R4z     { OP_R4,       SZ_Z     } +#define O_R5b     { OP_R5,       SZ_B     } +#define O_R5v     { OP_R5,       SZ_V     } +#define O_R5w     { OP_R5,       SZ_W     } +#define O_R5y     { OP_R5,       SZ_Y     } +#define O_R5z     { OP_R5,       SZ_Z     } +#define O_R6b     { OP_R6,       SZ_B     } +#define O_R6v     { OP_R6,       SZ_V     } +#define O_R6w     { OP_R6,       SZ_W     } +#define O_R6y     { OP_R6,       SZ_Y     } +#define O_R6z     { OP_R6,       SZ_Z     } +#define O_R7b     { OP_R7,       SZ_B     } +#define O_R7v     { OP_R7,       SZ_V     } +#define O_R7w     { OP_R7,       SZ_W     } +#define O_R7y     { OP_R7,       SZ_Y     } +#define O_R7z     { OP_R7,       SZ_Z     } +#define O_S       { OP_S,        SZ_W     } +#define O_SS      { OP_SS,       SZ_NA    } +#define O_ST0     { OP_ST0,      SZ_NA    } +#define O_ST1     { OP_ST1,      SZ_NA    } +#define O_ST2     { OP_ST2,      SZ_NA    } +#define O_ST3     { OP_ST3,      SZ_NA    } +#define O_ST4     { OP_ST4,      SZ_NA    } +#define O_ST5     { OP_ST5,      SZ_NA    } +#define O_ST6     { OP_ST6,      SZ_NA    } +#define O_ST7     { OP_ST7,      SZ_NA    } +#define O_U       { OP_U,        SZ_O     } +#define O_Ux      { OP_U,        SZ_X     } +#define O_V       { OP_V,        SZ_DQ    } +#define O_Vdq     { OP_V,        SZ_DQ    } +#define O_Vqq     { OP_V,        SZ_QQ    } +#define O_Vsd     { OP_V,        SZ_Q     } +#define O_Vx      { OP_V,        SZ_X     } +#define O_W       { OP_W,        SZ_DQ    } +#define O_Wdq     { OP_W,        SZ_DQ    } +#define O_Wqq     { OP_W,        SZ_QQ    } +#define O_Wsd     { OP_W,        SZ_Q     } +#define O_Wx      { OP_W,        SZ_X     } +#define O_eAX     { OP_eAX,      SZ_Z     } +#define O_eCX     { OP_eCX,      SZ_Z     } +#define O_eDX     { OP_eDX,      SZ_Z     } +#define O_rAX     { OP_rAX,      SZ_V     } +#define O_rCX     { OP_rCX,      SZ_V     } +#define O_rDX     { OP_rDX,      SZ_V     } +#define O_sIb     { OP_sI,       SZ_B     } +#define O_sIv     { OP_sI,       SZ_V     } +#define O_sIz     { OP_sI,       SZ_Z     } + +struct ud_itab_entry ud_itab[] = { +  /* 0000 */ { UD_Iinvalid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0001 */ { UD_Iaaa, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0002 */ { UD_Iaad, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0003 */ { UD_Iaam, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0004 */ { UD_Iaas, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0005 */ { UD_Iadc, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0006 */ { UD_Iadc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0007 */ { UD_Iadc, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0008 */ { UD_Iadc, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0009 */ { UD_Iadc, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0010 */ { UD_Iadc, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0011 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0012 */ { UD_Iadc, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 0013 */ { UD_Iadc, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0014 */ { UD_Iadc, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0015 */ { UD_Iadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0016 */ { UD_Iadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0017 */ { UD_Iadd, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0018 */ { UD_Iadd, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0019 */ { UD_Iadd, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0020 */ { UD_Iadd, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0021 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0022 */ { UD_Iadd, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 0023 */ { UD_Iadd, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0024 */ { UD_Iadd, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0025 */ { UD_Iaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0026 */ { UD_Ivaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0027 */ { UD_Iaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0028 */ { UD_Ivaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0029 */ { UD_Iaddsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0030 */ { UD_Ivaddsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0031 */ { UD_Iaddss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0032 */ { UD_Ivaddss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0033 */ { UD_Iaddsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0034 */ { UD_Ivaddsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0035 */ { UD_Iaddsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0036 */ { UD_Ivaddsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0037 */ { UD_Iaesdec, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0038 */ { UD_Ivaesdec, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0039 */ { UD_Iaesdeclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0040 */ { UD_Ivaesdeclast, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0041 */ { UD_Iaesenc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0042 */ { UD_Ivaesenc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0043 */ { UD_Iaesenclast, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0044 */ { UD_Ivaesenclast, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0045 */ { UD_Iaesimc, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0046 */ { UD_Ivaesimc, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0047 */ { UD_Iaeskeygenassist, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0048 */ { UD_Ivaeskeygenassist, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0049 */ { UD_Iand, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0050 */ { UD_Iand, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0051 */ { UD_Iand, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0052 */ { UD_Iand, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0053 */ { UD_Iand, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0054 */ { UD_Iand, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0055 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0056 */ { UD_Iand, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 0057 */ { UD_Iand, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0058 */ { UD_Iand, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0059 */ { UD_Iandpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0060 */ { UD_Ivandpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0061 */ { UD_Iandps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0062 */ { UD_Ivandps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0063 */ { UD_Iandnpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0064 */ { UD_Ivandnpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0065 */ { UD_Iandnps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0066 */ { UD_Ivandnps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0067 */ { UD_Iarpl, O_Ew, O_Gw, O_NONE, O_NONE, P_aso }, +  /* 0068 */ { UD_Imovsxd, O_Gq, O_Ed, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexx|P_rexr|P_rexb }, +  /* 0069 */ { UD_Icall, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0070 */ { UD_Icall, O_Eq, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 0071 */ { UD_Icall, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0072 */ { UD_Icall, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0073 */ { UD_Icall, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0074 */ { UD_Icbw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0075 */ { UD_Icwde, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0076 */ { UD_Icdqe, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0077 */ { UD_Iclc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0078 */ { UD_Icld, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0079 */ { UD_Iclflush, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0080 */ { UD_Iclgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0081 */ { UD_Icli, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0082 */ { UD_Iclts, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0083 */ { UD_Icmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0084 */ { UD_Icmovo, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0085 */ { UD_Icmovno, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0086 */ { UD_Icmovb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0087 */ { UD_Icmovae, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0088 */ { UD_Icmovz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0089 */ { UD_Icmovnz, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0090 */ { UD_Icmovbe, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0091 */ { UD_Icmova, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0092 */ { UD_Icmovs, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0093 */ { UD_Icmovns, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0094 */ { UD_Icmovp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0095 */ { UD_Icmovnp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0096 */ { UD_Icmovl, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0097 */ { UD_Icmovge, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0098 */ { UD_Icmovle, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0099 */ { UD_Icmovg, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0100 */ { UD_Icmp, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0101 */ { UD_Icmp, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0102 */ { UD_Icmp, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0103 */ { UD_Icmp, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0104 */ { UD_Icmp, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0105 */ { UD_Icmp, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0106 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0107 */ { UD_Icmp, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 0108 */ { UD_Icmp, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0109 */ { UD_Icmp, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0110 */ { UD_Icmppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0111 */ { UD_Ivcmppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0112 */ { UD_Icmpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0113 */ { UD_Ivcmpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0114 */ { UD_Icmpsb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_seg }, +  /* 0115 */ { UD_Icmpsw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, +  /* 0116 */ { UD_Icmpsd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, +  /* 0117 */ { UD_Icmpsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0118 */ { UD_Ivcmpsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0119 */ { UD_Icmpsq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw|P_seg }, +  /* 0120 */ { UD_Icmpss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0121 */ { UD_Ivcmpss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0122 */ { UD_Icmpxchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0123 */ { UD_Icmpxchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0124 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0125 */ { UD_Icmpxchg8b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0126 */ { UD_Icmpxchg16b, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0127 */ { UD_Icomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0128 */ { UD_Ivcomisd, O_Vsd, O_Wsd, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0129 */ { UD_Icomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0130 */ { UD_Ivcomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0131 */ { UD_Icpuid, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0132 */ { UD_Icvtdq2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0133 */ { UD_Ivcvtdq2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0134 */ { UD_Icvtdq2ps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0135 */ { UD_Ivcvtdq2ps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0136 */ { UD_Icvtpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0137 */ { UD_Ivcvtpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0138 */ { UD_Icvtpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0139 */ { UD_Icvtpd2ps, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0140 */ { UD_Ivcvtpd2ps, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0141 */ { UD_Icvtpi2ps, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0142 */ { UD_Icvtpi2pd, O_V, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0143 */ { UD_Icvtps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0144 */ { UD_Ivcvtps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0145 */ { UD_Icvtps2pd, O_V, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0146 */ { UD_Ivcvtps2pd, O_Vx, O_Wdq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0147 */ { UD_Icvtps2pi, O_P, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0148 */ { UD_Icvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0149 */ { UD_Ivcvtsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0150 */ { UD_Icvtsd2ss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0151 */ { UD_Ivcvtsd2ss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0152 */ { UD_Icvtsi2sd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0153 */ { UD_Ivcvtsi2sd, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0154 */ { UD_Icvtsi2ss, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0155 */ { UD_Ivcvtsi2ss, O_Vx, O_Hx, O_Ey, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0156 */ { UD_Icvtss2sd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0157 */ { UD_Ivcvtss2sd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0158 */ { UD_Icvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0159 */ { UD_Ivcvtss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0160 */ { UD_Icvttpd2dq, O_Vdq, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0161 */ { UD_Ivcvttpd2dq, O_Vdq, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0162 */ { UD_Icvttpd2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0163 */ { UD_Icvttps2dq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0164 */ { UD_Ivcvttps2dq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0165 */ { UD_Icvttps2pi, O_P, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0166 */ { UD_Icvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0167 */ { UD_Ivcvttsd2si, O_Gy, O_MqU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0168 */ { UD_Icvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0169 */ { UD_Ivcvttss2si, O_Gy, O_MdU, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0170 */ { UD_Icwd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0171 */ { UD_Icdq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0172 */ { UD_Icqo, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0173 */ { UD_Idaa, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 0174 */ { UD_Idas, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 0175 */ { UD_Idec, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0176 */ { UD_Idec, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0177 */ { UD_Idec, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0178 */ { UD_Idec, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0179 */ { UD_Idec, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0180 */ { UD_Idec, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0181 */ { UD_Idec, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0182 */ { UD_Idec, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0183 */ { UD_Idec, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0184 */ { UD_Idec, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0185 */ { UD_Idiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0186 */ { UD_Idiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0187 */ { UD_Idivpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0188 */ { UD_Ivdivpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0189 */ { UD_Idivps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0190 */ { UD_Ivdivps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0191 */ { UD_Idivsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0192 */ { UD_Ivdivsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0193 */ { UD_Idivss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0194 */ { UD_Ivdivss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0195 */ { UD_Idppd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0196 */ { UD_Ivdppd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0197 */ { UD_Idpps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0198 */ { UD_Ivdpps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0199 */ { UD_Iemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0200 */ { UD_Ienter, O_Iw, O_Ib, O_NONE, O_NONE, P_def64 }, +  /* 0201 */ { UD_Iextractps, O_MdRy, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 0202 */ { UD_Ivextractps, O_MdRy, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 0203 */ { UD_If2xm1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0204 */ { UD_Ifabs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0205 */ { UD_Ifadd, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0206 */ { UD_Ifadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0207 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0208 */ { UD_Ifadd, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0209 */ { UD_Ifadd, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0210 */ { UD_Ifadd, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0211 */ { UD_Ifadd, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0212 */ { UD_Ifadd, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0213 */ { UD_Ifadd, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0214 */ { UD_Ifadd, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0215 */ { UD_Ifadd, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0216 */ { UD_Ifadd, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0217 */ { UD_Ifadd, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0218 */ { UD_Ifadd, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0219 */ { UD_Ifadd, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0220 */ { UD_Ifadd, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0221 */ { UD_Ifadd, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0222 */ { UD_Ifadd, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0223 */ { UD_Ifaddp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0224 */ { UD_Ifaddp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0225 */ { UD_Ifaddp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0226 */ { UD_Ifaddp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0227 */ { UD_Ifaddp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0228 */ { UD_Ifaddp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0229 */ { UD_Ifaddp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0230 */ { UD_Ifaddp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0231 */ { UD_Ifbld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0232 */ { UD_Ifbstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0233 */ { UD_Ifchs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0234 */ { UD_Ifclex, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0235 */ { UD_Ifcmovb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0236 */ { UD_Ifcmovb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0237 */ { UD_Ifcmovb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0238 */ { UD_Ifcmovb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0239 */ { UD_Ifcmovb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0240 */ { UD_Ifcmovb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0241 */ { UD_Ifcmovb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0242 */ { UD_Ifcmovb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0243 */ { UD_Ifcmove, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0244 */ { UD_Ifcmove, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0245 */ { UD_Ifcmove, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0246 */ { UD_Ifcmove, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0247 */ { UD_Ifcmove, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0248 */ { UD_Ifcmove, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0249 */ { UD_Ifcmove, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0250 */ { UD_Ifcmove, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0251 */ { UD_Ifcmovbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0252 */ { UD_Ifcmovbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0253 */ { UD_Ifcmovbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0254 */ { UD_Ifcmovbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0255 */ { UD_Ifcmovbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0256 */ { UD_Ifcmovbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0257 */ { UD_Ifcmovbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0258 */ { UD_Ifcmovbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0259 */ { UD_Ifcmovu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0260 */ { UD_Ifcmovu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0261 */ { UD_Ifcmovu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0262 */ { UD_Ifcmovu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0263 */ { UD_Ifcmovu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0264 */ { UD_Ifcmovu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0265 */ { UD_Ifcmovu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0266 */ { UD_Ifcmovu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0267 */ { UD_Ifcmovnb, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0268 */ { UD_Ifcmovnb, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0269 */ { UD_Ifcmovnb, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0270 */ { UD_Ifcmovnb, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0271 */ { UD_Ifcmovnb, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0272 */ { UD_Ifcmovnb, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0273 */ { UD_Ifcmovnb, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0274 */ { UD_Ifcmovnb, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0275 */ { UD_Ifcmovne, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0276 */ { UD_Ifcmovne, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0277 */ { UD_Ifcmovne, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0278 */ { UD_Ifcmovne, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0279 */ { UD_Ifcmovne, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0280 */ { UD_Ifcmovne, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0281 */ { UD_Ifcmovne, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0282 */ { UD_Ifcmovne, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0283 */ { UD_Ifcmovnbe, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0284 */ { UD_Ifcmovnbe, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0285 */ { UD_Ifcmovnbe, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0286 */ { UD_Ifcmovnbe, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0287 */ { UD_Ifcmovnbe, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0288 */ { UD_Ifcmovnbe, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0289 */ { UD_Ifcmovnbe, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0290 */ { UD_Ifcmovnbe, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0291 */ { UD_Ifcmovnu, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0292 */ { UD_Ifcmovnu, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0293 */ { UD_Ifcmovnu, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0294 */ { UD_Ifcmovnu, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0295 */ { UD_Ifcmovnu, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0296 */ { UD_Ifcmovnu, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0297 */ { UD_Ifcmovnu, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0298 */ { UD_Ifcmovnu, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0299 */ { UD_Ifucomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0300 */ { UD_Ifucomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0301 */ { UD_Ifucomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0302 */ { UD_Ifucomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0303 */ { UD_Ifucomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0304 */ { UD_Ifucomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0305 */ { UD_Ifucomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0306 */ { UD_Ifucomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0307 */ { UD_Ifcom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0308 */ { UD_Ifcom, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0309 */ { UD_Ifcom, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0310 */ { UD_Ifcom, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0311 */ { UD_Ifcom, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0312 */ { UD_Ifcom, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0313 */ { UD_Ifcom, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0314 */ { UD_Ifcom, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0315 */ { UD_Ifcom, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0316 */ { UD_Ifcom, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0317 */ { UD_Ifcom2, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0318 */ { UD_Ifcom2, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0319 */ { UD_Ifcom2, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0320 */ { UD_Ifcom2, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0321 */ { UD_Ifcom2, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0322 */ { UD_Ifcom2, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0323 */ { UD_Ifcom2, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0324 */ { UD_Ifcom2, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0325 */ { UD_Ifcomp3, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0326 */ { UD_Ifcomp3, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0327 */ { UD_Ifcomp3, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0328 */ { UD_Ifcomp3, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0329 */ { UD_Ifcomp3, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0330 */ { UD_Ifcomp3, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0331 */ { UD_Ifcomp3, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0332 */ { UD_Ifcomp3, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0333 */ { UD_Ifcomi, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0334 */ { UD_Ifcomi, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0335 */ { UD_Ifcomi, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0336 */ { UD_Ifcomi, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0337 */ { UD_Ifcomi, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0338 */ { UD_Ifcomi, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0339 */ { UD_Ifcomi, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0340 */ { UD_Ifcomi, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0341 */ { UD_Ifucomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0342 */ { UD_Ifucomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0343 */ { UD_Ifucomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0344 */ { UD_Ifucomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0345 */ { UD_Ifucomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0346 */ { UD_Ifucomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0347 */ { UD_Ifucomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0348 */ { UD_Ifucomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0349 */ { UD_Ifcomip, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0350 */ { UD_Ifcomip, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0351 */ { UD_Ifcomip, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0352 */ { UD_Ifcomip, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0353 */ { UD_Ifcomip, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0354 */ { UD_Ifcomip, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0355 */ { UD_Ifcomip, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0356 */ { UD_Ifcomip, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0357 */ { UD_Ifcomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0358 */ { UD_Ifcomp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0359 */ { UD_Ifcomp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0360 */ { UD_Ifcomp, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0361 */ { UD_Ifcomp, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0362 */ { UD_Ifcomp, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0363 */ { UD_Ifcomp, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0364 */ { UD_Ifcomp, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0365 */ { UD_Ifcomp, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0366 */ { UD_Ifcomp, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0367 */ { UD_Ifcomp5, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0368 */ { UD_Ifcomp5, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0369 */ { UD_Ifcomp5, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0370 */ { UD_Ifcomp5, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0371 */ { UD_Ifcomp5, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0372 */ { UD_Ifcomp5, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0373 */ { UD_Ifcomp5, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0374 */ { UD_Ifcomp5, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0375 */ { UD_Ifcompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0376 */ { UD_Ifcos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0377 */ { UD_Ifdecstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0378 */ { UD_Ifdiv, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0379 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0380 */ { UD_Ifdiv, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0381 */ { UD_Ifdiv, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0382 */ { UD_Ifdiv, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0383 */ { UD_Ifdiv, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0384 */ { UD_Ifdiv, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0385 */ { UD_Ifdiv, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0386 */ { UD_Ifdiv, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0387 */ { UD_Ifdiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0388 */ { UD_Ifdiv, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0389 */ { UD_Ifdiv, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0390 */ { UD_Ifdiv, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0391 */ { UD_Ifdiv, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0392 */ { UD_Ifdiv, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0393 */ { UD_Ifdiv, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0394 */ { UD_Ifdiv, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0395 */ { UD_Ifdiv, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0396 */ { UD_Ifdivp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0397 */ { UD_Ifdivp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0398 */ { UD_Ifdivp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0399 */ { UD_Ifdivp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0400 */ { UD_Ifdivp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0401 */ { UD_Ifdivp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0402 */ { UD_Ifdivp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0403 */ { UD_Ifdivp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0404 */ { UD_Ifdivr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0405 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0406 */ { UD_Ifdivr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0407 */ { UD_Ifdivr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0408 */ { UD_Ifdivr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0409 */ { UD_Ifdivr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0410 */ { UD_Ifdivr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0411 */ { UD_Ifdivr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0412 */ { UD_Ifdivr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0413 */ { UD_Ifdivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0414 */ { UD_Ifdivr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0415 */ { UD_Ifdivr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0416 */ { UD_Ifdivr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0417 */ { UD_Ifdivr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0418 */ { UD_Ifdivr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0419 */ { UD_Ifdivr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0420 */ { UD_Ifdivr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0421 */ { UD_Ifdivr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0422 */ { UD_Ifdivrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0423 */ { UD_Ifdivrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0424 */ { UD_Ifdivrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0425 */ { UD_Ifdivrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0426 */ { UD_Ifdivrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0427 */ { UD_Ifdivrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0428 */ { UD_Ifdivrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0429 */ { UD_Ifdivrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0430 */ { UD_Ifemms, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0431 */ { UD_Iffree, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0432 */ { UD_Iffree, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0433 */ { UD_Iffree, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0434 */ { UD_Iffree, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0435 */ { UD_Iffree, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0436 */ { UD_Iffree, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0437 */ { UD_Iffree, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0438 */ { UD_Iffree, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0439 */ { UD_Iffreep, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0440 */ { UD_Iffreep, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0441 */ { UD_Iffreep, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0442 */ { UD_Iffreep, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0443 */ { UD_Iffreep, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0444 */ { UD_Iffreep, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0445 */ { UD_Iffreep, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0446 */ { UD_Iffreep, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0447 */ { UD_Ificom, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0448 */ { UD_Ificom, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0449 */ { UD_Ificomp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0450 */ { UD_Ificomp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0451 */ { UD_Ifild, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0452 */ { UD_Ifild, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0453 */ { UD_Ifild, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0454 */ { UD_Ifincstp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0455 */ { UD_Ifninit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0456 */ { UD_Ifiadd, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0457 */ { UD_Ifiadd, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0458 */ { UD_Ifidivr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0459 */ { UD_Ifidivr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0460 */ { UD_Ifidiv, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0461 */ { UD_Ifidiv, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0462 */ { UD_Ifisub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0463 */ { UD_Ifisub, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0464 */ { UD_Ifisubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0465 */ { UD_Ifisubr, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0466 */ { UD_Ifist, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0467 */ { UD_Ifist, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0468 */ { UD_Ifistp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0469 */ { UD_Ifistp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0470 */ { UD_Ifistp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0471 */ { UD_Ifisttp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0472 */ { UD_Ifisttp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0473 */ { UD_Ifisttp, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0474 */ { UD_Ifld, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0475 */ { UD_Ifld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0476 */ { UD_Ifld, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0477 */ { UD_Ifld, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0478 */ { UD_Ifld, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0479 */ { UD_Ifld, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0480 */ { UD_Ifld, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0481 */ { UD_Ifld, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0482 */ { UD_Ifld, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0483 */ { UD_Ifld, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0484 */ { UD_Ifld, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0485 */ { UD_Ifld1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0486 */ { UD_Ifldl2t, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0487 */ { UD_Ifldl2e, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0488 */ { UD_Ifldpi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0489 */ { UD_Ifldlg2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0490 */ { UD_Ifldln2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0491 */ { UD_Ifldz, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0492 */ { UD_Ifldcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0493 */ { UD_Ifldenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0494 */ { UD_Ifmul, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0495 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0496 */ { UD_Ifmul, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0497 */ { UD_Ifmul, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0498 */ { UD_Ifmul, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0499 */ { UD_Ifmul, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0500 */ { UD_Ifmul, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0501 */ { UD_Ifmul, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0502 */ { UD_Ifmul, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0503 */ { UD_Ifmul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0504 */ { UD_Ifmul, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0505 */ { UD_Ifmul, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0506 */ { UD_Ifmul, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0507 */ { UD_Ifmul, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0508 */ { UD_Ifmul, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0509 */ { UD_Ifmul, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0510 */ { UD_Ifmul, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0511 */ { UD_Ifmul, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0512 */ { UD_Ifmulp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0513 */ { UD_Ifmulp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0514 */ { UD_Ifmulp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0515 */ { UD_Ifmulp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0516 */ { UD_Ifmulp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0517 */ { UD_Ifmulp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0518 */ { UD_Ifmulp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0519 */ { UD_Ifmulp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0520 */ { UD_Ifimul, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0521 */ { UD_Ifimul, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0522 */ { UD_Ifnop, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0523 */ { UD_Ifndisi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0524 */ { UD_Ifneni, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0525 */ { UD_Ifnsetpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0526 */ { UD_Ifpatan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0527 */ { UD_Ifprem, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0528 */ { UD_Ifprem1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0529 */ { UD_Ifptan, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0530 */ { UD_Ifrndint, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0531 */ { UD_Ifrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0532 */ { UD_Ifrstpm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0533 */ { UD_Ifnsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0534 */ { UD_Ifscale, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0535 */ { UD_Ifsin, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0536 */ { UD_Ifsincos, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0537 */ { UD_Ifsqrt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0538 */ { UD_Ifstp, O_Mt, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0539 */ { UD_Ifstp, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0540 */ { UD_Ifstp, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0541 */ { UD_Ifstp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0542 */ { UD_Ifstp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0543 */ { UD_Ifstp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0544 */ { UD_Ifstp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0545 */ { UD_Ifstp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0546 */ { UD_Ifstp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0547 */ { UD_Ifstp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0548 */ { UD_Ifstp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0549 */ { UD_Ifstp1, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0550 */ { UD_Ifstp1, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0551 */ { UD_Ifstp1, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0552 */ { UD_Ifstp1, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0553 */ { UD_Ifstp1, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0554 */ { UD_Ifstp1, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0555 */ { UD_Ifstp1, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0556 */ { UD_Ifstp1, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0557 */ { UD_Ifstp8, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0558 */ { UD_Ifstp8, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0559 */ { UD_Ifstp8, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0560 */ { UD_Ifstp8, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0561 */ { UD_Ifstp8, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0562 */ { UD_Ifstp8, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0563 */ { UD_Ifstp8, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0564 */ { UD_Ifstp8, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0565 */ { UD_Ifstp9, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0566 */ { UD_Ifstp9, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0567 */ { UD_Ifstp9, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0568 */ { UD_Ifstp9, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0569 */ { UD_Ifstp9, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0570 */ { UD_Ifstp9, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0571 */ { UD_Ifstp9, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0572 */ { UD_Ifstp9, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0573 */ { UD_Ifst, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0574 */ { UD_Ifst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0575 */ { UD_Ifst, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0576 */ { UD_Ifst, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0577 */ { UD_Ifst, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0578 */ { UD_Ifst, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0579 */ { UD_Ifst, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0580 */ { UD_Ifst, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0581 */ { UD_Ifst, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0582 */ { UD_Ifst, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0583 */ { UD_Ifnstcw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0584 */ { UD_Ifnstenv, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0585 */ { UD_Ifnstsw, O_Mw, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0586 */ { UD_Ifnstsw, O_AX, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0587 */ { UD_Ifsub, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0588 */ { UD_Ifsub, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0589 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0590 */ { UD_Ifsub, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0591 */ { UD_Ifsub, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0592 */ { UD_Ifsub, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0593 */ { UD_Ifsub, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0594 */ { UD_Ifsub, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0595 */ { UD_Ifsub, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0596 */ { UD_Ifsub, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0597 */ { UD_Ifsub, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0598 */ { UD_Ifsub, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0599 */ { UD_Ifsub, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0600 */ { UD_Ifsub, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0601 */ { UD_Ifsub, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0602 */ { UD_Ifsub, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0603 */ { UD_Ifsub, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0604 */ { UD_Ifsub, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0605 */ { UD_Ifsubp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0606 */ { UD_Ifsubp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0607 */ { UD_Ifsubp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0608 */ { UD_Ifsubp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0609 */ { UD_Ifsubp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0610 */ { UD_Ifsubp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0611 */ { UD_Ifsubp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0612 */ { UD_Ifsubp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0613 */ { UD_Ifsubr, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0614 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0615 */ { UD_Ifsubr, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0616 */ { UD_Ifsubr, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0617 */ { UD_Ifsubr, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0618 */ { UD_Ifsubr, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0619 */ { UD_Ifsubr, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0620 */ { UD_Ifsubr, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0621 */ { UD_Ifsubr, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0622 */ { UD_Ifsubr, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0623 */ { UD_Ifsubr, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0624 */ { UD_Ifsubr, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0625 */ { UD_Ifsubr, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0626 */ { UD_Ifsubr, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0627 */ { UD_Ifsubr, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0628 */ { UD_Ifsubr, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0629 */ { UD_Ifsubr, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0630 */ { UD_Ifsubr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0631 */ { UD_Ifsubrp, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0632 */ { UD_Ifsubrp, O_ST1, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0633 */ { UD_Ifsubrp, O_ST2, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0634 */ { UD_Ifsubrp, O_ST3, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0635 */ { UD_Ifsubrp, O_ST4, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0636 */ { UD_Ifsubrp, O_ST5, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0637 */ { UD_Ifsubrp, O_ST6, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0638 */ { UD_Ifsubrp, O_ST7, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0639 */ { UD_Iftst, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0640 */ { UD_Ifucom, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0641 */ { UD_Ifucom, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0642 */ { UD_Ifucom, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0643 */ { UD_Ifucom, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0644 */ { UD_Ifucom, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0645 */ { UD_Ifucom, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0646 */ { UD_Ifucom, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0647 */ { UD_Ifucom, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0648 */ { UD_Ifucomp, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0649 */ { UD_Ifucomp, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0650 */ { UD_Ifucomp, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0651 */ { UD_Ifucomp, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0652 */ { UD_Ifucomp, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0653 */ { UD_Ifucomp, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0654 */ { UD_Ifucomp, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0655 */ { UD_Ifucomp, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0656 */ { UD_Ifucompp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0657 */ { UD_Ifxam, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0658 */ { UD_Ifxch, O_ST0, O_ST0, O_NONE, O_NONE, P_none }, +  /* 0659 */ { UD_Ifxch, O_ST0, O_ST1, O_NONE, O_NONE, P_none }, +  /* 0660 */ { UD_Ifxch, O_ST0, O_ST2, O_NONE, O_NONE, P_none }, +  /* 0661 */ { UD_Ifxch, O_ST0, O_ST3, O_NONE, O_NONE, P_none }, +  /* 0662 */ { UD_Ifxch, O_ST0, O_ST4, O_NONE, O_NONE, P_none }, +  /* 0663 */ { UD_Ifxch, O_ST0, O_ST5, O_NONE, O_NONE, P_none }, +  /* 0664 */ { UD_Ifxch, O_ST0, O_ST6, O_NONE, O_NONE, P_none }, +  /* 0665 */ { UD_Ifxch, O_ST0, O_ST7, O_NONE, O_NONE, P_none }, +  /* 0666 */ { UD_Ifxch4, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0667 */ { UD_Ifxch4, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0668 */ { UD_Ifxch4, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0669 */ { UD_Ifxch4, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0670 */ { UD_Ifxch4, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0671 */ { UD_Ifxch4, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0672 */ { UD_Ifxch4, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0673 */ { UD_Ifxch4, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0674 */ { UD_Ifxch7, O_ST0, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0675 */ { UD_Ifxch7, O_ST1, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0676 */ { UD_Ifxch7, O_ST2, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0677 */ { UD_Ifxch7, O_ST3, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0678 */ { UD_Ifxch7, O_ST4, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0679 */ { UD_Ifxch7, O_ST5, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0680 */ { UD_Ifxch7, O_ST6, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0681 */ { UD_Ifxch7, O_ST7, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0682 */ { UD_Ifxrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0683 */ { UD_Ifxsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0684 */ { UD_Ifxtract, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0685 */ { UD_Ifyl2x, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0686 */ { UD_Ifyl2xp1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0687 */ { UD_Ihlt, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0688 */ { UD_Iidiv, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0689 */ { UD_Iidiv, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0690 */ { UD_Iin, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0691 */ { UD_Iin, O_eAX, O_Ib, O_NONE, O_NONE, P_oso }, +  /* 0692 */ { UD_Iin, O_AL, O_DX, O_NONE, O_NONE, P_none }, +  /* 0693 */ { UD_Iin, O_eAX, O_DX, O_NONE, O_NONE, P_oso }, +  /* 0694 */ { UD_Iimul, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0695 */ { UD_Iimul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0696 */ { UD_Iimul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0697 */ { UD_Iimul, O_Gv, O_Ev, O_Iz, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0698 */ { UD_Iimul, O_Gv, O_Ev, O_sIb, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0699 */ { UD_Iinc, O_R0z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0700 */ { UD_Iinc, O_R1z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0701 */ { UD_Iinc, O_R2z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0702 */ { UD_Iinc, O_R3z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0703 */ { UD_Iinc, O_R4z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0704 */ { UD_Iinc, O_R5z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0705 */ { UD_Iinc, O_R6z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0706 */ { UD_Iinc, O_R7z, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0707 */ { UD_Iinc, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0708 */ { UD_Iinc, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0709 */ { UD_Iinsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, +  /* 0710 */ { UD_Iinsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, +  /* 0711 */ { UD_Iinsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, +  /* 0712 */ { UD_Iint1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0713 */ { UD_Iint3, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0714 */ { UD_Iint, O_Ib, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0715 */ { UD_Iinto, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 0716 */ { UD_Iinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0717 */ { UD_Iinvept, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, +  /* 0718 */ { UD_Iinvept, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, +  /* 0719 */ { UD_Iinvlpg, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0720 */ { UD_Iinvlpga, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0721 */ { UD_Iinvvpid, O_Gd, O_Mo, O_NONE, O_NONE, P_none }, +  /* 0722 */ { UD_Iinvvpid, O_Gq, O_Mo, O_NONE, O_NONE, P_none }, +  /* 0723 */ { UD_Iiretw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0724 */ { UD_Iiretd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0725 */ { UD_Iiretq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0726 */ { UD_Ijo, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0727 */ { UD_Ijo, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0728 */ { UD_Ijno, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0729 */ { UD_Ijno, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0730 */ { UD_Ijb, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0731 */ { UD_Ijb, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0732 */ { UD_Ijae, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0733 */ { UD_Ijae, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0734 */ { UD_Ijz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0735 */ { UD_Ijz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0736 */ { UD_Ijnz, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0737 */ { UD_Ijnz, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0738 */ { UD_Ijbe, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0739 */ { UD_Ijbe, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0740 */ { UD_Ija, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0741 */ { UD_Ija, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0742 */ { UD_Ijs, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0743 */ { UD_Ijs, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0744 */ { UD_Ijns, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0745 */ { UD_Ijns, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0746 */ { UD_Ijp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0747 */ { UD_Ijp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0748 */ { UD_Ijnp, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0749 */ { UD_Ijnp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0750 */ { UD_Ijl, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0751 */ { UD_Ijl, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0752 */ { UD_Ijge, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0753 */ { UD_Ijge, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0754 */ { UD_Ijle, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0755 */ { UD_Ijle, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0756 */ { UD_Ijg, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0757 */ { UD_Ijg, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0758 */ { UD_Ijcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, +  /* 0759 */ { UD_Ijecxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, +  /* 0760 */ { UD_Ijrcxz, O_Jb, O_NONE, O_NONE, O_NONE, P_aso }, +  /* 0761 */ { UD_Ijmp, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 0762 */ { UD_Ijmp, O_Fv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0763 */ { UD_Ijmp, O_Jz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 0764 */ { UD_Ijmp, O_Av, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 0765 */ { UD_Ijmp, O_Jb, O_NONE, O_NONE, O_NONE, P_def64 }, +  /* 0766 */ { UD_Ilahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0767 */ { UD_Ilar, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0768 */ { UD_Ildmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0769 */ { UD_Ilds, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, +  /* 0770 */ { UD_Ilea, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0771 */ { UD_Iles, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, +  /* 0772 */ { UD_Ilfs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0773 */ { UD_Ilgs, O_Gz, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0774 */ { UD_Ilidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0775 */ { UD_Ilss, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0776 */ { UD_Ileave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0777 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0778 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0779 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0780 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0781 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0782 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0783 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0784 */ { UD_Ilfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0785 */ { UD_Ilgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0786 */ { UD_Illdt, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0787 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0788 */ { UD_Ilmsw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0789 */ { UD_Ilock, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0790 */ { UD_Ilodsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, +  /* 0791 */ { UD_Ilodsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0792 */ { UD_Ilodsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0793 */ { UD_Ilodsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0794 */ { UD_Iloopne, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0795 */ { UD_Iloope, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0796 */ { UD_Iloop, O_Jb, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0797 */ { UD_Ilsl, O_Gv, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0798 */ { UD_Iltr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0799 */ { UD_Imaskmovq, O_P, O_N, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0800 */ { UD_Imaxpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0801 */ { UD_Ivmaxpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0802 */ { UD_Imaxps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0803 */ { UD_Ivmaxps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0804 */ { UD_Imaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0805 */ { UD_Ivmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0806 */ { UD_Imaxss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0807 */ { UD_Ivmaxss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0808 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0809 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0810 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0811 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0812 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0813 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0814 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0815 */ { UD_Imfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0816 */ { UD_Iminpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0817 */ { UD_Ivminpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0818 */ { UD_Iminps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0819 */ { UD_Ivminps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0820 */ { UD_Iminsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0821 */ { UD_Ivminsd, O_Vx, O_Hx, O_MqU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0822 */ { UD_Iminss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0823 */ { UD_Ivminss, O_Vx, O_Hx, O_MdU, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0824 */ { UD_Imonitor, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0825 */ { UD_Imontmul, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0826 */ { UD_Imov, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0827 */ { UD_Imov, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0828 */ { UD_Imov, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0829 */ { UD_Imov, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0830 */ { UD_Imov, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0831 */ { UD_Imov, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0832 */ { UD_Imov, O_MwRv, O_S, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0833 */ { UD_Imov, O_S, O_MwRv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0834 */ { UD_Imov, O_AL, O_Ob, O_NONE, O_NONE, P_none }, +  /* 0835 */ { UD_Imov, O_rAX, O_Ov, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, +  /* 0836 */ { UD_Imov, O_Ob, O_AL, O_NONE, O_NONE, P_none }, +  /* 0837 */ { UD_Imov, O_Ov, O_rAX, O_NONE, O_NONE, P_aso|P_oso|P_rexw }, +  /* 0838 */ { UD_Imov, O_R0b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0839 */ { UD_Imov, O_R1b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0840 */ { UD_Imov, O_R2b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0841 */ { UD_Imov, O_R3b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0842 */ { UD_Imov, O_R4b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0843 */ { UD_Imov, O_R5b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0844 */ { UD_Imov, O_R6b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0845 */ { UD_Imov, O_R7b, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 0846 */ { UD_Imov, O_R0v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0847 */ { UD_Imov, O_R1v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0848 */ { UD_Imov, O_R2v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0849 */ { UD_Imov, O_R3v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0850 */ { UD_Imov, O_R4v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0851 */ { UD_Imov, O_R5v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0852 */ { UD_Imov, O_R6v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0853 */ { UD_Imov, O_R7v, O_Iv, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 0854 */ { UD_Imov, O_R, O_C, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, +  /* 0855 */ { UD_Imov, O_R, O_D, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, +  /* 0856 */ { UD_Imov, O_C, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, +  /* 0857 */ { UD_Imov, O_D, O_R, O_NONE, O_NONE, P_rexr|P_rexw|P_rexb }, +  /* 0858 */ { UD_Imovapd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0859 */ { UD_Ivmovapd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0860 */ { UD_Imovapd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0861 */ { UD_Ivmovapd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0862 */ { UD_Imovaps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0863 */ { UD_Ivmovaps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0864 */ { UD_Imovaps, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0865 */ { UD_Ivmovaps, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0866 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0867 */ { UD_Imovd, O_P, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0868 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0869 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0870 */ { UD_Imovd, O_V, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0871 */ { UD_Ivmovd, O_Vx, O_Ey, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0872 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0873 */ { UD_Imovd, O_Ey, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0874 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0875 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0876 */ { UD_Imovd, O_Ey, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0877 */ { UD_Ivmovd, O_Ey, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0878 */ { UD_Imovhpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0879 */ { UD_Ivmovhpd, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0880 */ { UD_Imovhpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0881 */ { UD_Ivmovhpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0882 */ { UD_Imovhps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0883 */ { UD_Ivmovhps, O_Vx, O_Hx, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0884 */ { UD_Imovhps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0885 */ { UD_Ivmovhps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0886 */ { UD_Imovlhps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0887 */ { UD_Ivmovlhps, O_Vx, O_Hx, O_Ux, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0888 */ { UD_Imovlpd, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0889 */ { UD_Ivmovlpd, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0890 */ { UD_Imovlpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0891 */ { UD_Ivmovlpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0892 */ { UD_Imovlps, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0893 */ { UD_Ivmovlps, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0894 */ { UD_Imovlps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0895 */ { UD_Ivmovlps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0896 */ { UD_Imovhlps, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0897 */ { UD_Ivmovhlps, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0898 */ { UD_Imovmskpd, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, +  /* 0899 */ { UD_Ivmovmskpd, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb|P_vexl }, +  /* 0900 */ { UD_Imovmskps, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, +  /* 0901 */ { UD_Ivmovmskps, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexb }, +  /* 0902 */ { UD_Imovntdq, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0903 */ { UD_Ivmovntdq, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0904 */ { UD_Imovnti, O_M, O_Gy, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0905 */ { UD_Imovntpd, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0906 */ { UD_Ivmovntpd, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0907 */ { UD_Imovntps, O_M, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0908 */ { UD_Ivmovntps, O_M, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0909 */ { UD_Imovntq, O_M, O_P, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0910 */ { UD_Imovq, O_P, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0911 */ { UD_Imovq, O_V, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0912 */ { UD_Ivmovq, O_Vx, O_Eq, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0913 */ { UD_Imovq, O_Eq, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0914 */ { UD_Imovq, O_Eq, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0915 */ { UD_Ivmovq, O_Eq, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0916 */ { UD_Imovq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0917 */ { UD_Ivmovq, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0918 */ { UD_Imovq, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0919 */ { UD_Ivmovq, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0920 */ { UD_Imovq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0921 */ { UD_Imovq, O_Q, O_P, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0922 */ { UD_Imovsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, +  /* 0923 */ { UD_Imovsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0924 */ { UD_Imovsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0925 */ { UD_Imovsd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0926 */ { UD_Imovsd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0927 */ { UD_Imovsq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 0928 */ { UD_Imovss, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0929 */ { UD_Imovss, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0930 */ { UD_Imovsx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0931 */ { UD_Imovsx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0932 */ { UD_Imovupd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0933 */ { UD_Ivmovupd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0934 */ { UD_Imovupd, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0935 */ { UD_Ivmovupd, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0936 */ { UD_Imovups, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0937 */ { UD_Ivmovups, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0938 */ { UD_Imovups, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0939 */ { UD_Ivmovups, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0940 */ { UD_Imovzx, O_Gv, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0941 */ { UD_Imovzx, O_Gy, O_Ew, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0942 */ { UD_Imul, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0943 */ { UD_Imul, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0944 */ { UD_Imulpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0945 */ { UD_Ivmulpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0946 */ { UD_Imulps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0947 */ { UD_Ivmulps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0948 */ { UD_Imulsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0949 */ { UD_Ivmulsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0950 */ { UD_Imulss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0951 */ { UD_Ivmulss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0952 */ { UD_Imwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 0953 */ { UD_Ineg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0954 */ { UD_Ineg, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0955 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0956 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0957 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0958 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0959 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0960 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0961 */ { UD_Inop, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0962 */ { UD_Inot, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0963 */ { UD_Inot, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0964 */ { UD_Ior, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0965 */ { UD_Ior, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0966 */ { UD_Ior, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0967 */ { UD_Ior, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0968 */ { UD_Ior, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 0969 */ { UD_Ior, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 0970 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0971 */ { UD_Ior, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0972 */ { UD_Ior, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0973 */ { UD_Ior, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 0974 */ { UD_Iorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0975 */ { UD_Ivorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0976 */ { UD_Iorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0977 */ { UD_Ivorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0978 */ { UD_Iout, O_Ib, O_AL, O_NONE, O_NONE, P_none }, +  /* 0979 */ { UD_Iout, O_Ib, O_eAX, O_NONE, O_NONE, P_oso }, +  /* 0980 */ { UD_Iout, O_DX, O_AL, O_NONE, O_NONE, P_none }, +  /* 0981 */ { UD_Iout, O_DX, O_eAX, O_NONE, O_NONE, P_oso }, +  /* 0982 */ { UD_Ioutsb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, +  /* 0983 */ { UD_Ioutsw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, +  /* 0984 */ { UD_Ioutsd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_oso|P_seg }, +  /* 0985 */ { UD_Ipacksswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0986 */ { UD_Ivpacksswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0987 */ { UD_Ipacksswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0988 */ { UD_Ipackssdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0989 */ { UD_Ivpackssdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0990 */ { UD_Ipackssdw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0991 */ { UD_Ipackuswb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0992 */ { UD_Ivpackuswb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0993 */ { UD_Ipackuswb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0994 */ { UD_Ipaddb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0995 */ { UD_Ivpaddb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 0996 */ { UD_Ipaddb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0997 */ { UD_Ipaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0998 */ { UD_Ipaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 0999 */ { UD_Ivpaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1000 */ { UD_Ipaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1001 */ { UD_Ipaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1002 */ { UD_Ivpaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1003 */ { UD_Ipaddsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1004 */ { UD_Ipaddsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1005 */ { UD_Ivpaddsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1006 */ { UD_Ipaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1007 */ { UD_Ipaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1008 */ { UD_Ivpaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1009 */ { UD_Ipaddusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1010 */ { UD_Ipaddusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1011 */ { UD_Ivpaddusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1012 */ { UD_Ipaddusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1013 */ { UD_Ipaddusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1014 */ { UD_Ivpaddusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1015 */ { UD_Ipand, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1016 */ { UD_Ivpand, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1017 */ { UD_Ipand, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1018 */ { UD_Ipandn, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1019 */ { UD_Ivpandn, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1020 */ { UD_Ipandn, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1021 */ { UD_Ipavgb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1022 */ { UD_Ivpavgb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1023 */ { UD_Ipavgb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1024 */ { UD_Ipavgw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1025 */ { UD_Ivpavgw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1026 */ { UD_Ipavgw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1027 */ { UD_Ipcmpeqb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1028 */ { UD_Ipcmpeqb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1029 */ { UD_Ivpcmpeqb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1030 */ { UD_Ipcmpeqw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1031 */ { UD_Ipcmpeqw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1032 */ { UD_Ivpcmpeqw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1033 */ { UD_Ipcmpeqd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1034 */ { UD_Ipcmpeqd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1035 */ { UD_Ivpcmpeqd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1036 */ { UD_Ipcmpgtb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1037 */ { UD_Ivpcmpgtb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1038 */ { UD_Ipcmpgtb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1039 */ { UD_Ipcmpgtw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1040 */ { UD_Ivpcmpgtw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1041 */ { UD_Ipcmpgtw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1042 */ { UD_Ipcmpgtd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1043 */ { UD_Ivpcmpgtd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1044 */ { UD_Ipcmpgtd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1045 */ { UD_Ipextrb, O_MbRv, O_V, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, +  /* 1046 */ { UD_Ivpextrb, O_MbRv, O_Vx, O_Ib, O_NONE, P_aso|P_rexx|P_rexr|P_rexb|P_def64 }, +  /* 1047 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, +  /* 1048 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, +  /* 1049 */ { UD_Ipextrd, O_Ed, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, +  /* 1050 */ { UD_Ivpextrd, O_Ed, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexw|P_rexb }, +  /* 1051 */ { UD_Ipextrq, O_Eq, O_V, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, +  /* 1052 */ { UD_Ivpextrq, O_Eq, O_Vx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexb|P_def64 }, +  /* 1053 */ { UD_Ipextrw, O_Gd, O_U, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, +  /* 1054 */ { UD_Ivpextrw, O_Gd, O_Ux, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexb }, +  /* 1055 */ { UD_Ipextrw, O_Gd, O_N, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1056 */ { UD_Ipextrw, O_MwRd, O_V, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, +  /* 1057 */ { UD_Ivpextrw, O_MwRd, O_Vx, O_Ib, O_NONE, P_aso|P_rexw|P_rexx|P_rexr|P_rexb }, +  /* 1058 */ { UD_Ipinsrb, O_V, O_MbRd, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1059 */ { UD_Ipinsrw, O_P, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1060 */ { UD_Ipinsrw, O_V, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1061 */ { UD_Ivpinsrw, O_Vx, O_MwRy, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1062 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1063 */ { UD_Ipinsrd, O_V, O_Ed, O_Ib, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1064 */ { UD_Ipinsrq, O_V, O_Eq, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1065 */ { UD_Ivpinsrb, O_V, O_H, O_MbRd, O_Ib, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1066 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1067 */ { UD_Ivpinsrd, O_V, O_H, O_Ed, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1068 */ { UD_Ivpinsrq, O_V, O_H, O_Eq, O_Ib, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1069 */ { UD_Ipmaddwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1070 */ { UD_Ipmaddwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1071 */ { UD_Ivpmaddwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1072 */ { UD_Ipmaxsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1073 */ { UD_Ivpmaxsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1074 */ { UD_Ipmaxsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1075 */ { UD_Ipmaxub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1076 */ { UD_Ipmaxub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1077 */ { UD_Ivpmaxub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1078 */ { UD_Ipminsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1079 */ { UD_Ivpminsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1080 */ { UD_Ipminsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1081 */ { UD_Ipminub, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1082 */ { UD_Ivpminub, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1083 */ { UD_Ipminub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1084 */ { UD_Ipmovmskb, O_Gd, O_U, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, +  /* 1085 */ { UD_Ivpmovmskb, O_Gd, O_Ux, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, +  /* 1086 */ { UD_Ipmovmskb, O_Gd, O_N, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexb }, +  /* 1087 */ { UD_Ipmulhuw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1088 */ { UD_Ipmulhuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1089 */ { UD_Ivpmulhuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1090 */ { UD_Ipmulhw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1091 */ { UD_Ivpmulhw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1092 */ { UD_Ipmulhw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1093 */ { UD_Ipmullw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1094 */ { UD_Ipmullw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1095 */ { UD_Ivpmullw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1096 */ { UD_Ipop, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1097 */ { UD_Ipop, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1098 */ { UD_Ipop, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1099 */ { UD_Ipop, O_GS, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1100 */ { UD_Ipop, O_FS, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1101 */ { UD_Ipop, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1102 */ { UD_Ipop, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1103 */ { UD_Ipop, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1104 */ { UD_Ipop, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1105 */ { UD_Ipop, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1106 */ { UD_Ipop, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1107 */ { UD_Ipop, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1108 */ { UD_Ipop, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1109 */ { UD_Ipop, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1110 */ { UD_Ipopa, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, +  /* 1111 */ { UD_Ipopad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, +  /* 1112 */ { UD_Ipopfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 1113 */ { UD_Ipopfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 1114 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 1115 */ { UD_Ipopfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 1116 */ { UD_Ipor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1117 */ { UD_Ivpor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1118 */ { UD_Ipor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1119 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1120 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1121 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1122 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1123 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1124 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1125 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1126 */ { UD_Iprefetch, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1127 */ { UD_Iprefetchnta, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1128 */ { UD_Iprefetcht0, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1129 */ { UD_Iprefetcht1, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1130 */ { UD_Iprefetcht2, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1131 */ { UD_Ipsadbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1132 */ { UD_Ivpsadbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1133 */ { UD_Ipsadbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1134 */ { UD_Ipshufw, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1135 */ { UD_Ipsllw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1136 */ { UD_Ipsllw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1137 */ { UD_Ipsllw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1138 */ { UD_Ipsllw, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1139 */ { UD_Ipslld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1140 */ { UD_Ipslld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1141 */ { UD_Ipslld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1142 */ { UD_Ipslld, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1143 */ { UD_Ipsllq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1144 */ { UD_Ipsllq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1145 */ { UD_Ipsllq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1146 */ { UD_Ipsllq, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1147 */ { UD_Ipsraw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1148 */ { UD_Ipsraw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1149 */ { UD_Ivpsraw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1150 */ { UD_Ipsraw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1151 */ { UD_Ivpsraw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1152 */ { UD_Ipsraw, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1153 */ { UD_Ipsrad, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1154 */ { UD_Ipsrad, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1155 */ { UD_Ivpsrad, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1156 */ { UD_Ipsrad, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1157 */ { UD_Ipsrad, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1158 */ { UD_Ivpsrad, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1159 */ { UD_Ipsrlw, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1160 */ { UD_Ipsrlw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1161 */ { UD_Ipsrlw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1162 */ { UD_Ivpsrlw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1163 */ { UD_Ipsrlw, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1164 */ { UD_Ivpsrlw, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1165 */ { UD_Ipsrld, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1166 */ { UD_Ipsrld, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1167 */ { UD_Ipsrld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1168 */ { UD_Ivpsrld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1169 */ { UD_Ipsrld, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1170 */ { UD_Ivpsrld, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1171 */ { UD_Ipsrlq, O_N, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1172 */ { UD_Ipsrlq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1173 */ { UD_Ipsrlq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1174 */ { UD_Ivpsrlq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1175 */ { UD_Ipsrlq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1176 */ { UD_Ivpsrlq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1177 */ { UD_Ipsubb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1178 */ { UD_Ivpsubb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1179 */ { UD_Ipsubb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1180 */ { UD_Ipsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1181 */ { UD_Ivpsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1182 */ { UD_Ipsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1183 */ { UD_Ipsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1184 */ { UD_Ipsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1185 */ { UD_Ivpsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1186 */ { UD_Ipsubsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1187 */ { UD_Ipsubsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1188 */ { UD_Ivpsubsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1189 */ { UD_Ipsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1190 */ { UD_Ipsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1191 */ { UD_Ivpsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1192 */ { UD_Ipsubusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1193 */ { UD_Ipsubusb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1194 */ { UD_Ivpsubusb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1195 */ { UD_Ipsubusw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1196 */ { UD_Ipsubusw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1197 */ { UD_Ivpsubusw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1198 */ { UD_Ipunpckhbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1199 */ { UD_Ivpunpckhbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1200 */ { UD_Ipunpckhbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1201 */ { UD_Ipunpckhwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1202 */ { UD_Ivpunpckhwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1203 */ { UD_Ipunpckhwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1204 */ { UD_Ipunpckhdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1205 */ { UD_Ivpunpckhdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1206 */ { UD_Ipunpckhdq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1207 */ { UD_Ipunpcklbw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1208 */ { UD_Ivpunpcklbw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1209 */ { UD_Ipunpcklbw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1210 */ { UD_Ipunpcklwd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1211 */ { UD_Ivpunpcklwd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1212 */ { UD_Ipunpcklwd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1213 */ { UD_Ipunpckldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1214 */ { UD_Ivpunpckldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1215 */ { UD_Ipunpckldq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1216 */ { UD_Ipi2fw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1217 */ { UD_Ipi2fd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1218 */ { UD_Ipf2iw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1219 */ { UD_Ipf2id, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1220 */ { UD_Ipfnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1221 */ { UD_Ipfpnacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1222 */ { UD_Ipfcmpge, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1223 */ { UD_Ipfmin, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1224 */ { UD_Ipfrcp, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1225 */ { UD_Ipfrsqrt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1226 */ { UD_Ipfsub, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1227 */ { UD_Ipfadd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1228 */ { UD_Ipfcmpgt, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1229 */ { UD_Ipfmax, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1230 */ { UD_Ipfrcpit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1231 */ { UD_Ipfrsqit1, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1232 */ { UD_Ipfsubr, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1233 */ { UD_Ipfacc, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1234 */ { UD_Ipfcmpeq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1235 */ { UD_Ipfmul, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1236 */ { UD_Ipfrcpit2, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1237 */ { UD_Ipmulhrw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1238 */ { UD_Ipswapd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1239 */ { UD_Ipavgusb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1240 */ { UD_Ipush, O_ES, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1241 */ { UD_Ipush, O_CS, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1242 */ { UD_Ipush, O_SS, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1243 */ { UD_Ipush, O_DS, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1244 */ { UD_Ipush, O_GS, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1245 */ { UD_Ipush, O_FS, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1246 */ { UD_Ipush, O_R0v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1247 */ { UD_Ipush, O_R1v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1248 */ { UD_Ipush, O_R2v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1249 */ { UD_Ipush, O_R3v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1250 */ { UD_Ipush, O_R4v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1251 */ { UD_Ipush, O_R5v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1252 */ { UD_Ipush, O_R6v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1253 */ { UD_Ipush, O_R7v, O_NONE, O_NONE, O_NONE, P_oso|P_rexb|P_def64 }, +  /* 1254 */ { UD_Ipush, O_sIz, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 1255 */ { UD_Ipush, O_Ev, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1256 */ { UD_Ipush, O_sIb, O_NONE, O_NONE, O_NONE, P_oso|P_def64 }, +  /* 1257 */ { UD_Ipusha, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, +  /* 1258 */ { UD_Ipushad, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_inv64 }, +  /* 1259 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 1260 */ { UD_Ipushfw, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, +  /* 1261 */ { UD_Ipushfd, O_NONE, O_NONE, O_NONE, O_NONE, P_oso }, +  /* 1262 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, +  /* 1263 */ { UD_Ipushfq, O_NONE, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_def64 }, +  /* 1264 */ { UD_Ipxor, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1265 */ { UD_Ivpxor, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1266 */ { UD_Ipxor, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1267 */ { UD_Ircl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1268 */ { UD_Ircl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1269 */ { UD_Ircl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1270 */ { UD_Ircl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1271 */ { UD_Ircl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1272 */ { UD_Ircl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1273 */ { UD_Ircr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1274 */ { UD_Ircr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1275 */ { UD_Ircr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1276 */ { UD_Ircr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1277 */ { UD_Ircr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1278 */ { UD_Ircr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1279 */ { UD_Irol, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1280 */ { UD_Irol, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1281 */ { UD_Irol, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1282 */ { UD_Irol, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1283 */ { UD_Irol, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1284 */ { UD_Irol, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1285 */ { UD_Iror, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1286 */ { UD_Iror, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1287 */ { UD_Iror, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1288 */ { UD_Iror, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1289 */ { UD_Iror, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1290 */ { UD_Iror, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1291 */ { UD_Ircpps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1292 */ { UD_Ivrcpps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1293 */ { UD_Ircpss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1294 */ { UD_Ivrcpss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1295 */ { UD_Irdmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1296 */ { UD_Irdpmc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1297 */ { UD_Irdtsc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1298 */ { UD_Irdtscp, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1299 */ { UD_Irepne, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1300 */ { UD_Irep, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1301 */ { UD_Iret, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1302 */ { UD_Iret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1303 */ { UD_Iretf, O_Iw, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1304 */ { UD_Iretf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1305 */ { UD_Irsm, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1306 */ { UD_Irsqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1307 */ { UD_Ivrsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1308 */ { UD_Irsqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1309 */ { UD_Ivrsqrtss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1310 */ { UD_Isahf, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1311 */ { UD_Isalc, O_NONE, O_NONE, O_NONE, O_NONE, P_inv64 }, +  /* 1312 */ { UD_Isar, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1313 */ { UD_Isar, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1314 */ { UD_Isar, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1315 */ { UD_Isar, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1316 */ { UD_Isar, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1317 */ { UD_Isar, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1318 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1319 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1320 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1321 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1322 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1323 */ { UD_Ishl, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1324 */ { UD_Ishl, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1325 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1326 */ { UD_Ishl, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1327 */ { UD_Ishl, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1328 */ { UD_Ishl, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1329 */ { UD_Ishl, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1330 */ { UD_Ishr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1331 */ { UD_Ishr, O_Eb, O_CL, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1332 */ { UD_Ishr, O_Ev, O_I1, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1333 */ { UD_Ishr, O_Eb, O_I1, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1334 */ { UD_Ishr, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1335 */ { UD_Ishr, O_Ev, O_CL, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1336 */ { UD_Isbb, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1337 */ { UD_Isbb, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1338 */ { UD_Isbb, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1339 */ { UD_Isbb, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1340 */ { UD_Isbb, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1341 */ { UD_Isbb, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 1342 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1343 */ { UD_Isbb, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1344 */ { UD_Isbb, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 1345 */ { UD_Isbb, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1346 */ { UD_Iscasb, O_NONE, O_NONE, O_NONE, O_NONE, P_strz }, +  /* 1347 */ { UD_Iscasw, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, +  /* 1348 */ { UD_Iscasd, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, +  /* 1349 */ { UD_Iscasq, O_NONE, O_NONE, O_NONE, O_NONE, P_strz|P_oso|P_rexw }, +  /* 1350 */ { UD_Iseto, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1351 */ { UD_Isetno, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1352 */ { UD_Isetb, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1353 */ { UD_Isetae, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1354 */ { UD_Isetz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1355 */ { UD_Isetnz, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1356 */ { UD_Isetbe, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1357 */ { UD_Iseta, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1358 */ { UD_Isets, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1359 */ { UD_Isetns, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1360 */ { UD_Isetp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1361 */ { UD_Isetnp, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1362 */ { UD_Isetl, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1363 */ { UD_Isetge, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1364 */ { UD_Isetle, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1365 */ { UD_Isetg, O_Eb, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1366 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1367 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1368 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1369 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1370 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1371 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1372 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1373 */ { UD_Isfence, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1374 */ { UD_Isgdt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1375 */ { UD_Ishld, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1376 */ { UD_Ishld, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1377 */ { UD_Ishrd, O_Ev, O_Gv, O_Ib, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1378 */ { UD_Ishrd, O_Ev, O_Gv, O_CL, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1379 */ { UD_Ishufpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1380 */ { UD_Ivshufpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1381 */ { UD_Ishufps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1382 */ { UD_Ivshufps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1383 */ { UD_Isidt, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1384 */ { UD_Isldt, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1385 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1386 */ { UD_Ismsw, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1387 */ { UD_Isqrtps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1388 */ { UD_Ivsqrtps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1389 */ { UD_Isqrtpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1390 */ { UD_Ivsqrtpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1391 */ { UD_Isqrtsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1392 */ { UD_Ivsqrtsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1393 */ { UD_Isqrtss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1394 */ { UD_Ivsqrtss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1395 */ { UD_Istc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1396 */ { UD_Istd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1397 */ { UD_Istgi, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1398 */ { UD_Isti, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1399 */ { UD_Iskinit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1400 */ { UD_Istmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1401 */ { UD_Ivstmxcsr, O_Md, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1402 */ { UD_Istosb, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg }, +  /* 1403 */ { UD_Istosw, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 1404 */ { UD_Istosd, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 1405 */ { UD_Istosq, O_NONE, O_NONE, O_NONE, O_NONE, P_str|P_seg|P_oso|P_rexw }, +  /* 1406 */ { UD_Istr, O_MwRv, O_NONE, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1407 */ { UD_Isub, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1408 */ { UD_Isub, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1409 */ { UD_Isub, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1410 */ { UD_Isub, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1411 */ { UD_Isub, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1412 */ { UD_Isub, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 1413 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1414 */ { UD_Isub, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1415 */ { UD_Isub, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 1416 */ { UD_Isub, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1417 */ { UD_Isubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1418 */ { UD_Ivsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1419 */ { UD_Isubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1420 */ { UD_Ivsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1421 */ { UD_Isubsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1422 */ { UD_Ivsubsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1423 */ { UD_Isubss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1424 */ { UD_Ivsubss, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1425 */ { UD_Iswapgs, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1426 */ { UD_Isyscall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1427 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1428 */ { UD_Isysenter, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1429 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1430 */ { UD_Isysexit, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1431 */ { UD_Isysret, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1432 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1433 */ { UD_Itest, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1434 */ { UD_Itest, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1435 */ { UD_Itest, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1436 */ { UD_Itest, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 1437 */ { UD_Itest, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1438 */ { UD_Itest, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1439 */ { UD_Itest, O_Ev, O_Iz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1440 */ { UD_Iucomisd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1441 */ { UD_Ivucomisd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1442 */ { UD_Iucomiss, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1443 */ { UD_Ivucomiss, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1444 */ { UD_Iud2, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1445 */ { UD_Iunpckhpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1446 */ { UD_Ivunpckhpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1447 */ { UD_Iunpckhps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1448 */ { UD_Ivunpckhps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1449 */ { UD_Iunpcklps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1450 */ { UD_Ivunpcklps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1451 */ { UD_Iunpcklpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1452 */ { UD_Ivunpcklpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1453 */ { UD_Iverr, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1454 */ { UD_Iverw, O_Ew, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1455 */ { UD_Ivmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1456 */ { UD_Irdrand, O_R, O_NONE, O_NONE, O_NONE, P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1457 */ { UD_Ivmclear, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1458 */ { UD_Ivmxon, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1459 */ { UD_Ivmptrld, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1460 */ { UD_Ivmptrst, O_Mq, O_NONE, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1461 */ { UD_Ivmlaunch, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1462 */ { UD_Ivmresume, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1463 */ { UD_Ivmxoff, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1464 */ { UD_Ivmread, O_Ey, O_Gy, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1465 */ { UD_Ivmwrite, O_Gy, O_Ey, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_def64 }, +  /* 1466 */ { UD_Ivmrun, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1467 */ { UD_Ivmmcall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1468 */ { UD_Ivmload, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1469 */ { UD_Ivmsave, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1470 */ { UD_Iwait, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1471 */ { UD_Iwbinvd, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1472 */ { UD_Iwrmsr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1473 */ { UD_Ixadd, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexx|P_rexb }, +  /* 1474 */ { UD_Ixadd, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1475 */ { UD_Ixchg, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1476 */ { UD_Ixchg, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1477 */ { UD_Ixchg, O_R0v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1478 */ { UD_Ixchg, O_R1v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1479 */ { UD_Ixchg, O_R2v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1480 */ { UD_Ixchg, O_R3v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1481 */ { UD_Ixchg, O_R4v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1482 */ { UD_Ixchg, O_R5v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1483 */ { UD_Ixchg, O_R6v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1484 */ { UD_Ixchg, O_R7v, O_rAX, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1485 */ { UD_Ixgetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1486 */ { UD_Ixlatb, O_NONE, O_NONE, O_NONE, O_NONE, P_rexw|P_seg }, +  /* 1487 */ { UD_Ixor, O_Eb, O_Gb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1488 */ { UD_Ixor, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1489 */ { UD_Ixor, O_Gb, O_Eb, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1490 */ { UD_Ixor, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1491 */ { UD_Ixor, O_AL, O_Ib, O_NONE, O_NONE, P_none }, +  /* 1492 */ { UD_Ixor, O_rAX, O_sIz, O_NONE, O_NONE, P_oso|P_rexw }, +  /* 1493 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1494 */ { UD_Ixor, O_Ev, O_sIz, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1495 */ { UD_Ixor, O_Eb, O_Ib, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_inv64 }, +  /* 1496 */ { UD_Ixor, O_Ev, O_sIb, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1497 */ { UD_Ixorpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1498 */ { UD_Ivxorpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1499 */ { UD_Ixorps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1500 */ { UD_Ivxorps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1501 */ { UD_Ixcryptecb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1502 */ { UD_Ixcryptcbc, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1503 */ { UD_Ixcryptctr, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1504 */ { UD_Ixcryptcfb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1505 */ { UD_Ixcryptofb, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1506 */ { UD_Ixrstor, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1507 */ { UD_Ixsave, O_M, O_NONE, O_NONE, O_NONE, P_aso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1508 */ { UD_Ixsetbv, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1509 */ { UD_Ixsha1, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1510 */ { UD_Ixsha256, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1511 */ { UD_Ixstore, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1512 */ { UD_Ipclmulqdq, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1513 */ { UD_Ivpclmulqdq, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1514 */ { UD_Igetsec, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1515 */ { UD_Imovdqa, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1516 */ { UD_Ivmovdqa, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1517 */ { UD_Imovdqa, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1518 */ { UD_Ivmovdqa, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1519 */ { UD_Imaskmovdqu, O_V, O_U, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1520 */ { UD_Ivmaskmovdqu, O_Vx, O_Ux, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1521 */ { UD_Imovdq2q, O_P, O_U, O_NONE, O_NONE, P_aso|P_rexb }, +  /* 1522 */ { UD_Imovdqu, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1523 */ { UD_Ivmovdqu, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1524 */ { UD_Imovdqu, O_W, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1525 */ { UD_Ivmovdqu, O_Wx, O_Vx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1526 */ { UD_Imovq2dq, O_V, O_N, O_NONE, O_NONE, P_aso|P_rexr }, +  /* 1527 */ { UD_Ipaddq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1528 */ { UD_Ipaddq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1529 */ { UD_Ivpaddq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1530 */ { UD_Ipsubq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1531 */ { UD_Ivpsubq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1532 */ { UD_Ipsubq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1533 */ { UD_Ipmuludq, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1534 */ { UD_Ipmuludq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1535 */ { UD_Ipshufhw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1536 */ { UD_Ivpshufhw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1537 */ { UD_Ipshuflw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1538 */ { UD_Ivpshuflw, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1539 */ { UD_Ipshufd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1540 */ { UD_Ivpshufd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1541 */ { UD_Ipslldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1542 */ { UD_Ivpslldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1543 */ { UD_Ipsrldq, O_U, O_Ib, O_NONE, O_NONE, P_rexb }, +  /* 1544 */ { UD_Ivpsrldq, O_Hx, O_Ux, O_Ib, O_NONE, P_rexb }, +  /* 1545 */ { UD_Ipunpckhqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1546 */ { UD_Ivpunpckhqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1547 */ { UD_Ipunpcklqdq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1548 */ { UD_Ivpunpcklqdq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1549 */ { UD_Ihaddpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1550 */ { UD_Ivhaddpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1551 */ { UD_Ihaddps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1552 */ { UD_Ivhaddps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1553 */ { UD_Ihsubpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1554 */ { UD_Ivhsubpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1555 */ { UD_Ihsubps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1556 */ { UD_Ivhsubps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1557 */ { UD_Iinsertps, O_V, O_Md, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1558 */ { UD_Ivinsertps, O_Vx, O_Hx, O_Md, O_Ib, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1559 */ { UD_Ilddqu, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1560 */ { UD_Ivlddqu, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1561 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1562 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1563 */ { UD_Imovddup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1564 */ { UD_Ivmovddup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1565 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1566 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1567 */ { UD_Imovshdup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1568 */ { UD_Ivmovshdup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1569 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1570 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1571 */ { UD_Imovsldup, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1572 */ { UD_Ivmovsldup, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1573 */ { UD_Ipabsb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1574 */ { UD_Ipabsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1575 */ { UD_Ivpabsb, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1576 */ { UD_Ipabsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1577 */ { UD_Ipabsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1578 */ { UD_Ivpabsw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1579 */ { UD_Ipabsd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1580 */ { UD_Ipabsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1581 */ { UD_Ivpabsd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1582 */ { UD_Ipshufb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1583 */ { UD_Ipshufb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1584 */ { UD_Ivpshufb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1585 */ { UD_Iphaddw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1586 */ { UD_Iphaddw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1587 */ { UD_Ivphaddw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1588 */ { UD_Iphaddd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1589 */ { UD_Iphaddd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1590 */ { UD_Ivphaddd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1591 */ { UD_Iphaddsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1592 */ { UD_Iphaddsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1593 */ { UD_Ivphaddsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1594 */ { UD_Ipmaddubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1595 */ { UD_Ipmaddubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1596 */ { UD_Ivpmaddubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1597 */ { UD_Iphsubw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1598 */ { UD_Iphsubw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1599 */ { UD_Ivphsubw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1600 */ { UD_Iphsubd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1601 */ { UD_Iphsubd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1602 */ { UD_Ivphsubd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1603 */ { UD_Iphsubsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1604 */ { UD_Iphsubsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1605 */ { UD_Ivphsubsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1606 */ { UD_Ipsignb, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1607 */ { UD_Ipsignb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1608 */ { UD_Ivpsignb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1609 */ { UD_Ipsignd, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1610 */ { UD_Ipsignd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1611 */ { UD_Ivpsignd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1612 */ { UD_Ipsignw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1613 */ { UD_Ipsignw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1614 */ { UD_Ivpsignw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1615 */ { UD_Ipmulhrsw, O_P, O_Q, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1616 */ { UD_Ipmulhrsw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1617 */ { UD_Ivpmulhrsw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1618 */ { UD_Ipalignr, O_P, O_Q, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1619 */ { UD_Ipalignr, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1620 */ { UD_Ivpalignr, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1621 */ { UD_Ipblendvb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1622 */ { UD_Ipmuldq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1623 */ { UD_Ivpmuldq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1624 */ { UD_Ipminsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1625 */ { UD_Ivpminsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1626 */ { UD_Ipminsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1627 */ { UD_Ivpminsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1628 */ { UD_Ipminuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1629 */ { UD_Ivpminuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1630 */ { UD_Ipminud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1631 */ { UD_Ivpminud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1632 */ { UD_Ipmaxsb, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1633 */ { UD_Ivpmaxsb, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1634 */ { UD_Ipmaxsd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1635 */ { UD_Ivpmaxsd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1636 */ { UD_Ipmaxud, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1637 */ { UD_Ivpmaxud, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1638 */ { UD_Ipmaxuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1639 */ { UD_Ivpmaxuw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1640 */ { UD_Ipmulld, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1641 */ { UD_Ivpmulld, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1642 */ { UD_Iphminposuw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1643 */ { UD_Ivphminposuw, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1644 */ { UD_Iroundps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1645 */ { UD_Ivroundps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1646 */ { UD_Iroundpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1647 */ { UD_Ivroundpd, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1648 */ { UD_Iroundss, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1649 */ { UD_Ivroundss, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1650 */ { UD_Iroundsd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1651 */ { UD_Ivroundsd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1652 */ { UD_Iblendpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1653 */ { UD_Ivblendpd, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1654 */ { UD_Iblendps, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1655 */ { UD_Ivblendps, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1656 */ { UD_Iblendvpd, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1657 */ { UD_Iblendvps, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1658 */ { UD_Ibound, O_Gv, O_M, O_NONE, O_NONE, P_aso|P_oso }, +  /* 1659 */ { UD_Ibsf, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1660 */ { UD_Ibsr, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1661 */ { UD_Ibswap, O_R0y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1662 */ { UD_Ibswap, O_R1y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1663 */ { UD_Ibswap, O_R2y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1664 */ { UD_Ibswap, O_R3y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1665 */ { UD_Ibswap, O_R4y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1666 */ { UD_Ibswap, O_R5y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1667 */ { UD_Ibswap, O_R6y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1668 */ { UD_Ibswap, O_R7y, O_NONE, O_NONE, O_NONE, P_oso|P_rexw|P_rexb }, +  /* 1669 */ { UD_Ibt, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1670 */ { UD_Ibt, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1671 */ { UD_Ibtc, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1672 */ { UD_Ibtc, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1673 */ { UD_Ibtr, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1674 */ { UD_Ibtr, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1675 */ { UD_Ibts, O_Ev, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1676 */ { UD_Ibts, O_Ev, O_Ib, O_NONE, O_NONE, P_aso|P_oso|P_rexw|P_rexr|P_rexx|P_rexb }, +  /* 1677 */ { UD_Ipblendw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1678 */ { UD_Ivpblendw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1679 */ { UD_Impsadbw, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1680 */ { UD_Ivmpsadbw, O_Vx, O_Hx, O_Wx, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1681 */ { UD_Imovntdqa, O_V, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1682 */ { UD_Ivmovntdqa, O_Vx, O_M, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, +  /* 1683 */ { UD_Ipackusdw, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1684 */ { UD_Ivpackusdw, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, +  /* 1685 */ { UD_Ipmovsxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1686 */ { UD_Ivpmovsxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1687 */ { UD_Ipmovsxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1688 */ { UD_Ivpmovsxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1689 */ { UD_Ipmovsxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1690 */ { UD_Ivpmovsxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1691 */ { UD_Ipmovsxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1692 */ { UD_Ivpmovsxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1693 */ { UD_Ipmovsxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1694 */ { UD_Ivpmovsxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1695 */ { UD_Ipmovsxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1696 */ { UD_Ipmovzxbw, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1697 */ { UD_Ivpmovzxbw, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1698 */ { UD_Ipmovzxbd, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1699 */ { UD_Ivpmovzxbd, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1700 */ { UD_Ipmovzxbq, O_V, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1701 */ { UD_Ivpmovzxbq, O_Vx, O_MwU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1702 */ { UD_Ipmovzxwd, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1703 */ { UD_Ivpmovzxwd, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1704 */ { UD_Ipmovzxwq, O_V, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1705 */ { UD_Ivpmovzxwq, O_Vx, O_MdU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1706 */ { UD_Ipmovzxdq, O_V, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1707 */ { UD_Ivpmovzxdq, O_Vx, O_MqU, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1708 */ { UD_Ipcmpeqq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1709 */ { UD_Ivpcmpeqq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1710 */ { UD_Ipopcnt, O_Gv, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1711 */ { UD_Iptest, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1712 */ { UD_Ivptest, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb|P_vexl }, +  /* 1713 */ { UD_Ipcmpestri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1714 */ { UD_Ivpcmpestri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1715 */ { UD_Ipcmpestrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1716 */ { UD_Ivpcmpestrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1717 */ { UD_Ipcmpgtq, O_V, O_W, O_NONE, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1718 */ { UD_Ivpcmpgtq, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1719 */ { UD_Ipcmpistri, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1720 */ { UD_Ivpcmpistri, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1721 */ { UD_Ipcmpistrm, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1722 */ { UD_Ivpcmpistrm, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1723 */ { UD_Imovbe, O_Gv, O_Mv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1724 */ { UD_Imovbe, O_Mv, O_Gv, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1725 */ { UD_Icrc32, O_Gy, O_Eb, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1726 */ { UD_Icrc32, O_Gy, O_Ev, O_NONE, O_NONE, P_aso|P_oso|P_rexr|P_rexw|P_rexx|P_rexb }, +  /* 1727 */ { UD_Ivbroadcastss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1728 */ { UD_Ivbroadcastsd, O_Vqq, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1729 */ { UD_Ivextractf128, O_Wdq, O_Vqq, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1730 */ { UD_Ivinsertf128, O_Vqq, O_Hqq, O_Wdq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1731 */ { UD_Ivmaskmovps, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1732 */ { UD_Ivmaskmovps, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1733 */ { UD_Ivmaskmovpd, O_V, O_H, O_M, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1734 */ { UD_Ivmaskmovpd, O_M, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1735 */ { UD_Ivpermilpd, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1736 */ { UD_Ivpermilpd, O_V, O_W, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1737 */ { UD_Ivpermilps, O_Vx, O_Hx, O_Wx, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1738 */ { UD_Ivpermilps, O_Vx, O_Wx, O_Ib, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1739 */ { UD_Ivperm2f128, O_Vqq, O_Hqq, O_Wqq, O_Ib, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1740 */ { UD_Ivtestps, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1741 */ { UD_Ivtestpd, O_Vx, O_Wx, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1742 */ { UD_Ivzeroupper, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1743 */ { UD_Ivzeroall, O_NONE, O_NONE, O_NONE, O_NONE, P_none }, +  /* 1744 */ { UD_Ivblendvpd, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1745 */ { UD_Ivblendvps, O_Vx, O_Hx, O_Wx, O_Lx, P_aso|P_rexr|P_rexx|P_rexb|P_vexl }, +  /* 1746 */ { UD_Ivmovsd, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1747 */ { UD_Ivmovsd, O_V, O_Mq, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1748 */ { UD_Ivmovsd, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1749 */ { UD_Ivmovsd, O_Mq, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1750 */ { UD_Ivmovss, O_V, O_H, O_U, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1751 */ { UD_Ivmovss, O_V, O_Md, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1752 */ { UD_Ivmovss, O_U, O_H, O_V, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1753 */ { UD_Ivmovss, O_Md, O_V, O_NONE, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1754 */ { UD_Ivpblendvb, O_V, O_H, O_W, O_L, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1755 */ { UD_Ivpsllw, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1756 */ { UD_Ivpsllw, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1757 */ { UD_Ivpslld, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1758 */ { UD_Ivpslld, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1759 */ { UD_Ivpsllq, O_V, O_H, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +  /* 1760 */ { UD_Ivpsllq, O_H, O_V, O_W, O_NONE, P_aso|P_rexr|P_rexx|P_rexb }, +}; + + +const char* ud_mnemonics_str[] = { +    "aaa", +    "aad", +    "aam", +    "aas", +    "adc", +    "add", +    "addpd", +    "addps", +    "addsd", +    "addss", +    "addsubpd", +    "addsubps", +    "aesdec", +    "aesdeclast", +    "aesenc", +    "aesenclast", +    "aesimc", +    "aeskeygenassist", +    "and", +    "andnpd", +    "andnps", +    "andpd", +    "andps", +    "arpl", +    "blendpd", +    "blendps", +    "blendvpd", +    "blendvps", +    "bound", +    "bsf", +    "bsr", +    "bswap", +    "bt", +    "btc", +    "btr", +    "bts", +    "call", +    "cbw", +    "cdq", +    "cdqe", +    "clc", +    "cld", +    "clflush", +    "clgi", +    "cli", +    "clts", +    "cmc", +    "cmova", +    "cmovae", +    "cmovb", +    "cmovbe", +    "cmovg", +    "cmovge", +    "cmovl", +    "cmovle", +    "cmovno", +    "cmovnp", +    "cmovns", +    "cmovnz", +    "cmovo", +    "cmovp", +    "cmovs", +    "cmovz", +    "cmp", +    "cmppd", +    "cmpps", +    "cmpsb", +    "cmpsd", +    "cmpsq", +    "cmpss", +    "cmpsw", +    "cmpxchg", +    "cmpxchg16b", +    "cmpxchg8b", +    "comisd", +    "comiss", +    "cpuid", +    "cqo", +    "crc32", +    "cvtdq2pd", +    "cvtdq2ps", +    "cvtpd2dq", +    "cvtpd2pi", +    "cvtpd2ps", +    "cvtpi2pd", +    "cvtpi2ps", +    "cvtps2dq", +    "cvtps2pd", +    "cvtps2pi", +    "cvtsd2si", +    "cvtsd2ss", +    "cvtsi2sd", +    "cvtsi2ss", +    "cvtss2sd", +    "cvtss2si", +    "cvttpd2dq", +    "cvttpd2pi", +    "cvttps2dq", +    "cvttps2pi", +    "cvttsd2si", +    "cvttss2si", +    "cwd", +    "cwde", +    "daa", +    "das", +    "dec", +    "div", +    "divpd", +    "divps", +    "divsd", +    "divss", +    "dppd", +    "dpps", +    "emms", +    "enter", +    "extractps", +    "f2xm1", +    "fabs", +    "fadd", +    "faddp", +    "fbld", +    "fbstp", +    "fchs", +    "fclex", +    "fcmovb", +    "fcmovbe", +    "fcmove", +    "fcmovnb", +    "fcmovnbe", +    "fcmovne", +    "fcmovnu", +    "fcmovu", +    "fcom", +    "fcom2", +    "fcomi", +    "fcomip", +    "fcomp", +    "fcomp3", +    "fcomp5", +    "fcompp", +    "fcos", +    "fdecstp", +    "fdiv", +    "fdivp", +    "fdivr", +    "fdivrp", +    "femms", +    "ffree", +    "ffreep", +    "fiadd", +    "ficom", +    "ficomp", +    "fidiv", +    "fidivr", +    "fild", +    "fimul", +    "fincstp", +    "fist", +    "fistp", +    "fisttp", +    "fisub", +    "fisubr", +    "fld", +    "fld1", +    "fldcw", +    "fldenv", +    "fldl2e", +    "fldl2t", +    "fldlg2", +    "fldln2", +    "fldpi", +    "fldz", +    "fmul", +    "fmulp", +    "fndisi", +    "fneni", +    "fninit", +    "fnop", +    "fnsave", +    "fnsetpm", +    "fnstcw", +    "fnstenv", +    "fnstsw", +    "fpatan", +    "fprem", +    "fprem1", +    "fptan", +    "frndint", +    "frstor", +    "frstpm", +    "fscale", +    "fsin", +    "fsincos", +    "fsqrt", +    "fst", +    "fstp", +    "fstp1", +    "fstp8", +    "fstp9", +    "fsub", +    "fsubp", +    "fsubr", +    "fsubrp", +    "ftst", +    "fucom", +    "fucomi", +    "fucomip", +    "fucomp", +    "fucompp", +    "fxam", +    "fxch", +    "fxch4", +    "fxch7", +    "fxrstor", +    "fxsave", +    "fxtract", +    "fyl2x", +    "fyl2xp1", +    "getsec", +    "haddpd", +    "haddps", +    "hlt", +    "hsubpd", +    "hsubps", +    "idiv", +    "imul", +    "in", +    "inc", +    "insb", +    "insd", +    "insertps", +    "insw", +    "int", +    "int1", +    "int3", +    "into", +    "invd", +    "invept", +    "invlpg", +    "invlpga", +    "invvpid", +    "iretd", +    "iretq", +    "iretw", +    "ja", +    "jae", +    "jb", +    "jbe", +    "jcxz", +    "jecxz", +    "jg", +    "jge", +    "jl", +    "jle", +    "jmp", +    "jno", +    "jnp", +    "jns", +    "jnz", +    "jo", +    "jp", +    "jrcxz", +    "js", +    "jz", +    "lahf", +    "lar", +    "lddqu", +    "ldmxcsr", +    "lds", +    "lea", +    "leave", +    "les", +    "lfence", +    "lfs", +    "lgdt", +    "lgs", +    "lidt", +    "lldt", +    "lmsw", +    "lock", +    "lodsb", +    "lodsd", +    "lodsq", +    "lodsw", +    "loop", +    "loope", +    "loopne", +    "lsl", +    "lss", +    "ltr", +    "maskmovdqu", +    "maskmovq", +    "maxpd", +    "maxps", +    "maxsd", +    "maxss", +    "mfence", +    "minpd", +    "minps", +    "minsd", +    "minss", +    "monitor", +    "montmul", +    "mov", +    "movapd", +    "movaps", +    "movbe", +    "movd", +    "movddup", +    "movdq2q", +    "movdqa", +    "movdqu", +    "movhlps", +    "movhpd", +    "movhps", +    "movlhps", +    "movlpd", +    "movlps", +    "movmskpd", +    "movmskps", +    "movntdq", +    "movntdqa", +    "movnti", +    "movntpd", +    "movntps", +    "movntq", +    "movq", +    "movq2dq", +    "movsb", +    "movsd", +    "movshdup", +    "movsldup", +    "movsq", +    "movss", +    "movsw", +    "movsx", +    "movsxd", +    "movupd", +    "movups", +    "movzx", +    "mpsadbw", +    "mul", +    "mulpd", +    "mulps", +    "mulsd", +    "mulss", +    "mwait", +    "neg", +    "nop", +    "not", +    "or", +    "orpd", +    "orps", +    "out", +    "outsb", +    "outsd", +    "outsw", +    "pabsb", +    "pabsd", +    "pabsw", +    "packssdw", +    "packsswb", +    "packusdw", +    "packuswb", +    "paddb", +    "paddd", +    "paddq", +    "paddsb", +    "paddsw", +    "paddusb", +    "paddusw", +    "paddw", +    "palignr", +    "pand", +    "pandn", +    "pavgb", +    "pavgusb", +    "pavgw", +    "pblendvb", +    "pblendw", +    "pclmulqdq", +    "pcmpeqb", +    "pcmpeqd", +    "pcmpeqq", +    "pcmpeqw", +    "pcmpestri", +    "pcmpestrm", +    "pcmpgtb", +    "pcmpgtd", +    "pcmpgtq", +    "pcmpgtw", +    "pcmpistri", +    "pcmpistrm", +    "pextrb", +    "pextrd", +    "pextrq", +    "pextrw", +    "pf2id", +    "pf2iw", +    "pfacc", +    "pfadd", +    "pfcmpeq", +    "pfcmpge", +    "pfcmpgt", +    "pfmax", +    "pfmin", +    "pfmul", +    "pfnacc", +    "pfpnacc", +    "pfrcp", +    "pfrcpit1", +    "pfrcpit2", +    "pfrsqit1", +    "pfrsqrt", +    "pfsub", +    "pfsubr", +    "phaddd", +    "phaddsw", +    "phaddw", +    "phminposuw", +    "phsubd", +    "phsubsw", +    "phsubw", +    "pi2fd", +    "pi2fw", +    "pinsrb", +    "pinsrd", +    "pinsrq", +    "pinsrw", +    "pmaddubsw", +    "pmaddwd", +    "pmaxsb", +    "pmaxsd", +    "pmaxsw", +    "pmaxub", +    "pmaxud", +    "pmaxuw", +    "pminsb", +    "pminsd", +    "pminsw", +    "pminub", +    "pminud", +    "pminuw", +    "pmovmskb", +    "pmovsxbd", +    "pmovsxbq", +    "pmovsxbw", +    "pmovsxdq", +    "pmovsxwd", +    "pmovsxwq", +    "pmovzxbd", +    "pmovzxbq", +    "pmovzxbw", +    "pmovzxdq", +    "pmovzxwd", +    "pmovzxwq", +    "pmuldq", +    "pmulhrsw", +    "pmulhrw", +    "pmulhuw", +    "pmulhw", +    "pmulld", +    "pmullw", +    "pmuludq", +    "pop", +    "popa", +    "popad", +    "popcnt", +    "popfd", +    "popfq", +    "popfw", +    "por", +    "prefetch", +    "prefetchnta", +    "prefetcht0", +    "prefetcht1", +    "prefetcht2", +    "psadbw", +    "pshufb", +    "pshufd", +    "pshufhw", +    "pshuflw", +    "pshufw", +    "psignb", +    "psignd", +    "psignw", +    "pslld", +    "pslldq", +    "psllq", +    "psllw", +    "psrad", +    "psraw", +    "psrld", +    "psrldq", +    "psrlq", +    "psrlw", +    "psubb", +    "psubd", +    "psubq", +    "psubsb", +    "psubsw", +    "psubusb", +    "psubusw", +    "psubw", +    "pswapd", +    "ptest", +    "punpckhbw", +    "punpckhdq", +    "punpckhqdq", +    "punpckhwd", +    "punpcklbw", +    "punpckldq", +    "punpcklqdq", +    "punpcklwd", +    "push", +    "pusha", +    "pushad", +    "pushfd", +    "pushfq", +    "pushfw", +    "pxor", +    "rcl", +    "rcpps", +    "rcpss", +    "rcr", +    "rdmsr", +    "rdpmc", +    "rdrand", +    "rdtsc", +    "rdtscp", +    "rep", +    "repne", +    "ret", +    "retf", +    "rol", +    "ror", +    "roundpd", +    "roundps", +    "roundsd", +    "roundss", +    "rsm", +    "rsqrtps", +    "rsqrtss", +    "sahf", +    "salc", +    "sar", +    "sbb", +    "scasb", +    "scasd", +    "scasq", +    "scasw", +    "seta", +    "setae", +    "setb", +    "setbe", +    "setg", +    "setge", +    "setl", +    "setle", +    "setno", +    "setnp", +    "setns", +    "setnz", +    "seto", +    "setp", +    "sets", +    "setz", +    "sfence", +    "sgdt", +    "shl", +    "shld", +    "shr", +    "shrd", +    "shufpd", +    "shufps", +    "sidt", +    "skinit", +    "sldt", +    "smsw", +    "sqrtpd", +    "sqrtps", +    "sqrtsd", +    "sqrtss", +    "stc", +    "std", +    "stgi", +    "sti", +    "stmxcsr", +    "stosb", +    "stosd", +    "stosq", +    "stosw", +    "str", +    "sub", +    "subpd", +    "subps", +    "subsd", +    "subss", +    "swapgs", +    "syscall", +    "sysenter", +    "sysexit", +    "sysret", +    "test", +    "ucomisd", +    "ucomiss", +    "ud2", +    "unpckhpd", +    "unpckhps", +    "unpcklpd", +    "unpcklps", +    "vaddpd", +    "vaddps", +    "vaddsd", +    "vaddss", +    "vaddsubpd", +    "vaddsubps", +    "vaesdec", +    "vaesdeclast", +    "vaesenc", +    "vaesenclast", +    "vaesimc", +    "vaeskeygenassist", +    "vandnpd", +    "vandnps", +    "vandpd", +    "vandps", +    "vblendpd", +    "vblendps", +    "vblendvpd", +    "vblendvps", +    "vbroadcastsd", +    "vbroadcastss", +    "vcmppd", +    "vcmpps", +    "vcmpsd", +    "vcmpss", +    "vcomisd", +    "vcomiss", +    "vcvtdq2pd", +    "vcvtdq2ps", +    "vcvtpd2dq", +    "vcvtpd2ps", +    "vcvtps2dq", +    "vcvtps2pd", +    "vcvtsd2si", +    "vcvtsd2ss", +    "vcvtsi2sd", +    "vcvtsi2ss", +    "vcvtss2sd", +    "vcvtss2si", +    "vcvttpd2dq", +    "vcvttps2dq", +    "vcvttsd2si", +    "vcvttss2si", +    "vdivpd", +    "vdivps", +    "vdivsd", +    "vdivss", +    "vdppd", +    "vdpps", +    "verr", +    "verw", +    "vextractf128", +    "vextractps", +    "vhaddpd", +    "vhaddps", +    "vhsubpd", +    "vhsubps", +    "vinsertf128", +    "vinsertps", +    "vlddqu", +    "vmaskmovdqu", +    "vmaskmovpd", +    "vmaskmovps", +    "vmaxpd", +    "vmaxps", +    "vmaxsd", +    "vmaxss", +    "vmcall", +    "vmclear", +    "vminpd", +    "vminps", +    "vminsd", +    "vminss", +    "vmlaunch", +    "vmload", +    "vmmcall", +    "vmovapd", +    "vmovaps", +    "vmovd", +    "vmovddup", +    "vmovdqa", +    "vmovdqu", +    "vmovhlps", +    "vmovhpd", +    "vmovhps", +    "vmovlhps", +    "vmovlpd", +    "vmovlps", +    "vmovmskpd", +    "vmovmskps", +    "vmovntdq", +    "vmovntdqa", +    "vmovntpd", +    "vmovntps", +    "vmovq", +    "vmovsd", +    "vmovshdup", +    "vmovsldup", +    "vmovss", +    "vmovupd", +    "vmovups", +    "vmpsadbw", +    "vmptrld", +    "vmptrst", +    "vmread", +    "vmresume", +    "vmrun", +    "vmsave", +    "vmulpd", +    "vmulps", +    "vmulsd", +    "vmulss", +    "vmwrite", +    "vmxoff", +    "vmxon", +    "vorpd", +    "vorps", +    "vpabsb", +    "vpabsd", +    "vpabsw", +    "vpackssdw", +    "vpacksswb", +    "vpackusdw", +    "vpackuswb", +    "vpaddb", +    "vpaddd", +    "vpaddq", +    "vpaddsb", +    "vpaddsw", +    "vpaddusb", +    "vpaddusw", +    "vpaddw", +    "vpalignr", +    "vpand", +    "vpandn", +    "vpavgb", +    "vpavgw", +    "vpblendvb", +    "vpblendw", +    "vpclmulqdq", +    "vpcmpeqb", +    "vpcmpeqd", +    "vpcmpeqq", +    "vpcmpeqw", +    "vpcmpestri", +    "vpcmpestrm", +    "vpcmpgtb", +    "vpcmpgtd", +    "vpcmpgtq", +    "vpcmpgtw", +    "vpcmpistri", +    "vpcmpistrm", +    "vperm2f128", +    "vpermilpd", +    "vpermilps", +    "vpextrb", +    "vpextrd", +    "vpextrq", +    "vpextrw", +    "vphaddd", +    "vphaddsw", +    "vphaddw", +    "vphminposuw", +    "vphsubd", +    "vphsubsw", +    "vphsubw", +    "vpinsrb", +    "vpinsrd", +    "vpinsrq", +    "vpinsrw", +    "vpmaddubsw", +    "vpmaddwd", +    "vpmaxsb", +    "vpmaxsd", +    "vpmaxsw", +    "vpmaxub", +    "vpmaxud", +    "vpmaxuw", +    "vpminsb", +    "vpminsd", +    "vpminsw", +    "vpminub", +    "vpminud", +    "vpminuw", +    "vpmovmskb", +    "vpmovsxbd", +    "vpmovsxbq", +    "vpmovsxbw", +    "vpmovsxwd", +    "vpmovsxwq", +    "vpmovzxbd", +    "vpmovzxbq", +    "vpmovzxbw", +    "vpmovzxdq", +    "vpmovzxwd", +    "vpmovzxwq", +    "vpmuldq", +    "vpmulhrsw", +    "vpmulhuw", +    "vpmulhw", +    "vpmulld", +    "vpmullw", +    "vpor", +    "vpsadbw", +    "vpshufb", +    "vpshufd", +    "vpshufhw", +    "vpshuflw", +    "vpsignb", +    "vpsignd", +    "vpsignw", +    "vpslld", +    "vpslldq", +    "vpsllq", +    "vpsllw", +    "vpsrad", +    "vpsraw", +    "vpsrld", +    "vpsrldq", +    "vpsrlq", +    "vpsrlw", +    "vpsubb", +    "vpsubd", +    "vpsubq", +    "vpsubsb", +    "vpsubsw", +    "vpsubusb", +    "vpsubusw", +    "vpsubw", +    "vptest", +    "vpunpckhbw", +    "vpunpckhdq", +    "vpunpckhqdq", +    "vpunpckhwd", +    "vpunpcklbw", +    "vpunpckldq", +    "vpunpcklqdq", +    "vpunpcklwd", +    "vpxor", +    "vrcpps", +    "vrcpss", +    "vroundpd", +    "vroundps", +    "vroundsd", +    "vroundss", +    "vrsqrtps", +    "vrsqrtss", +    "vshufpd", +    "vshufps", +    "vsqrtpd", +    "vsqrtps", +    "vsqrtsd", +    "vsqrtss", +    "vstmxcsr", +    "vsubpd", +    "vsubps", +    "vsubsd", +    "vsubss", +    "vtestpd", +    "vtestps", +    "vucomisd", +    "vucomiss", +    "vunpckhpd", +    "vunpckhps", +    "vunpcklpd", +    "vunpcklps", +    "vxorpd", +    "vxorps", +    "vzeroall", +    "vzeroupper", +    "wait", +    "wbinvd", +    "wrmsr", +    "xadd", +    "xchg", +    "xcryptcbc", +    "xcryptcfb", +    "xcryptctr", +    "xcryptecb", +    "xcryptofb", +    "xgetbv", +    "xlatb", +    "xor", +    "xorpd", +    "xorps", +    "xrstor", +    "xsave", +    "xsetbv", +    "xsha1", +    "xsha256", +    "xstore", +    "invalid", +    "3dnow", +    "none", +    "db", +    "pause" +}; diff --git a/src/3p/udis86/itab.h b/src/3p/udis86/itab.h new file mode 100644 index 0000000..3d54c43 --- /dev/null +++ b/src/3p/udis86/itab.h @@ -0,0 +1,939 @@ +#ifndef UD_ITAB_H +#define UD_ITAB_H + +/* itab.h -- generated by udis86:scripts/ud_itab.py, do no edit */ + +/* ud_table_type -- lookup table types (see decode.c) */ +enum ud_table_type { +    UD_TAB__OPC_VEX, +    UD_TAB__OPC_TABLE, +    UD_TAB__OPC_X87, +    UD_TAB__OPC_MOD, +    UD_TAB__OPC_RM, +    UD_TAB__OPC_OSIZE, +    UD_TAB__OPC_MODE, +    UD_TAB__OPC_VEX_L, +    UD_TAB__OPC_3DNOW, +    UD_TAB__OPC_REG, +    UD_TAB__OPC_ASIZE, +    UD_TAB__OPC_VEX_W, +    UD_TAB__OPC_SSE, +    UD_TAB__OPC_VENDOR +}; + +/* ud_mnemonic -- mnemonic constants */ +enum ud_mnemonic_code { +    UD_Iaaa, +    UD_Iaad, +    UD_Iaam, +    UD_Iaas, +    UD_Iadc, +    UD_Iadd, +    UD_Iaddpd, +    UD_Iaddps, +    UD_Iaddsd, +    UD_Iaddss, +    UD_Iaddsubpd, +    UD_Iaddsubps, +    UD_Iaesdec, +    UD_Iaesdeclast, +    UD_Iaesenc, +    UD_Iaesenclast, +    UD_Iaesimc, +    UD_Iaeskeygenassist, +    UD_Iand, +    UD_Iandnpd, +    UD_Iandnps, +    UD_Iandpd, +    UD_Iandps, +    UD_Iarpl, +    UD_Iblendpd, +    UD_Iblendps, +    UD_Iblendvpd, +    UD_Iblendvps, +    UD_Ibound, +    UD_Ibsf, +    UD_Ibsr, +    UD_Ibswap, +    UD_Ibt, +    UD_Ibtc, +    UD_Ibtr, +    UD_Ibts, +    UD_Icall, +    UD_Icbw, +    UD_Icdq, +    UD_Icdqe, +    UD_Iclc, +    UD_Icld, +    UD_Iclflush, +    UD_Iclgi, +    UD_Icli, +    UD_Iclts, +    UD_Icmc, +    UD_Icmova, +    UD_Icmovae, +    UD_Icmovb, +    UD_Icmovbe, +    UD_Icmovg, +    UD_Icmovge, +    UD_Icmovl, +    UD_Icmovle, +    UD_Icmovno, +    UD_Icmovnp, +    UD_Icmovns, +    UD_Icmovnz, +    UD_Icmovo, +    UD_Icmovp, +    UD_Icmovs, +    UD_Icmovz, +    UD_Icmp, +    UD_Icmppd, +    UD_Icmpps, +    UD_Icmpsb, +    UD_Icmpsd, +    UD_Icmpsq, +    UD_Icmpss, +    UD_Icmpsw, +    UD_Icmpxchg, +    UD_Icmpxchg16b, +    UD_Icmpxchg8b, +    UD_Icomisd, +    UD_Icomiss, +    UD_Icpuid, +    UD_Icqo, +    UD_Icrc32, +    UD_Icvtdq2pd, +    UD_Icvtdq2ps, +    UD_Icvtpd2dq, +    UD_Icvtpd2pi, +    UD_Icvtpd2ps, +    UD_Icvtpi2pd, +    UD_Icvtpi2ps, +    UD_Icvtps2dq, +    UD_Icvtps2pd, +    UD_Icvtps2pi, +    UD_Icvtsd2si, +    UD_Icvtsd2ss, +    UD_Icvtsi2sd, +    UD_Icvtsi2ss, +    UD_Icvtss2sd, +    UD_Icvtss2si, +    UD_Icvttpd2dq, +    UD_Icvttpd2pi, +    UD_Icvttps2dq, +    UD_Icvttps2pi, +    UD_Icvttsd2si, +    UD_Icvttss2si, +    UD_Icwd, +    UD_Icwde, +    UD_Idaa, +    UD_Idas, +    UD_Idec, +    UD_Idiv, +    UD_Idivpd, +    UD_Idivps, +    UD_Idivsd, +    UD_Idivss, +    UD_Idppd, +    UD_Idpps, +    UD_Iemms, +    UD_Ienter, +    UD_Iextractps, +    UD_If2xm1, +    UD_Ifabs, +    UD_Ifadd, +    UD_Ifaddp, +    UD_Ifbld, +    UD_Ifbstp, +    UD_Ifchs, +    UD_Ifclex, +    UD_Ifcmovb, +    UD_Ifcmovbe, +    UD_Ifcmove, +    UD_Ifcmovnb, +    UD_Ifcmovnbe, +    UD_Ifcmovne, +    UD_Ifcmovnu, +    UD_Ifcmovu, +    UD_Ifcom, +    UD_Ifcom2, +    UD_Ifcomi, +    UD_Ifcomip, +    UD_Ifcomp, +    UD_Ifcomp3, +    UD_Ifcomp5, +    UD_Ifcompp, +    UD_Ifcos, +    UD_Ifdecstp, +    UD_Ifdiv, +    UD_Ifdivp, +    UD_Ifdivr, +    UD_Ifdivrp, +    UD_Ifemms, +    UD_Iffree, +    UD_Iffreep, +    UD_Ifiadd, +    UD_Ificom, +    UD_Ificomp, +    UD_Ifidiv, +    UD_Ifidivr, +    UD_Ifild, +    UD_Ifimul, +    UD_Ifincstp, +    UD_Ifist, +    UD_Ifistp, +    UD_Ifisttp, +    UD_Ifisub, +    UD_Ifisubr, +    UD_Ifld, +    UD_Ifld1, +    UD_Ifldcw, +    UD_Ifldenv, +    UD_Ifldl2e, +    UD_Ifldl2t, +    UD_Ifldlg2, +    UD_Ifldln2, +    UD_Ifldpi, +    UD_Ifldz, +    UD_Ifmul, +    UD_Ifmulp, +    UD_Ifndisi, +    UD_Ifneni, +    UD_Ifninit, +    UD_Ifnop, +    UD_Ifnsave, +    UD_Ifnsetpm, +    UD_Ifnstcw, +    UD_Ifnstenv, +    UD_Ifnstsw, +    UD_Ifpatan, +    UD_Ifprem, +    UD_Ifprem1, +    UD_Ifptan, +    UD_Ifrndint, +    UD_Ifrstor, +    UD_Ifrstpm, +    UD_Ifscale, +    UD_Ifsin, +    UD_Ifsincos, +    UD_Ifsqrt, +    UD_Ifst, +    UD_Ifstp, +    UD_Ifstp1, +    UD_Ifstp8, +    UD_Ifstp9, +    UD_Ifsub, +    UD_Ifsubp, +    UD_Ifsubr, +    UD_Ifsubrp, +    UD_Iftst, +    UD_Ifucom, +    UD_Ifucomi, +    UD_Ifucomip, +    UD_Ifucomp, +    UD_Ifucompp, +    UD_Ifxam, +    UD_Ifxch, +    UD_Ifxch4, +    UD_Ifxch7, +    UD_Ifxrstor, +    UD_Ifxsave, +    UD_Ifxtract, +    UD_Ifyl2x, +    UD_Ifyl2xp1, +    UD_Igetsec, +    UD_Ihaddpd, +    UD_Ihaddps, +    UD_Ihlt, +    UD_Ihsubpd, +    UD_Ihsubps, +    UD_Iidiv, +    UD_Iimul, +    UD_Iin, +    UD_Iinc, +    UD_Iinsb, +    UD_Iinsd, +    UD_Iinsertps, +    UD_Iinsw, +    UD_Iint, +    UD_Iint1, +    UD_Iint3, +    UD_Iinto, +    UD_Iinvd, +    UD_Iinvept, +    UD_Iinvlpg, +    UD_Iinvlpga, +    UD_Iinvvpid, +    UD_Iiretd, +    UD_Iiretq, +    UD_Iiretw, +    UD_Ija, +    UD_Ijae, +    UD_Ijb, +    UD_Ijbe, +    UD_Ijcxz, +    UD_Ijecxz, +    UD_Ijg, +    UD_Ijge, +    UD_Ijl, +    UD_Ijle, +    UD_Ijmp, +    UD_Ijno, +    UD_Ijnp, +    UD_Ijns, +    UD_Ijnz, +    UD_Ijo, +    UD_Ijp, +    UD_Ijrcxz, +    UD_Ijs, +    UD_Ijz, +    UD_Ilahf, +    UD_Ilar, +    UD_Ilddqu, +    UD_Ildmxcsr, +    UD_Ilds, +    UD_Ilea, +    UD_Ileave, +    UD_Iles, +    UD_Ilfence, +    UD_Ilfs, +    UD_Ilgdt, +    UD_Ilgs, +    UD_Ilidt, +    UD_Illdt, +    UD_Ilmsw, +    UD_Ilock, +    UD_Ilodsb, +    UD_Ilodsd, +    UD_Ilodsq, +    UD_Ilodsw, +    UD_Iloop, +    UD_Iloope, +    UD_Iloopne, +    UD_Ilsl, +    UD_Ilss, +    UD_Iltr, +    UD_Imaskmovdqu, +    UD_Imaskmovq, +    UD_Imaxpd, +    UD_Imaxps, +    UD_Imaxsd, +    UD_Imaxss, +    UD_Imfence, +    UD_Iminpd, +    UD_Iminps, +    UD_Iminsd, +    UD_Iminss, +    UD_Imonitor, +    UD_Imontmul, +    UD_Imov, +    UD_Imovapd, +    UD_Imovaps, +    UD_Imovbe, +    UD_Imovd, +    UD_Imovddup, +    UD_Imovdq2q, +    UD_Imovdqa, +    UD_Imovdqu, +    UD_Imovhlps, +    UD_Imovhpd, +    UD_Imovhps, +    UD_Imovlhps, +    UD_Imovlpd, +    UD_Imovlps, +    UD_Imovmskpd, +    UD_Imovmskps, +    UD_Imovntdq, +    UD_Imovntdqa, +    UD_Imovnti, +    UD_Imovntpd, +    UD_Imovntps, +    UD_Imovntq, +    UD_Imovq, +    UD_Imovq2dq, +    UD_Imovsb, +    UD_Imovsd, +    UD_Imovshdup, +    UD_Imovsldup, +    UD_Imovsq, +    UD_Imovss, +    UD_Imovsw, +    UD_Imovsx, +    UD_Imovsxd, +    UD_Imovupd, +    UD_Imovups, +    UD_Imovzx, +    UD_Impsadbw, +    UD_Imul, +    UD_Imulpd, +    UD_Imulps, +    UD_Imulsd, +    UD_Imulss, +    UD_Imwait, +    UD_Ineg, +    UD_Inop, +    UD_Inot, +    UD_Ior, +    UD_Iorpd, +    UD_Iorps, +    UD_Iout, +    UD_Ioutsb, +    UD_Ioutsd, +    UD_Ioutsw, +    UD_Ipabsb, +    UD_Ipabsd, +    UD_Ipabsw, +    UD_Ipackssdw, +    UD_Ipacksswb, +    UD_Ipackusdw, +    UD_Ipackuswb, +    UD_Ipaddb, +    UD_Ipaddd, +    UD_Ipaddq, +    UD_Ipaddsb, +    UD_Ipaddsw, +    UD_Ipaddusb, +    UD_Ipaddusw, +    UD_Ipaddw, +    UD_Ipalignr, +    UD_Ipand, +    UD_Ipandn, +    UD_Ipavgb, +    UD_Ipavgusb, +    UD_Ipavgw, +    UD_Ipblendvb, +    UD_Ipblendw, +    UD_Ipclmulqdq, +    UD_Ipcmpeqb, +    UD_Ipcmpeqd, +    UD_Ipcmpeqq, +    UD_Ipcmpeqw, +    UD_Ipcmpestri, +    UD_Ipcmpestrm, +    UD_Ipcmpgtb, +    UD_Ipcmpgtd, +    UD_Ipcmpgtq, +    UD_Ipcmpgtw, +    UD_Ipcmpistri, +    UD_Ipcmpistrm, +    UD_Ipextrb, +    UD_Ipextrd, +    UD_Ipextrq, +    UD_Ipextrw, +    UD_Ipf2id, +    UD_Ipf2iw, +    UD_Ipfacc, +    UD_Ipfadd, +    UD_Ipfcmpeq, +    UD_Ipfcmpge, +    UD_Ipfcmpgt, +    UD_Ipfmax, +    UD_Ipfmin, +    UD_Ipfmul, +    UD_Ipfnacc, +    UD_Ipfpnacc, +    UD_Ipfrcp, +    UD_Ipfrcpit1, +    UD_Ipfrcpit2, +    UD_Ipfrsqit1, +    UD_Ipfrsqrt, +    UD_Ipfsub, +    UD_Ipfsubr, +    UD_Iphaddd, +    UD_Iphaddsw, +    UD_Iphaddw, +    UD_Iphminposuw, +    UD_Iphsubd, +    UD_Iphsubsw, +    UD_Iphsubw, +    UD_Ipi2fd, +    UD_Ipi2fw, +    UD_Ipinsrb, +    UD_Ipinsrd, +    UD_Ipinsrq, +    UD_Ipinsrw, +    UD_Ipmaddubsw, +    UD_Ipmaddwd, +    UD_Ipmaxsb, +    UD_Ipmaxsd, +    UD_Ipmaxsw, +    UD_Ipmaxub, +    UD_Ipmaxud, +    UD_Ipmaxuw, +    UD_Ipminsb, +    UD_Ipminsd, +    UD_Ipminsw, +    UD_Ipminub, +    UD_Ipminud, +    UD_Ipminuw, +    UD_Ipmovmskb, +    UD_Ipmovsxbd, +    UD_Ipmovsxbq, +    UD_Ipmovsxbw, +    UD_Ipmovsxdq, +    UD_Ipmovsxwd, +    UD_Ipmovsxwq, +    UD_Ipmovzxbd, +    UD_Ipmovzxbq, +    UD_Ipmovzxbw, +    UD_Ipmovzxdq, +    UD_Ipmovzxwd, +    UD_Ipmovzxwq, +    UD_Ipmuldq, +    UD_Ipmulhrsw, +    UD_Ipmulhrw, +    UD_Ipmulhuw, +    UD_Ipmulhw, +    UD_Ipmulld, +    UD_Ipmullw, +    UD_Ipmuludq, +    UD_Ipop, +    UD_Ipopa, +    UD_Ipopad, +    UD_Ipopcnt, +    UD_Ipopfd, +    UD_Ipopfq, +    UD_Ipopfw, +    UD_Ipor, +    UD_Iprefetch, +    UD_Iprefetchnta, +    UD_Iprefetcht0, +    UD_Iprefetcht1, +    UD_Iprefetcht2, +    UD_Ipsadbw, +    UD_Ipshufb, +    UD_Ipshufd, +    UD_Ipshufhw, +    UD_Ipshuflw, +    UD_Ipshufw, +    UD_Ipsignb, +    UD_Ipsignd, +    UD_Ipsignw, +    UD_Ipslld, +    UD_Ipslldq, +    UD_Ipsllq, +    UD_Ipsllw, +    UD_Ipsrad, +    UD_Ipsraw, +    UD_Ipsrld, +    UD_Ipsrldq, +    UD_Ipsrlq, +    UD_Ipsrlw, +    UD_Ipsubb, +    UD_Ipsubd, +    UD_Ipsubq, +    UD_Ipsubsb, +    UD_Ipsubsw, +    UD_Ipsubusb, +    UD_Ipsubusw, +    UD_Ipsubw, +    UD_Ipswapd, +    UD_Iptest, +    UD_Ipunpckhbw, +    UD_Ipunpckhdq, +    UD_Ipunpckhqdq, +    UD_Ipunpckhwd, +    UD_Ipunpcklbw, +    UD_Ipunpckldq, +    UD_Ipunpcklqdq, +    UD_Ipunpcklwd, +    UD_Ipush, +    UD_Ipusha, +    UD_Ipushad, +    UD_Ipushfd, +    UD_Ipushfq, +    UD_Ipushfw, +    UD_Ipxor, +    UD_Ircl, +    UD_Ircpps, +    UD_Ircpss, +    UD_Ircr, +    UD_Irdmsr, +    UD_Irdpmc, +    UD_Irdrand, +    UD_Irdtsc, +    UD_Irdtscp, +    UD_Irep, +    UD_Irepne, +    UD_Iret, +    UD_Iretf, +    UD_Irol, +    UD_Iror, +    UD_Iroundpd, +    UD_Iroundps, +    UD_Iroundsd, +    UD_Iroundss, +    UD_Irsm, +    UD_Irsqrtps, +    UD_Irsqrtss, +    UD_Isahf, +    UD_Isalc, +    UD_Isar, +    UD_Isbb, +    UD_Iscasb, +    UD_Iscasd, +    UD_Iscasq, +    UD_Iscasw, +    UD_Iseta, +    UD_Isetae, +    UD_Isetb, +    UD_Isetbe, +    UD_Isetg, +    UD_Isetge, +    UD_Isetl, +    UD_Isetle, +    UD_Isetno, +    UD_Isetnp, +    UD_Isetns, +    UD_Isetnz, +    UD_Iseto, +    UD_Isetp, +    UD_Isets, +    UD_Isetz, +    UD_Isfence, +    UD_Isgdt, +    UD_Ishl, +    UD_Ishld, +    UD_Ishr, +    UD_Ishrd, +    UD_Ishufpd, +    UD_Ishufps, +    UD_Isidt, +    UD_Iskinit, +    UD_Isldt, +    UD_Ismsw, +    UD_Isqrtpd, +    UD_Isqrtps, +    UD_Isqrtsd, +    UD_Isqrtss, +    UD_Istc, +    UD_Istd, +    UD_Istgi, +    UD_Isti, +    UD_Istmxcsr, +    UD_Istosb, +    UD_Istosd, +    UD_Istosq, +    UD_Istosw, +    UD_Istr, +    UD_Isub, +    UD_Isubpd, +    UD_Isubps, +    UD_Isubsd, +    UD_Isubss, +    UD_Iswapgs, +    UD_Isyscall, +    UD_Isysenter, +    UD_Isysexit, +    UD_Isysret, +    UD_Itest, +    UD_Iucomisd, +    UD_Iucomiss, +    UD_Iud2, +    UD_Iunpckhpd, +    UD_Iunpckhps, +    UD_Iunpcklpd, +    UD_Iunpcklps, +    UD_Ivaddpd, +    UD_Ivaddps, +    UD_Ivaddsd, +    UD_Ivaddss, +    UD_Ivaddsubpd, +    UD_Ivaddsubps, +    UD_Ivaesdec, +    UD_Ivaesdeclast, +    UD_Ivaesenc, +    UD_Ivaesenclast, +    UD_Ivaesimc, +    UD_Ivaeskeygenassist, +    UD_Ivandnpd, +    UD_Ivandnps, +    UD_Ivandpd, +    UD_Ivandps, +    UD_Ivblendpd, +    UD_Ivblendps, +    UD_Ivblendvpd, +    UD_Ivblendvps, +    UD_Ivbroadcastsd, +    UD_Ivbroadcastss, +    UD_Ivcmppd, +    UD_Ivcmpps, +    UD_Ivcmpsd, +    UD_Ivcmpss, +    UD_Ivcomisd, +    UD_Ivcomiss, +    UD_Ivcvtdq2pd, +    UD_Ivcvtdq2ps, +    UD_Ivcvtpd2dq, +    UD_Ivcvtpd2ps, +    UD_Ivcvtps2dq, +    UD_Ivcvtps2pd, +    UD_Ivcvtsd2si, +    UD_Ivcvtsd2ss, +    UD_Ivcvtsi2sd, +    UD_Ivcvtsi2ss, +    UD_Ivcvtss2sd, +    UD_Ivcvtss2si, +    UD_Ivcvttpd2dq, +    UD_Ivcvttps2dq, +    UD_Ivcvttsd2si, +    UD_Ivcvttss2si, +    UD_Ivdivpd, +    UD_Ivdivps, +    UD_Ivdivsd, +    UD_Ivdivss, +    UD_Ivdppd, +    UD_Ivdpps, +    UD_Iverr, +    UD_Iverw, +    UD_Ivextractf128, +    UD_Ivextractps, +    UD_Ivhaddpd, +    UD_Ivhaddps, +    UD_Ivhsubpd, +    UD_Ivhsubps, +    UD_Ivinsertf128, +    UD_Ivinsertps, +    UD_Ivlddqu, +    UD_Ivmaskmovdqu, +    UD_Ivmaskmovpd, +    UD_Ivmaskmovps, +    UD_Ivmaxpd, +    UD_Ivmaxps, +    UD_Ivmaxsd, +    UD_Ivmaxss, +    UD_Ivmcall, +    UD_Ivmclear, +    UD_Ivminpd, +    UD_Ivminps, +    UD_Ivminsd, +    UD_Ivminss, +    UD_Ivmlaunch, +    UD_Ivmload, +    UD_Ivmmcall, +    UD_Ivmovapd, +    UD_Ivmovaps, +    UD_Ivmovd, +    UD_Ivmovddup, +    UD_Ivmovdqa, +    UD_Ivmovdqu, +    UD_Ivmovhlps, +    UD_Ivmovhpd, +    UD_Ivmovhps, +    UD_Ivmovlhps, +    UD_Ivmovlpd, +    UD_Ivmovlps, +    UD_Ivmovmskpd, +    UD_Ivmovmskps, +    UD_Ivmovntdq, +    UD_Ivmovntdqa, +    UD_Ivmovntpd, +    UD_Ivmovntps, +    UD_Ivmovq, +    UD_Ivmovsd, +    UD_Ivmovshdup, +    UD_Ivmovsldup, +    UD_Ivmovss, +    UD_Ivmovupd, +    UD_Ivmovups, +    UD_Ivmpsadbw, +    UD_Ivmptrld, +    UD_Ivmptrst, +    UD_Ivmread, +    UD_Ivmresume, +    UD_Ivmrun, +    UD_Ivmsave, +    UD_Ivmulpd, +    UD_Ivmulps, +    UD_Ivmulsd, +    UD_Ivmulss, +    UD_Ivmwrite, +    UD_Ivmxoff, +    UD_Ivmxon, +    UD_Ivorpd, +    UD_Ivorps, +    UD_Ivpabsb, +    UD_Ivpabsd, +    UD_Ivpabsw, +    UD_Ivpackssdw, +    UD_Ivpacksswb, +    UD_Ivpackusdw, +    UD_Ivpackuswb, +    UD_Ivpaddb, +    UD_Ivpaddd, +    UD_Ivpaddq, +    UD_Ivpaddsb, +    UD_Ivpaddsw, +    UD_Ivpaddusb, +    UD_Ivpaddusw, +    UD_Ivpaddw, +    UD_Ivpalignr, +    UD_Ivpand, +    UD_Ivpandn, +    UD_Ivpavgb, +    UD_Ivpavgw, +    UD_Ivpblendvb, +    UD_Ivpblendw, +    UD_Ivpclmulqdq, +    UD_Ivpcmpeqb, +    UD_Ivpcmpeqd, +    UD_Ivpcmpeqq, +    UD_Ivpcmpeqw, +    UD_Ivpcmpestri, +    UD_Ivpcmpestrm, +    UD_Ivpcmpgtb, +    UD_Ivpcmpgtd, +    UD_Ivpcmpgtq, +    UD_Ivpcmpgtw, +    UD_Ivpcmpistri, +    UD_Ivpcmpistrm, +    UD_Ivperm2f128, +    UD_Ivpermilpd, +    UD_Ivpermilps, +    UD_Ivpextrb, +    UD_Ivpextrd, +    UD_Ivpextrq, +    UD_Ivpextrw, +    UD_Ivphaddd, +    UD_Ivphaddsw, +    UD_Ivphaddw, +    UD_Ivphminposuw, +    UD_Ivphsubd, +    UD_Ivphsubsw, +    UD_Ivphsubw, +    UD_Ivpinsrb, +    UD_Ivpinsrd, +    UD_Ivpinsrq, +    UD_Ivpinsrw, +    UD_Ivpmaddubsw, +    UD_Ivpmaddwd, +    UD_Ivpmaxsb, +    UD_Ivpmaxsd, +    UD_Ivpmaxsw, +    UD_Ivpmaxub, +    UD_Ivpmaxud, +    UD_Ivpmaxuw, +    UD_Ivpminsb, +    UD_Ivpminsd, +    UD_Ivpminsw, +    UD_Ivpminub, +    UD_Ivpminud, +    UD_Ivpminuw, +    UD_Ivpmovmskb, +    UD_Ivpmovsxbd, +    UD_Ivpmovsxbq, +    UD_Ivpmovsxbw, +    UD_Ivpmovsxwd, +    UD_Ivpmovsxwq, +    UD_Ivpmovzxbd, +    UD_Ivpmovzxbq, +    UD_Ivpmovzxbw, +    UD_Ivpmovzxdq, +    UD_Ivpmovzxwd, +    UD_Ivpmovzxwq, +    UD_Ivpmuldq, +    UD_Ivpmulhrsw, +    UD_Ivpmulhuw, +    UD_Ivpmulhw, +    UD_Ivpmulld, +    UD_Ivpmullw, +    UD_Ivpor, +    UD_Ivpsadbw, +    UD_Ivpshufb, +    UD_Ivpshufd, +    UD_Ivpshufhw, +    UD_Ivpshuflw, +    UD_Ivpsignb, +    UD_Ivpsignd, +    UD_Ivpsignw, +    UD_Ivpslld, +    UD_Ivpslldq, +    UD_Ivpsllq, +    UD_Ivpsllw, +    UD_Ivpsrad, +    UD_Ivpsraw, +    UD_Ivpsrld, +    UD_Ivpsrldq, +    UD_Ivpsrlq, +    UD_Ivpsrlw, +    UD_Ivpsubb, +    UD_Ivpsubd, +    UD_Ivpsubq, +    UD_Ivpsubsb, +    UD_Ivpsubsw, +    UD_Ivpsubusb, +    UD_Ivpsubusw, +    UD_Ivpsubw, +    UD_Ivptest, +    UD_Ivpunpckhbw, +    UD_Ivpunpckhdq, +    UD_Ivpunpckhqdq, +    UD_Ivpunpckhwd, +    UD_Ivpunpcklbw, +    UD_Ivpunpckldq, +    UD_Ivpunpcklqdq, +    UD_Ivpunpcklwd, +    UD_Ivpxor, +    UD_Ivrcpps, +    UD_Ivrcpss, +    UD_Ivroundpd, +    UD_Ivroundps, +    UD_Ivroundsd, +    UD_Ivroundss, +    UD_Ivrsqrtps, +    UD_Ivrsqrtss, +    UD_Ivshufpd, +    UD_Ivshufps, +    UD_Ivsqrtpd, +    UD_Ivsqrtps, +    UD_Ivsqrtsd, +    UD_Ivsqrtss, +    UD_Ivstmxcsr, +    UD_Ivsubpd, +    UD_Ivsubps, +    UD_Ivsubsd, +    UD_Ivsubss, +    UD_Ivtestpd, +    UD_Ivtestps, +    UD_Ivucomisd, +    UD_Ivucomiss, +    UD_Ivunpckhpd, +    UD_Ivunpckhps, +    UD_Ivunpcklpd, +    UD_Ivunpcklps, +    UD_Ivxorpd, +    UD_Ivxorps, +    UD_Ivzeroall, +    UD_Ivzeroupper, +    UD_Iwait, +    UD_Iwbinvd, +    UD_Iwrmsr, +    UD_Ixadd, +    UD_Ixchg, +    UD_Ixcryptcbc, +    UD_Ixcryptcfb, +    UD_Ixcryptctr, +    UD_Ixcryptecb, +    UD_Ixcryptofb, +    UD_Ixgetbv, +    UD_Ixlatb, +    UD_Ixor, +    UD_Ixorpd, +    UD_Ixorps, +    UD_Ixrstor, +    UD_Ixsave, +    UD_Ixsetbv, +    UD_Ixsha1, +    UD_Ixsha256, +    UD_Ixstore, +    UD_Iinvalid, +    UD_I3dnow, +    UD_Inone, +    UD_Idb, +    UD_Ipause, +    UD_MAX_MNEMONIC_CODE +}; + +extern const char * ud_mnemonics_str[]; + +#endif /* UD_ITAB_H */ diff --git a/src/3p/udis86/syn-att.c b/src/3p/udis86/syn-att.c new file mode 100644 index 0000000..d1ba89b --- /dev/null +++ b/src/3p/udis86/syn-att.c @@ -0,0 +1,228 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void  +opr_cast(struct ud* u, struct ud_operand* op) +{ +  switch(op->size) { +  case 16 : case 32 : +    ud_asmprintf(u, "*");   break; +  default: break; +  } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void  +gen_operand(struct ud* u, struct ud_operand* op) +{ +  switch(op->type) { +  case UD_OP_CONST: +    ud_asmprintf(u, "$0x%x", op->lval.udword); +    break; + +  case UD_OP_REG: +    ud_asmprintf(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); +    break; + +  case UD_OP_MEM: +    if (u->br_far) { +        opr_cast(u, op); +    } +    if (u->pfx_seg) { +      ud_asmprintf(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); +    } +    if (op->offset != 0) {  +      ud_syn_print_mem_disp(u, op, 0); +    } +    if (op->base) { +      ud_asmprintf(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); +    } +    if (op->index) { +      if (op->base) { +        ud_asmprintf(u, ","); +      } else { +        ud_asmprintf(u, "("); +      } +      ud_asmprintf(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); +    } +    if (op->scale) { +      ud_asmprintf(u, ",%d", op->scale); +    } +    if (op->base || op->index) { +      ud_asmprintf(u, ")"); +    } +    break; + +  case UD_OP_IMM: +    ud_asmprintf(u, "$"); +    ud_syn_print_imm(u, op); +    break; + +  case UD_OP_JIMM: +    ud_syn_print_addr(u, ud_syn_rel_target(u, op)); +    break; + +  case UD_OP_PTR: +    switch (op->size) { +      case 32: +        ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg,  +          op->lval.ptr.off & 0xFFFF); +        break; +      case 48: +        ud_asmprintf(u, "$0x%x, $0x%x", op->lval.ptr.seg,  +          op->lval.ptr.off); +        break; +    } +    break; +       +  default: return; +  } +} + +/* ============================================================================= + * translates to AT&T syntax  + * ============================================================================= + */ +extern void  +ud_translate_att(struct ud *u) +{ +  int size = 0; +  int star = 0; + +  /* check if P_OSO prefix is used */ +  if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { +  switch (u->dis_mode) { +    case 16:  +      ud_asmprintf(u, "o32 "); +      break; +    case 32: +    case 64: +      ud_asmprintf(u, "o16 "); +      break; +  } +  } + +  /* check if P_ASO prefix was used */ +  if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { +  switch (u->dis_mode) { +    case 16:  +      ud_asmprintf(u, "a32 "); +      break; +    case 32: +      ud_asmprintf(u, "a16 "); +      break; +    case 64: +      ud_asmprintf(u, "a32 "); +      break; +  } +  } + +  if (u->pfx_lock) +    ud_asmprintf(u,  "lock "); +  if (u->pfx_rep) { +    ud_asmprintf(u, "rep "); +  } else if (u->pfx_repe) { +    ud_asmprintf(u, "repe "); +  } else if (u->pfx_repne) { +    ud_asmprintf(u, "repne "); +  } + +  /* special instructions */ +  switch (u->mnemonic) { +  case UD_Iretf:  +    ud_asmprintf(u, "lret ");  +    break; +  case UD_Idb: +    ud_asmprintf(u, ".byte 0x%x", u->operand[0].lval.ubyte); +    return; +  case UD_Ijmp: +  case UD_Icall: +    if (u->br_far) ud_asmprintf(u,  "l"); +        if (u->operand[0].type == UD_OP_REG) { +          star = 1; +        } +    ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); +    break; +  case UD_Ibound: +  case UD_Ienter: +    if (u->operand[0].type != UD_NONE) +      gen_operand(u, &u->operand[0]); +    if (u->operand[1].type != UD_NONE) { +      ud_asmprintf(u, ","); +      gen_operand(u, &u->operand[1]); +    } +    return; +  default: +    ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); +  } + +  if (size == 8) { +    ud_asmprintf(u, "b"); +  } else if (size == 16) { +    ud_asmprintf(u, "w"); +  } else if (size == 64) { +    ud_asmprintf(u, "q"); +  } + +  if (star) { +    ud_asmprintf(u, " *"); +  } else { +    ud_asmprintf(u, " "); +  } + +  if (u->operand[3].type != UD_NONE) { +    gen_operand(u, &u->operand[3]); +    ud_asmprintf(u, ", "); +  } +  if (u->operand[2].type != UD_NONE) { +    gen_operand(u, &u->operand[2]); +    ud_asmprintf(u, ", "); +  } +  if (u->operand[1].type != UD_NONE) { +    gen_operand(u, &u->operand[1]); +    ud_asmprintf(u, ", "); +  } +  if (u->operand[0].type != UD_NONE) { +    gen_operand(u, &u->operand[0]); +  } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/syn-intel.c b/src/3p/udis86/syn-intel.c new file mode 100644 index 0000000..0664fea --- /dev/null +++ b/src/3p/udis86/syn-intel.c @@ -0,0 +1,224 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "extern.h" +#include "decode.h" +#include "itab.h" +#include "syn.h" +#include "udint.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void  +opr_cast(struct ud* u, struct ud_operand* op) +{ +  if (u->br_far) { +    ud_asmprintf(u, "far ");  +  } +  switch(op->size) { +  case  8:  ud_asmprintf(u, "byte " ); break; +  case 16:  ud_asmprintf(u, "word " ); break; +  case 32:  ud_asmprintf(u, "dword "); break; +  case 64:  ud_asmprintf(u, "qword "); break; +  case 80:  ud_asmprintf(u, "tword "); break; +  case 128: ud_asmprintf(u, "oword "); break; +  case 256: ud_asmprintf(u, "yword "); break; +  default: break; +  } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ +  switch(op->type) { +  case UD_OP_REG: +    ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); +    break; + +  case UD_OP_MEM: +    if (syn_cast) { +      opr_cast(u, op); +    } +    ud_asmprintf(u, "["); +    if (u->pfx_seg) { +      ud_asmprintf(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); +    } +    if (op->base) { +      ud_asmprintf(u, "%s", ud_reg_tab[op->base - UD_R_AL]); +    } +    if (op->index) { +      ud_asmprintf(u, "%s%s", op->base != UD_NONE? "+" : "", +                              ud_reg_tab[op->index - UD_R_AL]); +      if (op->scale) { +        ud_asmprintf(u, "*%d", op->scale); +      } +    } +    if (op->offset != 0) { +      ud_syn_print_mem_disp(u, op, (op->base  != UD_NONE ||  +                                    op->index != UD_NONE) ? 1 : 0); +    } +    ud_asmprintf(u, "]"); +    break; +       +  case UD_OP_IMM: +    ud_syn_print_imm(u, op); +    break; + + +  case UD_OP_JIMM: +    ud_syn_print_addr(u, ud_syn_rel_target(u, op)); +    break; + +  case UD_OP_PTR: +    switch (op->size) { +      case 32: +        ud_asmprintf(u, "word 0x%x:0x%x", op->lval.ptr.seg,  +          op->lval.ptr.off & 0xFFFF); +        break; +      case 48: +        ud_asmprintf(u, "dword 0x%x:0x%x", op->lval.ptr.seg,  +          op->lval.ptr.off); +        break; +    } +    break; + +  case UD_OP_CONST: +    if (syn_cast) opr_cast(u, op); +    ud_asmprintf(u, "%d", op->lval.udword); +    break; + +  default: return; +  } +} + +/* ============================================================================= + * translates to intel syntax  + * ============================================================================= + */ +extern void +ud_translate_intel(struct ud* u) +{ +  /* check if P_OSO prefix is used */ +  if (!P_OSO(u->itab_entry->prefix) && u->pfx_opr) { +    switch (u->dis_mode) { +    case 16: ud_asmprintf(u, "o32 "); break; +    case 32: +    case 64: ud_asmprintf(u, "o16 "); break; +    } +  } + +  /* check if P_ASO prefix was used */ +  if (!P_ASO(u->itab_entry->prefix) && u->pfx_adr) { +    switch (u->dis_mode) { +    case 16: ud_asmprintf(u, "a32 "); break; +    case 32: ud_asmprintf(u, "a16 "); break; +    case 64: ud_asmprintf(u, "a32 "); break; +    } +  } + +  if (u->pfx_seg && +      u->operand[0].type != UD_OP_MEM && +      u->operand[1].type != UD_OP_MEM ) { +    ud_asmprintf(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); +  } + +  if (u->pfx_lock) { +    ud_asmprintf(u, "lock "); +  } +  if (u->pfx_rep) { +    ud_asmprintf(u, "rep "); +  } else if (u->pfx_repe) { +    ud_asmprintf(u, "repe "); +  } else if (u->pfx_repne) { +    ud_asmprintf(u, "repne "); +  } + +  /* print the instruction mnemonic */ +  ud_asmprintf(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + +  if (u->operand[0].type != UD_NONE) { +    int cast = 0; +    ud_asmprintf(u, " "); +    if (u->operand[0].type == UD_OP_MEM) { +      if (u->operand[1].type == UD_OP_IMM   || +          u->operand[1].type == UD_OP_CONST || +          u->operand[1].type == UD_NONE     || +          (u->operand[0].size != u->operand[1].size)) { +          cast = 1; +      } else if (u->operand[1].type == UD_OP_REG && +                 u->operand[1].base == UD_R_CL) { +          switch (u->mnemonic) { +          case UD_Ircl: +          case UD_Irol: +          case UD_Iror: +          case UD_Ircr: +          case UD_Ishl: +          case UD_Ishr: +          case UD_Isar: +              cast = 1; +              break; +          default: break; +          } +      } +    } +    gen_operand(u, &u->operand[0], cast); +  } + +  if (u->operand[1].type != UD_NONE) { +    int cast = 0; +    ud_asmprintf(u, ", "); +    if (u->operand[1].type == UD_OP_MEM && +        u->operand[0].size != u->operand[1].size &&  +        !ud_opr_is_sreg(&u->operand[0])) { +      cast = 1; +    } +    gen_operand(u, &u->operand[1], cast); +  } + +  if (u->operand[2].type != UD_NONE) { +    int cast = 0; +    ud_asmprintf(u, ", "); +    if (u->operand[2].type == UD_OP_MEM && +        u->operand[2].size != u->operand[1].size) { +      cast = 1; +    } +    gen_operand(u, &u->operand[2], cast); +  } + +  if (u->operand[3].type != UD_NONE) { +    ud_asmprintf(u, ", "); +    gen_operand(u, &u->operand[3], 0); +  } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/syn.c b/src/3p/udis86/syn.c new file mode 100644 index 0000000..1b9e1d4 --- /dev/null +++ b/src/3p/udis86/syn.c @@ -0,0 +1,212 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "types.h" +#include "decode.h" +#include "syn.h" +#include "udint.h" + +/*  + * Register Table - Order Matters (types.h)! + * + */ +const char* ud_reg_tab[] =  +{ +  "al",   "cl",   "dl",   "bl", +  "ah",   "ch",   "dh",   "bh", +  "spl",  "bpl",  "sil",  "dil", +  "r8b",  "r9b",  "r10b", "r11b", +  "r12b", "r13b", "r14b", "r15b", + +  "ax",   "cx",   "dx",   "bx", +  "sp",   "bp",   "si",   "di", +  "r8w",  "r9w",  "r10w", "r11w", +  "r12w", "r13w", "r14w", "r15w", +   +  "eax",  "ecx",  "edx",  "ebx", +  "esp",  "ebp",  "esi",  "edi", +  "r8d",  "r9d",  "r10d", "r11d", +  "r12d", "r13d", "r14d", "r15d", +   +  "rax",  "rcx",  "rdx",  "rbx", +  "rsp",  "rbp",  "rsi",  "rdi", +  "r8",   "r9",   "r10",  "r11", +  "r12",  "r13",  "r14",  "r15", + +  "es",   "cs",   "ss",   "ds", +  "fs",   "gs",  + +  "cr0",  "cr1",  "cr2",  "cr3", +  "cr4",  "cr5",  "cr6",  "cr7", +  "cr8",  "cr9",  "cr10", "cr11", +  "cr12", "cr13", "cr14", "cr15", +   +  "dr0",  "dr1",  "dr2",  "dr3", +  "dr4",  "dr5",  "dr6",  "dr7", +  "dr8",  "dr9",  "dr10", "dr11", +  "dr12", "dr13", "dr14", "dr15", + +  "mm0",  "mm1",  "mm2",  "mm3", +  "mm4",  "mm5",  "mm6",  "mm7", + +  "st0",  "st1",  "st2",  "st3", +  "st4",  "st5",  "st6",  "st7",  + +  "xmm0", "xmm1", "xmm2", "xmm3", +  "xmm4", "xmm5", "xmm6", "xmm7", +  "xmm8", "xmm9", "xmm10", "xmm11", +  "xmm12", "xmm13", "xmm14", "xmm15", + +  "ymm0", "ymm1", "ymm2",   "ymm3", +  "ymm4", "ymm5", "ymm6",   "ymm7", +  "ymm8", "ymm9", "ymm10",  "ymm11", +  "ymm12", "ymm13", "ymm14", "ymm15", + +  "rip" +}; + + +uint64_t +ud_syn_rel_target(struct ud *u, struct ud_operand *opr) +{ +  const uint64_t trunc_mask = 0xffffffffffffffffull >> (64 - u->opr_mode); +  switch (opr->size) { +  case 8 : return (u->pc + opr->lval.sbyte)  & trunc_mask; +  case 16: return (u->pc + opr->lval.sword)  & trunc_mask; +  case 32: return (u->pc + opr->lval.sdword) & trunc_mask; +  default: UD_ASSERT(!"invalid relative offset size."); +    return 0ull; +  } +} + + +/* + * asmprintf + *    Printf style function for printing translated assembly + *    output. Returns the number of characters written and + *    moves the buffer pointer forward. On an overflow, + *    returns a negative number and truncates the output. + */ +int +ud_asmprintf(struct ud *u, const char *fmt, ...) +{ +  int ret; +  int avail; +  va_list ap; +  va_start(ap, fmt); +  avail = u->asm_buf_size - u->asm_buf_fill - 1 /* nullchar */; +  ret = vsnprintf((char*) u->asm_buf + u->asm_buf_fill, avail, fmt, ap); +  if (ret < 0 || ret > avail) { +      u->asm_buf_fill = u->asm_buf_size - 1; +  } else { +      u->asm_buf_fill += ret; +  } +  va_end(ap); +  return ret; +} + + +void +ud_syn_print_addr(struct ud *u, uint64_t addr) +{ +  const char *name = NULL; +  if (u->sym_resolver) { +    int64_t offset = 0; +    name = u->sym_resolver(u, addr, &offset); +    if (name) { +      if (offset) { +        ud_asmprintf(u, "%s%+" FMT64 "d", name, offset); +      } else { +        ud_asmprintf(u, "%s", name); +      } +      return; +    } +  } +  ud_asmprintf(u, "0x%" FMT64 "x", addr); +} + + +void +ud_syn_print_imm(struct ud* u, const struct ud_operand *op) +{ +  uint64_t v; +  if (op->_oprcode == OP_sI && op->size != u->opr_mode) { +    if (op->size == 8) { +      v = (int64_t)op->lval.sbyte; +    } else { +      UD_ASSERT(op->size == 32); +      v = (int64_t)op->lval.sdword; +    } +    if (u->opr_mode < 64) { +      v = v & ((1ull << u->opr_mode) - 1ull); +    } +  } else { +    switch (op->size) { +    case 8 : v = op->lval.ubyte;  break; +    case 16: v = op->lval.uword;  break; +    case 32: v = op->lval.udword; break; +    case 64: v = op->lval.uqword; break; +    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ +    } +  } +  ud_asmprintf(u, "0x%" FMT64 "x", v); +} + + +void +ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign) +{ +  UD_ASSERT(op->offset != 0); + if (op->base == UD_NONE && op->index == UD_NONE) { +    uint64_t v; +    UD_ASSERT(op->scale == UD_NONE && op->offset != 8); +    /* unsigned mem-offset */ +    switch (op->offset) { +    case 16: v = op->lval.uword;  break; +    case 32: v = op->lval.udword; break; +    case 64: v = op->lval.uqword; break; +    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ +    } +    ud_asmprintf(u, "0x%" FMT64 "x", v); +  } else { +    int64_t v; +    UD_ASSERT(op->offset != 64); +    switch (op->offset) { +    case 8 : v = op->lval.sbyte;  break; +    case 16: v = op->lval.sword;  break; +    case 32: v = op->lval.sdword; break; +    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ +    } +    if (v < 0) { +      ud_asmprintf(u, "-0x%" FMT64 "x", -v); +    } else if (v > 0) { +      ud_asmprintf(u, "%s0x%" FMT64 "x", sign? "+" : "", v); +    } +  } +} + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/syn.h b/src/3p/udis86/syn.h new file mode 100644 index 0000000..d3b1e3f --- /dev/null +++ b/src/3p/udis86/syn.h @@ -0,0 +1,53 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "types.h" +#ifndef __UD_STANDALONE__ +# include <stdarg.h> +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +uint64_t ud_syn_rel_target(struct ud*, struct ud_operand*); + +#ifdef __GNUC__ +int ud_asmprintf(struct ud *u, const char *fmt, ...) +    __attribute__ ((format (printf, 2, 3))); +#else +int ud_asmprintf(struct ud *u, const char *fmt, ...); +#endif + +void ud_syn_print_addr(struct ud *u, uint64_t addr); +void ud_syn_print_imm(struct ud* u, const struct ud_operand *op); +void ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *, int sign); + +#endif /* UD_SYN_H */ + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/types.h b/src/3p/udis86/types.h new file mode 100644 index 0000000..69072ca --- /dev/null +++ b/src/3p/udis86/types.h @@ -0,0 +1,260 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifdef __KERNEL__ +  /*  +   * -D__KERNEL__ is automatically passed on the command line when +   * building something as part of the Linux kernel. Assume standalone +   * mode. +   */ +# include <linux/kernel.h> +# include <linux/string.h> +# ifndef __UD_STANDALONE__ +#  define __UD_STANDALONE__ 1 +# endif +#endif /* __KERNEL__ */ + +#if !defined(__UD_STANDALONE__) +# include <stdint.h> +# include <stdio.h> +#endif + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ +  UD_NONE, + +  /* 8 bit GPRs */ +  UD_R_AL,  UD_R_CL,  UD_R_DL,  UD_R_BL, +  UD_R_AH,  UD_R_CH,  UD_R_DH,  UD_R_BH, +  UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, +  UD_R_R8B, UD_R_R9B, UD_R_R10B,  UD_R_R11B, +  UD_R_R12B,  UD_R_R13B,  UD_R_R14B,  UD_R_R15B, + +  /* 16 bit GPRs */ +  UD_R_AX,  UD_R_CX,  UD_R_DX,  UD_R_BX, +  UD_R_SP,  UD_R_BP,  UD_R_SI,  UD_R_DI, +  UD_R_R8W, UD_R_R9W, UD_R_R10W,  UD_R_R11W, +  UD_R_R12W,  UD_R_R13W,  UD_R_R14W,  UD_R_R15W, +   +  /* 32 bit GPRs */ +  UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, +  UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, +  UD_R_R8D, UD_R_R9D, UD_R_R10D,  UD_R_R11D, +  UD_R_R12D,  UD_R_R13D,  UD_R_R14D,  UD_R_R15D, +   +  /* 64 bit GPRs */ +  UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, +  UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, +  UD_R_R8,  UD_R_R9,  UD_R_R10, UD_R_R11, +  UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + +  /* segment registers */ +  UD_R_ES,  UD_R_CS,  UD_R_SS,  UD_R_DS, +  UD_R_FS,  UD_R_GS,   + +  /* control registers*/ +  UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, +  UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, +  UD_R_CR8, UD_R_CR9, UD_R_CR10,  UD_R_CR11, +  UD_R_CR12,  UD_R_CR13,  UD_R_CR14,  UD_R_CR15, +   +  /* debug registers */ +  UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, +  UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, +  UD_R_DR8, UD_R_DR9, UD_R_DR10,  UD_R_DR11, +  UD_R_DR12,  UD_R_DR13,  UD_R_DR14,  UD_R_DR15, + +  /* mmx registers */ +  UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, +  UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + +  /* x87 registers */ +  UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, +  UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7,  + +  /* extended multimedia registers */ +  UD_R_XMM0,  UD_R_XMM1,  UD_R_XMM2,  UD_R_XMM3, +  UD_R_XMM4,  UD_R_XMM5,  UD_R_XMM6,  UD_R_XMM7, +  UD_R_XMM8,  UD_R_XMM9,  UD_R_XMM10, UD_R_XMM11, +  UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + +  /* 256B multimedia registers */ +  UD_R_YMM0,  UD_R_YMM1,  UD_R_YMM2,  UD_R_YMM3, +  UD_R_YMM4,  UD_R_YMM5,  UD_R_YMM6,  UD_R_YMM7, +  UD_R_YMM8,  UD_R_YMM9,  UD_R_YMM10, UD_R_YMM11, +  UD_R_YMM12, UD_R_YMM13, UD_R_YMM14, UD_R_YMM15, + +  UD_R_RIP, + +  /* Operand Types */ +  UD_OP_REG,  UD_OP_MEM,  UD_OP_PTR,  UD_OP_IMM,   +  UD_OP_JIMM, UD_OP_CONST +}; + +#include "itab.h" + +union ud_lval { +  int8_t     sbyte; +  uint8_t    ubyte; +  int16_t    sword; +  uint16_t   uword; +  int32_t    sdword; +  uint32_t   udword; +  int64_t    sqword; +  uint64_t   uqword; +  struct { +    uint16_t seg; +    uint32_t off; +  } ptr; +}; + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand { +  enum ud_type    type; +  uint16_t        size; +  enum ud_type    base; +  enum ud_type    index; +  uint8_t         scale;   +  uint8_t         offset; +  union ud_lval   lval; +  /* +   * internal use only +   */ +  uint64_t        _legacy; /* this will be removed in 1.8 */ +  uint8_t         _oprcode; +}; + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ +  /* +   * input buffering +   */ +  int       (*inp_hook) (struct ud*); +#ifndef __UD_STANDALONE__ +  FILE*     inp_file; +#endif +  const uint8_t* inp_buf; +  size_t    inp_buf_size; +  size_t    inp_buf_index; +  uint8_t   inp_curr; +  size_t    inp_ctr; +  uint8_t   inp_sess[64]; +  int       inp_end; +  int       inp_peek; + +  void      (*translator)(struct ud*); +  uint64_t  insn_offset; +  char      insn_hexcode[64]; + +  /* +   * Assembly output buffer +   */ +  char     *asm_buf; +  size_t    asm_buf_size; +  size_t    asm_buf_fill; +  char      asm_buf_int[128]; + +  /* +   * Symbol resolver for use in the translation phase. +   */ +  const char* (*sym_resolver)(struct ud*, uint64_t addr, int64_t *offset); + +  uint8_t   dis_mode; +  uint64_t  pc; +  uint8_t   vendor; +  enum ud_mnemonic_code mnemonic; +  struct ud_operand operand[4]; +  uint8_t   error; +  uint8_t   _rex; +  uint8_t   pfx_rex; +  uint8_t   pfx_seg; +  uint8_t   pfx_opr; +  uint8_t   pfx_adr; +  uint8_t   pfx_lock; +  uint8_t   pfx_str; +  uint8_t   pfx_rep; +  uint8_t   pfx_repe; +  uint8_t   pfx_repne; +  uint8_t   opr_mode; +  uint8_t   adr_mode; +  uint8_t   br_far; +  uint8_t   br_near; +  uint8_t   have_modrm; +  uint8_t   modrm; +  uint8_t   modrm_offset; +  uint8_t   vex_op; +  uint8_t   vex_b1; +  uint8_t   vex_b2; +  uint8_t   primary_opcode; +  void *    user_opaque_data; +  struct ud_itab_entry * itab_entry; +  struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type          ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud             ud_t; +typedef struct ud_operand     ud_operand_t; + +#define UD_SYN_INTEL          ud_translate_intel +#define UD_SYN_ATT            ud_translate_att +#define UD_EOI                (-1) +#define UD_INP_CACHE_SZ       32 +#define UD_VENDOR_AMD         0 +#define UD_VENDOR_INTEL       1 +#define UD_VENDOR_ANY         2 + +#endif + +/* +vim: set ts=2 sw=2 expandtab +*/ diff --git a/src/3p/udis86/udint.h b/src/3p/udis86/udint.h new file mode 100644 index 0000000..2d6d3cb --- /dev/null +++ b/src/3p/udis86/udint.h @@ -0,0 +1,95 @@ +/* udis86 - libudis86/udint.h -- definitions for internal use only + *  + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _UDINT_H_ +#define _UDINT_H_ + +#include "types.h" + +#if defined(UD_DEBUG) +# include <assert.h> +# define UD_ASSERT(_x) assert(_x) +#else +# define UD_ASSERT(_x) +#endif + +#if defined(UD_DEBUG) +  #define UDERR(u, msg) \ +    do { \ +      (u)->error = 1; \ +      fprintf(stderr, "decode-error: %s:%d: %s", \ +              __FILE__, __LINE__, (msg)); \ +    } while (0) +#else +  #define UDERR(u, m) \ +    do { \ +      (u)->error = 1; \ +    } while (0) +#endif /* !LOGERR */ + +#define UD_RETURN_ON_ERROR(u) \ +  do { \ +    if ((u)->error != 0) { \ +      return (u)->error; \ +    } \ +  } while (0) + +#define UD_RETURN_WITH_ERROR(u, m) \ +  do { \ +    UDERR(u, m); \ +    return (u)->error; \ +  } while (0) + +#ifndef __UD_STANDALONE__ +# define UD_NON_STANDALONE(x) x +#else +# define UD_NON_STANDALONE(x) +#endif + +/* printf formatting int64 specifier */ +#ifdef FMT64 +# undef FMT64 +#endif +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define FMT64 "I64" +#else +# if defined(__APPLE__) +#  define FMT64 "ll" +# elif defined(__amd64__) || defined(__x86_64__) +#  define FMT64 "l" +# else  +#  define FMT64 "ll" +# endif /* !x64 */ +#endif + +/* define an inline macro */ +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define UD_INLINE __inline /* MS Visual Studio requires __inline +                               instead of inline for C code */ +#else +# define UD_INLINE inline +#endif + +#endif /* _UDINT_H_ */ diff --git a/src/3p/udis86/udis86.c b/src/3p/udis86/udis86.c new file mode 100644 index 0000000..ee37a49 --- /dev/null +++ b/src/3p/udis86/udis86.c @@ -0,0 +1,456 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2013 Vivek Thampi + * All rights reserved. + *  + * Redistribution and use in source and binary forms, with or without modification,  + * are permitted provided that the following conditions are met: + *  + *     * Redistributions of source code must retain the above copyright notice,  + *       this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright notice,  + *       this list of conditions and the following disclaimer in the documentation  + *       and/or other materials provided with the distribution. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR  + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "udint.h" +#include "extern.h" +#include "decode.h" + +#if !defined(__UD_STANDALONE__) +# include <string.h> +#endif /* !__UD_STANDALONE__ */ + +static void ud_inp_init(struct ud *u); + +/* ============================================================================= + * ud_init + *    Initializes ud_t object. + * ============================================================================= + */ +extern void  +ud_init(struct ud* u) +{ +  memset((void*)u, 0, sizeof(struct ud)); +  ud_set_mode(u, 16); +  u->mnemonic = UD_Iinvalid; +  ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ +  ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ + +  ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); +} + + +/* ============================================================================= + * ud_disassemble + *    Disassembles one instruction and returns the number of  + *    bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ +  int len; +  if (u->inp_end) { +    return 0; +  } +  if ((len = ud_decode(u)) > 0) { +    if (u->translator != NULL) { +      u->asm_buf[0] = '\0'; +      u->translator(u); +    } +  } +  return len; +} + + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void  +ud_set_mode(struct ud* u, uint8_t m) +{ +  switch(m) { +  case 16: +  case 32: +  case 64: u->dis_mode = m ; return; +  default: u->dis_mode = 16; return; +  } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void  +ud_set_vendor(struct ud* u, unsigned v) +{ +  switch(v) { +  case UD_VENDOR_INTEL: +    u->vendor = v; +    break; +  case UD_VENDOR_ANY: +    u->vendor = v; +    break; +  default: +    u->vendor = UD_VENDOR_AMD; +  } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin.  + * ============================================================================= + */ +extern void  +ud_set_pc(struct ud* u, uint64_t o) +{ +  u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void  +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ +  u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +const char*  +ud_insn_asm(const struct ud* u)  +{ +  return u->asm_buf; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +uint64_t +ud_insn_off(const struct ud* u)  +{ +  return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +const char*  +ud_insn_hex(struct ud* u)  +{ +  u->insn_hexcode[0] = 0; +  if (!u->error) { +    unsigned int i; +    const unsigned char *src_ptr = ud_insn_ptr(u); +    char* src_hex; +    src_hex = (char*) u->insn_hexcode; +    /* for each byte used to decode instruction */ +    for (i = 0; i < ud_insn_len(u) && i < sizeof(u->insn_hexcode) / 2; +         ++i, ++src_ptr) { +      sprintf(src_hex, "%02x", *src_ptr & 0xFF); +      src_hex += 2; +    } +  } +  return u->insn_hexcode; +} + + +/* ============================================================================= + * ud_insn_ptr + *    Returns a pointer to buffer containing the bytes that were + *    disassembled. + * ============================================================================= + */ +extern const uint8_t*  +ud_insn_ptr(const struct ud* u)  +{ +  return (u->inp_buf == NULL) ?  +            u->inp_sess : u->inp_buf + (u->inp_buf_index - u->inp_ctr); +} + + +/* ============================================================================= + * ud_insn_len + *    Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int  +ud_insn_len(const struct ud* u)  +{ +  return u->inp_ctr; +} + + +/* ============================================================================= + * ud_insn_get_opr + *    Return the operand struct representing the nth operand of + *    the currently disassembled instruction. Returns NULL if + *    there's no such operand. + * ============================================================================= + */ +const struct ud_operand* +ud_insn_opr(const struct ud *u, unsigned int n) +{ +  if (n > 3 || u->operand[n].type == UD_NONE) { +    return NULL;  +  } else { +    return &u->operand[n]; +  } +} + + +/* ============================================================================= + * ud_opr_is_sreg + *    Returns non-zero if the given operand is of a segment register type. + * ============================================================================= + */ +int +ud_opr_is_sreg(const struct ud_operand *opr) +{ +  return opr->type == UD_OP_REG &&  +         opr->base >= UD_R_ES   && +         opr->base <= UD_R_GS; +} + + +/* ============================================================================= + * ud_opr_is_sreg + *    Returns non-zero if the given operand is of a general purpose + *    register type. + * ============================================================================= + */ +int +ud_opr_is_gpr(const struct ud_operand *opr) +{ +  return opr->type == UD_OP_REG &&  +         opr->base >= UD_R_AL   && +         opr->base <= UD_R_R15; +} + + +/* ============================================================================= + * ud_set_user_opaque_data + * ud_get_user_opaque_data + *    Get/set user opaqute data pointer + * ============================================================================= + */ +void +ud_set_user_opaque_data(struct ud * u, void* opaque) +{ +  u->user_opaque_data = opaque; +} + +void* +ud_get_user_opaque_data(const struct ud *u) +{ +  return u->user_opaque_data; +} + + +/* ============================================================================= + * ud_set_asm_buffer + *    Allow the user to set an assembler output buffer. If `buf` is NULL, + *    we switch back to the internal buffer. + * ============================================================================= + */ +void +ud_set_asm_buffer(struct ud *u, char *buf, size_t size) +{ +  if (buf == NULL) { +    ud_set_asm_buffer(u, u->asm_buf_int, sizeof(u->asm_buf_int)); +  } else { +    u->asm_buf = buf; +    u->asm_buf_size = size; +  } +} + + +/* ============================================================================= + * ud_set_sym_resolver + *    Set symbol resolver for relative targets used in the translation + *    phase. + * + *    The resolver is a function that takes a uint64_t address and returns a + *    symbolic name for the that address. The function also takes a second + *    argument pointing to an integer that the client can optionally set to a + *    non-zero value for offsetted targets. (symbol+offset) The function may + *    also return NULL, in which case the translator only prints the target + *    address. + * + *    The function pointer maybe NULL which resets symbol resolution. + * ============================================================================= + */ +void +ud_set_sym_resolver(struct ud *u, const char* (*resolver)(struct ud*,  +                                                          uint64_t addr, +                                                          int64_t *offset)) +{ +  u->sym_resolver = resolver; +} + + +/* ============================================================================= + * ud_insn_mnemonic + *    Return the current instruction mnemonic. + * ============================================================================= + */ +enum ud_mnemonic_code +ud_insn_mnemonic(const struct ud *u) +{ +  return u->mnemonic; +} + + +/* ============================================================================= + * ud_lookup_mnemonic + *    Looks up mnemonic code in the mnemonic string table. + *    Returns NULL if the mnemonic code is invalid. + * ============================================================================= + */ +const char* +ud_lookup_mnemonic(enum ud_mnemonic_code c) +{ +  if (c < UD_MAX_MNEMONIC_CODE) { +    return ud_mnemonics_str[c]; +  } else { +    return NULL; +  } +} + + +/*  + * ud_inp_init + *    Initializes the input system. + */ +static void +ud_inp_init(struct ud *u) +{ +  u->inp_hook      = NULL; +  u->inp_buf       = NULL; +  u->inp_buf_size  = 0; +  u->inp_buf_index = 0; +  u->inp_curr      = 0; +  u->inp_ctr       = 0; +  u->inp_end       = 0; +  u->inp_peek      = UD_EOI; +  UD_NON_STANDALONE(u->inp_file = NULL); +} + + +/* ============================================================================= + * ud_inp_set_hook + *    Sets input hook. + * ============================================================================= + */ +void  +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ +  ud_inp_init(u); +  u->inp_hook = hook; +} + +/* ============================================================================= + * ud_inp_set_buffer + *    Set buffer as input. + * ============================================================================= + */ +void  +ud_set_input_buffer(register struct ud* u, const uint8_t* buf, size_t len) +{ +  ud_inp_init(u); +  u->inp_buf = buf; +  u->inp_buf_size = len; +  u->inp_buf_index = 0; +} + + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file + *    Set FILE as input. + * ============================================================================= + */ +static int  +inp_file_hook(struct ud* u) +{ +  return fgetc(u->inp_file); +} + +void  +ud_set_input_file(register struct ud* u, FILE* f) +{ +  ud_inp_init(u); +  u->inp_hook = inp_file_hook; +  u->inp_file = f; +} +#endif /* __UD_STANDALONE__ */ + + +/* ============================================================================= + * ud_input_skip + *    Skip n input bytes. + * ============================================================================ + */ +void  +ud_input_skip(struct ud* u, size_t n) +{ +  if (u->inp_end) { +    return; +  } +  if (u->inp_buf == NULL) { +    while (n--) { +      int c = u->inp_hook(u); +      if (c == UD_EOI) { +        goto eoi; +      } +    } +    return; +  } else { +    if (n > u->inp_buf_size || +        u->inp_buf_index > u->inp_buf_size - n) { +      u->inp_buf_index = u->inp_buf_size;  +      goto eoi; +    } +    u->inp_buf_index += n;  +    return; +  } +eoi: +  u->inp_end = 1; +  UDERR(u, "cannot skip, eoi received\b"); +  return; +} + + +/* ============================================================================= + * ud_input_end + *    Returns non-zero on end-of-input. + * ============================================================================= + */ +int +ud_input_end(const struct ud *u) +{ +  return u->inp_end; +} + +/* vim:set ts=2 sw=2 expandtab */ diff --git a/src/abi.h b/src/abi.h new file mode 100644 index 0000000..54c504a --- /dev/null +++ b/src/abi.h @@ -0,0 +1,91 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_ABI_H +#define INC_ABI_H + +#include "intdefs.h" + +/* + * This file defines miscellaneous C++ ABI stuff. Looking at it may cause + * brain damage and/or emotional trauma. + */ + +#ifdef _WIN32 // Windows RTTI stuff, obviously only used on Windows. + +// MSVC RTTI is quite a black box, but thankfully there's some useful sources: +// - https://doxygen.reactos.org/d0/dcf/cxx_8h_source.html +// - https://blog.quarkslab.com/visual-c-rtti-inspection.html +// - https://www.geoffchappell.com/studies/msvc/language/predefined/ +// - https://docs.rs/pelite/0.5.0/src/pelite/pe32/msvc.rs.html + +// By the way, while I'm here I'd just like to point out how ridiculous this +// layout is. Whoever decided to put this many levels of indirection over what +// should be a small lookup table is an absolute nutcase. I hope that individual +// has gotten some help by now, mostly for the sake of others. + +struct msvc_rtti_descriptor { +	void *vtab; +	void *unknown; // ??? +	// XXX: pretty sure this is supposed to be flexible, but too lazy to write +	// the stupid union init macros to make that fully portable +	char classname[80]; +}; + +// "pointer to member displacement" +struct msvc_pmd { int mdisp, pdisp, vdisp; }; + +struct msvc_basedesc { +	struct msvc_rtti_descriptor *desc; +	uint nbases; +	struct msvc_pmd where; +	uint attr; +}; + +struct msvc_rtti_hierarchy { +	uint sig; +	uint attrs; +	uint nbaseclasses; +	struct msvc_basedesc **baseclasses; +}; + +struct msvc_rtti_locator { +	uint sig; +	int baseoff; +	// ctor offset, or some flags; reactos and rust pelite say different things? +	int unknown; +	struct msvc_rtti_descriptor *desc; +	struct msvc_rtti_hierarchy *hier; +}; + +// I mean seriously look at this crap! +#define DEF_MSVC_BASIC_RTTI(mod, name, vtab, typestr) \ +mod struct msvc_rtti_locator name; \ +static struct msvc_rtti_descriptor _desc_##name = {(vtab) + 1, 0, typestr}; \ +static struct msvc_basedesc _basedesc_##name = {&_desc_##name, 0, {0, 0, 0}, 0}; \ +mod struct msvc_rtti_locator name = { \ +	0, 0, 0, \ +	&_desc_##name, \ +	&(struct msvc_rtti_hierarchy){ \ +		0, 1 /* per engine */, 1, (struct msvc_basedesc *[]){&_basedesc_##name} \ +	} \ +}; + +#endif + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/bitbuf.h b/src/bitbuf.h new file mode 100644 index 0000000..8ce5535 --- /dev/null +++ b/src/bitbuf.h @@ -0,0 +1,99 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_BITBUF_H +#define INC_BITBUF_H + +#include <stdbool.h> + +#include "intdefs.h" + +// NOTE: This code assumes it's running on a little endian machine, because, +// well, the game runs on a little endian machine. +// *technically* this could break unit tests in a contrived cross-compile +// scenario? right now none of the tests care about actual bit values, and we +// don't cross compile, so this won't matter till later. :) + +// handle 8 bytes at a time (COULD do 16 with SSE, but who cares this is fine) +typedef uvlong bitbuf_cell; +static const int bitbuf_cell_bits = sizeof(bitbuf_cell) * 8; +static const int bitbuf_align = _Alignof(bitbuf_cell); + +/* A bit buffer, ABI-compatible with bf_write defined in tier1/bitbuf.h */ +struct bitbuf { +	union { +		char *buf; /* NOTE: the buffer MUST be aligned as bitbuf_cell! */ +		bitbuf_cell *buf_as_cells; +	}; +	int sz, nbits, curbit; +	bool overflow, assert_on_overflow; +	const char *debugname; +}; + +/* Append a value to the bitbuffer, with a specfied length in bits. */ +static inline void bitbuf_appendbits(struct bitbuf *bb, bitbuf_cell x, +		int nbits) { +	int idx = bb->curbit / bitbuf_cell_bits; +	int shift = bb->curbit % bitbuf_cell_bits; +	// OR into the existing cell (lower bits were already set!) +	bb->buf_as_cells[idx] |= x << shift; +	// assign the next cell (that also clears the upper bits for the next OR) +	// note: if nbits fits in the first cell, this just 0s the next cell, which +	// is absolutely fine +	bb->buf_as_cells[idx + 1] = x >> (bitbuf_cell_bits - shift); +	bb->curbit += nbits; +} + +/* Append a byte to the bitbuffer - same as appendbits(8) but more convenient */ +static inline void bitbuf_appendbyte(struct bitbuf *bb, uchar x) { +	bitbuf_appendbits(bb, x, 8); +} + +/* Append a sequence of bytes to the bitbuffer, with length given in bytes */ +static inline void bitbuf_appendbuf(struct bitbuf *bb, const char *buf, +		uint len) { +	// NOTE! This function takes advantage of the fact that nothing unaligned +	// is page aligned, so accessing slightly outside the bounds of buf can't +	// segfault. This is absolutely definitely technically UB, but it's unit +	// tested and apparently works in practice. If something weird happens +	// further down the line, sorry! +	usize unalign = (usize)buf % bitbuf_align; +	if (unalign) { +		// round down the pointer +		bitbuf_cell *p = (bitbuf_cell *)((usize)buf & ~(bitbuf_align - 1)); +		// shift the stored value (if it were big endian, the shift would have +		// to be the other way, or something) +		bitbuf_appendbits(bb, *p >> (unalign * 8), (bitbuf_align - unalign) * 8); +		buf += sizeof(bitbuf_cell) - unalign; +		len -= unalign; +	} +	bitbuf_cell *aligned = (bitbuf_cell *)buf; +	for (; len > sizeof(bitbuf_cell); len -= sizeof(bitbuf_cell), ++aligned) { +		bitbuf_appendbits(bb, *aligned, bitbuf_cell_bits); +	} +	// unaligned end bytes +	bitbuf_appendbits(bb, *aligned, len * 8); +} + +/* Clear the bitbuffer to make it ready to append new data */ +static inline void bitbuf_reset(struct bitbuf *bb) { +	bb->buf[0] = 0; // we have to zero out the lowest cell since it gets ORed +	bb->curbit = 0; +} + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/cmeta.c b/src/build/cmeta.c new file mode 100644 index 0000000..b895253 --- /dev/null +++ b/src/build/cmeta.c @@ -0,0 +1,238 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "../intdefs.h" +#include "../os.h" + +/* + * This file does C metadata parsing/scraping for the build system. This + * facilitates tasks ranging from determining header dependencies to searching + * for certain magic macros (for example cvar/command declarations) to generate + * other code. + * + * It's a bit of a mess since it's kind of just hacked together for use at build + * time. Don't worry about it too much. + */ + +// too lazy to write a C tokenizer at the moment, so let's just yoink some code +// from a hacked-up copy of chibicc, a nice minimal C compiler with code that's +// pretty easy to work with. it does leak memory by design, but build stuff is +// all one-shot so that's fine. +#include "../3p/chibicc/unicode.c" +// type sentinels from type.c (don't bring in the rest of type.c because it +// circularly depends on other stuff and we really only want tokenize here) +Type *ty_void = &(Type){TY_VOID, 1, 1}; +Type *ty_bool = &(Type){TY_BOOL, 1, 1}; +Type *ty_char = &(Type){TY_CHAR, 1, 1}; +Type *ty_short = &(Type){TY_SHORT, 2, 2}; +Type *ty_int = &(Type){TY_INT, 4, 4}; +Type *ty_long = &(Type){TY_LONG, 8, 8}; +Type *ty_uchar = &(Type){TY_CHAR, 1, 1, true}; +Type *ty_ushort = &(Type){TY_SHORT, 2, 2, true}; +Type *ty_uint = &(Type){TY_INT, 4, 4, true}; +Type *ty_ulong = &(Type){TY_LONG, 8, 8, true}; +Type *ty_float = &(Type){TY_FLOAT, 4, 4}; +Type *ty_double = &(Type){TY_DOUBLE, 8, 8}; +Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16}; +// inline just a couple more things, super lazy, but whatever +static Type *new_type(TypeKind kind, int size, int align) { +  Type *ty = calloc(1, sizeof(Type)); +  ty->kind = kind; +  ty->size = size; +  ty->align = align; +  return ty; +} +Type *array_of(Type *base, int len) { +  Type *ty = new_type(TY_ARRAY, base->size * len, base->align); +  ty->base = base; +  ty->array_len = len; +  return ty; +} +#include "../3p/chibicc/hashmap.c" +#include "../3p/chibicc/strings.c" +#include "../3p/chibicc/tokenize.c" +// one more copypaste from preprocess.c for #include <filename> and then I'm +// done I promise +static char *join_tokens(Token *tok, Token *end) { +  int len = 1; +  for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { +    if (t != tok && t->has_space) +      len++; +    len += t->len; +  } +  char *buf = calloc(1, len); +  int pos = 0; +  for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { +    if (t != tok && t->has_space) +      buf[pos++] = ' '; +    strncpy(buf + pos, t->loc, t->len); +    pos += t->len; +  } +  buf[pos] = '\0'; +  return buf; +} + +#ifdef _WIN32 +#include "../3p/openbsd/asprintf.c" // missing from libc; plonked here for now +#endif + +static void die1(const char *s) { +	fprintf(stderr, "cmeta: fatal: %s\n", s); +	exit(100); +} + +static char *readsource(const os_char *f) { +	int fd = os_open(f, O_RDONLY); +#ifndef _WIN32 +	if (fd == -1) die2("couldn't open ", f); +#else +	// TODO/FIXME/TEMP this is dumb and bad +	if (fd == -1) { fprintf(stderr, "couldn't open %S", f); exit(100); } +#endif +	uint bufsz = 8192; +	char *buf = malloc(bufsz); +	if (!buf) die1("couldn't allocate memory"); +	int nread; +	int off = 0; +	while ((nread = read(fd, buf + off, bufsz - off)) > 0) { +		off += nread; +		if (off == bufsz) { +			bufsz *= 2; +			// somewhat arbitrary cutoff +			if (bufsz == 1 << 30) die1("input file is too large"); +			buf = realloc(buf, bufsz); +			if (!buf) die1("couldn't reallocate memory"); +		} +	} +	if (nread == -1) die1("couldn't read file"); +	buf[off] = 0; +	close(fd); +	return buf; +} + +// as per cmeta.h this is totally opaque; it's actually just a Token in disguise +struct cmeta; + +const struct cmeta *cmeta_loadfile(const os_char *f) { +	char *buf = readsource(f); +#ifdef _WIN32 +	char *realname = malloc(wcslen(f) + 1); +	if (!realname) die1("couldn't allocate memory"); +	// XXX: being lazy about Unicode right now; a general purpose tool should +	// implement WTF8 or something. SST itself doesn't have any unicode paths +	// though, so don't really care as much. +	*realname = *f; +	for (const ushort *p = f + 1; p[-1]; ++p) realname[p - f] = *p; +#else +	const char *realname = f; +#endif +	return (const struct cmeta *)tokenize_buf(realname, buf); +} + +// NOTE: we don't care about conditional includes, nor do we expand macros. We +// just parse the minimum info to get what we need for SST. Also, there's not +// too much in the way of syntax checking; if an error gets ignored the compiler +// picks it anyway, and gives far better diagnostics. +void cmeta_includes(const struct cmeta *cm, +		void (*cb)(const char *f, bool issys, void *ctxt), void *ctxt) { +	Token *tp = (Token *)cm; +	if (!tp || !tp->next || !tp->next->next) return; // #, include, "string" +	while (tp) { +		if (!tp->at_bol || !equal(tp, "#")) { tp = tp->next; continue; } +		if (!equal(tp->next, "include")) { tp = tp->next->next; continue; } +		tp = tp->next->next; +		if (!tp) break; +		if (tp->at_bol) tp = tp->next; +		if (!tp) break; +		if (tp->kind == TK_STR) { +			// include strings are a special case; they don't have \escapes. +			char *copy = malloc(tp->len - 1); +			if (!copy) die1("couldn't allocate memory"); +			memcpy(copy, tp->loc + 1, tp->len - 2); +			copy[tp->len - 2] = '\0'; +			cb(copy, false, ctxt); +			//free(copy); // ?????? +		} +		else if (equal(tp, "<")) { +			tp = tp->next; +			if (!tp) break; +			Token *end = tp; +			while (!equal(end, ">")) { +				end = end->next; +				if (!end) return; // shouldn't happen in valid source obviously +				if (end->at_bol) break; // ?????? +			} +			char *joined = join_tokens(tp, end); // just use func from chibicc +			cb(joined, true, ctxt); +			//free(joined); // ?????? +		} +		// get to the next line (standard allows extra tokens because) +		while (!tp->at_bol) { +			tp = tp->next; +			if (!tp) return; +		} +	} +} + +// AGAIN, NOTE: this doesn't *perfectly* match top level decls only in the event +// that someone writes something weird, but we just don't really care because +// we're not writing something weird. Don't write something weird! +void cmeta_conmacros(const struct cmeta *cm, void (*cb)(const char *, bool)) { +	Token *tp = (Token *)cm; +	if (!tp || !tp->next || !tp->next->next) return; // DEF_xyz, (, name +	while (tp) { +		bool isplusminus = false, isvar = false; +		if (equal(tp, "DEF_CCMD_PLUSMINUS")) isplusminus = true; +		else if (equal(tp, "DEF_CVAR") || equal(tp, "DEF_CVAR_MIN") || +				equal(tp, "DEF_CVAR_MINMAX")) { +			isvar = true; +		} +		else if (!equal(tp, "DEF_CCMD") && !equal(tp, "DEF_CCMD_HERE")) { +			tp = tp->next; continue; +		} +		if (!equal(tp->next, "(")) { tp = tp->next->next; continue; } +		tp = tp->next->next; +		if (isplusminus) { +			// XXX: this is stupid but whatever +			char *plusname = malloc(sizeof("PLUS_") + tp->len); +			if (!plusname) die1("couldn't allocate memory"); +			memcpy(plusname, "PLUS_", 5); +			memcpy(plusname + sizeof("PLUS_") - 1, tp->loc, tp->len); +			plusname[sizeof("PLUS_") - 1 + tp->len] = '\0'; +			cb(plusname, false); +			char *minusname = malloc(sizeof("MINUS_") + tp->len); +			if (!minusname) die1("couldn't allocate memory"); +			memcpy(minusname, "MINUS_", 5); +			memcpy(minusname + sizeof("MINUS_") - 1, tp->loc, tp->len); +			minusname[sizeof("MINUS_") - 1 + tp->len] = '\0'; +			cb(minusname, false); +		} +		else { +			char *name = malloc(tp->len + 1); +			if (!name) die1("couldn't allocate memory"); +			memcpy(name, tp->loc, tp->len); +			name[tp->len] = '\0'; +			cb(name, isvar); +		} +		tp = tp->next; +	} +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/cmeta.h b/src/build/cmeta.h new file mode 100644 index 0000000..3319e3a --- /dev/null +++ b/src/build/cmeta.h @@ -0,0 +1,44 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_CMETA_H +#define INC_CMETA_H + +#include <stdbool.h> + +#include "../os.h" + +struct cmeta; + +const struct cmeta *cmeta_loadfile(const os_char *f); + +/* + * Iterates through all the #include directives in a file, passing each one in + * turn to the callback cb. + */ +void cmeta_includes(const struct cmeta *cm, +		void (*cb)(const char *f, bool issys, void *ctxt), void *ctxt); + +/* + * Iterates through all commands and variables declared using the macros in + * con_.h, passing each one in turn to the callback cb. + */ +void cmeta_conmacros(const struct cmeta *cm, +		void (*cb)(const char *name, bool isvar)); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/codegen.c b/src/build/codegen.c new file mode 100644 index 0000000..c9be0ef --- /dev/null +++ b/src/build/codegen.c @@ -0,0 +1,95 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../os.h" +#include "cmeta.h" + +static const char *cmdnames[4096]; // arbitrary limit! +static int ncmdnames = 0; +static const char *varnames[4096]; // arbitrary limit! +static int nvarnames = 0; + +static void die(const char *s) { +	fprintf(stderr, "codegen: %s\n", s); +	exit(100); +} + +#define PUT(array, ent) do { \ +	if (n##array == sizeof(array) / sizeof(*array)) { \ +		fprintf(stderr, "codegen: out of space; make " #array " bigger!\n"); \ +		exit(1); \ +	} \ +	array[n##array++] = ent; \ +} while (0) + +static void oncondef(const char *name, bool isvar) { +	if (isvar) PUT(varnames, name); else PUT(cmdnames, name); +} + +#define _(x) \ +	if (fprintf(out, "%s\n", x) < 0) die("couldn't write to file"); +#define F(f, ...) \ +	if (fprintf(out, f "\n", __VA_ARGS__) < 0) die("couldn't write to file"); +#define H() \ +_( "/* This file is autogenerated by src/build/codegen.c. DO NOT EDIT! */") \ +_( "") + +int OS_MAIN(int argc, os_char *argv[]) { +	for (++argv; *argv; ++argv) { +		const struct cmeta *cm = cmeta_loadfile(*argv); +		cmeta_conmacros(cm, &oncondef); +	} + +	FILE *out = fopen(".build/include/cmdinit.gen.h", "wb"); +	if (!out) die("couldn't open cmdinit.gen.h"); +	H(); +	for (const char *const *pp = cmdnames; +			pp - cmdnames < ncmdnames; ++pp) { +F( "extern struct con_cmd *%s;", *pp) +	} +	for (const char *const *pp = varnames; +			pp - varnames < nvarnames; ++pp) { +F( "extern struct con_var *%s;", *pp) +	} +_( "") +_( "static void regcmds(void (*VCALLCONV f)(void *, void *)) {") +	for (const char *const *pp = cmdnames; +			pp - cmdnames < ncmdnames; ++pp) { +F( "	f(_con_iface, %s);", *pp) +	} +	for (const char *const *pp = varnames; +			pp - varnames < nvarnames; ++pp) { +F( "	initval(%s);", *pp) +F( "	f(_con_iface, %s);", *pp) +	} +_( "}") +_( "") +_( "static void freevars(void) {") +	for (const char *const *pp = varnames; +			pp - varnames < nvarnames; ++pp) { +F( "	extfree(%s->strval);", *pp) +	} +_( "}") +	if (fflush(out) == EOF) die("couldn't fully write cmdinit.gen.h"); + +	return 0; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c new file mode 100644 index 0000000..87de07d --- /dev/null +++ b/src/build/mkgamedata.c @@ -0,0 +1,238 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../intdefs.h" +#include "../kv.h" +#include "../os.h" +#include "../noreturn.h" // must come after os.h due to __declspec(noreturn) + +#ifdef _WIN32 +#define fS "S" +#else +#define fS "s" +#endif + +static noreturn die(const char *s) { +	fprintf(stderr, "mkgamedata: %s\n", s); +	exit(100); +} + +/* + * We keep the gamedata KV format as simple and flat as possible: + * + *   <varname> <expr> + *   <varname> { <gametype> <expr> <gametype> <expr> ... [default <expr>] } + *   [however many entries...] + * + * If that doesn't make sense, just look at one of the existing data files and + * then it should be obvious. :^) + * + * Note: if `default` isn't given in a conditional block, that piece of gamedata + * is considered unavailable and modules that use it won't get initialised/used. + */ +struct ent { +	const char *name; +	// normally I'd be inclined to do some pointer bitpacking meme but that's +	// annoying and doesn't matter here so here's an bool and 7 bytes of padding +	bool iscond; +	union { +		struct { +			struct ent_cond { +				const char *name; +				// note: can be any old C expression; just plopped in +				const char *expr; +				struct ent_cond *next; +			} *cond; +			// store user-specified defaults in a special place to make the +			// actual codegen logic easier +			const char *defval; +		}; +		const char *expr; +	}; +	struct ent *next; +} *ents_head, **ents_tail = &ents_head; + +struct parsestate { +	const os_char *filename; +	struct kv_parser *parser; +	const char *lastkey; +	struct ent_cond **nextcond; +	bool incond; +}; + +static noreturn badparse(struct parsestate *state, const char *e) { +	fprintf(stderr, "mkgamedata: %" fS ":%d:%d: parse error: %s", +			state->filename, state->parser->line, state->parser->col, e); +	exit(1); +} + +static void kv_cb(enum kv_token type, const char *p, uint len, void *ctxt) { +	struct parsestate *state = ctxt; +	switch (type) { +		case KV_IDENT: case KV_IDENT_QUOTED: +			char *k = malloc(len + 1); +			if (!k) die("couldn't allocate key string"); +			memcpy(k, p, len); +			k[len] = '\0'; +			state->lastkey = k; +			break;  +		case KV_NEST_START: +			if (state->incond) badparse(state, "unexpected nested object"); +			state->incond = true; +			struct ent *e = malloc(sizeof(*e)); +			if (!e) die("couldn't allocate memory"); +			e->name = state->lastkey; +			e->iscond = true; +			e->cond = 0; +			e->defval = 0; +			state->nextcond = &e->cond; +			e->next = 0; +			*ents_tail = e; +			ents_tail = &e->next; +			break; +		case KV_NEST_END: +			state->incond = false; +			break; +		case KV_VAL: case KV_VAL_QUOTED: +			if (state->incond) { +				// dumb special case mentioned above +				if (!strcmp(state->lastkey, "default")) { +					(*ents_tail)->defval = state->lastkey; +					break; +				} +				struct ent_cond *c = malloc(sizeof(*c)); +				if (!c) die("couldn't allocate memory"); +				c->name = state->lastkey; +				char *expr = malloc(len + 1); +				if (!expr) die("couldn't allocate value/expression string"); +				memcpy(expr, p, len); +				expr[len] = '\0'; +				c->expr = expr; +				c->next = 0; +				*(state->nextcond) = c; +				state->nextcond = &c->next; +			} +			else { +				// also kind of dumb but whatever +				struct ent *e = malloc(sizeof(*e)); +				if (!e) die("couldn't allocate memory"); +				e->name = state->lastkey; +				e->iscond = false; +				char *expr = malloc(len + 1); +				if (!expr) die("couldn't allocate value/expression string"); +				memcpy(expr, p, len); +				expr[len] = '\0'; +				e->expr = expr; +				e->next = 0; +				*ents_tail = e; +				ents_tail = &e->next; +			} +	} +} + +#define _(x) \ +	if (fprintf(out, "%s\n", x) < 0) die("couldn't write to file"); +#define F(f, ...) \ +	if (fprintf(out, f "\n", __VA_ARGS__) < 0) die("couldn't write to file"); +#define H() \ +_( "/* This file is autogenerated by src/build/mkgamedata.c. DO NOT EDIT! */") \ +_( "") + +int OS_MAIN(int argc, os_char *argv[]) { +	for (++argv; *argv; ++argv) { +		int fd = os_open(*argv, O_RDONLY); +		if (fd == -1) die("couldn't open file"); +		struct kv_parser kv = {0}; +		struct parsestate state = {*argv, &kv}; +		char buf[1024]; +		int nread; +		while (nread = read(fd, buf, sizeof(buf))) { +			if (nread == -1) die("couldn't read file"); +			kv_parser_feed(&kv, buf, nread, &kv_cb, &state); +			if (kv.state == KV_PARSER_ERROR) goto ep; +		} +		kv_parser_done(&kv); +		if (kv.state == KV_PARSER_ERROR) { +ep:			fprintf(stderr, "mkgamedata: %" fS ":%d:%d: bad syntax: %s\n", +					*argv, kv.line, kv.col, kv.errmsg); +			exit(1); +		} +		close(fd); +	} + +	FILE *out = fopen(".build/include/gamedata.gen.h", "wb"); +	if (!out) die("couldn't open gamedata.gen.h"); +	H(); +	for (struct ent *e = ents_head; e; e = e->next) { +		if (e->iscond) { +F( "extern int gamedata_%s;", e->name) +			if (e->defval) { +F( "#define gamedata_has_%s true", e->name) +			} +			else { +F( "extern bool gamedata_has_%s;", e->name) +			} +		} +		else { +F( "enum { gamedata_%s = %s };", e->name, e->expr) +F( "#define gamedata_has_%s true", e->name) +		} +	} + +	out = fopen(".build/include/gamedatainit.gen.h", "wb"); +	if (!out) die("couldn't open gamedatainit.gen.h"); +	H(); +	for (struct ent *e = ents_head; e; e = e->next) { +		if (e->iscond) { +			if (e->defval) { +F( "int gamedata_%s = %s", e->name, e->defval); +			} +			else { +F( "int gamedata_%s;", e->name); +F( "bool gamedata_has_%s = false;", e->name); +			} +		} +	} +_( "") +_( "void gamedata_init(void) {") +	for (struct ent *e = ents_head; e; e = e->next) { +		if (e->iscond) { +			for (struct ent_cond *c = e->cond; c; c = c->next) { +				if (!e->defval) { +					// XXX: not bothering to generate `else`s. technically this +					// has different semantics; we hope that the compiler can +					// just do the right thing either way. +F( "	if (GAMETYPE_MATCHES(%s)) {", c->name) +F( "		gamedata_%s = %s;", e->name, c->expr) +F( "		gamedata_has_%s = true;", e->name) +_( "	}") +				} +				else { +F( "	if (GAMETYPE_MATCHES(%s)) %s = %s;", c->name, e->name, c->expr) +				} +			} +		} +	} +_( "}") + +	return 0; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/con_.c b/src/con_.c new file mode 100644 index 0000000..54a8e9a --- /dev/null +++ b/src/con_.c @@ -0,0 +1,533 @@ +/* XXX: THIS FILE SHOULD BE CALLED `con.c` BUT WINDOWS IS STUPID */ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include "abi.h" +#include "con_.h" +#include "extmalloc.h" +#include "gameinfo.h" +#include "gametype.h" +#include "mem.h" +#include "os.h" +#include "vcall.h" +#include "version.h" + +/******************************************************************************\ + * Have you ever noticed that when someone comments "here be dragons" there's * + * no actual dragons? Turns out, that's because the dragons all migrated over * + * here, so that they could build multiple inheritance vtables in C, by hand. * + *                                                                            * + * Don't get set on fire.                                                     * +\******************************************************************************/ + +// given to us by the engine to unregister cvars in bulk on plugin unload +static int dllid; + +// external spaghetti variable, exists only because valve are bad +int con_cmdclient; + +// these have to be extern because of varargs nonsense - they get wrapped in a +// macro for the actual api (con_colourmsg) +void *_con_iface; +void (*_con_colourmsgf)(void *this, const struct con_colour *c, const char *fmt, +		...) _CON_PRINTF(3, 4); + +// XXX: the const and non-const entries might actually be flipped on windows, +// not 100% sure, but dunno if it's worth essentially duping most of these when +// the actual executed machine code is probably identical anyway. +DECL_VFUNC0(int, AllocateDLLIdentifier, 5) +DECL_VFUNC0(int, AllocateDLLIdentifier_p2, 8) +DECL_VFUNC(void, RegisterConCommand, 6, /*ConCommandBase*/ void *) +DECL_VFUNC(void, RegisterConCommand_p2, 9, /*ConCommandBase*/ void *) +DECL_VFUNC(void, UnregisterConCommands, 8, int) +DECL_VFUNC(void, UnregisterConCommands_p2, 11, int) +// DECL_VFUNC(void *, FindCommandBase, 10, const char *) +DECL_VFUNC(void *, FindCommandBase_p2, 13, const char *) +DECL_VFUNC(struct con_var *, FindVar, 12, const char *) +// DECL_VFUNC0(const struct con_var *, FindVar_const, 13, const char *) +DECL_VFUNC(struct con_var *, FindVar_p2, 15, const char *) +DECL_VFUNC(struct con_cmd *, FindCommand, 14, const char *) +DECL_VFUNC(struct con_cmd *, FindCommand_p2, 17, const char *) +DECL_VFUNC(void, CallGlobalChangeCallbacks, 20, struct con_var *, const char *, +		float) +DECL_VFUNC(void, CallGlobalChangeCallbacks_l4d, 18, struct con_var *, +		const char *, float) +DECL_VFUNC(void, CallGlobalChangeCallbacks_p2, 21, struct con_var *, +		const char *, float) +DECL_VFUNC_CDECL(void, ConsoleColorPrintf_004, 23, const struct con_colour *, +		const char *, ...) +DECL_VFUNC_CDECL(void, ConsoleColorPrintf_l4d, 21, const struct con_colour *, +		const char *, ...) +DECL_VFUNC_CDECL(void, ConsoleColorPrintf_p2, 24, const struct con_colour *, +		const char *, ...) + +static void initval(struct con_var *v) { +	// v->strlen is set to defaultval len in _DEF_CVAR so we don't need to call +	// strlen() on each string :) +	v->strval = extmalloc(v->strlen); +	memcpy(v->strval, v->defaultval, v->strlen); +	// FIXME: technically this can be compile time too, using _Generic. Do that! +	v->fval = atof(v->strval); v->ival = v->fval; +} + +// generated by build/codegen.c, defines regcmds() and freevars() +#include <cmdinit.gen.h> + +// to try and be like the engine even though it's probably not actually +// required, we call the Internal* virtual functions by actual virtual lookup. +// since the vtables are filled dynamically (below), we store this index; other +// indexes are just offset from this one since the 3-or-4 functions are all +// right next to each other. +static int vtidx_InternalSetValue; + +// implementatiosn of virtual functions for our vars and commands below... + +static void VCALLCONV dtor(void *_) {} // we don't use constructors/destructors + +static bool VCALLCONV IsCommand_cmd(void *this) { return true; } +static bool VCALLCONV IsCommand_var(void *this) { return false; } + +// flag stuff +static bool VCALLCONV IsFlagSet_cmd(struct con_cmd *this, int flags) { +	return !!(this->base.flags & flags); +} +static bool VCALLCONV IsFlagSet_var(struct con_var *this, int flags) { +	return !!(this->parent->base.flags & flags); +} +static void VCALLCONV AddFlags_cmd(struct con_cmd *this, int flags) { +	this->base.flags |= flags; +} +static void VCALLCONV AddFlags_var(struct con_var *this, int flags) { +	this->parent->base.flags |= flags; +} +static void VCALLCONV RemoveFlags_cmd(struct con_cmd *this, int flags) { +	this->base.flags &= ~flags; +} +static void VCALLCONV RemoveFlags_var(struct con_var *this, int flags) { +	this->parent->base.flags &= ~flags; +} +static int VCALLCONV GetFlags_cmd(struct con_cmd *this) { +	return this->base.flags; +} +static int VCALLCONV GetFlags_var(struct con_var *this) { +	return this->parent->base.flags; +} + +// basic registration stuff +static const char *VCALLCONV GetName_cmd(struct con_cmd *this) { +	return this->base.name; +} +static const char *VCALLCONV GetName_var(struct con_var *this) { +	return this->parent->base.name; +} +static const char *VCALLCONV GetHelpText_cmd(struct con_cmd *this) { +	return this->base.help; +} +static const char *VCALLCONV GetHelpText_var(struct con_var *this) { +	return this->parent->base.help; +} +static bool VCALLCONV IsRegistered(struct con_cmdbase *this) { +	return this->registered; +} +static int VCALLCONV GetDLLIdentifier(struct con_cmdbase *this) { +	return dllid; +} +static void VCALLCONV Create_base(struct con_cmdbase *this, const char *name, +		const char *help, int flags) {} // nop, we static init already +static void VCALLCONV Init(struct con_cmdbase *this) {} // "" + +static bool VCALLCONV ClampValue(struct con_var *this, float *f) { +	if (this->hasmin && this->minval > *f) { *f = this->minval; return true; } +	if (this->hasmax && this->maxval < *f) { *f = this->maxval; return true; } +	return false; +} + +// command-specific stuff +int VCALLCONV AutoCompleteSuggest(void *this, const char *partial, +		/* CUtlVector */ void *commands) { +	// TODO(autocomplete): implement this if needed later +	return 0; +} +bool VCALLCONV CanAutoComplete(void *this) { +	return false; +} +void VCALLCONV Dispatch(struct con_cmd *this, const struct con_cmdargs *args) { +	// only try cb, cbv1 and iface should never get used by us +	if (this->use_newcb && this->cb) this->cb(args); +} + +// var-specific stuff +static void VCALLCONV ChangeStringValue(struct con_var *this, const char *s, +		float oldf) { +	char *old = alloca(this->strlen); +	memcpy(old, this->strval, this->strlen); +	int len = strlen(s) + 1; +	if (len > this->strlen) { +		this->strval = extrealloc(this->strval, len); +		this->strlen = len; +	} +	memcpy(this->strval, s, len); +	//if (cb) {...} // not bothering +	// also note: portal2 has a *list* of callbacks, although that part of ABI +	// doesn't matter as far as plugin compat goes, so still not bothering +	// we do however bother to call global callbacks, as is polite. +	if (GAMETYPE_MATCHES(Portal2)) { +		VCALL(_con_iface, CallGlobalChangeCallbacks_p2, this, old, oldf); +	} +	else if (GAMETYPE_MATCHES(L4D)) { +		VCALL(_con_iface, CallGlobalChangeCallbacks_l4d, this, old, oldf); +	} +	else { +		VCALL(_con_iface, CallGlobalChangeCallbacks, this, old, oldf); +	} +} + +static void VCALLCONV InternalSetValue(struct con_var *this, const char *v) { +	float oldf = this->fval; +	float newf = atof(v); +	char tmp[32]; +	// NOTE: calling our own ClampValue and ChangeString, not bothering with +	// vtable (it's internal anyway, so we're never calling into engine code) +	if (ClampValue(this, &newf)) { +		snprintf(tmp, sizeof(tmp), "%f", newf); +		v = tmp; +	} +	this->fval = newf; +	this->ival = (int)newf; +	if (!(this->base.flags & CON_NOPRINT)) ChangeStringValue(this, v, oldf); +} + +static void VCALLCONV InternalSetFloatValue(struct con_var *this, float v) { +	if (v == this->fval) return; +	ClampValue(this, &v); +	float old = this->fval; +	this->fval = v; this->ival = (int)this->fval; +	if (!(this->base.flags & CON_NOPRINT)) { +		char tmp[32]; +		snprintf(tmp, sizeof(tmp), "%f", this->fval); +		ChangeStringValue(this, tmp, old); +	} +} + +static void VCALLCONV InternalSetIntValue(struct con_var *this, int v) { +	if (v == this->ival) return; +	float f = (float)v; +	if (ClampValue(this, &f)) v = (int)f; +	float old = this->fval; +	this->fval = f; this->ival = v; +	if (!(this->base.flags & CON_NOPRINT)) { +		char tmp[32]; +		snprintf(tmp, sizeof(tmp), "%f", this->fval); +		ChangeStringValue(this, tmp, old); +	} +} + +// Hack: IConVar things get this-adjusted pointers, we just reverse the offset +// to get the top pointer. +static void VCALLCONV SetValue_str_thunk(void *thisoff, const char *v) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	((void (*VCALLCONV)(void *, const char *))(this->parent->base.vtable[ +			vtidx_InternalSetValue]))(this, v); +} +static void VCALLCONV SetValue_f_thunk(void *thisoff, float v) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	((void (*VCALLCONV)(void *, float))(this->parent->base.vtable[ +			vtidx_InternalSetValue + 1]))(this, v); +} +static void VCALLCONV SetValue_i_thunk(void *thisoff, int v) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	((void (*VCALLCONV)(void *, int))(this->parent->base.vtable[ +			vtidx_InternalSetValue + 2]))(this, v); +} +static void VCALLCONV SetValue_colour_thunk(void *thisoff, struct con_colour v) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	((void (*VCALLCONV)(void *, struct con_colour))(this->parent->base.vtable[ +			vtidx_InternalSetValue + 3]))(this, v); +} + +// more misc thunks, hopefully these just compile to a sub and a jmp +static const char *VCALLCONV GetName_thunk(void *thisoff) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	return GetName_var(this); +} +static bool VCALLCONV IsFlagSet_thunk(void *thisoff, int flags) { +	struct con_var *this = mem_offset(thisoff, +			-offsetof(struct con_var, vtable_iconvar)); +	return IsFlagSet_var(this, flags); +} + +// dunno what this is actually for... +static int VCALLCONV GetSplitScreenPlayerSlot(void *thisoff) { return 0; } + +// aand yet another Create nop +static void VCALLCONV Create_var(void *thisoff, const char *name, +		const char *defaultval, int flags, const char *helpstr, bool hasmin, +		float min, bool hasmax, float max, void *cb) {} + +#ifdef _WIN32 +#define NVDTOR 1 +#else +#define NVDTOR 2 // Itanium ABI has 2 because I said so +#endif + +// the first few members of ConCommandBase are the same between versions +void *_con_vtab_cmd[14 + NVDTOR] = { +	(void *)&dtor, +#ifndef _WIN32 +	(void *)&dtor2, +#endif +	(void *)&IsCommand_cmd, +	(void *)&IsFlagSet_cmd, +	(void *)&AddFlags_cmd +}; + +// the engine does dynamic_casts on ConVar at some points so we have to fill out +// bare minimum rtti to prevent crashes. oh goody. +#ifdef _WIN32 +DEF_MSVC_BASIC_RTTI(static, varrtti, _con_realvtab_var, "sst_ConVar") +#endif + +void *_con_realvtab_var[20] = { +#ifdef _WIN32 +	&varrtti, +#else +	// this, among many other things, will be totally different on linux +#endif +	(void *)&dtor, +#ifndef _WIN32 +	(void *)&dtor2, +#endif +	(void *)&IsCommand_var, +	(void *)&IsFlagSet_var, +	(void *)&AddFlags_var +}; + +void *_con_vtab_iconvar[7] = { +#ifdef _WIN32 +	0 // because of crazy overload vtable order we can't prefill *anything* +#else +	// colour is the last of the 4 on linux so we can at least prefill these 3 +	(void *)&SetValue_str_thunk, +	(void *)&SetValue_f_thunk, +	(void *)&SetValue_i_thunk +#endif +}; + +static void fillvts(void) { +	void **pc = _con_vtab_cmd + 3 + NVDTOR, **pv = _con_vtab_var + 3 + NVDTOR, +			**pi = _con_vtab_iconvar +#ifndef _WIN32 +				+ 3 +#endif +			; +	if (GAMETYPE_MATCHES(L4Dbased)) { // 007 base +		*pc++ = (void *)&RemoveFlags_cmd; +		*pc++ = (void *)&GetFlags_cmd; +		*pv++ = (void *)&RemoveFlags_var; +		*pv++ = (void *)&GetFlags_var; +	} +	// base stuff in cmd +	*pc++ = (void *)&GetName_cmd; +	*pc++ = (void *)&GetHelpText_cmd; +	*pc++ = (void *)&IsRegistered; +	*pc++ = (void *)&GetDLLIdentifier; +	*pc++ = (void *)&Create_base; +	*pc++ = (void *)&Init; +	// cmd-specific +	*pc++ = (void *)&AutoCompleteSuggest; +	*pc++ = (void *)&CanAutoComplete; +	*pc++ = (void *)&Dispatch; +	// base stuff in var +	*pv++ = (void *)&GetName_var; +	*pv++ = (void *)&GetHelpText_var; +	*pv++ = (void *)&IsRegistered; +	*pv++ = (void *)&GetDLLIdentifier; +	*pv++ = (void *)&Create_base; +	*pv++ = (void *)&Init; +	// var-specific +	vtidx_InternalSetValue = pv - _con_vtab_var; +	*pv++ = (void *)&InternalSetValue; +	*pv++ = (void *)&InternalSetFloatValue; +	*pv++ = (void *)&InternalSetIntValue; +	if (GAMETYPE_MATCHES(L4D2) || GAMETYPE_MATCHES(Portal2)) { // ugh, annoying +		// This is InternalSetColorValue, but that's basically the same thing, +		// when you think about it. +		*pv++ = (void *)&InternalSetIntValue; +	} +	*pv++ = (void *)&ClampValue;; +	*pv++ = (void *)&ChangeStringValue; +	*pv++ = (void *)&Create_var; +	if (GAMETYPE_MATCHES(L4D2) || GAMETYPE_MATCHES(Portal2)) { +		*pi++ = (void *)&SetValue_colour_thunk; +	} +#ifdef _WIN32 +	// see above: these aren't prefilled due the the reverse order +	*pi++ = (void *)&SetValue_i_thunk; +	*pi++ = (void *)&SetValue_f_thunk; +	*pi++ = (void *)&SetValue_str_thunk; +#endif +	*pi++ = (void *)&GetName_thunk; +	// GetBaseName (we just return actual name in all cases) +	if (GAMETYPE_MATCHES(L4Dbased)) *pi++ = (void *)&GetName_thunk; +	*pi++ = (void *)&IsFlagSet_thunk; +	// last one: not in 004, but doesn't matter. one less branch! +	*pi++ = (void *)&GetSplitScreenPlayerSlot; +} + +bool con_init(void *(*f)(const char *, int *), int plugin_ver) { +	int ifacever; // for error messages +	if (_con_iface = f("VEngineCvar007", 0)) { +		// GENIUS HACK (BUT STILL BAD): Portal 2 has everything in ICvar shifted +		// down 3 places due to the extra stuff in IAppSystem. This means that +		// if we look up the Portal 2-specific cvar using FindCommandBase, it +		// *actually* calls the const-overloaded FindVar on other branches, +		// which just happens to still work fine. From there, we can figure out +		// the actual ABI to use to avoid spectacular crashes. +		if (VCALL(_con_iface, FindCommandBase_p2, "portal2_square_portals")) { +			_con_colourmsgf = VFUNC(_con_iface, ConsoleColorPrintf_p2); +			dllid = VCALL0(_con_iface, AllocateDLLIdentifier_p2); +			_gametype_tag |= _gametype_tag_Portal2; +			fillvts(); +			regcmds(VFUNC(_con_iface, RegisterConCommand_p2)); +			return true; +		} +		if (VCALL(_con_iface, FindCommand, "l4d2_snd_adrenaline")) { +			_con_colourmsgf = VFUNC(_con_iface, ConsoleColorPrintf_l4d); +			dllid = VCALL0(_con_iface, AllocateDLLIdentifier); +			_gametype_tag |= _gametype_tag_L4D2; +			fillvts(); +			regcmds(VFUNC(_con_iface, RegisterConCommand)); +			return true; +		} +		if (VCALL(_con_iface, FindVar, "z_difficulty")) { +			_con_colourmsgf = VFUNC(_con_iface, ConsoleColorPrintf_l4d); +			dllid = VCALL0(_con_iface, AllocateDLLIdentifier); +			_gametype_tag |= _gametype_tag_L4D1; +			fillvts(); // XXX: is this all kinda dupey? maybe rearrange one day. +			regcmds(VFUNC(_con_iface, RegisterConCommand)); +			return true; +		} +		con_warn("sst: error: game \"%s\" is unsupported (using " +					"VEngineCvar007)\n", gameinfo_title); +		ifacever = 7; +		goto e; +	} +	if (_con_iface = f("VEngineCvar004", 0)) { +		// TODO(compat): are there any cases where 004 is incompatible? could +		// this crash? find out! +		_con_colourmsgf = VFUNC(_con_iface, ConsoleColorPrintf_004); +		dllid = VCALL0(_con_iface, AllocateDLLIdentifier); +		// even more spaghetti! we need the plugin interface version to +		// accurately distinguish 2007/2013 branches +		if (plugin_ver == 3) _gametype_tag |= _gametype_tag_2013; +		else _gametype_tag |= _gametype_tag_OrangeBox; +		fillvts(); +		regcmds(VFUNC(_con_iface, RegisterConCommand)); +		return true; +	} +	if (f("VEngineCvar003", 0)) { +		ifacever = 3; +		goto warnoe; +	} +	if (f("VEngineCvar002", 0)) { +		// I don't suppose there's anything below 002 worth caring about? Shrug. +		ifacever = 2; +warnoe:	con_warn("sst: error: old engine console support is not implemented\n"); +		goto e; +	} +	con_warn("sst: error: couldn't find a supported console interface\n"); +	ifacever = -1; // meh +e:	con_msg("\n\n"); +	con_msg("-- Please include ALL of the following if asking for help:\n"); +	con_msg("--   plugin:     " LONGNAME " v" VERSION "\n"); +	con_msg("--   game:       %s\n", gameinfo_title); +	con_msg("--   interfaces: %d/%d\n", plugin_ver, ifacever); +	con_msg("\n\n"); +	return false; +} + +void con_disconnect(void) { +	if (GAMETYPE_MATCHES(Portal2)) { +		VCALL(_con_iface, UnregisterConCommands_p2, dllid); +	} +	else { +		VCALL(_con_iface, UnregisterConCommands, dllid); +	} +	freevars(); +} + +struct con_var *con_findvar(const char *name) { +	if (GAMETYPE_MATCHES(Portal2)) { +		return VCALL(_con_iface, FindVar_p2, name); +	} +	else { +		return VCALL(_con_iface, FindVar, name); +	} +} + +struct con_cmd *con_findcmd(const char *name) { +	if (GAMETYPE_MATCHES(Portal2)) { +		return VCALL(_con_iface, FindCommand_p2, name); +	} +	else { +		return VCALL(_con_iface, FindCommand, name); +	} +} + +#define GETTER(T, N, M) \ +	T N(const struct con_var *v) { return v->parent->M; } +GETTER(const char *, con_getvarstr, strval) +GETTER(float, con_getvarf, fval) +GETTER(int, con_getvari, ival) +#undef GETTER + +#define SETTER(T, I, N) \ +	void N(struct con_var *v, T x) { \ +		((void (*VCALLCONV)(struct con_var *, T))(v->vtable_iconvar[I]))(v, x); \ +	} +// vtable indexes for str/int/float are consistently at the start, hooray. +// unfortunately the windows overload ordering meme still applies... +#ifdef _WIN32 +SETTER(const char *, 2, con_setvarstr) +SETTER(float, 1, con_setvarf) +SETTER(int, 0, con_setvari) +#else +SETTER(const char *, 0, con_setvarstr) +SETTER(float, 1, con_setvarf) +SETTER(int, 2, con_setvari) +#endif +#undef SETTER + +con_cmdcb con_getcmdcb(const struct con_cmd *cmd) { +	if (cmd->use_newcmdiface || !cmd->use_newcb) return 0; +	return cmd->cb; +} + +con_cmdcbv1 con_getcmdcbv1(const struct con_cmd *cmd) { +	if (cmd->use_newcmdiface || cmd->use_newcb) return 0; +	return cmd->cb_v1; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/con_.h b/src/con_.h new file mode 100644 index 0000000..4c662fe --- /dev/null +++ b/src/con_.h @@ -0,0 +1,278 @@ +/* XXX: THIS FILE SHOULD BE CALLED `con.h` BUT WINDOWS IS STUPID */ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_CON_H +#define INC_CON_H + +#include <stdbool.h> + +#include "intdefs.h" + +#if defined(__GNUC__) || defined(__clang__) +#define _CON_PRINTF(x, y) __attribute__((format(printf, (x), (y)))) +#else +#define _CON_PRINTF(x, y) +#endif + +#define CON_CMD_MAX_ARGC 64 +#define CON_CMD_MAX_LENGTH 512 + +/* arguments to a console command, parsed by the engine (SDK name: CCommand) */ +struct con_cmdargs { +	int argc; +	int argv0size; +	char argsbuf[CON_CMD_MAX_LENGTH]; +	char argvbuf[CON_CMD_MAX_LENGTH]; +	const char *argv[CON_CMD_MAX_ARGC]; +}; + +/* an ARGB colour, passed to con_colourmsg */ +struct con_colour { +	union { +		struct { u8 r, g, b, a; }; +		u32 val; +		uchar bytes[4]; +	}; +}; + +#define CON_CMD_MAXCOMPLETE 64 +#define CON_CMD_MAXCOMPLLEN 64 + +/* ConVar/ConCommand flag bits - undocumented ones are probably not useful... */ +enum { +	CON_UNREG = 1, +	CON_DEVONLY = 1 << 1,		/* hide unless developer 1 is set */ +	CON_SERVERSIDE = 1 << 2,	/* set con_cmdclient and run on server side */ +	CON_CLIENTDLL = 1 << 3, +	CON_HIDDEN = 1 << 4,		/* hide completely, often useful to remove! */ +	CON_PROTECTED = 1 << 5,		/* don't send to clients (example: password) */ +	CON_SPONLY = 1 << 6, +	CON_ARCHIVE = 1 << 7,		/* save in config - plugin would need a VDF! */ +	CON_NOTIFY = 1 << 8,		/* announce changes in game chat */ +	CON_USERINFO = 1 << 9, +	CON_PRINTABLE = 1 << 10,	/* do not allow non-printable values */ +	CON_UNLOGGED = 1 << 11, +	CON_NOPRINT = 1 << 12,		/* do not attempt to print, contains junk! */ +	CON_REPLICATE = 1 << 13,	/* client will use server's value */ +	CON_CHEAT = 1 << 14,		/* require sv_cheats 1 to change from default */ +	CON_DEMO = 1 << 16,			/* record value at the start of a demo */ +	CON_NORECORD = 1 << 17,		/* don't record the command to a demo, ever */ +	CON_NOTCONN = 1 << 22,		/* cannot be changed while in-game */ +	CON_SRVEXEC = 1 << 28,		/* server can make clients run the command */ +	CON_NOSRVQUERY = 1 << 29,	/* server cannot query the clientside value */ +	CON_CCMDEXEC = 1 << 30		/* ClientCmd() function may run the command */ +}; + +/* A callback function invoked to execute a command. */ +typedef void (*con_cmdcb)(const struct con_cmdargs *cmd); + +/* Obsolete callback; not used by SST, but might still exist in the engine. */ +typedef void (*con_cmdcbv1)(void); + +/* + * This is an autocompletion callback for suggesting arguments to a command. + * XXX: Autocompletion isn't really totally figured out or implemented yet. + */ +typedef int (*con_complcb)(const char *part, +		char cmds[CON_CMD_MAXCOMPLETE][CON_CMD_MAXCOMPLLEN]); + +/* + * These are called by the plugin load/unload functions; they have no use + * elsewhere. + */ +bool con_init(void *(*f)(const char *, int *), int plugin_ver); +void con_disconnect(void); + +/* + * These types *pretty much* match those in the engine. Their fields can be + * accessed and played with if you know what you're doing! + */ + +struct con_cmdbase { // ConCommandBase in engine +	void **vtable; +	struct con_cmdbase *next; +	bool registered; +	const char *name; +	const char *help; +	uint flags; +}; + +struct con_cmd { // ConCommand in engine +	struct con_cmdbase base; +	union { +		con_cmdcbv1 cb_v1; +		con_cmdcb cb; +		/*ICommandCallback*/ void *cb_iface; // does source even use this? +	}; +	union { +		con_complcb complcb; +		/*ICommandCompletionCallback*/ void *complcb_iface; // ...or this? +	}; +	bool has_complcb : 1, use_newcb : 1, use_newcmdiface : 1; +}; + +// con_var will be a bit different on linux; see offset_to_top etc. +#ifdef __linux__ +#error FIXME: redo multi-vtable crap for itanium ABI! +#endif + +struct con_var { // ConVar in engine +	struct con_cmdbase base; +	void **vtable_iconvar; // IConVar in engine (pure virtual) +	struct con_var *parent; +	const char *defaultval; +	char *strval; +	uint strlen; +	float fval; +	int ival; +	bool hasmin; +	// bool hasmax; // better packing here, might break engine ABI +	int minval; +	bool hasmax; // just sticking to sdk position for now +	int maxval; +	//void *cb; we don't currently bother with callback support. add if needed! +}; + + +/* + * These functions get and set the values of console variables in a + * neatly-abstracted manner. Note: cvar values are always strings internally - + * numerical values are just interpretations of the underlying value. + */ +struct con_var *con_findvar(const char *name); +struct con_cmd *con_findcmd(const char *name); +const char *con_getvarstr(const struct con_var *v); +float con_getvarf(const struct con_var *v); +int con_getvari(const struct con_var *v); +void con_setvarstr(struct con_var *v, const char *s); +void con_setvarf(struct con_var *v, float f); +void con_setvari(struct con_var *v, int i); + +/* + * These functions grab the callback function from an existing command, allowing + * it to be called directly or further dug into for convenient research. + * + * They perform sanity checks to ensure that the command implements the type of + * callback being requested. If this is already known, consider just grabbing + * the member directly to avoid the small amount of unnecessary work. + */ +con_cmdcb con_getcmdcb(const struct con_cmd *cmd); +con_cmdcbv1 con_getcmdcbv1(const struct con_cmd *cmd); + +/* + * These functions provide basic console output, in white and red colours + * respectively. They are aliases to direct tier0 calls, so they work early on + * even before anything else is initialised. + */ +#if defined(__GNUC__) || defined(__clang__) +#ifdef _WIN32 +#define __asm__(x) __asm__("_" x) // stupid mangling meme (not on linux, right?) +#endif +void con_msg(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Msg"); +void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Warning"); +#undef __asm__ +#else +#error Need an equivalent of asm names for your compiler! +#endif + +extern void *_con_iface; +extern void (*_con_colourmsgf)(void *this, const struct con_colour *c, +		const char *fmt, ...) _CON_PRINTF(3, 4); +/* + * This provides the same functionality as ConColorMsg which was removed from + * tier0 in the L4D engine branch - specifically, it allows printing a message + * with an arbitrary RGBA colour. It must only be used after a successful + * con_init() call. + */ +#define con_colourmsg(c, ...) _con_colourmsgf(_con_iface, c, __VA_ARGS__) + +/* + * The index of the client responsible for the currently executing command, + * or -1 if serverside. This is a global variable because of Source spaghetti, + * rather than unforced plugin spaghetti. + */ +extern int con_cmdclient; + +// internal detail, used by DEF_* macros below +extern void *_con_vtab_cmd[]; +// msvc rtti tables are offset negatively from the vtable pointer. to make this +// a constant expression we have to use a macro +#define _con_vtab_var (_con_realvtab_var + 1) +extern void *_con_realvtab_var[]; +extern void *_con_vtab_iconvar[]; + +#define _DEF_CVAR(name_, desc, value, hasmin_, min, hasmax_, max, flags_) \ +	static struct con_var _cvar_##name_ = { \ +		.base = { \ +			.vtable = _con_vtab_var, \ +			.name = "" #name_, .help = "" desc, .flags = (flags_) \ +		}, \ +		.vtable_iconvar = _con_vtab_iconvar, \ +		.parent = &_cvar_##name_, /* bizarre, but how the engine does it */ \ +		.defaultval = value, .strlen = sizeof("" value), \ +		.hasmin = hasmin_, .minval = (min), .hasmax = hasmax_, .maxval = (max) \ +	}; \ +	struct con_var *name_ = (struct con_var *)&_cvar_##name_; + +/* Defines a console variable with no min/max values. */ +#define DEF_CVAR(name, desc, value, flags) \ +	_DEF_CVAR(name, desc, value, false, 0, false, 0, flags) + +/* Defines a console variable with a given mininum numeric value. */ +#define DEF_CVAR_MIN(name_, desc, value, min, flags_) \ +	_DEF_CVAR(name, desc, value, true, min, false, 0, flags) + +/* Defines a console variable in the given numeric value range. */ +#define DEF_CVAR_MINMAX(name_, desc, value, min, max, flags_) \ +	_DEF_CVAR(name, desc, value, true, min, true, max, flags) + +#define _DEF_CCMD(varname, name_, desc, func, flags_) \ +	static struct con_cmd _ccmd_##varname = { \ +		.base = { \ +			.vtable = _con_vtab_cmd, \ +			.name = "" #name_, .help = "" desc, .flags = (flags_) \ +		}, \ +		.cb = &func, \ +		.use_newcb = true \ +	}; \ +	struct con_cmd *varname = (struct con_cmd *)&_ccmd_##varname; + +/* Defines a command with a given function as its handler. */ +#define DEF_CCMD(name, desc, func, flags) \ +	_DEF_CCMD(name, name, desc, func, flags) + +/* + * Defines two complementary +- commands, with PLUS_ and MINUS_ prefixes on + * their C names. + */ +#define DEF_CCMD_PLUSMINUS(name, descplus, fplus, descminus, fminus, flags) \ +	_DEF_CCMD(PLUS_##name, "+" name, descplus, fplus, flags) \ +	_DEF_CCMD(MINUS_##name, "-" name, descminus, fminus, flags) + +/* + * Defines a console command with the handler function body immediately + * following the macro (like in Source itself). + */ +#define DEF_CCMD_HERE(name, desc, flags) \ +	static void _cmdf_##name(const struct con_cmdargs *cmd); \ +	_DEF_CCMD(name, name, desc, _cmdf_##name, flags) \ +	static void _cmdf_##name(const struct con_cmdargs *cmd) \ +	/* { body here } */ + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/dbg.c b/src/dbg.c new file mode 100644 index 0000000..ca4e08e --- /dev/null +++ b/src/dbg.c @@ -0,0 +1,49 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 "con_.h" +#include "intdefs.h" +#include "ppmagic.h" +#include "udis86.h" + +void dbg_hexdump(char *name, const void *p, int len) { +	struct con_colour nice_colour = {160, 64, 200, 255}; // a nice purple colour +	con_colourmsg(&nice_colour, "Hex dump \"%s\" (%p):", name, p); +	for (const uchar *cp = p; cp - (uchar *)p < len; ++cp) { +		// group into words and wrap every 8 words +		switch ((cp - (uchar *)p) & 31) { +			case 0: con_msg("\n"); break; +			CASES(4, 8, 12, 16, 20, 24, 28): con_msg(" "); +		} +		con_colourmsg(&nice_colour, "%02X ", *cp); +	} +	con_msg("\n"); +} + +void dbg_asmdump(char *name, const void *p, int len) { +	struct con_colour nice_colour = {40, 160, 140, 255}; // a nice teal colour +	struct ud udis; +	ud_init(&udis); +	ud_set_mode(&udis, 32); +	ud_set_input_buffer(&udis, p, len); +	ud_set_syntax(&udis, UD_SYN_INTEL); +	con_colourmsg(&nice_colour, "Disassembly \"%s\" (%p)\n", name, p); +	while (ud_disassemble(&udis)) { +		con_colourmsg(&nice_colour, "  %s\n", ud_insn_asm(&udis)); +	} +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/dbg.h b/src/dbg.h new file mode 100644 index 0000000..983e65f --- /dev/null +++ b/src/dbg.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_DBG_H +#define INC_DBG_H + +/* prints out a basic hexadecimal listing of a byte range */ +void dbg_hexdump(char *name, const void *p, int len); + +/* prints out a disassembly of some instructions in memory */ +void dbg_asmdump(char *name, const void *p, int len); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/demodefs.h b/src/demodefs.h new file mode 100644 index 0000000..8aab77b --- /dev/null +++ b/src/demodefs.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_DEMODEFS_H +#define INC_DEMODEFS_H + +#include "intdefs.h" + +/* + * This file has demo format-related constants, mostly derived from Uncrafted's + * C# demo parser. + */ + +/* Windows' MAX_PATH is also used for player/map/etc. names in the demo... */ +#define DEMO_HDR_STRLEN 260 + +struct demo_hdr { +	char sig[8]; /* HL2DEMO\0 */ +	s32 demover; +	s32 netver; +	char servername[DEMO_HDR_STRLEN]; +	char playername[DEMO_HDR_STRLEN]; +	char mapname[DEMO_HDR_STRLEN]; +	char gamedir[DEMO_HDR_STRLEN]; +	float realtime; +	s32 nticks; +	s32 nframes; +	s32 signonlen; +}; + +enum demo_cmd { +	// all protocols: +	DEMO_CMD_SIGNON = 1, +	DEMO_CMD_PACKET, +	DEMO_CMD_SYNC, +	DEMO_CMD_CONCMD, +	DEMO_CMD_USERCMD, +	DEMO_CMD_DATATABLES, +	DEMO_CMD_STOP, +	DEMO_CMD_STRINGTABLES14 = 8, // protocols 14 and 15 +	DEMO_CMD_CUSTOMDATA = 8, // protocol 36+ +	DEMO_CMD_STRINGTABLES36  // " +}; + +/* these are seemingly consistent across games/branches */ +#define DEMO_MAXEDICTBITS 11 +#define DEMO_MAXEDICTS (1 << DEMO_MAXEDICTS) +#define DEMO_NETHANDLESERIALBITS 10 +#define DEMO_NETHANDLEBITS (DEMO_MAXEDICTBITS + DEMO_NETHANDLEBITS) +#define DEMO_NULLHANDLE ((1u << DEMO_NETHANDLEBITS) - 1) +#define DEMO_SUBSTRINGBITS 5 +#define DEMO_MAXUSERDATABITS 14 +#define DEMO_HANDLESERIALBITS 10 +// TODO: clarify what these ones do, and/or remove +#define DEMO_MAXNETMSG 6 +#define DEMO_AREABITSNUMBITS 8 +#define DEMO_MAXSNDIDXBITS 13 +#define DEMO_SNDSEQBITS 10 +#define DEMO_MAXSNDLVLBITS 9 +#define DEMO_MAXSNDDELAYBITS 13 +#define DEMO_SNDSEQMASK ((1 << DEMO_SNDSEQBITS) - 1) +// end of todo :^) +#define DEMO_PLAYERNAMELEN 32 +#define DEMO_GUIDLEN 32 + +/* protocol versions (seem somewhat arbitrary but just copying Uncrafted) */ +// (note: these aren't version numbers, they're just our own identifiers) +enum { +	DEMO_PROTO_HL2OE, +	DEMO_PROTO_PORTAL_5135, +	DEMO_PROTO_PORTAL_3420, +	DEMO_PROTO_PORTAL_STEAM, +	DEMO_PROTO_PORTAL2, +	DEMO_PROTO_L4D2000, +	DEMO_PROTO_L4D2042, +	DEMO_PROTO_UNKNOWN +}; + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/demorec.c b/src/demorec.c new file mode 100644 index 0000000..8e4c2c2 --- /dev/null +++ b/src/demorec.c @@ -0,0 +1,218 @@ +/* + * Copyright © 2021 Willian Henrique <wsimanbrazil@yahoo.com.br> + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> + +#include "con_.h" +#include "hook.h" +#include "gamedata.h" +#include "intdefs.h" +#include "mem.h" +#include "os.h" +#include "udis86.h" +#include "vcall.h" + +#define SIGNONSTATE_SPAWN 5 // ready to receive entity packets +#define SIGNONSTATE_FULL 6 // fully connected, first non-delta packet receieved + +typedef void (*VCALLCONV f_StopRecording)(void *); +typedef void (*VCALLCONV f_SetSignonState)(void *, int); + +static void *demorecorder; +static struct con_cmd *cmd_stop; +static bool *recording; +static int *demonum; +static f_SetSignonState orig_SetSignonState; +static f_StopRecording orig_StopRecording; +static con_cmdcb orig_stop_callback; + +static int auto_demonum = 1; +static bool auto_recording = false; + +DEF_CVAR(sst_autorecord, "Continue recording demos through map changes", +		"0", CON_ARCHIVE | CON_HIDDEN) + +static void VCALLCONV hook_StopRecording(void *this) { +	// This hook will get called twice per loaded save (in most games/versions, +	// at least, according to SAR people): first with m_bLoadgame set to false +	// and then with it set to true. This will set m_nDemoNumber to 0 and +	// m_bRecording to false +	orig_StopRecording(this); + +	if (auto_recording && con_getvari(sst_autorecord)) { +		*demonum = auto_demonum; +		*recording = true; +	} +	else { +		auto_demonum = 1; +		auto_recording = false; +	} +} + +static void VCALLCONV hook_SetSignonState(void *this, int state) { +	// SIGNONSTATE_FULL *may* happen twice per load, depending on the game, so +	// use SIGNONSTATE_SPAWN for demo number increase +	if (state == SIGNONSTATE_SPAWN && auto_recording) auto_demonum++; +	// Starting a demo recording will call this function with SIGNONSTATE_FULL +	// After a load, the engine's demo recorder will only start recording when +	// it reaches this state, so this is a good time to set the flag if needed +	else if (state == SIGNONSTATE_FULL) { +		// Changing sessions may unset the recording flag (or so says NeKzor), +		// so if we want to be recording, we want to tell the engine to record. +		// But also, if the engine is already recording, we want our state to +		// reflect *that*. IOW, if either thing is set, also set the other one. +		auto_recording |= *recording; *recording = auto_recording; + +		// FIXME: this will override demonum incorrectly if the plugin is +		// loaded while demos are already being recorded +		if (auto_recording) *demonum = auto_demonum; +	} +	orig_SetSignonState(this, state); +} + +static void hook_stop_callback(const struct con_cmdargs *args) { +	auto_recording = false; +	orig_stop_callback(args); +} + +// This finds the "demorecorder" global variable (the engine-wide CDemoRecorder +// instance). +static inline void *find_demorecorder(struct con_cmd *cmd_stop) { +	// The "stop" command calls the virtual function demorecorder.IsRecording(), +	// so just look for the load of the "this" pointer +	struct ud udis; +	ud_init(&udis); +	ud_set_mode(&udis, 32); +	ud_set_input_buffer(&udis, (uchar *)con_getcmdcb(cmd_stop), 32); +	while (ud_disassemble(&udis)) { +#ifdef _WIN32 +		if (ud_insn_mnemonic(&udis) == UD_Imov) { +			const struct ud_operand *dest = ud_insn_opr(&udis, 0); +			const struct ud_operand *src = ud_insn_opr(&udis, 1); +			// looking for a mov from an address into ECX, as per thiscall +			if (dest->type == UD_OP_REG && dest->base == UD_R_ECX && +					src->type == UD_OP_MEM) { +				return *(void **)src->lval.udword; +			} +		} +#else +#error TODO(linux): implement linux equivalent (cdecl!) +#endif +	} +	return 0; +} + +// This finds "m_bRecording" and "m_nDemoNumber" using the pointer to the +// original "StopRecording" demorecorder function +static inline bool find_recmembers(void *stop_recording_func, void *demorec) { +	struct ud udis; +	ud_init(&udis); +	ud_set_mode(&udis, 32); +	// TODO(opt): consider the below: is it really needed? does it matter? +	// way overshooting the size of the function in bytes to make sure it +	// accomodates for possible differences in different games. we make sure +	// to stop early if we find a RET so should be fine +	ud_set_input_buffer(&udis, (uchar *)stop_recording_func, 200); +	while (ud_disassemble(&udis)) { +#ifdef _WIN32 +		enum ud_mnemonic_code code = ud_insn_mnemonic(&udis); +		if (code == UD_Imov) { +			const struct ud_operand *dest = ud_insn_opr(&udis, 0); +			const struct ud_operand *src = ud_insn_opr(&udis, 1); +			// m_nDemoNumber and m_bRecording are both set to 0 +			// looking for movs with immediates equal to 0 +			// the byte immediate refers to m_bRecording +			if (src->type == UD_OP_IMM && src->lval.ubyte == 0) { +				if (src->size == 8) { +					recording = (bool *)mem_offset(demorec, dest->lval.udword); +				} +				else { +					demonum = (int *)mem_offset(demorec, dest->lval.udword); +				} +				if (recording && demonum) return true; // blegh +			} +		} +		else if (code == UD_Iret) { +			return false; +		} +#else // linux is probably different here idk +#error TODO(linux): implement linux equivalent +#endif +	} +	return false; +} + +bool demorec_init(void) { +	if (!gamedata_has_vtidx_SetSignonState || +			!gamedata_has_vtidx_StopRecording) { +		con_warn("demorec: missing gamedata entries for this engine\n"); +		return false; +	} + +	cmd_stop = con_findcmd("stop"); +	if (!cmd_stop) { // can *this* even happen? I hope not! +		con_warn("demorec: couldn't find \"stop\" command\n"); +		return false; +	} + +	demorecorder = find_demorecorder(cmd_stop); +	if (!demorecorder) { +		con_warn("demorec: couldn't find demo recorder instance\n"); +		return false; +	} + +	void **vtable = *(void ***)demorecorder; + +	// XXX: 16 is totally arbitrary here! figure out proper bounds later +	if (!os_mprot(vtable, 16 * sizeof(void *), PAGE_EXECUTE_READWRITE)) { +#ifdef _WIN32 +		char err[128]; +		OS_WINDOWS_ERROR(err); +#else +		const char *err = strerror(errno); +#endif +		con_warn("demorec: couldn't unprotect CDemoRecorder vtable: %s\n", err); +		return false; +	} + +	if (!find_recmembers(vtable[7], demorecorder)) { +		con_warn("demorec: couldn't find m_bRecording and m_nDemoNumber\n"); +		return false; +	} + +	orig_SetSignonState = (f_SetSignonState)hook_vtable(vtable, +			gamedata_vtidx_SetSignonState, (void *)&hook_SetSignonState); +	orig_StopRecording = (f_StopRecording)hook_vtable(vtable, +			gamedata_vtidx_StopRecording, (void *)&hook_StopRecording); + +	orig_stop_callback = cmd_stop->cb; +	cmd_stop->cb = &hook_stop_callback; + +	sst_autorecord->base.flags &= ~CON_HIDDEN; +	return true; +} + +void demorec_end(void) { +	void **vtable = *(void ***)demorecorder; +	unhook_vtable(vtable, gamedata_vtidx_SetSignonState, +			(void *)orig_SetSignonState); +	unhook_vtable(vtable, gamedata_vtidx_StopRecording, +			(void *)orig_StopRecording); +	cmd_stop->cb = orig_stop_callback; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/demorec.h b/src/demorec.h new file mode 100644 index 0000000..9d8e73e --- /dev/null +++ b/src/demorec.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2021 Willian Henrique <wsimanbrazil@yahoo.com.br> + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_DEMOREC_H +#define INC_DEMOREC_H + +#include <stdbool.h> + +bool demorec_init(void); +void demorec_end(void); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/dll.rc b/src/dll.rc new file mode 100644 index 0000000..59f5354 --- /dev/null +++ b/src/dll.rc @@ -0,0 +1,33 @@ +/* This file is dedicated to the public domain. */ + +#include "version.h" + +#define EN_GB 0x809 + +1 VERSIONINFO +FILEVERSION VERSION_MAJOR,VERSION_MINOR,0,0 +PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,0,0 +FILEFLAGSMASK 0x17L +FILEFLAGS 0 +FILEOS 4 + +BEGIN +	BLOCK "StringFileInfo" +	BEGIN +		BLOCK "080904b0" +		BEGIN +			VALUE "FileDescription", LONGNAME +			VALUE "FileVersion", VERSION +			VALUE "InternalName", NAME +			VALUE "LegalCopyright", "Copyright (C) 2021 Michael Smith and others. All rights reserved." +			VALUE "ProductName", LONGNAME +			VALUE "ProductVersion", VERSION +		END +	END +	BLOCK "VarFileInfo" +	BEGIN +		VALUE "Translation", EN_GB, 1200 +	END +END + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/extmalloc.c b/src/extmalloc.c new file mode 100644 index 0000000..9814d1b --- /dev/null +++ b/src/extmalloc.c @@ -0,0 +1,60 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 "intdefs.h" +#include "vcall.h" + +// FIXME: this is duped from os.h because I don't want to pull in Windows.h, +// consider splitting out the IMPORT/EXPORT defs to some other thing? +#ifdef _WIN32 +#define IMPORT __declspec(dllimport) // only needed for variables +#else +#define IMPORT +#endif + +// XXX: not sure if "ext" is the best naming convention? use brain later + +IMPORT void *g_pMemAlloc; + +// this interface has changed a bit between versions but thankfully the basic +// functions we care about have always been at the start - nice and easy. +// unfortunately though, because the debug and non-debug versions are overloads +// and Microsoft are a bunch of crazies who decided vtable order should be +// affected by naming (overloads are grouped, and *reversed* inside of a +// group!?), we get this amusing ABI difference between platforms: +#ifdef _WIN32 +DECL_VFUNC(void *, Alloc, 1, usize sz) +DECL_VFUNC(void *, Realloc, 3, void *mem, usize sz) +DECL_VFUNC(void, Free, 5, void *mem) +#else +DECL_VFUNC(void *, Alloc, 0, usize sz) +DECL_VFUNC(void *, Realloc, 1, void *mem, usize sz) +DECL_VFUNC(void, Free, 2, void *mem) +#endif + +void *extmalloc(usize sz) { +	return VCALL(g_pMemAlloc, Alloc, sz); +} + +void *extrealloc(void *mem, usize sz) { +	return VCALL(g_pMemAlloc, Realloc, mem, sz); +} + +void extfree(void *mem) { +	VCALL(g_pMemAlloc, Free, mem); +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/extmalloc.h b/src/extmalloc.h new file mode 100644 index 0000000..90c4f83 --- /dev/null +++ b/src/extmalloc.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_EXTMALLOC_H +#define INC_EXTMALLOC_H + +#include "intdefs.h" + +/* + * These functions are just like malloc/realloc/free, but they call into + * Valve's memory allocator wrapper, which ensures that allocations crossing + * plugin/engine boundaries won't cause any weird issues. + */ +void *extmalloc(usize sz); +void *extrealloc(void *mem, usize sz); +void extfree(void *mem); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/factory.h b/src/factory.h new file mode 100644 index 0000000..a72afef --- /dev/null +++ b/src/factory.h @@ -0,0 +1,13 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_FACTORY_H +#define INC_FACTORY_H + +/* Access to game and engine factories obtained on plugin load */ + +typedef void *(*ifacefactory)(const char *name, int *ret); +extern ifacefactory factory_client, factory_server, factory_engine; + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gamedata.c b/src/gamedata.c new file mode 100644 index 0000000..7ebbb22 --- /dev/null +++ b/src/gamedata.c @@ -0,0 +1,31 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> + +#include "gametype.h" + +// same as gamedata.h, not worth putting in its own thing +// (it's also in con_.c. whatever) +#ifdef _WIN32 +#define NVDTOR 1 +#else +#define NVDTOR 2 +#endif + +#include <gamedatainit.gen.h> + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gamedata.h b/src/gamedata.h new file mode 100644 index 0000000..808bae0 --- /dev/null +++ b/src/gamedata.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_GAMEDATA_H +#define INC_GAMEDATA_H + +#ifdef _WIN32 +#define NVDTOR 1 +#else +#define NVDTOR 2 +#endif +#include <gamedata.gen.h> +#undef NVDTOR + +void gamedata_init(void); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gameinfo.c b/src/gameinfo.c new file mode 100644 index 0000000..32f5051 --- /dev/null +++ b/src/gameinfo.c @@ -0,0 +1,372 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#ifdef _WIN32 +#include <shlwapi.h> +#endif + +#include "con_.h" +#include "intdefs.h" +#include "kv.h" +#include "os.h" + +// Formatting for os_char * -> char * (or vice versa) - needed for con_warn()s +// with file paths, etc +#ifdef _WIN32 +#define fS "S" // os string (wide string) to regular string +#define Fs L"S" // regular string to os string (wide string) +#else +// everything is just a regular string already +#define fS "s" +#define Fs "s" +#endif + +static os_char exedir[PATH_MAX]; +static os_char gamedir[PATH_MAX]; +static char _gameinfo_title[64] = {0}; +const char *gameinfo_title = _gameinfo_title; +static os_char _gameinfo_clientlib[PATH_MAX] = {0}; +const os_char *gameinfo_clientlib = _gameinfo_clientlib; +static os_char _gameinfo_serverlib[PATH_MAX] = {0}; +const os_char *gameinfo_serverlib = _gameinfo_serverlib; + +// magical argc/argv grabber so we don't have to go through procfs +#ifdef __linux__ +static const char *prog_argv; +static int storeargs(int argc, char *argv[]) { +	prog_argv = argv; +	return 0; +} +__attribute__((used, section(".init_array"))) +static void *pstoreargs = &storeargs; +#endif + +// case insensitive substring match, expects s2 to be lowercase already! +// note: in theory this shouldn't need to be case sensitive, but I've seen mods +// use both lowercase and TitleCase so this is just to be as lenient as possible +static bool matchtok(const char *s1, const char *s2, usize sz) { +	for (; sz; --sz, ++s1, ++s2) if (tolower(*s1) != *s2) return false; +	return true; +} + +static void try_gamelib(const os_char *path, os_char *outpath) { +	// _technically_ this is toctou, but I don't think that matters here +	if (os_access(path, F_OK) != -1) { +		os_strcpy(outpath, path); +	} +	else if (errno != ENOENT) { +		con_warn("gameinfo: failed to access %" fS ": %s\n", path, +				strerror(errno)); +	} +} + +// note: p and len are a non-null-terminated string +static inline void do_gamelib_search(const char *p, uint len, bool isgamebin) { +	// sanity check: don't do a bunch of work for no reason +	if (len >= PATH_MAX - 1 - (sizeof("client" OS_DLSUFFIX) - 1)) goto toobig; +	os_char bindir[PATH_MAX]; +	os_char *outp = bindir; +	// this should really be an snprintf, meh whatever +	os_strcpy(bindir, exedir); +	outp = bindir + os_strlen(bindir); +	// quick note about windows encoding conversion: this MIGHT clobber the +	// encoding of non-ascii mod names, but it's unclear if/how source handles +	// that anyway, so we just have to assume there *are no* non-ascii mod +	// names, since they'd also be clobbered, probably. if I'm wrong this can +	// just change later to an explicit charset conversion, so... it's kinda +	// whatever, I guess +	const os_char *fmt = isgamebin ? +		OS_LIT("/%.*") Fs OS_LIT("/") : +		OS_LIT("/%.*") Fs OS_LIT("/bin/"); +	int spaceleft = PATH_MAX; +	if (len >= 25 && matchtok(p, "|all_source_engine_paths|", 25)) { +		// this special path doesn't seem any different to normal, +		// why is this a thing? +		p += 25; len -= 25; +	} +	else if (len >= 15 && matchtok(p, "|gameinfo_path|", 15)) { +		// search in the actual mod/game directory +		p += 15; len -= 15; +		int ret = os_snprintf(bindir, PATH_MAX, OS_LIT("%s"), gamedir); +		outp = bindir + ret; +		spaceleft -= ret; +	} +	else { +#ifdef _WIN32 +		// sigh +		char api_needs_null_term[PATH_MAX]; +		memcpy(api_needs_null_term, p, len * sizeof(*p)); +		api_needs_null_term[len] = L'\0'; +		if (!PathIsRelativeA(api_needs_null_term)) +#else +		if (*p == "/") // so much easier :') +#endif +	{ +		// the mod path is absolute, so we're not sticking anything else in +		// front of it, so skip the leading slash in fmt and point the pointer +		// at the start of the buffer +		++fmt; +		outp = bindir; +	}} + +	// leave room for server/client.dll/so (note: server and client happen to +	// conveniently have the same number of letters) +	int fmtspace = spaceleft - (sizeof("client" OS_DLSUFFIX) - 1); +	int ret = os_snprintf(outp, fmtspace, fmt, len, p); +	if (ret >= fmtspace) { +toobig: con_warn("gameinfo: skipping an overly long search path\n"); +		return; +	} +	outp += ret; +	if (!*gameinfo_clientlib) { +		os_strcpy(outp, OS_LIT("client" OS_DLSUFFIX)); +		try_gamelib(bindir, _gameinfo_clientlib); +	} +	if (!*gameinfo_serverlib) { +		os_strcpy(outp, OS_LIT("server" OS_DLSUFFIX)); +		try_gamelib(bindir, _gameinfo_serverlib); +	} +} + +// state for the callback below to keep it somewhat reentrant-ish (except where +// it isn't because I got lazy and wrote some spaghetti) +struct kv_parsestate { +	// after parsing a key we *don't* care about, how many nested subkeys have +	// we come across? +	short dontcarelvl; +	// after parsing a key we *do* care about, which key in the matchkeys[] +	// array below are we looking for next? +	schar nestlvl; +	// what kind of key did we just match? +	schar matchtype; +}; + +// this is a sprawling mess. Too Bad! +static void kv_cb(enum kv_token type, const char *p, uint len, void *_ctxt) { +	struct kv_parsestate *ctxt = _ctxt; + +	static const struct { +		const char *s; +		uint len; +	} matchkeys[] = { +		{"gameinfo", 8}, +		{"filesystem", 10}, +		{"searchpaths", 11} +	}; + +	// values for ctxt->matchtype +	enum { +		mt_none, +		mt_title, +		mt_nest, +		mt_game, +		mt_gamebin +	}; + +	#define MATCH(s) (len == sizeof(s) - 1 && matchtok(p, s, sizeof(s) - 1)) +	switch (type) { +		case KV_IDENT: case KV_IDENT_QUOTED: +			if (ctxt->nestlvl == 1 && MATCH("game")) { +				ctxt->matchtype = mt_title; +			} +			else if (ctxt->nestlvl == 3) { +				// for some reason there's a million different ways of +				// specifying the same type of path +				if (MATCH("mod+game") || MATCH("game+mod") || MATCH("game") || +						MATCH("mod")) { +					ctxt->matchtype = mt_game; +				} +				else if (MATCH("gamebin")) { +					ctxt->matchtype = mt_gamebin; +				} +			} +			else if (len == matchkeys[ctxt->nestlvl].len && +					matchtok(p, matchkeys[ctxt->nestlvl].s, len)) { +				ctxt->matchtype = mt_nest; +			} +			break; +		case KV_NEST_START: +			if (ctxt->matchtype == mt_nest) ++ctxt->nestlvl; +			else ++ctxt->dontcarelvl; +			ctxt->matchtype = mt_none; +			break; +		case KV_VAL: case KV_VAL_QUOTED: +			if (ctxt->dontcarelvl) break; +			if (ctxt->matchtype == mt_title) { +				// title really shouldn't get this long, but truncate just to +				// avoid any trouble... +				// also note: leaving 1 byte of space for null termination (the +				// buffer is already zeroed initially) +				if (len > sizeof(_gameinfo_title) - 1) { +					len = sizeof(_gameinfo_title) - 1; +				} +				memcpy(_gameinfo_title, p, len); +			} +			else if (ctxt->matchtype == mt_game || +					ctxt->matchtype == mt_gamebin) { +				// if we already have everything, we can just stop! +				if (*gameinfo_clientlib && *gameinfo_serverlib) break; +				do_gamelib_search(p, len, ctxt->matchtype == mt_gamebin); +			} +			ctxt->matchtype = mt_none; +			break; +		case KV_NEST_END: +			if (ctxt->dontcarelvl) --ctxt->dontcarelvl; else --ctxt->nestlvl; +	} +	#undef MATCH +} + +bool gameinfo_init(void) { +	const os_char *modname = OS_LIT("hl2"); +#ifdef _WIN32 +	int len = GetModuleFileNameW(0, exedir, PATH_MAX); +	if (!len) { +		char err[128]; +		OS_WINDOWS_ERROR(err); +		con_warn("gameinfo: couldn't get EXE path: %s\n", err); +		return false; +	} +	// if the buffer is full and has no null, it's truncated +	if (len == PATH_MAX && exedir[len - 1] != L'\0') { +		con_warn("gameinfo: EXE path is too long!\n"); +		return false; +	} +#else +	int len = readlink("/proc/self/exe", exedir, PATH_MAX); +	if (len == -1) { +		con_warn("gameinfo: couldn't get program path: %s\n", strerror(errno)); +		return false; +	} +	// if the buffer is full at all, it's truncated (readlink never writes \0) +	if (len == PATH_MAX) { +		con_warn("gameinfo: program path is too long!\n"); +		return false; +	} +	else { +		exedir[len] = '\0'; +	} +#endif +	// find the last slash +	os_char *p; +	for (p = exedir + len - 1; *p != OS_LIT('/') +#ifdef _WIN32 +			&& *p != L'\\' +#endif +	;		--p); +	// ... and split on it +	*p = 0; +	const os_char *exename = p + 1; +#ifdef _WIN32 +	// try and infer the default mod name (when -game isn't given) from the exe +	// name for a few known games +	if (!_wcsicmp(exename, L"left4dead2.exe")) modname = L"left4dead2"; +	else if (!_wcsicmp(exename, L"left4dead.exe")) modname = L"left4dead"; +	else if (!_wcsicmp(exename, L"portal2.exe")) modname = L"portal2"; + +	const ushort *args = GetCommandLineW(); +	const ushort *argp = args; +	ushort modbuf[PATH_MAX]; +	// have to take the _last_ occurence of -game because sourcemods get the +	// flag twice, for some reason +	while (argp = wcsstr(argp, L" -game ")) { +		argp += 7; +		while (*argp == L' ') ++argp; +		ushort sep = L' '; +		// WARNING: not handling escaped quotes and such nonsense, since you +		// can't have quotes in filepaths anyway outside of UNC and I'm just +		// assuming there's no way Source could even be started with such an +		// insanely named mod. We'll see how this assumption holds up! +		if (*argp == L'"') { +			++argp; +			sep = L'"'; +		} +		ushort *bufp = modbuf; +		for (; *argp != L'\0' && *argp != sep; ++argp, ++bufp) { +			if (bufp - modbuf == PATH_MAX - 1) { +				con_warn("gameinfo: mod name parameter is too long\n"); +				return false; +			} +			*bufp = *argp; +		} +		*bufp = L'\0'; +		modname = modbuf; +	} +	bool isrelative = PathIsRelativeW(modname); +#else +	// also do the executable name check just for portal2_linux +	if (!strcmp(exename, "portal2_linux")) modname = "portal2"; +	// ah, the sane, straightforward world of unix command line arguments :) +	for (char **pp = prog_argv + 1; *pp; ++pp) { +		if (!strcmp(*pp, "-game")) { +			if (!*++pp) break; +			modname = *pp; +		} +	} +	// ah, the sane, straightforward world of unix paths :) +	bool isrelative = modname[0] != '/'; +#endif + +	int ret = isrelative ? +		os_snprintf(gamedir, PATH_MAX, OS_LIT("%s/%s"), exedir, modname) : +		// mod name might actually be an absolute (if installed in steam +		// sourcemods for example) +		os_snprintf(gamedir, PATH_MAX, OS_LIT("%s"), modname); +	if (ret >= PATH_MAX) { +		con_warn("gameinfo: game directory path is too long!\n"); +		return false; +	} +	os_char gameinfopath[PATH_MAX]; +	if (os_snprintf(gameinfopath, PATH_MAX, OS_LIT("%s/gameinfo.txt"), +			gamedir, modname) >= PATH_MAX) { +		con_warn("gameinfo: gameinfo.text path is too long!\n"); +		return false; +	} + +	int fd = os_open(gameinfopath, O_RDONLY); +	if (fd == -1) { +		con_warn("gameinfo: couldn't open gameinfo.txt: %s\n", strerror(errno)); +		return false; +	} +	char buf[1024]; +	struct kv_parser kvp = {0}; +	struct kv_parsestate ctxt = {0}; +	int nread; +	while (nread = read(fd, buf, sizeof(buf))) { +		if (nread == -1) { +			con_warn("gameinfo: couldn't read gameinfo.txt: %s\n", +					strerror(errno)); +			goto e; +		} +		kv_parser_feed(&kvp, buf, nread, &kv_cb, &ctxt); +		if (kvp.state == KV_PARSER_ERROR) goto ep; +	} +	kv_parser_done(&kvp); +	if (kvp.state == KV_PARSER_ERROR) goto ep; + +	close(fd); +	return true; + +ep:	con_warn("gameinfo: couldn't parse gameinfo.txt (%d:%d): %s\n", +			kvp.line, kvp.col, kvp.errmsg); +e:	close(fd); +	return false; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gameinfo.h b/src/gameinfo.h new file mode 100644 index 0000000..845c9c2 --- /dev/null +++ b/src/gameinfo.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_GAMEINFO_H +#define INC_GAMEINFO_H + +#include "intdefs.h" +#include "os.h" + +/* These variables are only set after calling gameinfo_init(). */ +extern const char *gameinfo_title;			/* Name of the game (window title) */ +extern const os_char *gameinfo_clientlib;	/* Path to the client library */ +extern const os_char *gameinfo_serverlib;	/* Path to the server library */ + +/* + * This function is called early in the plugin load and does a whole bunch of + * spaghetti magic to figure out which game/engine we're in and where its + * libraries (which we want to hook) are located. + */ +bool gameinfo_init(void); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gametype.h b/src/gametype.h new file mode 100644 index 0000000..8a823f8 --- /dev/null +++ b/src/gametype.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_GAMETYPE_H +#define INC_GAMETYPE_H + +#include "intdefs.h" + +extern u32 _gametype_tag; + +#define _gametype_tag_OE		1 +#define _gametype_tag_OrangeBox	2 +#define _gametype_tag_L4D1		4 +#define _gametype_tag_L4D2		8 +#define _gametype_tag_Portal2	16 +#define _gametype_tag_2013		32 + +#define _gametype_tag_L4D		(_gametype_tag_L4D1	| _gametype_tag_L4D2) +#define _gametype_tag_L4Dbased	(_gametype_tag_L4D	| _gametype_tag_Portal2) + +#define GAMETYPE_MATCHES(x) !!(_gametype_tag & (_gametype_tag_##x)) + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/hook.c b/src/hook.c new file mode 100644 index 0000000..410e23e --- /dev/null +++ b/src/hook.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <string.h> + +#include "con_.h" +#include "intdefs.h" +#include "mem.h" +#include "os.h" +#include "udis86.h" + +// Warning: half-arsed hacky implementation (because that's all we really need) +// Almost certainly breaks in some weird cases. Oh well! Most of the time, +// vtable hooking is more reliable, this is only for, uh, emergencies. + +#if defined(_WIN32) && !defined(_WIN64) + +__attribute__((aligned(4096))) +static uchar trampolines[4096]; +static uchar *nexttrampoline = trampolines; +__attribute__((constructor)) +static void setrwx(void) { +	// PE doesn't support rwx sections, not sure about ELF. Eh, just hack it in +	// a constructor instead. If this fails and we segfault later, too bad! +	os_mprot(trampolines, sizeof(trampolines), PAGE_EXECUTE_READWRITE); +} + +#define RELJMP 0xE9 // the first byte of a 5-byte jmp + +void *hook_inline(void *func_, void *target) { +	uchar *func = func_; +	if (!os_mprot(func, 5, PAGE_EXECUTE_READWRITE)) return false; +	struct ud udis; +	ud_init(&udis); +	ud_set_mode(&udis, 32); +	// max insn length is 15, we overwrite 5, so max to copy is 4 + 15 = 19 +	ud_set_input_buffer(&udis, func, 19); +	int len = 0; +	while (ud_disassemble(&udis) && len < 5) { +		if (ud_insn_mnemonic(&udis) == UD_Ijmp || +				ud_insn_mnemonic(&udis) == UD_Icall) { +			con_warn("hook_inline: jmp adjustment NYI\n"); +			return 0; +		} +		len += ud_insn_len(&udis); +	} +	// for simplicity, just bump alloc the trampoline. no need to free anyway +	if (nexttrampoline - trampolines > len + 6) goto nospc; +	uchar *trampoline = (uchar *)InterlockedExchangeAdd( +			(volatile long *)&nexttrampoline, len + 6); +	if (trampoline - trampolines > len + 6) { // avoid TOCTOU +nospc:	con_warn("hook_inline: out of trampoline space\n"); +		return 0; +	} +	*trampoline++ = len; // stick length in front for quicker unhooking +	memcpy(trampoline, func, len); +	trampoline[len] = RELJMP; +	uint diff = func - (trampoline + 5); // goto the continuation +	memcpy(trampoline + len + 1, &diff, 4); +	uchar jmp[8]; +	jmp[0] = RELJMP; +	diff = (uchar *)target - (func + 5); // goto the hook target +	memcpy(jmp + 1, &diff, 4); +	// pad with original bytes so we can do an 8-byte atomic write +	memcpy(jmp + 5, func + 5, 3); +	*(volatile uvlong *)func = *(uvlong *)jmp; // (assuming function is aligned) +	FlushInstructionCache(GetCurrentProcess(), func, len); +	return trampoline; +} + +void unhook_inline(void *orig) { +	uchar *p = (uchar *)orig; +	int len = p[-1]; +	uint off = mem_load32(p + len + 1); +	uchar *q = p + off + 5; +	memcpy(q, p, 5); // XXX not atomic atm! (does any of it even need to be?) +	FlushInstructionCache(GetCurrentProcess(), q, 5); +} + +#else + +// TODO(linux): Implement for Linux and/or x86_64 when needed... + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/hook.h b/src/hook.h new file mode 100644 index 0000000..02c41dc --- /dev/null +++ b/src/hook.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_HOOK_H +#define INC_HOOK_H + +#include "intdefs.h" + +/* + * Replaces a vtable entry with a target function and returns the original + * function. + */ +static inline void *hook_vtable(void **vtable, usize off, void *target) { +	void *orig = vtable[off]; +	vtable[off] = target; +	return orig; +} + +/* + * Puts an original function back after hooking. + */ +static inline void unhook_vtable(void **vtable, usize off, void *orig) { +	vtable[off] = orig; +} + +/* + * Returns a trampoline pointer, or null if hooking failed. Unlike hook_vtable, + * handles memory protection for you. + */ +void *hook_inline(void *func, void *target); + +/* + * Reverts the function to its original unhooked state. Takes the pointer to the + * callable "original" function, i.e. the trampoline, NOT the initial function + * pointer from before hooking. + */ +void unhook_inline(void *orig); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/intdefs.h b/src/intdefs.h new file mode 100644 index 0000000..a3370d7 --- /dev/null +++ b/src/intdefs.h @@ -0,0 +1,35 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_INTDEFS_H +#define INC_INTDEFS_H + +typedef signed char schar; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef long long vlong; +typedef unsigned long long uvlong; + +typedef schar s8; +typedef uchar u8; +typedef short s16; +typedef ushort u16; +typedef int s32; +typedef uint u32; +typedef vlong s64; +typedef uvlong u64; + +// just in case there's ever a need to support 64-bit builds of Source, define a +// size type, since Windows isn't LP64 so (u)long won't quite do +#ifdef _WIN64 +typedef vlong ssize; +typedef uvlong usize; +#else +typedef long ssize; +typedef ulong usize; +#endif + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/kv.c b/src/kv.c new file mode 100644 index 0000000..8258b16 --- /dev/null +++ b/src/kv.c @@ -0,0 +1,231 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> + +#include "intdefs.h" +#include "kv.h" + +#define EOF -1 + +void kv_parser_feed(struct kv_parser *this, const char *in, uint sz, +		kv_parser_cb cb, void *ctxt) { +	const char *p = in; +	short c; + +	// slight hack, makes init more convenient (just {0}) +	if (!this->line) this->line = 1; +	if (!this->outp) this->outp = this->tokbuf; + +	// this is a big ol' blob of ugly state machine macro spaghetti - too bad! +	#define INCCOL() (*p == '\n' ? (++this->line, this->col = 0) : ++this->col) +	#define READ() (p == in + sz ? EOF : (INCCOL(), *p++)) +	#define ERROR(s) do { \ +		this->state = KV_PARSER_ERROR; \ +		this->errmsg = s; \ +		return; \ +	} while (0) +	#define OUT(c) do { \ +		if (this->outp - this->tokbuf == KV_TOKEN_MAX) { \ +			ERROR("token unreasonably large!"); \ +		} \ +		*this->outp++ = (c); \ +	} while (0) +	#define CASE_WS case ' ': case '\t': case '\n': case '\r' +	// note: multi-eval +	#define IS_WS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r') +	#define STATE(s) case s: s +	#define HANDLE_EOF() do { case EOF: return; } while (0) +	#define SKIP_COMMENT(next) do { \ +		this->state = next; \ +		this->incomment = true; \ +		goto start;  \ +	} while (0) +	#define GOTO(s) do { this->state = s; goto s; } while (0) +	#define CB(type) do { \ +		cb(type, this->tokbuf, this->outp - this->tokbuf, ctxt); \ +		this->outp = this->tokbuf; \ +	} while (0) + +	// parser states, implemented by STATE() macros below +	enum { +		ok, +		ok_slash, +		ident, +		ident_slash, +		identq, +		sep, +		sep_slash, +		val, +		val_slash, +		valq +	}; + +start: // special spaghetti so we don't have a million different comment states +	if (this->incomment) while ((c = READ()) != '\n') if (c == EOF) return; +	this->incomment = false; + +switch (this->state) { + +STATE(ok): +	switch (c = READ()) { +		HANDLE_EOF(); +		CASE_WS: goto ok; +		case '#': ERROR("kv macros not supported"); +		case '{': ERROR("unexpected control character"); +		case '}': +			if (!this->nestlvl) ERROR("too many closing braces"); +			--this->nestlvl; +			char c_ = c; +			cb(KV_NEST_END, &c_, 1, ctxt); +			goto ok; +		case '"': GOTO(identq); +		case '/': GOTO(ok_slash); +		default: GOTO(ident); +	} + +STATE(ok_slash): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '/': SKIP_COMMENT(ok); +		default: OUT('/'); GOTO(ident); +	} + +ident: +	OUT(c); +case ident: // continue here +	switch (c = READ()) { +		HANDLE_EOF(); +		case '{': +			CB(KV_IDENT); +			++this->nestlvl; +			char c_ = c; +			cb(KV_NEST_START, &c_, 1, ctxt); +			GOTO(ok); +		case '}': case '"': ERROR("unexpected control character"); +		CASE_WS: +			CB(KV_IDENT); +			GOTO(sep); +		case '/': GOTO(ident_slash); +		default: goto ident; +	} + +STATE(ident_slash): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '/': +			CB(KV_IDENT); +			SKIP_COMMENT(sep); +		default: OUT('/'); GOTO(ident); +	} + +STATE(identq): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '"': +			CB(KV_IDENT_QUOTED); +			GOTO(sep); +		default: OUT(c); goto identq; +	} + +STATE(sep): +	do c = READ(); while (IS_WS(c)); +	switch (c) { +		HANDLE_EOF(); +		case '[': ERROR("conditionals not supported"); +		case '{':; +			char c_ = c; +			++this->nestlvl; +			cb(KV_NEST_START, &c_, 1, ctxt); +			GOTO(ok); +		case '"': GOTO(valq); +		case '}': ERROR("unexpected control character"); +		case '/': GOTO(sep_slash); +		default: GOTO(val); +	} + +STATE(sep_slash): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '/': SKIP_COMMENT(sep); +		default: OUT('/'); GOTO(val); +	} + +val: +	OUT(c); +case val: // continue here +	switch (c = READ()) { +		HANDLE_EOF(); +		case '{': case '"': ERROR("unexpected control character"); +		// might get } with no whitespace +		case '}': +			CB(KV_VAL); +			--this->nestlvl; +			char c_ = c; +			cb(KV_NEST_END, &c_, 1, ctxt); +			GOTO(ok); +		CASE_WS: +			CB(KV_VAL); +			GOTO(ok); +		case '/': GOTO(val_slash); +		default: goto val; +	} + +STATE(val_slash): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '/': +			CB(KV_VAL); +			SKIP_COMMENT(ok); +		default: OUT('/'); GOTO(val); +	} + +STATE(valq): +	switch (c = READ()) { +		HANDLE_EOF(); +		case '"': +			CB(KV_VAL_QUOTED); +			GOTO(ok); +		default: OUT(c); goto valq; +	} + +} + +	#undef CB +	#undef GOTO +	#undef SKIP_COMMENT +	#undef HANDLE_EOF +	#undef STATE +	#undef IS_WS +	#undef CASE_WS +	#undef OUT +	#undef ERROR +	#undef READ +	#undef INCCOL +} + +void kv_parser_done(struct kv_parser *this) { +	if (this->state > 0) { +		this->state = -1; +		this->errmsg = "unexpected end of input"; +	} +	else if (this->state == 0 && this->nestlvl != 0) { +		this->state = -1; +		this->errmsg = "unterminated object (unbalanced braces)"; +	} +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/kv.h b/src/kv.h new file mode 100644 index 0000000..6de2c67 --- /dev/null +++ b/src/kv.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_KV_H +#define INC_KV_H + +#include <stdbool.h> + +#include "intdefs.h" + +/* + * Maximum length of a single token. Since this code is trying to avoid dynamic + * memory allocations, this arbitrary limit is chosen to accomodate all known + * "reasonable" tokens likely to come in any real files, probably. + */ +#define KV_TOKEN_MAX 512 + +/* + * Contains all the state associated with parsing (lexing?) a KeyValues file. + * Should be zeroed out prior to the first call (initialise with `= {0};`). + */ +struct kv_parser { +	ushort line, col;	/* the current line and column in the text */ +	schar state;		/* internal, shouldn't usually be touched directly */ +	bool incomment;		/* internal */ +	ushort nestlvl;		/* internal */ +	const char *errmsg; /* the error message, *IF* parsing just failed */ + +	// trying to avoid dynamic allocations - valve's own parser seems to have +	// a similar limit as well and our use case doesn't really need to worry +	// about stupid massive values, so it's fine +	char *outp; +	char tokbuf[KV_TOKEN_MAX]; +}; + +#define KV_PARSER_ERROR -1 + +/* + * These are the tokens that can be receieved by a kv_parser_cb (below). + * The x-macro and string descriptions are given to allow for easy debug + * stringification. Note that this "parser" is really just lexing out these + * tokens - handling the actual structure of the file should be done in the + * callback. This is so that data can be streamed rather than all read into + * memory at once. + */ +#define KV_TOKENS(X) \ +	X(KV_IDENT, "ident") \ +	X(KV_IDENT_QUOTED, "quoted-ident") \ +	X(KV_VAL, "value") \ +	X(KV_VAL_QUOTED, "quoted-value") \ +	X(KV_NEST_START, "object-start") \ +	X(KV_NEST_END, "object-end") + +#define _ENUM(s, ignore) s, +enum kv_token { KV_TOKENS(_ENUM) }; +#undef _ENUM + +typedef void (*kv_parser_cb)(enum kv_token type, const char *p, uint len, +		void *ctxt); + +/* + * Feed a block of text into the lexer. This would usually be a block of data + * read in from a file. + * + * The lexer is reentrant and can be fed arbitrarily sized blocks of data at a + * time. The function may return early in the event of an error; you must check + * if parser->state == KV_PARSER_ERROR between calls! Continuing to try parsing + * after an error is undefined. + */ +// FIXME: revise API usage so errors aren't passed through "state" value +void kv_parser_feed(struct kv_parser *this, const char *in, uint sz, +		kv_parser_cb cb, void *ctxt); + +/* + * This indicates that parsing is done; if the state is midway through a token + * this will be converted into an error state which can be checked in the same + * way as noted above. + */ +void kv_parser_done(struct kv_parser *this); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/mem.h b/src/mem.h new file mode 100644 index 0000000..2a3573d --- /dev/null +++ b/src/mem.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_MEMUTIL_H +#define INC_MEMUTIL_H + +#include "intdefs.h" + +/* retrieves a 32-bit integer from an unaligned pointer; avoids UB, probably */ +static inline u32 mem_load32(const void *p) { +	const uchar *cp = p; +	return (u32)cp[0] | (u32)cp[1] << 8 | (u32)cp[2] << 16 | (u32)cp[3] << 24; +} + +/* retrieves a 64-bit integer from an unaligned pointer; avoids UB, possibly */ +static inline u64 mem_load64(const void *p) { +	return (u64)mem_load32(p) | (u64)mem_load32((uchar *)p + 4) << 32; +} + +/* retrieves a pointer from an unaligned pointer-to-pointer; avoids UB, maybe */ +static inline void *mem_loadptr(const void *p) { +#if defined(_WIN64) || defined(__x86_64__) +	return (void *)mem_load64(p); +#else +	return (void *)mem_load32(p); +#endif +} + +/* retreives a signed offset from an unaligned pointer; avoids UB, hopefully */ +static inline ssize mem_loadoffset(const void *p) { +	return (ssize)mem_loadptr(p); +} + +/* stores a 32-bit integer to an unaligned pointer; avoids UB, most likely */ +static inline void mem_store32(void *p, u32 val) { +	uchar *cp = p; +	cp[0] = val; cp[1] = val >> 8; cp[2] = val >> 16; cp[3] = val >> 24; +} + +/* stores a 64-bit integer to an unaligned pointer; avoids UB, I'd assume */ +static inline void mem_store64(void *p, u64 val) { +	mem_store32(p, val); mem_store32((uchar *)p + 4, val >> 32); +} + +/* stores a pointer value to an unaligned pointer; avoids UB, I guess */ +static inline void mem_storeptr(void *to, const void *val) { +#if defined(_WIN64) || defined(__x86_64__) +	mem_store64(to, (u64)val); +#else +	mem_store32(to, (u32)val); +#endif +} + +/* adds a byte count to a pointer, and returns something that can be assigned + * to any pointer type */ +static inline void *mem_offset(void *p, int off) { return (char *)p + off; } + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/noreturn.h b/src/noreturn.h new file mode 100644 index 0000000..81b2bae --- /dev/null +++ b/src/noreturn.h @@ -0,0 +1,11 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_NORETURN_H +#define INC_NORETURN_H + +#undef noreturn +#define noreturn _Noreturn void + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/os.c b/src/os.c new file mode 100644 index 0000000..96e3c48 --- /dev/null +++ b/src/os.c @@ -0,0 +1,56 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> + +#include "intdefs.h" +#include "os.h" + +#ifdef _WIN32 +// SystemFunction036 is the *real* name of "RtlGenRandom," and is also +// incorrectly defined in system headers. Yay, Windows. +int __stdcall SystemFunction036(void *buf, ulong sz); +#endif + +bool os_mprot(void *addr, int len, int fl) { +#ifdef _WIN32 +	ulong old; +	return !!VirtualProtect(addr, len, fl, &old); +#else +	// round down address and round up size +	addr = (void *)((ulong)addr & ~(4095)); +	len = len + 4095 & ~(4095); +	return mprotect(addr, len, fl) != -1; +#endif +} + +#ifdef _WIN32 +void *os_dlsym(void *m, const char *s) { +	return (void *)GetProcAddress(m, s); +} +#endif + +void os_randombytes(void *buf, int sz) { +	// if these calls ever fail, the system is fundamentally broken with no +	// recourse, so just loop until success. hopefully nothing will go wrong. +#ifdef _WIN32 +	while (!SystemFunction036(buf, sz)); +#else +	while (getentropy(buf, sz) == -1); +#endif +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/os.h b/src/os.h new file mode 100644 index 0000000..c74d2d0 --- /dev/null +++ b/src/os.h @@ -0,0 +1,145 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_OS_H +#define INC_OS_H + +#include <stdbool.h> + +/* + * Here we declare an absolute ton of wrappers, macros, compatibility shims, + * reimplementations and so on to try in vain to sweep the inconsistencies + * between Windows and not-Windows under the rug. + * + * If this file gets any bigger it might need to be split up a bit... + */ + +#include <fcntl.h> +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include <io.h> +#include <wchar.h> +#include <Windows.h> +#else +#include <dlfcn.h> +#include <limits.h> +#include <sys/mman.h> +#include <unistd.h> +#endif + +#include "intdefs.h" + +#ifdef _WIN32 + +#define IMPORT __declspec(dllimport) // only needed for variables +#define EXPORT __declspec(dllexport) +void *os_dlsym(void *mod, const char *sym); +#define os_char ushort +#define _OS_CAT(L, x) L##x +#define OS_LIT(x) _OS_CAT(L, x) +#define os_snprintf _snwprintf +#define os_strchr wcschr +#define os_strcmp wcscmp +#define os_strcpy wcscpy +#define os_strlen wcslen +#define strncasecmp _strnicmp // stupid! +#define OS_DLSUFFIX ".dll" +#ifndef PATH_MAX +// XXX win32/crt has this dumb 260 limit even though the actual kernel imposes +// no limit (though apparently NTFS has a limit of 65535). Theoerically we could +// do some memes with UNC paths to extend it to at least have parity with Unix +// PATH_MAX (4096), but for now we just kind of accept that Windows is a +// disaster. +#define PATH_MAX MAX_PATH +#endif +#define os_fopen _wfopen +// yuck :( +#define _os_open3(path, flags, mode) _wopen((path), (flags) | _O_BINARY, (mode)) +#define _os_open2(path, flags) _wopen((path), (flags) | _O_BINARY) +#define _os_open(a, b, c, x, ...) x +#define os_open(...) _os_open(__VA_ARGS__, _os_open3, _os_open2, _)(__VA_ARGS__) +#define os_access _waccess +#define os_stat _stat64 +// ucrt defines __stat64 to _stat64. we want _wstat64 to be the actual function +#define _stat64(path, buf) _wstat64(path, buf) +// why exactly _does_ windows prefix so many things with underscores? +#define read _read +#define write _write +#define close _close +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_CLOEXEC _O_NOINHERIT +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define F_OK 0 +#define R_OK 4 +#define W_OK 2 +#define X_OK R_OK // there's no actual X bit +#define alloca _alloca +#define os_getenv _wgetenv +#define OS_MAIN wmain +// just dump this boilerplate here as well, I spose +#define OS_WINDOWS_ERROR(arrayname) \ +	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), \ +			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), arrayname, \ +			sizeof(arrayname), 0) + +#else + +#ifdef __GNUC__ +#define EXPORT __attribute__((visibility("default"))) +#else +#define EXPORT int novis[-!!"visibility attribute requires Clang or GCC"]; +#endif +#define IMPORT +#define os_dlsym dlsym +#define os_char char +#define OS_LIT(x) x +#define os_snprintf snprintf +#define os_strchr strchr +#define os_strcmp strcmp +#define os_strcpy strcpy +#define os_strlen strlen +#define OS_DLSUFFIX ".so" +#define os_fopen fopen +#define os_open open +#define os_access access +#define os_stat stat +// unix mprot flags are much nicer but cannot be defined in terms of the windows +// ones, so we use the windows ones and define them in terms of the unix ones. +// another victory for stupid! +#define PAGE_NOACCESS			0 +#define PAGE_READONLY			PROT_READ +#define PAGE_READWRITE			PROT_READ | PROT_WRITE +#define PAGE_EXECUTE_READ		PROT_READ |              PROT_EXEC +#define PAGE_EXECUTE_READWRITE	PROT_READ | PROT_WRITE | PROT_EXEC +#define os_getenv getenv +#define OS_MAIN main + +#endif + +bool os_mprot(void *addr, int len, int fl); +/* + * NOTE: this should be called with a reasonably small buffer (e.g., the size of + * a private key). The maximum size of the buffer on Linux is 256, on Windows + * it's God Knows What. + */ +void os_randombytes(void *buf, int len); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/ppmagic.h b/src/ppmagic.h new file mode 100644 index 0000000..32c1f46 --- /dev/null +++ b/src/ppmagic.h @@ -0,0 +1,81 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_PPMAGIC_H +#define INC_PPMAGIC_H + +/* random preprocessor shenanigans */ + +#define _PPMAGIC_DO02(m, sep, x, y) m(x) sep m(y) +#define _PPMAGIC_DO03(m, sep, x, y, z) m(x) sep m(y) sep m(z) +#define _PPMAGIC_DO04(m, sep, w, x, y, z) m(w) sep m(x) sep m(y) sep m(z) +#define _PPMAGIC_DO05(m, sep, x, ...) m(x) sep _PPMAGIC_DO04(m, sep, __VA_ARGS__) +// repetitive nonsense {{{ +#define _PPMAGIC_DO06(m, sep, x, ...) m(x) sep _PPMAGIC_DO05(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO07(m, sep, x, ...) m(x) sep _PPMAGIC_DO06(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO08(m, sep, x, ...) m(x) sep _PPMAGIC_DO07(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO09(m, sep, x, ...) m(x) sep _PPMAGIC_DO08(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO10(m, sep, x, ...) m(x) sep _PPMAGIC_DO09(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO11(m, sep, x, ...) m(x) sep _PPMAGIC_DO10(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO12(m, sep, x, ...) m(x) sep _PPMAGIC_DO11(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO13(m, sep, x, ...) m(x) sep _PPMAGIC_DO12(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO14(m, sep, x, ...) m(x) sep _PPMAGIC_DO13(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO15(m, sep, x, ...) m(x) sep _PPMAGIC_DO14(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO16(m, sep, x, ...) m(x) sep _PPMAGIC_DO15(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO17(m, sep, x, ...) m(x) sep _PPMAGIC_DO16(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO18(m, sep, x, ...) m(x) sep _PPMAGIC_DO17(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO19(m, sep, x, ...) m(x) sep _PPMAGIC_DO18(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO20(m, sep, x, ...) m(x) sep _PPMAGIC_DO19(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO21(m, sep, x, ...) m(x) sep _PPMAGIC_DO20(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO22(m, sep, x, ...) m(x) sep _PPMAGIC_DO21(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO23(m, sep, x, ...) m(x) sep _PPMAGIC_DO22(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO24(m, sep, x, ...) m(x) sep _PPMAGIC_DO23(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO25(m, sep, x, ...) m(x) sep _PPMAGIC_DO24(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO26(m, sep, x, ...) m(x) sep _PPMAGIC_DO25(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO27(m, sep, x, ...) m(x) sep _PPMAGIC_DO26(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO28(m, sep, x, ...) m(x) sep _PPMAGIC_DO27(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO29(m, sep, x, ...) m(x) sep _PPMAGIC_DO28(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO30(m, sep, x, ...) m(x) sep _PPMAGIC_DO29(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO31(m, sep, x, ...) m(x) sep _PPMAGIC_DO30(m, sep, __VA_ARGS__) +#define _PPMAGIC_DO32(m, sep, x, ...) m(x) sep _PPMAGIC_DO31(m, sep, __VA_ARGS__) +// }}} + +#define _PPMAGIC_DO_N( \ +x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14, x15, x16, \ +x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, \ +		N, ...) \ +	_PPMAGIC_DO##N + +/* + * applies the given single-argument macro m to each of a list of up to 32 + * parameters, with the optional token sep inserted in between. + */ +#define PPMAGIC_MAP(m, sep, ...) \ +	_PPMAGIC_DO_N(__VA_ARGS__, \ +		32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ +		16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01) \ +	(m, sep, __VA_ARGS__) + +/* expands to up to 32 case labels at once, for matching multiple values */ +#define CASES(...) PPMAGIC_MAP(case, :, __VA_ARGS__) + +#define _PPMAGIC_0x(n) 0x##n, +/* expands to a byte array with each digit prefixed with 0x */ +#define HEXBYTES(...) {PPMAGIC_MAP(_PPMAGIC_0x, , __VA_ARGS__)} + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 fdm=marker diff --git a/src/sst.c b/src/sst.c new file mode 100644 index 0000000..dbee4b7 --- /dev/null +++ b/src/sst.c @@ -0,0 +1,206 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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 <stdbool.h> + +#include "con_.h" +#include "demorec.h" +#include "factory.h" +#include "gamedata.h" +#include "gameinfo.h" +#include "gametype.h" +#include "hook.h" +#include "os.h" +#include "vcall.h" +#include "version.h" + +#define RGBA(r, g, b, a) (&(struct con_colour){(r), (g), (b), (a)}) + +u32 _gametype_tag = 0; // spaghetti: no point making a .c file for 1 variable + +static int plugin_ver; +// this is where we start dynamically adding virtual functions, see vtable[] +// array below +static const void **vtable_firstdiff; + +// most plugin callbacks are unused - define dummy functions for each signature +static void VCALLCONV nop_v_v(void *this) {} +static void VCALLCONV nop_b_v(void *this, bool b) {} +static void VCALLCONV nop_p_v(void *this, void *p) {} +static void VCALLCONV nop_pp_v(void *this, void *p1, void *p2) {} +static void VCALLCONV nop_pii_v(void *this, void *p, int i1, int i2) {} +static int VCALLCONV nop_p_i(void *this, void *p) { return 0; } +static int VCALLCONV nop_pp_i(void *this, void *p1, void *p2) { return 0; } +static int VCALLCONV nop_5pi_i(void *this, void *p1, void *p2, void *p3, +		void *p4, void *p5, int i) { return 0; } +static void VCALLCONV nop_ipipp_v(void *this, int i1, void *p1, int i2, +		void *p2, void *p3) {} + +#ifdef __linux__ +// we need to keep this reference to dlclose() it later - see below +static void *clientlib = 0; +#endif + +// more source spaghetti wow! +static void VCALLCONV SetCommandClient(void *this, int i) { con_cmdclient = i; } + +ifacefactory factory_client = 0, factory_server = 0, factory_engine = 0; + +// TODO(featgen): I wanted some nice fancy automatic feature system that +// figures out the dependencies at build time and generates all the init glue +// but we want to actually release the plugin this decade so for now I'm just +// plonking ~~some bools~~ one bool here and worrying about it later. :^) +static bool has_demorec = false; + +static bool do_load(ifacefactory enginef, ifacefactory serverf) { +	factory_engine = enginef; factory_server = serverf; +#ifndef __linux__ +	void *clientlib = 0; +#endif +	if (!gameinfo_init() || !con_init(enginef, plugin_ver)) return false; +	const void **p = vtable_firstdiff; +	if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect +	*p++ = (void *)&nop_p_v;		  // ClientDisconnect +	*p++ = (void *)&nop_pp_v;		  // ClientPutInServer +	*p++ = (void *)&SetCommandClient; // SetCommandClient +	*p++ = (void *)&nop_p_v;		  // ClientSettingsChanged +	*p++ = (void *)&nop_5pi_i;		  // ClientConnect +	*p++ = plugin_ver > 1 ? (void *)&nop_pp_i : (void *)&nop_p_i; // ClientCommand +	*p++ = (void *)&nop_pp_i;		  // NetworkIDValidated +	// remaining stuff here is backwards compatible, so added unconditionally +	*p++ = (void *)&nop_ipipp_v;	  // OnQueryCvarValueFinished (002+) +	*p++ = (void *)&nop_p_v;		  // OnEdictAllocated +	*p   = (void *)&nop_p_v;		  // OnEdictFreed + +#ifdef _WIN32 +	//if (gameinfo_serverlib) serverlib = GetModuleHandleW(gameinfo_serverlib); +	if (gameinfo_clientlib) clientlib = GetModuleHandleW(gameinfo_clientlib); +#else +	// Linux Source load order seems to be different to the point where if we +	// +plugin_load or use a vdf then RTLD_NOLOAD won't actually find these, so +	// we have to just dlopen them normally - and then remember to decrement the +	// refcount again later in do_unload() so nothing gets leaked +	//if (gameinfo_serverlib) serverlib = dlopen(gameinfo_serverlib, 0); +	if (gameinfo_clientlib) clientlib = dlopen(gameinfo_clientlib, 0); +#endif +	if (!clientlib) { +		con_warn("sst: warning: couldn't get the game's client library\n"); +		goto nc; +	} +	factory_client = (ifacefactory)os_dlsym(clientlib, "CreateInterface"); +	if (!factory_client) { +		con_warn("sst: warning: couldn't get client's CreateInterface\n"); +	} + +nc:	gamedata_init(); +	// TODO(autojump): we'd init that here +	has_demorec = demorec_init(); + +	con_colourmsg(RGBA(64, 255, 64, 255), +			NAME " v" VERSION " successfully loaded"); +	con_colourmsg(RGBA(255, 255, 255, 255), " for game "); +	con_colourmsg(RGBA(0, 255, 255, 255), "%s\n", gameinfo_title); +	return true; +} + +static void do_unload(void) { +	// TODO(autojump): we'd end that here +	if (has_demorec) demorec_end(); + +#ifdef __linux__ +	//if (serverlib) dlclose(serverlib); +	if (clientlib) dlclose(clientlib); +#endif +	con_disconnect(); +} + +// since this is static/global, it only becomes false again when the plugin SO +// is unloaded/reloaded +static bool already_loaded = false; +static bool skip_unload = false; + +static bool VCALLCONV Load(void *this, ifacefactory enginef, +		ifacefactory serverf) { +	if (already_loaded) { +		con_warn("Already loaded! Doing nothing!\n"); +		skip_unload = true; +		return false; +	} +	already_loaded = do_load(enginef, serverf); +	skip_unload = !already_loaded; +	return already_loaded; +} + +static void Unload(void *this) { +	// the game tries to unload on a failed load, for some reason +	if (skip_unload) { +		skip_unload = false; +		return; +	} +	do_unload(); +} + +static void VCALLCONV Pause(void *this) { +	con_warn(NAME " doesn't support plugin_pause - ignoring\n"); +} +static void VCALLCONV UnPause(void *this) { +	con_warn(NAME " doesn't support plugin_unpause - ignoring\n"); +} + +static const char *VCALLCONV GetPluginDescription(void *this) { +	return LONGNAME " v" VERSION; +} + +DEF_CCMD_HERE(sst_printversion, "Display plugin version information", 0) { +	con_msg("v" VERSION "\n"); +} + +#define MAX_VTABLE_FUNCS 21 +static const void *vtable[MAX_VTABLE_FUNCS] = { +	// start off with the members which (thankfully...) are totally stable +	// between interface versions - the *remaining* members get filled in just +	// in time by do_load() once we've figured out what engine branch we're on +	(void *)&Load, +	(void *)&Unload, +	(void *)&Pause, +	(void *)&UnPause, +	(void *)&GetPluginDescription, +	(void *)&nop_p_v,	// LevelInit +	(void *)&nop_pii_v, // ServerActivate +	(void *)&nop_b_v,	// GameFrame +	(void *)&nop_v_v,	// LevelShutdown +	(void *)&nop_p_v	// ClientActive +	// At this point, Alien Swarm and Portal 2 add ClientFullyConnect, so we +	// can't hardcode any more of the layout! +}; +// end MUST point AFTER the last of the above entries +static const void **vtable_firstdiff = vtable + 10; +// this is equivalent to a class with no members! +static const void *const *const plugin_obj = vtable; + +EXPORT const void *CreateInterface(const char *name, int *ret) { +	if (!strncmp(name, "ISERVERPLUGINCALLBACKS00", 24)) { +		if ((name[24] >= '1' || name[24] <= '3') && name[25] == '\0') { +			if (ret) *ret = 0; +			plugin_ver = name[24] - '0'; +			return &plugin_obj; +		} +	} +	if (ret) *ret = 1; +	return 0; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/tier0stub.c b/src/tier0stub.c new file mode 100644 index 0000000..a043bea --- /dev/null +++ b/src/tier0stub.c @@ -0,0 +1,19 @@ +/* This file is dedicated to the public domain. */ + +// Produce a dummy tier0.dll/libtier0.so to allow linking without dlsym faff. +// Windows needs additional care because it's dumb. + +#ifdef _WIN32 +#define F(name) __declspec(dllexport) void name(void) {} +#define V(name) __declspec(dllexport) void *name; +#else +#define F(name) void *name; +#define V(name) void *name; +#endif + +F(Msg); +F(Warning); +// F(Error); // unused in plugin +V(g_pMemAlloc); + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/udis86.c b/src/udis86.c new file mode 100644 index 0000000..fcb3656 --- /dev/null +++ b/src/udis86.c @@ -0,0 +1,11 @@ +/* This file is dedicated to the public domain. */ + +#include "3p/udis86/udis86.c" +#include "3p/udis86/decode.c" +#include "3p/udis86/itab.c" +// this stuff is optional but llvm is smart enough to remove it if it's unused, +// so we keep it in here to be able to use it conveniently for debugging etc. +#include "3p/udis86/syn.c" +#include "3p/udis86/syn-intel.c" + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/udis86.h b/src/udis86.h new file mode 100644 index 0000000..ba5af90 --- /dev/null +++ b/src/udis86.h @@ -0,0 +1,12 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_UDIS86_H +#define INC_UDIS86_H + +#include "3p/udis86/types.h" +#include "3p/udis86/extern.h" +#include "3p/udis86/itab.h" + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/unreachable.h b/src/unreachable.h new file mode 100644 index 0000000..99c82b5 --- /dev/null +++ b/src/unreachable.h @@ -0,0 +1,14 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_UNREACHABLE_H +#define INC_UNREACHABLE_H + +#if defined(__GNUC__) || defined(__clang__) +#define unreachable __builtin_unreachable() +#else +#define unreachable do; while (0) +#endif + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/vcall.h b/src/vcall.h new file mode 100644 index 0000000..c92f8db --- /dev/null +++ b/src/vcall.h @@ -0,0 +1,57 @@ +/* + * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * + * 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. + */ + +#ifndef INC_VCALL_H +#define INC_VCALL_H + +/* + * Convenient facilities for calling simple (single-table) virtual functions on + * possibly-opaque pointers to C++ objects. + */ + +#ifdef _WIN32 +#if defined(__GNUC__) || defined(__clang__) +#define VCALLCONV __thiscall +#else +// XXX: could support MSVC via __fastcall and dummy param, but is there a point? +#error C __thiscall support requires Clang or GCC +#endif +#else +#define VCALLCONV +#endif + +#define DECL_VFUNC0(ret, name, idx) \ +	enum { _VTIDX_##name = (idx) }; \ +	typedef ret (*VCALLCONV _VFUNC_##name)(void *this); + +#define DECL_VFUNC(ret, name, idx, ...) \ +	enum { _VTIDX_##name = (idx) }; \ +	typedef ret (*VCALLCONV _VFUNC_##name)(void *this, __VA_ARGS__); + +// not bothering to provide a zero-argument version because the main use of +// this is vararg functions, which error if __thiscall +#define DECL_VFUNC_CDECL(ret, name, idx, ...) \ +	enum { _VTIDX_##name = (idx) }; \ +	typedef ret (*_VFUNC_##name)(void *this, __VA_ARGS__); + +#define VFUNC(x, name) ((*(_VFUNC_##name **)(x))[_VTIDX_##name]) + +#define VCALL0(x, name) (VFUNC(x, name)(x)) +#define VCALL(x, name, ...) VFUNC(x, name)(x, __VA_ARGS__) + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..b75b78f --- /dev/null +++ b/src/version.h @@ -0,0 +1,5 @@ +#define NAME "SST" +#define LONGNAME "Source Speedrun Tools" +#define VERSION_MAJOR 0 +#define VERSION_MINOR 1 +#define VERSION "Beta 0.1"  | 
