From da6f343032cb01597dc7866e66f091adf3243a62 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 20 Nov 2021 03:10:50 +0000 Subject: Initial public snapshot With code from Bill. Thanks Bill! --- .editorconfig | 11 + .gitignore | 8 + LICENCE | 27 + README | 50 + TODO/autocomplete | 4 + TODO/autojump | 5 + TODO/compat | 3 + TODO/featgen | 9 + TODO/linux | 6 + TODO/opt | 5 + compile | 57 + compile.bat | 62 + gamedata/engine.kv | 11 + gamedata/gamelib.kv | 14 + src/3p/README | 24 + src/3p/chibicc/LICENSE | 21 + src/3p/chibicc/chibicc.h | 486 ++++ src/3p/chibicc/codegen.c | 1595 ++++++++++++ src/3p/chibicc/hashmap.c | 165 ++ src/3p/chibicc/main.c | 791 ++++++ src/3p/chibicc/parse.c | 3368 ++++++++++++++++++++++++ src/3p/chibicc/preprocess.c | 1208 +++++++++ src/3p/chibicc/strings.c | 17 + src/3p/chibicc/tokenize.c | 799 ++++++ src/3p/chibicc/type.c | 307 +++ src/3p/chibicc/unicode.c | 189 ++ src/3p/openbsd/asprintf.c | 81 + src/3p/udis86/decode.c | 1266 +++++++++ src/3p/udis86/decode.h | 197 ++ src/3p/udis86/extern.h | 113 + src/3p/udis86/itab.c | 5945 +++++++++++++++++++++++++++++++++++++++++++ src/3p/udis86/itab.h | 939 +++++++ src/3p/udis86/syn-att.c | 228 ++ src/3p/udis86/syn-intel.c | 224 ++ src/3p/udis86/syn.c | 212 ++ src/3p/udis86/syn.h | 53 + src/3p/udis86/types.h | 260 ++ src/3p/udis86/udint.h | 95 + src/3p/udis86/udis86.c | 456 ++++ src/abi.h | 91 + src/bitbuf.h | 99 + src/build/cmeta.c | 238 ++ src/build/cmeta.h | 44 + src/build/codegen.c | 95 + src/build/mkgamedata.c | 238 ++ src/con_.c | 533 ++++ src/con_.h | 278 ++ src/dbg.c | 49 + src/dbg.h | 28 + src/demodefs.h | 94 + src/demorec.c | 218 ++ src/demorec.h | 28 + src/dll.rc | 33 + src/extmalloc.c | 60 + src/extmalloc.h | 33 + src/factory.h | 13 + src/gamedata.c | 31 + src/gamedata.h | 32 + src/gameinfo.c | 372 +++ src/gameinfo.h | 37 + src/gametype.h | 38 + src/hook.c | 99 + src/hook.h | 54 + src/intdefs.h | 35 + src/kv.c | 231 ++ src/kv.h | 96 + src/mem.h | 73 + src/noreturn.h | 11 + src/os.c | 56 + src/os.h | 145 ++ src/ppmagic.h | 81 + src/sst.c | 206 ++ src/tier0stub.c | 19 + src/udis86.c | 11 + src/udis86.h | 12 + src/unreachable.h | 14 + src/vcall.h | 57 + src/version.h | 5 + test/bitbuf.test.c | 34 + test/hook.test.c | 45 + test/kv.test.c | 49 + test/test.h | 234 ++ tools/todo | 5 + tools/todo.sh | 35 + tools/todo.vim | 41 + 85 files changed, 23641 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 README create mode 100644 TODO/autocomplete create mode 100644 TODO/autojump create mode 100644 TODO/compat create mode 100644 TODO/featgen create mode 100644 TODO/linux create mode 100644 TODO/opt create mode 100644 compile create mode 100644 compile.bat create mode 100644 gamedata/engine.kv create mode 100644 gamedata/gamelib.kv create mode 100644 src/3p/README create mode 100644 src/3p/chibicc/LICENSE create mode 100644 src/3p/chibicc/chibicc.h create mode 100644 src/3p/chibicc/codegen.c create mode 100644 src/3p/chibicc/hashmap.c create mode 100644 src/3p/chibicc/main.c create mode 100644 src/3p/chibicc/parse.c create mode 100644 src/3p/chibicc/preprocess.c create mode 100644 src/3p/chibicc/strings.c create mode 100644 src/3p/chibicc/tokenize.c create mode 100644 src/3p/chibicc/type.c create mode 100644 src/3p/chibicc/unicode.c create mode 100644 src/3p/openbsd/asprintf.c create mode 100644 src/3p/udis86/decode.c create mode 100644 src/3p/udis86/decode.h create mode 100644 src/3p/udis86/extern.h create mode 100644 src/3p/udis86/itab.c create mode 100644 src/3p/udis86/itab.h create mode 100644 src/3p/udis86/syn-att.c create mode 100644 src/3p/udis86/syn-intel.c create mode 100644 src/3p/udis86/syn.c create mode 100644 src/3p/udis86/syn.h create mode 100644 src/3p/udis86/types.h create mode 100644 src/3p/udis86/udint.h create mode 100644 src/3p/udis86/udis86.c create mode 100644 src/abi.h create mode 100644 src/bitbuf.h create mode 100644 src/build/cmeta.c create mode 100644 src/build/cmeta.h create mode 100644 src/build/codegen.c create mode 100644 src/build/mkgamedata.c create mode 100644 src/con_.c create mode 100644 src/con_.h create mode 100644 src/dbg.c create mode 100644 src/dbg.h create mode 100644 src/demodefs.h create mode 100644 src/demorec.c create mode 100644 src/demorec.h create mode 100644 src/dll.rc create mode 100644 src/extmalloc.c create mode 100644 src/extmalloc.h create mode 100644 src/factory.h create mode 100644 src/gamedata.c create mode 100644 src/gamedata.h create mode 100644 src/gameinfo.c create mode 100644 src/gameinfo.h create mode 100644 src/gametype.h create mode 100644 src/hook.c create mode 100644 src/hook.h create mode 100644 src/intdefs.h create mode 100644 src/kv.c create mode 100644 src/kv.h create mode 100644 src/mem.h create mode 100644 src/noreturn.h create mode 100644 src/os.c create mode 100644 src/os.h create mode 100644 src/ppmagic.h create mode 100644 src/sst.c create mode 100644 src/tier0stub.c create mode 100644 src/udis86.c create mode 100644 src/udis86.h create mode 100644 src/unreachable.h create mode 100644 src/vcall.h create mode 100644 src/version.h create mode 100644 test/bitbuf.test.c create mode 100644 test/hook.test.c create mode 100644 test/kv.test.c create mode 100644 test/test.h create mode 100644 tools/todo create mode 100644 tools/todo.sh create mode 100644 tools/todo.vim diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1953ba8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = tab +indent_size = 4 + +[*.{bat,cmd}] +end_of_line = crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6861589 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.build/ +/sst.dll +/sst.pdb +/sst.so +/compile_commands.json + +# a place to plonk crap you don't wanna commit yet +/junk diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..76ade89 --- /dev/null +++ b/LICENCE @@ -0,0 +1,27 @@ +Except where otherwise noted, the following terms apply: +================================================================================ +Copyright © 2021 Michael Smith +Copyright © 2021 Willian Henrique + +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. +================================================================================ +Parts of this software distribution are released into the public domain. Check +the copyright notices in individual files for full details. + +The README file is also in the public domain. + +Files under src/3p/ are written and licensed by third parties. See the copyright +notices in and alongside those files for details of authorship and relevant +redistribution rights. Some - but not all - of this third party code is +incorporated into binary builds (sst.dll or sst.so). Please take care when +redistributing binaries to respect all the relevant copyright notices. diff --git a/README b/README new file mode 100644 index 0000000..188b0ad --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +Hey! This README is for those looking to work on the code. For actual plugin +documentation, you'll want to look at the website, which doesn't exist yet, +or ask for help in the relevant speedrunning Discord, which does exist yet. + +NOTE: Please read and understand LICENCE before redistributing this software! + +== Compiling == + +Windows: + - Install the Windows 10 SDK via the Visual Studio Installer. + - Install native Clang from https://clang.llvm.org (NOT MinGW/MSYS2 Clang!). + - Run compile.bat (in lieu of a better build tool, to be figured out later). + +Linux: + - Install Clang (and LLD) via your system package manager. Technically, GCC + might be able to compile this too, but Clang is heavily preferred in the name + of consistency and using GCC will require a decent amount of fiddling. It's + also not tested and might break and I probably won't care that much. + - Install 32-bit C libraries and the C library headers if they're not already + installed. + - Run ./compile (in lieu of a better build tool, to be figured out later). + +* NOTE: Linux builds currently fail; there's a bunch of code that needs to be + written. See also TODO/linux. + +== How and where to install == + +Very old Source builds load plugins from the top-level bin/ folder, while +relatively modern builds load them from the mod-specific directory, e.g. +left4dead2/, hl2/. Since this plugin is designed to be universal, the +recommended practice is to put it in bin/ regardless and then use the command +`plugin_load ../bin/sst`. The way the paths work out, that ends up always +working no matter what, and allows the plugin to be shared between different +mods in the same engine installation where relevant. It's also possible to just +back out of the game installation with ../ and load from whatever directory you +want, as long as it's not on a different Windows drive letter. In that case, the +plugin can be shared between multiple games. That said, it's a little more +annoying to do and a little less self contained. + +IMPORTANT: If your game supports multiplayer, always launch with -insecure as a +launch option before loading this plugin! In theory, plugins shouldn't be able +to load without this, or it should at least be impossible to connect to a VAC- +enabled server with the plugin loaded, but -insecure is the safest way to ensure +VAC is completely disabled, otherwise you use the plugin AT YOUR OWN RISK OF +BEING BANNED! + +Note: some very old (and very new) Source builds don't have a plugin_load +command. It may be possible (although questionably necessary) to support these +games with an injector to forcibly load the library into the game. For the time +being, however, these versions are unsupported. diff --git a/TODO/autocomplete b/TODO/autocomplete new file mode 100644 index 0000000..cc3e867 --- /dev/null +++ b/TODO/autocomplete @@ -0,0 +1,4 @@ +Console command autocompletion +==== +This isn't really something we need right now, but is worth documenting in case +we deem it useful later. diff --git a/TODO/autojump b/TODO/autojump new file mode 100644 index 0000000..7b9c8b4 --- /dev/null +++ b/TODO/autojump @@ -0,0 +1,5 @@ +Add back autojump +==== +I already wrote (and rewrote) the code long ago, but it's a secret until we +further raise the verification standard for L4D2. Stupid, but less stupid than +having no idea whether anyone's cheating, I guess. diff --git a/TODO/compat b/TODO/compat new file mode 100644 index 0000000..7f210e8 --- /dev/null +++ b/TODO/compat @@ -0,0 +1,3 @@ +Support more games and engine branches +==== +Pretty self-explanatory. diff --git a/TODO/featgen b/TODO/featgen new file mode 100644 index 0000000..b71f32a --- /dev/null +++ b/TODO/featgen @@ -0,0 +1,9 @@ +Feature setup code generation +==== +Once the plugin has enough features across enough C files, setting up and +tearing down everything will get kind of annoying. That's where some more code +generation might be nice. + +There's a few vague ideas of how to do this but really it's pretty open-ended +overall. In the meantime, we're just calling functions manually. Not a huge +deal, really. diff --git a/TODO/linux b/TODO/linux new file mode 100644 index 0000000..d89baca --- /dev/null +++ b/TODO/linux @@ -0,0 +1,6 @@ +Support Linux (again) +==== +Early prototype code was written with both Linux and Windows in mind, but since +then most of the development happens to have happened on Windows, which has left +some gaps. We want to eventually add back support for Linux once all the +important Windows stuff is out of the way. diff --git a/TODO/opt b/TODO/opt new file mode 100644 index 0000000..03add29 --- /dev/null +++ b/TODO/opt @@ -0,0 +1,5 @@ +Improve performance +==== +Simple C code tends to go fast, but that doesn't mean we can't make a note of +possibly-weird stuff that should maybe go more fast if we care enough to +improve it. diff --git a/compile b/compile new file mode 100644 index 0000000..fa0240e --- /dev/null +++ b/compile @@ -0,0 +1,57 @@ +#!/bin/sh -e +# This file is dedicated to the public domain. + +case "`uname -s`" in + # weird people using Windows Bash might type ./compile, help them out :) + *NT*) + echo "You're on Windows, idiot! Running compile.bat for you." + exec cmd /c compile.bat ;; +esac + +mkdir -p .build/include + +warnings=-Wall -pedantic -Wno-parentheses -Wno-missing-braces + +objs= +cc() { + objs="$objs .build/${1%%.c}.o" + clang -m32 -c -O2 -flto -fpic $warnings -I.build/include \ + -D_FILE_OFFSET_BITS=64 -DFILE_BASENAME="${1%%.c}" \ + -o ".build/${1%%.c}.o" "src/$1" +} + +ld() { + clang -m32 -shared -O2 -flto -fpic -s -fuse-ld=lld -L.build -ldl -o sst.so$objs +} + +src="\ + con_.c \ + demorec.c \ + dbg.c \ + extmalloc.c \ + gameinfo.c \ + hook.c \ + kv.c \ + os.c \ + sst.c \ + udis86.c" + +clang -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -o .build/codegen \ + src/build/codegen.c src/build/cmeta.c src/os.c +clang -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -o .build/mkgamedata \ + src/build/mkgamedata.c src/kv.c src/os.c +.build/codegen `for s in $src; do echo "src/$s"; done` +.build/mkgamedata gamedata/gamelib.kv gamedata/engine.kv +for s in $src; do cc "$s" done +clang -m32 -shared -fpic -fuse-ld=lld -O0 -w -o .build/libtier0.so src/tier0stub.c +ld + +clang -fuse-ld=lld -O2 -g3 -include test/test.h -o .build/bitbuf.test test/bitbuf.test.c +.build/bitbuf.test +# skipping this test on linux for now, since inline hooks aren't compiled in +#clang -m32 -fuse-ld=lld -O2 -g3 -include test/test.h -o .build/hook.test test/hook.test.c +#.build/hook.test +clang -fuse-ld=lld -O2 -g3 -include test/test.h -o .build/kv test/kv.test.c +.build/kv.test + +# vi: sw=4 tw=4 noet tw=80 cc=80 diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000..4fce747 --- /dev/null +++ b/compile.bat @@ -0,0 +1,62 @@ +:: This file is dedicated to the public domain. +@echo off + +if not exist .build\ ( + md .build + attrib +H .build +) +if not exist .build\include\ md .build\include + +set warnings=-Wall -pedantic -Wno-parentheses -Wno-missing-braces + +set objs= +goto :main + +:cc +for /F %%b in ("%1") do set basename=%%~nb +set objs=%objs% .build/%basename%.o +clang -m32 -c -O2 -flto %warnings% -I.build/include -D_CRT_SECURE_NO_WARNINGS ^ +-DFILE_BASENAME=%basename% -o .build/%basename%.o %1 || exit /b +goto :eof + +:main +clang -municode -O2 -fuse-ld=lld %warnings% -D_CRT_SECURE_NO_WARNINGS -ladvapi32 ^ +-o .build/codegen.exe src/build/codegen.c src/build/cmeta.c src/os.c || exit /b +clang -municode -O2 -fuse-ld=lld %warnings% -D_CRT_SECURE_NO_WARNINGS -ladvapi32 ^ +-o .build/mkgamedata.exe src/build/mkgamedata.c src/kv.c src/os.c || exit /b +.build\codegen.exe src/con_.c src/demorec.c src/dbg.c src/gamedata.c ^ +src/gameinfo.c src/hook.c src/kv.c src/os.c src/sst.c src/udis86.c || exit /b +.build\mkgamedata.exe gamedata/engine.kv gamedata/gamelib.kv || exit /b +:: llvm-rc doesn't preprocess, looks like it might later: +:: https://reviews.llvm.org/D100755?id=339141 +:: in the meantime, manually run through clang -E +clang -E -xc src/dll.rc>.build\dll.pp.rc || exit /b +llvm-rc /FO .build\dll.res .build\dll.pp.rc || exit /b +:: might as well remove the temp file afterwards +del .build\dll.pp.rc +clang -m32 -shared -fuse-ld=lld -O0 -w -o .build/tier0.dll src/tier0stub.c +call :cc src/con_.c || exit /b +call :cc src/demorec.c || exit /b +call :cc src/dbg.c || exit /b +call :cc src/extmalloc.c || exit /b +call :cc src/gamedata.c || exit /b +call :cc src/gameinfo.c || exit /b +call :cc src/hook.c || exit /b +call :cc src/kv.c || exit /b +call :cc src/os.c || exit /b +call :cc src/sst.c || exit /b +call :cc src/udis86.c || exit /b +clang -m32 -shared -O2 -flto -fuse-ld=lld -Wl,/implib:.build/sst.lib,/Brepro ^ +-L.build -ladvapi32 -ltier0 -lshlwapi -o sst.dll%objs% .build/dll.res || exit /b +:: get rid of another useless file (can we just not create this???) +del .build\sst.lib + +clang -fuse-ld=lld -O2 -g3 -include test/test.h -o .build/bitbuf.test.exe test/bitbuf.test.c || exit /b +.build\bitbuf.test.exe || exit /b +:: special case: test must be 32-bit +clang -m32 -fuse-ld=lld -O2 -g3 -ladvapi32 -include test/test.h -o .build/hook.test.exe test/hook.test.c || exit /b +.build\hook.test.exe || exit /b +clang -fuse-ld=lld -O2 -g3 -include test/test.h -o .build/kv.test.exe test/kv.test.c || exit /b +.build\kv.test.exe || exit /b + +:: vi: sw=4 tw=4 noet tw=80 cc=80 diff --git a/gamedata/engine.kv b/gamedata/engine.kv new file mode 100644 index 0000000..81a0545 --- /dev/null +++ b/gamedata/engine.kv @@ -0,0 +1,11 @@ +// = engine library = + +// CDemoRecorder +vtidx_SetSignonState 3 +vtidx_StopRecording 7 +vtidx_RecordPacket 11 + +// VEngineClient +vtidx_GetEngineBuildNumber { L4D2 99 } + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/gamedata/gamelib.kv b/gamedata/gamelib.kv new file mode 100644 index 0000000..388abb9 --- /dev/null +++ b/gamedata/gamelib.kv @@ -0,0 +1,14 @@ +// = client and server libaries = + +// CGameMovement +vtidx_CheckJumpButton { + 2013 "28 + NVDTOR" + L4D "32 + NVDTOR" + Portal2 "35 + NVDTOR" +} +off_mv 8 + +// I(Server|Client)Unknown +vtidx_GetBaseEntity 4 + +// vi: sw=4 ts=4 noet tw=80 cc=80 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 +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +// 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 +#include +//#include +#include +//#include +//#include +#include +//#include + +// 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 ] \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(": 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(": 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 + 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("", 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; +// ^ +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(), 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 + * + * 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 +#include /* for INT_MAX */ +#include +#include /* for vsnprintf */ +#include + +#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 +#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 +#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 +# include +# ifndef __UD_STANDALONE__ +# define __UD_STANDALONE__ 1 +# endif +#endif /* __KERNEL__ */ + +#if !defined(__UD_STANDALONE__) +# include +# include +#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 +# 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 +#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 + * + * 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 + * + * 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 + +#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 + * + * 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 +#include +#include + +#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 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 + * + * 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 + +#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 + * + * 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 +#include +#include + +#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 + * + * 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 +#include +#include + +#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: + * + * + * { ... [default ] } + * [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 + * + * 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 +#include +#include + +#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 + +// 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 + * + * 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 + +#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 + * + * 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 + * + * 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 + * + * 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 + * Copyright © 2021 Michael Smith + * + * 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 + +#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 + * Copyright © 2021 Michael Smith + * + * 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 + +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 + * + * 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 + * + * 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 + * + * 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 + +#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 + +// 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 + * + * 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 +#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 + * + * 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 +#include +#include +#include +#ifdef _WIN32 +#include +#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 + * + * 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 + * + * 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 + * + * 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 + +#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 + * + * 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 + * + * 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 + +#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 + * + * 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 + +#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 + * + * 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 + * + * 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 + +#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 + * + * 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 + +/* + * 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 +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else +#include +#include +#include +#include +#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 + * + * 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 + * + * 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 + +#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 + * + * 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" diff --git a/test/bitbuf.test.c b/test/bitbuf.test.c new file mode 100644 index 0000000..58d1c4d --- /dev/null +++ b/test/bitbuf.test.c @@ -0,0 +1,34 @@ +/* This file is dedicated to the public domain. */ + +{.desc = "the bit buffer implementation"}; + +#include "../src/bitbuf.h" +#include "../src/intdefs.h" + +#include +#include + +static union { + char buf[512]; + bitbuf_cell buf_align[512 / sizeof(bitbuf_cell)]; +} bb_buf; +static struct bitbuf bb = {bb_buf.buf, 512, 512 * 8, 0, false, false, "test"}; + +TEST("The possible UB in bitbuf_appendbuf shouldn't trigger horrible bugs", 0) { + char unalign[3] = {'X', 'X', 'X'}; + char _buf[32 + sizeof(bitbuf_cell)]; + char *buf = _buf; + if (bitbuf_align <= 1) { + // *shouldn't* happen + fputs("what's going on with the alignment???\n", stderr); + return false; + } + // make sure the pointer is definitely misaligned + while (!((usize)buf % bitbuf_align)) ++buf; + + memcpy(buf, "Misaligned test buffer contents!", 32); + bitbuf_appendbuf(&bb, buf, 32); + return !memcmp(bb.buf, buf, 32); +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/test/hook.test.c b/test/hook.test.c new file mode 100644 index 0000000..a918e22 --- /dev/null +++ b/test/hook.test.c @@ -0,0 +1,45 @@ +/* This file is dedicated to the public domain. */ + +{.desc = "inline function hooking"}; + +#ifdef _WIN32 + +#include "../src/udis86.c" +#include "../src/os.c" +#include "../src/hook.c" + +#include +#include +#include + +// stubs +void con_warn(const char *msg, ...) { + va_list l; + va_start(l, msg); + vfprintf(stderr, msg, l); + va_end(l); +} + +__attribute__((noinline)) +static int some_function(int a, int b) { return a + b; } +static int (*orig_some_function)(int, int); +static int some_hook(int a, int b) { + return orig_some_function(a, b) + 5; +} + +TEST("Inline hooks should be able to wrap the original function", 0) { + orig_some_function = hook_inline(&some_function, &some_hook); + if (!orig_some_function) return false; + return some_function(5, 5) == 15; +} + +TEST("Inline hooks should be removable again", 0) { + orig_some_function = hook_inline(&some_function, &some_hook); + if (!orig_some_function) return false; + unhook_inline(orig_some_function); + return some_function(5, 5) == 10; +} + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/test/kv.test.c b/test/kv.test.c new file mode 100644 index 0000000..cd08d16 --- /dev/null +++ b/test/kv.test.c @@ -0,0 +1,49 @@ +/* This file is dedicated to the public domain. */ + +{.desc = "the KeyValues parser"}; + +// undef conflicting macros +#undef ERROR // windows.h +#undef OUT // " +#undef EOF // stdio.h +#include "../src/kv.c" + +#include "../src/intdefs.h" +#include "../src/noreturn.h" + +static noreturn die(const struct kv_parser *kvp) { + fprintf(stderr, "parse error: %d:%d: %s\n", kvp->line, kvp->col, + kvp->errmsg); + exit(1); +} + +static void tokcb(enum kv_token type, const char *p, uint len, + void *ctxt) { + // nop - we're just testing the tokeniser +} + +static const char data[] = +"KeyValues {\n\tKey/1\tVal1! \tKey2\nVal2// comment\n\"String Key\"// also comment\nVal3 Key4{ Key5 \"Value Five\" } // one more\n\t\n}" +; +static const int sz = sizeof(data) - 1; + +TEST("parsing should work with any buffer size", 0) { + for (int chunksz = 3; chunksz <= sz; ++chunksz) { + struct kv_parser kvp = {0}; + // sending data in chunks to test reentrancy + for (int chunk = 0; chunk * chunksz < sz; ++chunk) { + int thischunk = chunksz; + if (chunk * chunksz + thischunk > sz) { + thischunk = sz - chunk * chunksz; + } + kv_parser_feed(&kvp, data + chunk * chunksz, thischunk, + tokcb, 0); + if (kvp.state == KV_PARSER_ERROR) die(&kvp); + } + kv_parser_done(&kvp); + if (kvp.state == KV_PARSER_ERROR) die(&kvp); + } + return true; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..3893359 --- /dev/null +++ b/test/test.h @@ -0,0 +1,234 @@ +/* + * test.h - Michael Smith + * I hereby dedicate the contents of this file to the public domain. In + * jurisdictions with no public domain, go you your supreme court and get a + * public domain. + */ + +/* + * NOTE: This is a hacky black magic Windows port! If you only want Unix + * support, the less-atrocious original resides in a Git repository: + * https://gitlab.com/mikesmiffy128/test.h + */ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#ifdef __clang__ +#define _TEST_SILENCE_CLANG \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") +#define _TEST_UNSILENCE_CLANG \ + _Pragma("clang diagnostic pop") +#else +#define _TEST_SILENCE_CLANG +#define _TEST_UNSILENCE_CLANG +#endif + +static struct _test_desc { + char *desc; + int default_flags; +} _test_desc; + +static struct _test { + char *desc; + /* Optional attributes that can be set to further customise the test case */ + // Note: the first attribute here has to have a default value of 0 because + // of __VA_ARGS__ requiring at least one argument + int expected_exit; /* expected exit from forked child (255 is reserved!) */ + int flags; /* see flags below */ + int timeout; /* milisecond timeout on forked child (if forking) */ + bool (*_f)(void); + struct _test *_next; +} *_tests = 0, **_tests_tail = &_tests; +static int _ntests = 0; + +/* Test flags - currently just NOFORK but you could add your own custom ones! */ +#define NOFORK 1 + +#define _TEST_USE_DEFAULT_FLAGS -1 // indicator to use global default_flags +#define _TEST_DEFAULT_TIMEOUT 1000 // 1s seems reasonable + +#define _TESTCAT1(a, b) a##b +#define _TESTCAT(a, b) _TESTCAT1(a, b) +#define _TESTSTR1(x) #x +#define _TESTSTR(x) _TESTSTR1(x) +#define TEST(desc_, ...) \ + static bool _TESTCAT(_test_f_, __LINE__)(void); \ + _TEST_SILENCE_CLANG \ + static struct _test _TESTCAT(_test_, __LINE__) = { \ + .flags = _TEST_USE_DEFAULT_FLAGS, \ + .timeout = _TEST_DEFAULT_TIMEOUT, \ + .desc = __FILE__":"_TESTSTR(__LINE__)": "desc_, \ + __VA_ARGS__, \ + ._f = &_TESTCAT(_test_f_, __LINE__) \ + }; \ + _TEST_UNSILENCE_CLANG \ + /* constructor adds tests to the list tail to run them in order */ \ + __attribute__((constructor(100 + __LINE__))) \ + static void _TESTCAT(_test_init_, __LINE__)(void) { \ + if (_TESTCAT(_test_, __LINE__).flags == _TEST_USE_DEFAULT_FLAGS) { \ + _TESTCAT(_test_, __LINE__).flags = _test_desc.default_flags; \ + } \ + _TESTCAT(_test_, __LINE__)._next = *_tests_tail; \ + *_tests_tail = &_TESTCAT(_test_, __LINE__); \ + _tests_tail = &_TESTCAT(_test_, __LINE__)._next; \ + ++_ntests; \ + } \ + static bool _TESTCAT(_test_f_, __LINE__)(void) + +#ifdef _WIN32 +// since we can't fork, we CreateProcess ourselves and use WriteProcessMemory +// to set this function pointer to call the test we want to call +static volatile bool (*_test_entry_f)(void) = 0; +unsigned short _test_exepath[MAX_PATH]; +#define _test_perror_win(thing) do { \ + char err[128]; \ + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), \ + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err, sizeof(err), 0); \ + fprintf(stderr, thing ": %s\n", err); \ + return false; \ +} while (0) +#else +static sigset_t _test_sigmask = {0}; +#endif + +static bool _run_test(struct _test *t) { + if (t->flags & NOFORK) return t->_f(); + +#ifdef _WIN32 + STARTUPINFOW startinfo = {0}; + PROCESS_INFORMATION info; + if (!CreateProcessW(_test_exepath, L"", 0, 0, 1, CREATE_SUSPENDED, 0, 0, + &startinfo, &info)) { + _test_perror_win("CreateProcess"); + } + if (!WriteProcessMemory(info.hProcess, (void *)&_test_entry_f, &t->_f, + sizeof(t->_f), 0)) { + TerminateProcess(info.hProcess, -1); + _test_perror_win("WriteProcessMemory"); + } + ResumeThread(info.hThread); + bool success; + if (t->timeout) { + if (WaitForSingleObject(info.hProcess, t->timeout) == WAIT_TIMEOUT) { + TerminateProcess(info.hProcess, -1); + fprintf(stderr, "child process timed out after %d milliseconds\n", + t->timeout); + success = false; + goto r; + } + } + else { + WaitForSingleObject(info.hProcess, INFINITE); + } + unsigned long status; + GetExitCodeProcess(info.hProcess, &status); + success = status == t->expected_exit; +r: CloseHandle(info.hProcess); + CloseHandle(info.hThread); + return success; +#else + pid_t pid = fork(); + if (pid == -1) { + perror("fork"); + return false; + } + if (!pid) { + bool ret = t->_f(); + if (!ret) exit(255); + exit(t->expected_exit); + } + if (t->timeout) { + struct timespec ts = { t->timeout / 1000, (t->timeout % 1000) * 1000000 }; + if (!pselect(0, 0, 0, 0, &ts, &_test_sigmask)) { + // if pselect returned zero it must've timed out + fprintf(stderr, "child process timed out after %d milliseconds\n", + t->timeout); + kill(pid, SIGKILL); // XXX should this be a less harsh signal? + waitpid(pid, 0, 0); // still have to reap the zombie process + return false; + } + } + // either there was no timeout value or pselect errored meaning we should + // have something to wait() on! + int status; + wait(&status); + if (WIFEXITED(status)) { + return WEXITSTATUS(status) == t->expected_exit; + } + else /* WIFSIGNALED(status) */ { + fprintf(stderr, "child process killed by signal %d (%s)\n", + WTERMSIG(status), strsignal(WTERMSIG(status))); + return false; + } +#endif +} + +#ifndef _WIN32 +static void _test_sigchld(int sig) {} +#endif + +/* + * Main test driver, does the important stuff + */ +int main(void) { +#ifdef _WIN32 + // if _test_entry_f points at something, we're the """forked""" child + if (_test_entry_f) return !_test_entry_f(); + GetModuleFileNameW(0, _test_exepath, sizeof(_test_exepath) / + sizeof(*_test_exepath)); + // make the output look correct + void *con = GetStdHandle(STD_OUTPUT_HANDLE); + unsigned long conmode; + GetConsoleMode(con, &conmode); + SetConsoleMode(con, conmode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); +#else + // set up no-op SIGCHLD handling so we can (ab)use pselect() to do race-free + // timeouts + struct sigaction sa; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = &_test_sigchld; + sigaction(SIGCHLD, &sa, 0); + sigaddset(&_test_sigmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &_test_sigmask, 0); + sigemptyset(&_test_sigmask); +#endif + + int thistest = 1; + bool failed = false; + for (struct _test *t = _tests; t; t = t->_next, ++thistest) { + if (!_run_test(t)) { + if (!failed) { + fprintf(stderr, "\ +\x1b[1;31m==== TESTS FAILED ====\x1b[0m\n\ +Testing \x1b[36m%s\x1b[0m failed on the following cases:\n\ +", _test_desc.desc); + } + failed = true; + fprintf(stderr, "[%02d/%02d] %s\n", thistest, _ntests, t->desc); + } + } +#ifdef _WIN32 + SetConsoleMode(con, conmode); +#endif + return !!failed; +} + +// get any normal main()s out the way in the tested code +#define main _main + +static struct _test_desc _test_desc = // user input follows this header + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/tools/todo b/tools/todo new file mode 100644 index 0000000..7903eb3 --- /dev/null +++ b/tools/todo @@ -0,0 +1,5 @@ +#!/bin/sh -e +# This file is dedicated to the public domain. + +. "$0.sh" +todo "$@" diff --git a/tools/todo.sh b/tools/todo.sh new file mode 100644 index 0000000..44c7929 --- /dev/null +++ b/tools/todo.sh @@ -0,0 +1,35 @@ +# This file is dedicated to the public domain. + +todo() { + if [ $# = 0 ]; then + printf "Active TODO list:\n\n" + ls TODO/ | { + while read _l; do + printf " * %s: " "$_l" + head -n1 "TODO/$_l" + done + } + return + fi + if [ $# != 1 ]; then + echo "expected 0 or 1 argument(s)" + return + fi + if [ -f "TODO/$1" ]; then + printf "Active TODO item: " + _f="TODO/$1" + elif [ -f "TODO/.$1" ]; then + printf "Inactive TODO item: " + _f="TODO/.$1" + else + echo "TODO item not found: $1" + return + fi + head -n1 "$_f" + printf "\n" + sed -n '/^====$/,$p' "$_f" + printf "====\n\nMentions in project:\n" + git grep -Fn "TODO($1)" || echo "" +} + +# vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/tools/todo.vim b/tools/todo.vim new file mode 100644 index 0000000..04c704d --- /dev/null +++ b/tools/todo.vim @@ -0,0 +1,41 @@ +" This file is dedicated to the public domain. + +function! Todo(...) + if exists("a:1") && a:1 != "" + copen + " cex "" | cadde to avoid insta-jumping + if &shell == "cmd.exe" + cex "" | cadde system("git grep -nF TODO(".shellescape(a:1).")") + else + cex "" | cadde system("git grep -nF \"TODO(\"".shellescape(a:1)."\\)") + endif + else + " just displaying like this for now... + if &shell == "cmd.exe" + " FIXME: write a Windows batch equivalent!? in the meantime, you + " need a Unix shell to track issues :^) + cex "" | cclose | !sh tools/todo + else + cex "" | cclose | !tools/todo + endif + endif +endfunction + +function! TodoEdit(...) + if exists("a:1") && a:1 != "" + exec "tabe TODO/".a:1 + if line('$') == 1 && getline(1) == '' + normal o==== + 1 + else + 3 " XXX should really search for ====, but this is fine for now + endif + else + echoerr "Specify an issue ID" + endif +endfunction + +command! -nargs=? Todo call Todo("") +command! -nargs=? TodoEdit call TodoEdit("") + +" vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3