From 95b142e4e5346648437f9022d313929a023a5ad0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 23 Dec 2020 23:42:56 -0800 Subject: [PATCH] Make minor improvements --- dsp/scale/gyarados.c | 1 + examples/unbourne.c | 3 + libc/bits/bits.h | 13 +- libc/bits/bswap.h | 36 +- libc/bits/popcnt.c | 1 - libc/bits/popcnt.h | 2 +- libc/bits/pushpop.h | 62 +- libc/calls/calls.h | 8 +- libc/calls/clock_gettime.c | 2 +- libc/calls/isdebuggerpresent.c | 2 +- libc/calls/sigaction.c | 10 +- libc/calls/unlink_s.c | 2 +- libc/fmt/conv.h | 1 - libc/fmt/dirname.c | 4 + libc/fmt/leb128.h | 11 + libc/fmt/pflink.h | 2 +- libc/fmt/sleb128.c | 39 + libc/fmt/strerror.c | 2 +- libc/fmt/unsleb128.c | 6 +- libc/integral/c.inc | 52 +- libc/intrin/mpsadbw.h | 5 +- libc/intrin/palignr.h | 59 +- libc/intrin/pslldq.h | 43 +- libc/intrin/psrldq.h | 43 +- libc/log/log.h | 5 +- libc/macros-cpp.internal.inc | 21 +- libc/macros.h | 5 +- libc/macros.internal.inc | 20 + libc/nexgen32e/crc32.h | 2 +- libc/nexgen32e/crc32c-sse42.c | 2 +- libc/nexgen32e/memmove.inc | 467 -- libc/nexgen32e/strlen.S | 52 + libc/sock/bind.c | 4 +- libc/sock/connect-sysv.c | 3 +- libc/sock/sendto.c | 2 +- libc/stdio/fputc.c | 24 +- libc/stdio/fputcfb.c | 53 + libc/stdio/fputs.c | 2 +- libc/stdio/fread.c | 4 +- libc/stdio/g_stdbuf.c | 4 +- libc/stdio/stdio.h | 2 +- libc/str/chomp.c | 2 +- libc/str/decodentsutf16.c | 3 + libc/str/hextoint.c | 4 + libc/str/isgraph.c | 3 + libc/str/isprint.c | 3 + libc/str/ispunct.c | 3 + libc/str/mbtowc.c | 2 +- libc/str/str.h | 67 +- libc/str/strlcpy.c | 3 +- libc/str/strsignal.c | 2 +- libc/str/tpdecodecb.internal.h | 10 +- libc/str/tpencode.ncabi.c | 2 + libc/str/utf16.h | 15 +- libc/testlib/ugly.h | 6 +- libc/x/x.h | 1 + libc/x/xdirname.c | 28 + libc/x/xjoinpaths.c | 17 +- test/libc/fmt/basename_test.c | 10 - test/libc/fmt/dirname_test.c | 34 + test/libc/stdio/fputs_test.c | 33 + test/libc/stdio/fread_test.c | 36 + libc/str/strlen.c => test/libc/str/bsr_test.c | 47 +- test/libc/str/strlen_test.c | 8 +- test/libc/x/xjoinpaths_test.c | 31 + test/net/http/uricspn_test.c | 2 +- third_party/chibicc/README.cosmo | 1 + third_party/chibicc/as.c | 3858 +++++++++++++---- third_party/chibicc/asm.c | 3 +- third_party/chibicc/chibicc.c | 8 +- third_party/chibicc/chibicc.h | 5 +- third_party/chibicc/codegen.c | 13 +- third_party/chibicc/hashmap.c | 3 +- third_party/chibicc/hog.s | 34 - third_party/chibicc/parse.c | 15 +- third_party/chibicc/preprocess.c | 2 +- third_party/chibicc/test/bitfield_test.c | 23 + third_party/chibicc/test/dce_test.c | 50 +- third_party/chibicc/test/initializer_test.c | 802 ---- third_party/chibicc/test/sizeof_test.c | 4 + third_party/chibicc/tokenize.c | 2 +- third_party/chibicc/type.c | 4 +- third_party/compiler_rt/clear_cache.c | 183 - third_party/compiler_rt/trampoline_setup.c | 51 - third_party/duktape/duk_config.h | 4 +- tool/build/build.mk | 1 - tool/build/lib/elfwriter.c | 18 +- tool/build/lib/elfwriter.h | 1 + tool/build/lib/interner.c | 6 +- tool/build/lib/iovs.c | 2 +- tool/build/mkdeps.c | 2 +- tool/decode/elf.c | 20 +- tool/emacs/c.lang | 3 - tool/emacs/cosmo-c-builtins.el | 5 +- tool/emacs/cosmo-stuff.el | 2 +- 95 files changed, 3818 insertions(+), 2760 deletions(-) create mode 100644 libc/fmt/leb128.h create mode 100644 libc/fmt/sleb128.c delete mode 100644 libc/nexgen32e/memmove.inc create mode 100644 libc/nexgen32e/strlen.S create mode 100644 libc/stdio/fputcfb.c create mode 100644 libc/x/xdirname.c create mode 100644 test/libc/fmt/dirname_test.c create mode 100644 test/libc/stdio/fputs_test.c create mode 100644 test/libc/stdio/fread_test.c rename libc/str/strlen.c => test/libc/str/bsr_test.c (74%) create mode 100644 test/libc/x/xjoinpaths_test.c delete mode 100644 third_party/chibicc/hog.s delete mode 100644 third_party/chibicc/test/initializer_test.c delete mode 100644 third_party/compiler_rt/clear_cache.c delete mode 100644 third_party/compiler_rt/trampoline_setup.c diff --git a/dsp/scale/gyarados.c b/dsp/scale/gyarados.c index 873e9fc9..6e3957ab 100644 --- a/dsp/scale/gyarados.c +++ b/dsp/scale/gyarados.c @@ -74,6 +74,7 @@ static struct SamplingSolution *NewSamplingSolution(long n, long s) { ss->indices = xcalloc(n * s, sizeof(short)); return ss; } + static bool IsNormalized(int n, double A[n]) { int i; double x; diff --git a/examples/unbourne.c b/examples/unbourne.c index c3c28735..cd4a058a 100644 --- a/examples/unbourne.c +++ b/examples/unbourne.c @@ -144,6 +144,9 @@ #include "third_party/gdtoa/gdtoa.h" #include "third_party/musl/passwd.h" +#define likely(expr) __builtin_expect(!!(expr), 1) +#define unlikely(expr) __builtin_expect(!!(expr), 0) + #undef CEOF #undef rflag diff --git a/libc/bits/bits.h b/libc/bits/bits.h index b883bb44..f87dddfc 100644 --- a/libc/bits/bits.h +++ b/libc/bits/bits.h @@ -263,11 +263,12 @@ unsigned long hamming(unsigned long, unsigned long) pureconst; * @return LOCALVAR[0] * @see xchg() */ -#define lockxchg(MEMORY, LOCALVAR) \ - ({ \ - static_assert(typescompatible(typeof(*(MEMORY)), typeof(*(LOCALVAR)))); \ - asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \ - *(LOCALVAR); \ +#define lockxchg(MEMORY, LOCALVAR) \ + ({ \ + _Static_assert( \ + __builtin_types_compatible_p(typeof(*(MEMORY)), typeof(*(LOCALVAR)))); \ + asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \ + *(LOCALVAR); \ }) /** @@ -376,7 +377,7 @@ unsigned long hamming(unsigned long, unsigned long) pureconst; #define __BitOp(OP, BIT, MEM) \ ({ \ bool OldBit; \ - if (isconstant(BIT)) { \ + if (__builtin_constant_p(BIT)) { \ asm(CFLAG_ASM(OP "%z1\t%2,%1") \ : CFLAG_CONSTRAINT(OldBit), \ "+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \ diff --git a/libc/bits/bswap.h b/libc/bits/bswap.h index 6bde2bc9..1dad0e4b 100644 --- a/libc/bits/bswap.h +++ b/libc/bits/bswap.h @@ -8,39 +8,9 @@ uint32_t bswap_32(uint32_t) pureconst; uint32_t bswap_64(uint32_t) pureconst; #if defined(__GNUC__) && !defined(__STRICT_ANSI__) - -#define bswap_16(U16) \ - (isconstant(U16) ? ((((U16)&0xff00) >> 010) | (((U16)&0x00ff) << 010)) : ({ \ - uint16_t Swapped16, Werd16 = (U16); \ - asm("xchg\t%b0,%h0" : "=Q"(Swapped16) : "0"(Werd16)); \ - Swapped16; \ - })) - -#define bswap_32(U32) \ - (isconstant(U32) \ - ? ((((U32)&0xff000000) >> 030) | (((U32)&0x000000ff) << 030) | \ - (((U32)&0x00ff0000) >> 010) | (((U32)&0x0000ff00) << 010)) \ - : ({ \ - uint32_t Swapped32, Werd32 = (U32); \ - asm("bswap\t%0" : "=r"(Swapped32) : "0"(Werd32)); \ - Swapped32; \ - })) - -#define bswap_64(U64) \ - (isconstant(U64) ? ((((U64)&0xff00000000000000ul) >> 070) | \ - (((U64)&0x00000000000000fful) << 070) | \ - (((U64)&0x00ff000000000000ul) >> 050) | \ - (((U64)&0x000000000000ff00ul) << 050) | \ - (((U64)&0x0000ff0000000000ul) >> 030) | \ - (((U64)&0x0000000000ff0000ul) << 030) | \ - (((U64)&0x000000ff00000000ul) >> 010) | \ - (((U64)&0x00000000ff000000ul) << 010)) \ - : ({ \ - uint64_t Swapped64, Werd64 = (U64); \ - asm("bswap\t%0" : "=r"(Swapped64) : "0"(Werd64)); \ - Swapped64; \ - })) - +#define bswap_16(x) __builtin_bswap16(x) +#define bswap_32(x) __builtin_bswap32(x) +#define bswap_64(x) __builtin_bswap64(x) #endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */ COSMOPOLITAN_C_END_ diff --git a/libc/bits/popcnt.c b/libc/bits/popcnt.c index 3e4a8524..02619b97 100644 --- a/libc/bits/popcnt.c +++ b/libc/bits/popcnt.c @@ -20,7 +20,6 @@ #include "libc/bits/popcnt.h" uint64_t(popcnt)(uint64_t x) { - uint32_t r; x = x - ((x >> 1) & 0x5555555555555555); x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f; diff --git a/libc/bits/popcnt.h b/libc/bits/popcnt.h index e3f95540..c6018224 100644 --- a/libc/bits/popcnt.h +++ b/libc/bits/popcnt.h @@ -8,7 +8,7 @@ unsigned long popcnt(unsigned long) pureconst; #if defined(__GNUC__) && !defined(__STRICT_ANSI__) #define popcnt(X) \ - (isconstant(X) ? __builtin_popcountll(X) : ({ \ + (__builtin_constant_p(X) ? __builtin_popcountll(X) : ({ \ unsigned long Res, Pop = (X); \ if (X86_HAVE(POPCNT)) { \ asm("popcnt\t%1,%0" : "=r"(Res) : "r"(Pop) : "cc"); \ diff --git a/libc/bits/pushpop.h b/libc/bits/pushpop.h index 16656c7d..57c223fa 100644 --- a/libc/bits/pushpop.h +++ b/libc/bits/pushpop.h @@ -10,42 +10,44 @@ #if !defined(__GNUC__) || defined(__STRICT_ANSI__) #define pushpop(x) (x) #else -#define pushpop(x) \ - ({ \ - typeof(x) Popped; \ - if (isconstant(x) && (TYPE_SIGNED(typeof(x)) ? (intptr_t)(x) + 128 < 256 \ - : (intptr_t)(x) < 128)) { \ - if (x) { \ - asm("push\t%1\n\t" \ - "pop\t%q0" \ - : "=r"(Popped) \ - : "ir"(x)); \ - } else { \ - asm("xor\t%k0,%k0" : "=r"(Popped)); \ - } \ - } else { \ - asm("" : "=r"(Popped) : "0"(x)); \ - } \ - Popped; \ +#define pushpop(x) \ + ({ \ + typeof(x) Popped; \ + if (__builtin_constant_p(x) && \ + (TYPE_SIGNED(typeof(x)) ? (intptr_t)(x) + 128 < 256 \ + : (intptr_t)(x) < 128)) { \ + if (x) { \ + asm("push\t%1\n\t" \ + "pop\t%q0" \ + : "=r"(Popped) \ + : "ir"(x)); \ + } else { \ + asm("xor\t%k0,%k0" : "=r"(Popped)); \ + } \ + } else { \ + asm("" : "=r"(Popped) : "0"(x)); \ + } \ + Popped; \ }) #endif #if !defined(__GNUC__) || defined(__STRICT_ANSI__) #define pushmov(d, x) (*(d) = (x)) #else -#define pushmov(d, x) \ - ({ \ - typeof(*(d)) Popped = (x); \ - if (isconstant(x) && (TYPE_SIGNED(typeof(x)) ? (intptr_t)(x) + 128 < 256 \ - : (intptr_t)(x) < 128)) { \ - asm("pushq\t%1\n\t" \ - "popq\t%0" \ - : "=m"(*(d)) \ - : "ir"(Popped)); \ - } else { \ - *(d) = Popped; \ - } \ - Popped; \ +#define pushmov(d, x) \ + ({ \ + typeof(*(d)) Popped = (x); \ + if (__builtin_constant_p(x) && \ + (TYPE_SIGNED(typeof(x)) ? (intptr_t)(x) + 128 < 256 \ + : (intptr_t)(x) < 128)) { \ + asm("pushq\t%1\n\t" \ + "popq\t%0" \ + : "=m"(*(d)) \ + : "ir"(Popped)); \ + } else { \ + *(d) = Popped; \ + } \ + Popped; \ }) #endif diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 0f4e3fca..08e8179f 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -227,9 +227,9 @@ uint32_t gettid(void) nosideeffect; uint32_t getuid(void) nosideeffect; uint32_t umask(int32_t); -#define getcwd(BUF, SIZE) \ - (isconstant(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \ - : getcwd(BUF, SIZE)) +#define getcwd(BUF, SIZE) \ + (__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \ + : getcwd(BUF, SIZE)) /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § system calls » formatting ─╬─│┼ @@ -249,7 +249,7 @@ void _init_wincrash(void); #define __SIGACTION(FN, SIG, ...) \ ({ \ if (SupportsWindows()) { \ - if (isconstant(SIG)) { \ + if (__builtin_constant_p(SIG)) { \ switch (SIG) { \ case SIGINT: \ case SIGQUIT: \ diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 24e2a65f..bb011355 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -63,7 +63,7 @@ int clock_gettime(int clockid, struct timespec *out_ts) { return clock_gettime$sysv(clockid, out_ts); } else { int rc; - static_assert(sizeof(struct timeval) == sizeof(struct timespec)); + _Static_assert(sizeof(struct timeval) == sizeof(struct timespec)); if (out_ts) { out_ts->tv_sec = 0; out_ts->tv_nsec = 0; diff --git a/libc/calls/isdebuggerpresent.c b/libc/calls/isdebuggerpresent.c index c923902c..0232daf0 100644 --- a/libc/calls/isdebuggerpresent.c +++ b/libc/calls/isdebuggerpresent.c @@ -33,7 +33,7 @@ #define kBufSize 1024 #define kProcStatus "/proc/self/status" -alignas(16) static const char kGdbPid[] = "TracerPid:\t"; +_Alignas(16) static const char kGdbPid[] = "TracerPid:\t"; /** * Determines if gdb, strace, windbg, etc. is controlling process. diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 1b2ffe46..18f92556 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -121,11 +121,11 @@ static void sigaction$native2cosmo(union metasigaction *sa) { * @asyncsignalsafe */ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { - static_assert(sizeof(struct sigaction) > sizeof(struct sigaction$linux) && - sizeof(struct sigaction) > sizeof(struct sigaction$xnu_in) && - sizeof(struct sigaction) > sizeof(struct sigaction$xnu_out) && - sizeof(struct sigaction) > sizeof(struct sigaction$freebsd) && - sizeof(struct sigaction) > sizeof(struct sigaction$openbsd)); + _Static_assert(sizeof(struct sigaction) > sizeof(struct sigaction$linux) && + sizeof(struct sigaction) > sizeof(struct sigaction$xnu_in) && + sizeof(struct sigaction) > sizeof(struct sigaction$xnu_out) && + sizeof(struct sigaction) > sizeof(struct sigaction$freebsd) && + sizeof(struct sigaction) > sizeof(struct sigaction$openbsd)); int rc, rva, oldrva; struct sigaction *ap, copy; if (!(0 < sig && sig < NSIG) || sig == SIGKILL || sig == SIGSTOP) { diff --git a/libc/calls/unlink_s.c b/libc/calls/unlink_s.c index ea9f5464..bc07c68a 100644 --- a/libc/calls/unlink_s.c +++ b/libc/calls/unlink_s.c @@ -21,7 +21,7 @@ #include "libc/calls/calls.h" /** - * Deletes file, the Cosmopolitan way. + * Deletes file. * * The caller's variable is made NULL. Note that we define unlink(NULL) * as a no-op. diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index 0b568cfe..231c3363 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -19,7 +19,6 @@ long labs(long) libcesque pureconst; long long llabs(long long) libcesque pureconst; char *ltpcpy(char *, long) paramsnonnull() libcesque nocallback; int llog10(unsigned long) libcesque pureconst; -int unsleb128(const void *, size_t, int64_t *); int atoi(const char *) paramsnonnull() libcesque; long atol(const char *) paramsnonnull() libcesque; long long atoll(const char *) paramsnonnull() libcesque; diff --git a/libc/fmt/dirname.c b/libc/fmt/dirname.c index 56bd8b34..6ab72be2 100644 --- a/libc/fmt/dirname.c +++ b/libc/fmt/dirname.c @@ -22,6 +22,10 @@ #define ISDELIM(c) (c == '/' || c == '\\' || c == '.') +/** + * Returns directory portion of path. + * @param s is mutated + */ char *dirname(char *s) { size_t i, n; if (!(n = strlen(s))) return s; diff --git a/libc/fmt/leb128.h b/libc/fmt/leb128.h new file mode 100644 index 00000000..13e4ebeb --- /dev/null +++ b/libc/fmt/leb128.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_FMT_LEB128_H_ +#define COSMOPOLITAN_LIBC_FMT_LEB128_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int sleb128(const void *, size_t, int128_t); +int unsleb128(const void *, size_t, int128_t *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_FMT_LEB128_H_ */ diff --git a/libc/fmt/pflink.h b/libc/fmt/pflink.h index d92a33c2..881d11eb 100644 --- a/libc/fmt/pflink.h +++ b/libc/fmt/pflink.h @@ -46,7 +46,7 @@ #define ___PFLINK(FMT, FN, C) 1 #else #define ___PFLINK(FMT, FN, C) \ - !isconstant(FMT) || ((FMT) && __builtin_##FN(FMT, C) != NULL) + !__builtin_constant_p(FMT) || ((FMT) && __builtin_##FN(FMT, C) != NULL) #endif #if defined(__GNUC__) && __GNUC__ < 6 diff --git a/libc/fmt/sleb128.c b/libc/fmt/sleb128.c new file mode 100644 index 00000000..66c21b96 --- /dev/null +++ b/libc/fmt/sleb128.c @@ -0,0 +1,39 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/leb128.h" + +/** + * Encodes sleb-128 signed integer. + */ +int sleb128(const void *buf, size_t size, int128_t x) { + int c; + unsigned i; + for (i = 0; i < size; ++i) { + c = x & 0x7f; + x >>= 7; + if ((x == 0 && !(c & 0x40)) || (x == -1 && (c & 0x40))) { + break; + } else { + c |= 0x80; + } + ((char *)buf)[i] = c; + } + return i; +} diff --git a/libc/fmt/strerror.c b/libc/fmt/strerror.c index 7077c5d9..6d999989 100644 --- a/libc/fmt/strerror.c +++ b/libc/fmt/strerror.c @@ -24,7 +24,7 @@ * @see strerror_r() */ char *strerror(int err) { - alignas(1) static char buf[512]; + _Alignas(1) static char buf[512]; strerror_r(err, buf, sizeof(buf)); return buf; } diff --git a/libc/fmt/unsleb128.c b/libc/fmt/unsleb128.c index 12c1c7c7..e9583321 100644 --- a/libc/fmt/unsleb128.c +++ b/libc/fmt/unsleb128.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" +#include "libc/fmt/leb128.h" /** * Decodes a GNU-style varint from a buffer. @@ -25,9 +25,9 @@ * The GNU Assembler is able to encode numbers this way, since it's used * by the DWARF debug format. */ -int unsleb128(const void *buf, size_t size, int64_t *out) { +int unsleb128(const void *buf, size_t size, int128_t *out) { int b; - int64_t r, w; + int128_t r, w; unsigned char c; const unsigned char *p, *pe; pe = (p = buf) + size; diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 1d4e55ba..f1f51b9b 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -806,36 +806,6 @@ typedef uint64_t uintmax_t; do { \ } while (0) -#ifndef likely -#define likely(expr) __builtin_expect(!!(expr), 1) -#endif - -#ifndef unlikely -#define unlikely(expr) __builtin_expect(!!(expr), 0) -#endif - -/** - * Evaluates ternary expression without type promotion. - */ -#ifndef chooseexpr -#define chooseexpr(pred, a, b) __builtin_choose_expr(pred, a, b) -#endif - -/** - * Returns true if expression can be evaluated at compile-time. - */ -#ifndef isconstant -#define isconstant(expr) __builtin_constant_p(expr) -#endif - -#ifndef static_assert -#define static_assert(expr) _Static_assert(expr, #expr) -#endif - -#ifndef typescompatible -#define typescompatible(a, b) __builtin_types_compatible_p(a, b) -#endif - #ifndef __STRICT_ANSI__ #define testonly noinline _Section(".test") #define textstartup _Section(".text.startup") noinstrument @@ -873,10 +843,6 @@ typedef uint64_t uintmax_t; #define offsetof(type, member) __builtin_offsetof(type, member) #endif -#ifndef alignas -#define alignas(x) _Alignas(x) -#endif - #ifndef _Section #ifndef __STRICT_ANSI__ #define _Section(s) __attribute__((__section__(s))) @@ -1029,15 +995,15 @@ typedef uint64_t uintmax_t; * Pulls another module, by symbol, into linkage. * @note nop is discarded by ape/ape.lds */ -#define YOINK(SYMBOL) \ - do { \ - _Static_assert(!typescompatible(typeof(SYMBOL), char[]), \ - "Please YOINK(symbol), not YOINK(\"symbol\")"); \ - asm(".pushsection .yoink\n\t" \ - "nop\t%a0\n\t" \ - ".popsection" \ - : /* no outputs */ \ - : "X"(SYMBOL)); \ +#define YOINK(SYMBOL) \ + do { \ + _Static_assert(!__builtin_types_compatible_p(typeof(SYMBOL), char[]), \ + "Please YOINK(symbol), not YOINK(\"symbol\")"); \ + asm(".pushsection .yoink\n\t" \ + "nop\t%a0\n\t" \ + ".popsection" \ + : /* no outputs */ \ + : "X"(SYMBOL)); \ } while (0) /** diff --git a/libc/intrin/mpsadbw.h b/libc/intrin/mpsadbw.h index f26034e4..33271ab1 100644 --- a/libc/intrin/mpsadbw.h +++ b/libc/intrin/mpsadbw.h @@ -10,11 +10,12 @@ void mpsadbw(uint16_t[8], const uint8_t[16], const uint8_t[16], uint8_t); __intrin_xmm_t __mpsadbws(__intrin_xmm_t, __intrin_xmm_t); #define mpsadbw(C, B, A, I) \ do { \ - if (likely(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE4_1))) { \ + if (__builtin_expect(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE4_1), \ + 1)) { \ __intrin_xmm_t *Xmm0 = (void *)(C); \ const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \ const __intrin_xmm_t *Xmm2 = (const __intrin_xmm_t *)(A); \ - if (isconstant(I)) { \ + if (__builtin_constant_p(I)) { \ if (!X86_NEED(AVX)) { \ asm("mpsadbw\t%2,%1,%0" \ : "=x"(*Xmm0) \ diff --git a/libc/intrin/palignr.h b/libc/intrin/palignr.h index 638ac166..55659bd0 100644 --- a/libc/intrin/palignr.h +++ b/libc/intrin/palignr.h @@ -9,35 +9,36 @@ void palignr(void *, const void *, const void *, unsigned long); #if !defined(__STRICT_ANSI__) && !defined(__chibicc__) __intrin_xmm_t __palignrs(__intrin_xmm_t, __intrin_xmm_t); -#define palignr(C, B, A, I) \ - do { \ - if (likely(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSSE3))) { \ - __intrin_xmm_t *Xmm0 = (void *)(C); \ - const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \ - const __intrin_xmm_t *Xmm2 = (const __intrin_xmm_t *)(A); \ - if (isconstant(I)) { \ - if (!X86_NEED(AVX)) { \ - asm("palignr\t%2,%1,%0" \ - : "=x"(*Xmm0) \ - : "x"(*Xmm2), "i"(I), "0"(*Xmm1)); \ - } else { \ - asm("vpalignr\t%3,%2,%1,%0" \ - : "=x"(*Xmm0) \ - : "x"(*Xmm1), "x"(*Xmm2), "i"(I)); \ - } \ - } else { \ - unsigned long Vimm = (I); \ - typeof(__palignrs) *Fn; \ - if (likely(Vimm < 32)) { \ - Fn = (typeof(__palignrs) *)((uintptr_t)&__palignrs + Vimm * 8); \ - *Xmm0 = Fn(*Xmm1, *Xmm2); \ - } else { \ - memset(Xmm0, 0, 16); \ - } \ - } \ - } else { \ - palignr(C, B, A, I); \ - } \ +#define palignr(C, B, A, I) \ + do { \ + if (__builtin_expect(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSSE3), \ + 1)) { \ + __intrin_xmm_t *Xmm0 = (void *)(C); \ + const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \ + const __intrin_xmm_t *Xmm2 = (const __intrin_xmm_t *)(A); \ + if (__builtin_constant_p(I)) { \ + if (!X86_NEED(AVX)) { \ + asm("palignr\t%2,%1,%0" \ + : "=x"(*Xmm0) \ + : "x"(*Xmm2), "i"(I), "0"(*Xmm1)); \ + } else { \ + asm("vpalignr\t%3,%2,%1,%0" \ + : "=x"(*Xmm0) \ + : "x"(*Xmm1), "x"(*Xmm2), "i"(I)); \ + } \ + } else { \ + unsigned long Vimm = (I); \ + typeof(__palignrs) *Fn; \ + if (__builtin_expect(Vimm < 32, 1)) { \ + Fn = (typeof(__palignrs) *)((uintptr_t)&__palignrs + Vimm * 8); \ + *Xmm0 = Fn(*Xmm1, *Xmm2); \ + } else { \ + memset(Xmm0, 0, 16); \ + } \ + } \ + } else { \ + palignr(C, B, A, I); \ + } \ } while (0) #endif diff --git a/libc/intrin/pslldq.h b/libc/intrin/pslldq.h index d7c19b3a..aa951a90 100644 --- a/libc/intrin/pslldq.h +++ b/libc/intrin/pslldq.h @@ -8,27 +8,28 @@ void pslldq(uint8_t[16], const uint8_t[16], unsigned long); #ifndef __STRICT_ANSI__ __intrin_xmm_t __pslldqs(__intrin_xmm_t); -#define pslldq(B, A, I) \ - do { \ - if (likely(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE2))) { \ - __intrin_xmm_t *Xmm0 = (void *)(B); \ - const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(A); \ - if (isconstant(I)) { \ - if (!X86_NEED(AVX)) { \ - asm("pslldq\t%1,%0" : "=x"(*Xmm0) : "i"(I), "0"(*Xmm1)); \ - } else { \ - asm("vpslldq\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \ - } \ - } else { \ - unsigned long Vimm = (I); \ - typeof(__pslldqs) *Fn; \ - if (Vimm > 16) Vimm = 16; \ - Fn = (typeof(__pslldqs) *)((uintptr_t)&__pslldqs + Vimm * 8); \ - *Xmm0 = Fn(*Xmm1); \ - } \ - } else { \ - pslldq(B, A, I); \ - } \ +#define pslldq(B, A, I) \ + do { \ + if (__builtin_expect(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE2), \ + 1)) { \ + __intrin_xmm_t *Xmm0 = (void *)(B); \ + const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(A); \ + if (__builtin_constant_p(I)) { \ + if (!X86_NEED(AVX)) { \ + asm("pslldq\t%1,%0" : "=x"(*Xmm0) : "i"(I), "0"(*Xmm1)); \ + } else { \ + asm("vpslldq\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \ + } \ + } else { \ + unsigned long Vimm = (I); \ + typeof(__pslldqs) *Fn; \ + if (Vimm > 16) Vimm = 16; \ + Fn = (typeof(__pslldqs) *)((uintptr_t)&__pslldqs + Vimm * 8); \ + *Xmm0 = Fn(*Xmm1); \ + } \ + } else { \ + pslldq(B, A, I); \ + } \ } while (0) #endif diff --git a/libc/intrin/psrldq.h b/libc/intrin/psrldq.h index 534ce5fd..15345409 100644 --- a/libc/intrin/psrldq.h +++ b/libc/intrin/psrldq.h @@ -8,27 +8,28 @@ void psrldq(uint8_t[16], const uint8_t[16], unsigned long); #ifndef __STRICT_ANSI__ __intrin_xmm_t __psrldqs(__intrin_xmm_t); -#define psrldq(B, A, I) \ - do { \ - if (likely(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE2))) { \ - __intrin_xmm_t *Xmm0 = (void *)(B); \ - const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(A); \ - if (isconstant(I)) { \ - if (!X86_NEED(AVX)) { \ - asm("psrldq\t%1,%0" : "=x"(*Xmm0) : "i"(I), "0"(*Xmm1)); \ - } else { \ - asm("vpsrldq\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \ - } \ - } else { \ - unsigned long Vimm = (I); \ - typeof(__psrldqs) *Fn; \ - if (Vimm > 16) Vimm = 16; \ - Fn = (typeof(__psrldqs) *)((uintptr_t)&__psrldqs + Vimm * 8); \ - *Xmm0 = Fn(*Xmm1); \ - } \ - } else { \ - psrldq(B, A, I); \ - } \ +#define psrldq(B, A, I) \ + do { \ + if (__builtin_expect(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSE2), \ + 1)) { \ + __intrin_xmm_t *Xmm0 = (void *)(B); \ + const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(A); \ + if (__builtin_constant_p(I)) { \ + if (!X86_NEED(AVX)) { \ + asm("psrldq\t%1,%0" : "=x"(*Xmm0) : "i"(I), "0"(*Xmm1)); \ + } else { \ + asm("vpsrldq\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \ + } \ + } else { \ + unsigned long Vimm = (I); \ + typeof(__psrldqs) *Fn; \ + if (Vimm > 16) Vimm = 16; \ + Fn = (typeof(__psrldqs) *)((uintptr_t)&__psrldqs + Vimm * 8); \ + *Xmm0 = Fn(*Xmm1); \ + } \ + } else { \ + psrldq(B, A, I); \ + } \ } while (0) #endif diff --git a/libc/log/log.h b/libc/log/log.h index a873b8a4..a5d68d64 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -56,8 +56,9 @@ bool isrunningundermake(void); extern unsigned g_loglevel; /* log level for runtime check */ -#define LOGGABLE(LEVEL) \ - ((!isconstant(LEVEL) || (LEVEL) <= LOGGABLELEVEL) && (LEVEL) <= g_loglevel) +#define LOGGABLE(LEVEL) \ + ((!__builtin_constant_p(LEVEL) || (LEVEL) <= LOGGABLELEVEL) && \ + (LEVEL) <= g_loglevel) #define LOGF(FMT, ...) \ do { \ diff --git a/libc/macros-cpp.internal.inc b/libc/macros-cpp.internal.inc index 8eb5e602..71192841 100644 --- a/libc/macros-cpp.internal.inc +++ b/libc/macros-cpp.internal.inc @@ -1,5 +1,24 @@ -/* clang-format off */ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/relocations.h" +/* clang-format off */ #if __MNO_VZEROUPPER__ + 0 #define vzeroupper diff --git a/libc/macros.h b/libc/macros.h index d3b242a8..8f2f8d74 100644 --- a/libc/macros.h +++ b/libc/macros.h @@ -13,6 +13,9 @@ #define TRUE 1 #define FALSE 0 +#define alignas(x) _Alignas(x) +#define static_assert(x) _Static_assert(x, #x) + #define ROUNDUP(X, K) (((X) + (K)-1) & -(K)) #define ROUNDDOWN(X, K) ((X) & -(K)) #define ABS(X) ((X) >= 0 ? (X) : -(X)) @@ -20,7 +23,7 @@ #define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) #define PASTE(A, B) __PASTE(A, B) #define STRINGIFY(A) __STRINGIFY(A) -#define EQUIVALENT(X, Y) (isconstant((X) == (Y)) && ((X) == (Y))) +#define EQUIVALENT(X, Y) (__builtin_constant_p((X) == (Y)) && ((X) == (Y))) #define TYPE_BIT(type) (sizeof(type) * CHAR_BIT) #define TYPE_SIGNED(type) (((type)-1) < 0) #define TYPE_INTEGRAL(type) (((type)0.5) != 0.5) diff --git a/libc/macros.internal.inc b/libc/macros.internal.inc index d0bf11d2..9aaccaed 100644 --- a/libc/macros.internal.inc +++ b/libc/macros.internal.inc @@ -1,3 +1,23 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ + / Shorthand notation for widely-acknowledged sections. .macro .rodata .section .rodata,"a",@progbits diff --git a/libc/nexgen32e/crc32.h b/libc/nexgen32e/crc32.h index 186eeb2f..0d209012 100644 --- a/libc/nexgen32e/crc32.h +++ b/libc/nexgen32e/crc32.h @@ -5,7 +5,7 @@ COSMOPOLITAN_C_START_ void crc32init(uint32_t[hasatleast 256], uint32_t); uint32_t crc32_z(uint32_t, const void *, size_t); -extern uint32_t (*const crc32c)(uint32_t, const void *, size_t) paramsnonnull(); +extern uint32_t (*const crc32c)(uint32_t, const void *, size_t); uint32_t crc32c$pure(uint32_t, const void *, size_t) strlenesque hidden; uint32_t crc32c$sse42(uint32_t, const void *, size_t) strlenesque hidden; uint32_t crc32$pclmul(uint32_t, const void *, size_t) hidden; diff --git a/libc/nexgen32e/crc32c-sse42.c b/libc/nexgen32e/crc32c-sse42.c index f724f541..dea093f2 100644 --- a/libc/nexgen32e/crc32c-sse42.c +++ b/libc/nexgen32e/crc32c-sse42.c @@ -23,7 +23,7 @@ * Hashes data with hardware acceleration at 10GBps. * @note needs Nehalem+ c. 2008 or Bulldozer+ c. 2011 */ -uint32_t crc32c$sse42(uint32_t init, const void *data, size_t n) { +optimizespeed uint32_t crc32c$sse42(uint32_t init, const void *data, size_t n) { const unsigned char *p = (const unsigned char *)data; const unsigned char *pe = (const unsigned char *)data + n; uint32_t h = init ^ 0xffffffff; diff --git a/libc/nexgen32e/memmove.inc b/libc/nexgen32e/memmove.inc deleted file mode 100644 index 4356254f..00000000 --- a/libc/nexgen32e/memmove.inc +++ /dev/null @@ -1,467 +0,0 @@ -/* -Copyright (c) 2014, Intel Corporation -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. - - * Neither the name of Intel Corporation nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - -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. -*/ - -.ident "\n -memmove (Licensed BSD-3)\n -Copyright 2014 Intel Corporation" -.include "libc/disclaimer.inc" - -#ifndef L -# define L(label) .L##label -#endif - -#ifndef SHARED_CACHE_SIZE_HALF -#define SHARED_CACHE_SIZE_HALF (4 * 1024 * 1024) -#endif - - push %rbx - push %rdx - push %r8 - push %r9 - -/* Check whether we should copy backward or forward. */ - cmp %rsi, %rdi - je L(mm_return) - jg L(mm_len_0_or_more_backward) - -/* Now do checks for lengths. We do [0..16], [0..32], [0..64], [0..128] - separately. */ - cmp $16, %rdx - jbe L(mm_len_0_16_bytes_forward) - - cmp $32, %rdx - ja L(mm_len_32_or_more_forward) - -/* Copy [0..32] and return. */ - movdqu (%rsi), %xmm0 - movdqu -16(%rsi, %rdx), %xmm1 - movdqu %xmm0, (%rdi) - movdqu %xmm1, -16(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_32_or_more_forward): - cmp $64, %rdx - ja L(mm_len_64_or_more_forward) - -/* Copy [0..64] and return. */ - movdqu (%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu -16(%rsi, %rdx), %xmm2 - movdqu -32(%rsi, %rdx), %xmm3 - movdqu %xmm0, (%rdi) - movdqu %xmm1, 16(%rdi) - movdqu %xmm2, -16(%rdi, %rdx) - movdqu %xmm3, -32(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_64_or_more_forward): - cmp $128, %rdx - ja L(mm_len_128_or_more_forward) - -/* Copy [0..128] and return. */ - movdqu (%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu 32(%rsi), %xmm2 - movdqu 48(%rsi), %xmm3 - movdqu -64(%rsi, %rdx), %xmm4 - movdqu -48(%rsi, %rdx), %xmm5 - movdqu -32(%rsi, %rdx), %xmm6 - movdqu -16(%rsi, %rdx), %xmm7 - movdqu %xmm0, (%rdi) - movdqu %xmm1, 16(%rdi) - movdqu %xmm2, 32(%rdi) - movdqu %xmm3, 48(%rdi) - movdqu %xmm4, -64(%rdi, %rdx) - movdqu %xmm5, -48(%rdi, %rdx) - movdqu %xmm6, -32(%rdi, %rdx) - movdqu %xmm7, -16(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_128_or_more_forward): -/* Aligning the address of destination. */ -/* save first unaligned 64 bytes */ - movdqu (%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu 32(%rsi), %xmm2 - movdqu 48(%rsi), %xmm3 - - lea 64(%rdi), %r8 - and $-64, %r8 /* r8 now aligned to next 64 byte boundary */ - sub %rdi, %rsi /* rsi = src - dst = diff */ - - movdqu (%r8, %rsi), %xmm4 - movdqu 16(%r8, %rsi), %xmm5 - movdqu 32(%r8, %rsi), %xmm6 - movdqu 48(%r8, %rsi), %xmm7 - - movdqu %xmm0, (%rdi) - movdqu %xmm1, 16(%rdi) - movdqu %xmm2, 32(%rdi) - movdqu %xmm3, 48(%rdi) - movdqa %xmm4, (%r8) - movaps %xmm5, 16(%r8) - movaps %xmm6, 32(%r8) - movaps %xmm7, 48(%r8) - add $64, %r8 - - lea (%rdi, %rdx), %rbx - and $-64, %rbx - cmp %r8, %rbx - jbe L(mm_copy_remaining_forward) - - cmp $SHARED_CACHE_SIZE_HALF, %rdx - jae L(mm_large_page_loop_forward) - - .p2align 4 -L(mm_main_loop_forward): - - prefetcht0 128(%r8, %rsi) - - movdqu (%r8, %rsi), %xmm0 - movdqu 16(%r8, %rsi), %xmm1 - movdqu 32(%r8, %rsi), %xmm2 - movdqu 48(%r8, %rsi), %xmm3 - movdqa %xmm0, (%r8) - movaps %xmm1, 16(%r8) - movaps %xmm2, 32(%r8) - movaps %xmm3, 48(%r8) - lea 64(%r8), %r8 - cmp %r8, %rbx - ja L(mm_main_loop_forward) - -L(mm_copy_remaining_forward): - add %rdi, %rdx - sub %r8, %rdx -/* We copied all up till %rdi position in the dst. - In %rdx now is how many bytes are left to copy. - Now we need to advance %r8. */ - lea (%r8, %rsi), %r9 - -L(mm_remaining_0_64_bytes_forward): - cmp $32, %rdx - ja L(mm_remaining_33_64_bytes_forward) - cmp $16, %rdx - ja L(mm_remaining_17_32_bytes_forward) - test %rdx, %rdx - .p2align 4,,2 - je L(mm_return) - - cmpb $8, %dl - ja L(mm_remaining_9_16_bytes_forward) - cmpb $4, %dl - .p2align 4,,5 - ja L(mm_remaining_5_8_bytes_forward) - cmpb $2, %dl - .p2align 4,,1 - ja L(mm_remaining_3_4_bytes_forward) - movzbl -1(%r9,%rdx), %esi - movzbl (%r9), %ebx - movb %sil, -1(%r8,%rdx) - movb %bl, (%r8) - jmp L(mm_return) - -L(mm_remaining_33_64_bytes_forward): - movdqu (%r9), %xmm0 - movdqu 16(%r9), %xmm1 - movdqu -32(%r9, %rdx), %xmm2 - movdqu -16(%r9, %rdx), %xmm3 - movdqu %xmm0, (%r8) - movdqu %xmm1, 16(%r8) - movdqu %xmm2, -32(%r8, %rdx) - movdqu %xmm3, -16(%r8, %rdx) - jmp L(mm_return) - -L(mm_remaining_17_32_bytes_forward): - movdqu (%r9), %xmm0 - movdqu -16(%r9, %rdx), %xmm1 - movdqu %xmm0, (%r8) - movdqu %xmm1, -16(%r8, %rdx) - jmp L(mm_return) - -L(mm_remaining_5_8_bytes_forward): - movl (%r9), %esi - movl -4(%r9,%rdx), %ebx - movl %esi, (%r8) - movl %ebx, -4(%r8,%rdx) - jmp L(mm_return) - -L(mm_remaining_9_16_bytes_forward): - mov (%r9), %rsi - mov -8(%r9, %rdx), %rbx - mov %rsi, (%r8) - mov %rbx, -8(%r8, %rdx) - jmp L(mm_return) - -L(mm_remaining_3_4_bytes_forward): - movzwl -2(%r9,%rdx), %esi - movzwl (%r9), %ebx - movw %si, -2(%r8,%rdx) - movw %bx, (%r8) - jmp L(mm_return) - -L(mm_len_0_16_bytes_forward): - testb $24, %dl - jne L(mm_len_9_16_bytes_forward) - testb $4, %dl - .p2align 4,,5 - jne L(mm_len_5_8_bytes_forward) - test %rdx, %rdx - .p2align 4,,2 - je L(mm_return) - testb $2, %dl - .p2align 4,,1 - jne L(mm_len_2_4_bytes_forward) - movzbl -1(%rsi,%rdx), %ebx - movzbl (%rsi), %esi - movb %bl, -1(%rdi,%rdx) - movb %sil, (%rdi) - jmp L(mm_return) - -L(mm_len_2_4_bytes_forward): - movzwl -2(%rsi,%rdx), %ebx - movzwl (%rsi), %esi - movw %bx, -2(%rdi,%rdx) - movw %si, (%rdi) - jmp L(mm_return) - -L(mm_len_5_8_bytes_forward): - movl (%rsi), %ebx - movl -4(%rsi,%rdx), %esi - movl %ebx, (%rdi) - movl %esi, -4(%rdi,%rdx) - jmp L(mm_return) - -L(mm_len_9_16_bytes_forward): - mov (%rsi), %rbx - mov -8(%rsi, %rdx), %rsi - mov %rbx, (%rdi) - mov %rsi, -8(%rdi, %rdx) - jmp L(mm_return) - -L(mm_recalc_len): -/* Compute in %rdx how many bytes are left to copy after - the main loop stops. */ - mov %rbx, %rdx - sub %rdi, %rdx -/* The code for copying backwards. */ -L(mm_len_0_or_more_backward): - -/* Now do checks for lengths. We do [0..16], [16..32], [32..64], [64..128] - separately. */ - cmp $16, %rdx - jbe L(mm_len_0_16_bytes_backward) - - cmp $32, %rdx - ja L(mm_len_32_or_more_backward) - -/* Copy [0..32] and return. */ - movdqu (%rsi), %xmm0 - movdqu -16(%rsi, %rdx), %xmm1 - movdqu %xmm0, (%rdi) - movdqu %xmm1, -16(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_32_or_more_backward): - cmp $64, %rdx - ja L(mm_len_64_or_more_backward) - -/* Copy [0..64] and return. */ - movdqu (%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu -16(%rsi, %rdx), %xmm2 - movdqu -32(%rsi, %rdx), %xmm3 - movdqu %xmm0, (%rdi) - movdqu %xmm1, 16(%rdi) - movdqu %xmm2, -16(%rdi, %rdx) - movdqu %xmm3, -32(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_64_or_more_backward): - cmp $128, %rdx - ja L(mm_len_128_or_more_backward) - -/* Copy [0..128] and return. */ - movdqu (%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu 32(%rsi), %xmm2 - movdqu 48(%rsi), %xmm3 - movdqu -64(%rsi, %rdx), %xmm4 - movdqu -48(%rsi, %rdx), %xmm5 - movdqu -32(%rsi, %rdx), %xmm6 - movdqu -16(%rsi, %rdx), %xmm7 - movdqu %xmm0, (%rdi) - movdqu %xmm1, 16(%rdi) - movdqu %xmm2, 32(%rdi) - movdqu %xmm3, 48(%rdi) - movdqu %xmm4, -64(%rdi, %rdx) - movdqu %xmm5, -48(%rdi, %rdx) - movdqu %xmm6, -32(%rdi, %rdx) - movdqu %xmm7, -16(%rdi, %rdx) - jmp L(mm_return) - -L(mm_len_128_or_more_backward): -/* Aligning the address of destination. We need to save - 16 bits from the source in order not to overwrite them. */ - - movdqu -16(%rsi, %rdx), %xmm0 - movdqu -32(%rsi, %rdx), %xmm1 - movdqu -48(%rsi, %rdx), %xmm2 - movdqu -64(%rsi, %rdx), %xmm3 - - lea (%rdi, %rdx), %r9 - and $-64, %r9 /* r9 = aligned dst */ - - mov %rsi, %r8 - sub %rdi, %r8 /* r8 = src - dst, diff */ - - movdqu -16(%r9, %r8), %xmm4 - movdqu -32(%r9, %r8), %xmm5 - movdqu -48(%r9, %r8), %xmm6 - movdqu -64(%r9, %r8), %xmm7 - - movdqu %xmm0, -16(%rdi, %rdx) - movdqu %xmm1, -32(%rdi, %rdx) - movdqu %xmm2, -48(%rdi, %rdx) - movdqu %xmm3, -64(%rdi, %rdx) - movdqa %xmm4, -16(%r9) - movaps %xmm5, -32(%r9) - movaps %xmm6, -48(%r9) - movaps %xmm7, -64(%r9) - lea -64(%r9), %r9 - - lea 64(%rdi), %rbx - and $-64, %rbx - - cmp %r9, %rbx - jae L(mm_recalc_len) - - cmp $SHARED_CACHE_SIZE_HALF, %rdx - jae L(mm_large_page_loop_backward) - - .p2align 4 -L(mm_main_loop_backward): - - prefetcht0 -128(%r9, %r8) - - movdqu -64(%r9, %r8), %xmm0 - movdqu -48(%r9, %r8), %xmm1 - movdqu -32(%r9, %r8), %xmm2 - movdqu -16(%r9, %r8), %xmm3 - movdqa %xmm0, -64(%r9) - movaps %xmm1, -48(%r9) - movaps %xmm2, -32(%r9) - movaps %xmm3, -16(%r9) - lea -64(%r9), %r9 - cmp %r9, %rbx - jb L(mm_main_loop_backward) - jmp L(mm_recalc_len) - -/* Copy [0..16] and return. */ -L(mm_len_0_16_bytes_backward): - testb $24, %dl - jnz L(mm_len_9_16_bytes_backward) - testb $4, %dl - .p2align 4,,5 - jnz L(mm_len_5_8_bytes_backward) - test %rdx, %rdx - .p2align 4,,2 - je L(mm_return) - testb $2, %dl - .p2align 4,,1 - jne L(mm_len_3_4_bytes_backward) - movzbl -1(%rsi,%rdx), %ebx - movzbl (%rsi), %ecx - movb %bl, -1(%rdi,%rdx) - movb %cl, (%rdi) - jmp L(mm_return) - -L(mm_len_3_4_bytes_backward): - movzwl -2(%rsi,%rdx), %ebx - movzwl (%rsi), %ecx - movw %bx, -2(%rdi,%rdx) - movw %cx, (%rdi) - jmp L(mm_return) - -L(mm_len_9_16_bytes_backward): - movl -4(%rsi,%rdx), %ebx - movl -8(%rsi,%rdx), %ecx - movl %ebx, -4(%rdi,%rdx) - movl %ecx, -8(%rdi,%rdx) - sub $8, %rdx - jmp L(mm_len_0_16_bytes_backward) - -L(mm_len_5_8_bytes_backward): - movl (%rsi), %ebx - movl -4(%rsi,%rdx), %ecx - movl %ebx, (%rdi) - movl %ecx, -4(%rdi,%rdx) - -L(mm_return): - pop %r9 - pop %r8 - pop %rdx - pop %rbx - pop %rbp - ret - -/* Big length copy forward part. */ - - .p2align 4 -L(mm_large_page_loop_forward): - movdqu (%r8, %rsi), %xmm0 - movdqu 16(%r8, %rsi), %xmm1 - movdqu 32(%r8, %rsi), %xmm2 - movdqu 48(%r8, %rsi), %xmm3 - movntdq %xmm0, (%r8) - movntdq %xmm1, 16(%r8) - movntdq %xmm2, 32(%r8) - movntdq %xmm3, 48(%r8) - lea 64(%r8), %r8 - cmp %r8, %rbx - ja L(mm_large_page_loop_forward) - sfence - jmp L(mm_copy_remaining_forward) - -/* Big length copy backward part. */ - .p2align 4 -L(mm_large_page_loop_backward): - movdqu -64(%r9, %r8), %xmm0 - movdqu -48(%r9, %r8), %xmm1 - movdqu -32(%r9, %r8), %xmm2 - movdqu -16(%r9, %r8), %xmm3 - movntdq %xmm0, -64(%r9) - movntdq %xmm1, -48(%r9) - movntdq %xmm2, -32(%r9) - movntdq %xmm3, -16(%r9) - lea -64(%r9), %r9 - cmp %r9, %rbx - jb L(mm_large_page_loop_backward) - sfence - jmp L(mm_recalc_len) diff --git a/libc/nexgen32e/strlen.S b/libc/nexgen32e/strlen.S new file mode 100644 index 00000000..6bf51964 --- /dev/null +++ b/libc/nexgen32e/strlen.S @@ -0,0 +1,52 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.h" + +/ Returns length of NUL-terminated string. +/ +/ @param rdi is non-null NUL-terminated string pointer +/ @return rax is number of bytes (excluding NUL) +/ @clob ax,dx,cx,xmm3,xmm4 +/ @note h/t agner fog +/ @asyncsignalsafe +strlen: .leafprologue + .profilable + mov %rdi,%rax + mov %edi,%ecx + and $15,%ecx + and $-16,%rax + pxor %xmm4,%xmm4 + movdqa (%rax),%xmm3 + pcmpeqb %xmm4,%xmm3 + pmovmskb %xmm3,%edx + shr %cl,%edx + shl %cl,%edx + bsf %edx,%edx + jnz 2f +1: lea 16(%rax),%rax + movdqa (%rax),%xmm3 + pcmpeqb %xmm4,%xmm3 + pmovmskb %xmm3,%edx + bsf %edx,%edx + jz 1b +2: add %rdx,%rax + sub %rdi,%rax + .leafepilogue + .endfn strlen,globl diff --git a/libc/sock/bind.c b/libc/sock/bind.c index e9a33b02..fa804f29 100644 --- a/libc/sock/bind.c +++ b/libc/sock/bind.c @@ -42,8 +42,8 @@ int bind(int fd, const void *addr, uint32_t addrsize) { return bind$sysv(fd, addr, addrsize); } else { struct sockaddr_in$bsd addr2; - static_assert(sizeof(struct sockaddr_in) == - sizeof(struct sockaddr_in$bsd)); + _Static_assert(sizeof(struct sockaddr_in) == + sizeof(struct sockaddr_in$bsd)); memcpy(&addr2, addr, sizeof(struct sockaddr_in)); sockaddr2bsd(&addr2); return bind$sysv(fd, &addr2, addrsize); diff --git a/libc/sock/connect-sysv.c b/libc/sock/connect-sysv.c index b8b188bd..c6578caf 100644 --- a/libc/sock/connect-sysv.c +++ b/libc/sock/connect-sysv.c @@ -28,7 +28,8 @@ int connect$sysv(int fd, const void *addr, uint32_t addrsize) { return __connect$sysv(fd, addr, addrsize); } else { struct sockaddr_in$bsd addr2; - static_assert(sizeof(struct sockaddr_in) == sizeof(struct sockaddr_in$bsd)); + _Static_assert(sizeof(struct sockaddr_in) == + sizeof(struct sockaddr_in$bsd)); memcpy(&addr2, addr, sizeof(struct sockaddr_in)); sockaddr2bsd(&addr2); return connect$sysv(fd, &addr2, addrsize); diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index bf3b18c9..ac49a0bc 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -47,7 +47,7 @@ */ ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags, const void *opt_addr, uint32_t addrsize) { - static_assert(sizeof(struct sockaddr_in) == sizeof(struct sockaddr_in$bsd)); + _Static_assert(sizeof(struct sockaddr_in) == sizeof(struct sockaddr_in$bsd)); if (!IsWindows()) { if (!IsBsd() || !opt_addr) { return sendto$sysv(fd, buf, size, flags, opt_addr, addrsize); diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c index 7f589c42..2ac26e14 100644 --- a/libc/stdio/fputc.c +++ b/libc/stdio/fputc.c @@ -21,14 +21,8 @@ #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" -/** - * Writes byte to stream. - * - * @return c (as unsigned char) if written or -1 w/ errno - */ -noinstrument int fputc(int c, FILE *f) { +static noinline int __fputcg(int c, FILE *f) { if (f->beg < f->size) { - c &= 0xff; f->buf[f->beg++] = c; if (f->beg == f->size || f->bufmode == _IONBF || (f->bufmode == _IOLBF && c == '\n')) { @@ -38,8 +32,22 @@ noinstrument int fputc(int c, FILE *f) { f->beg = 0; } } - return c; + return c & 0xff; } else { return __fseteof(f); } } + +/** + * Writes byte to stream. + * @return c (as unsigned char) if written or -1 w/ errno + * @see putc() if called within loop + */ +noinstrument int fputc(int c, FILE *f) { + if (f->beg + 1 < f->size && f->bufmode == _IOFBF) { + f->buf[f->beg++] = c; + return c & 0xff; + } else { + return __fputcg(c, f); + } +} diff --git a/libc/stdio/fputcfb.c b/libc/stdio/fputcfb.c new file mode 100644 index 00000000..442c5371 --- /dev/null +++ b/libc/stdio/fputcfb.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/stdio/internal.h" +#include "libc/stdio/stdio.h" + +static noinline int slowpath(int c, FILE *f) { + if (f->beg < f->size) { + c &= 0xff; + f->buf[f->beg++] = c; + if (f->beg == f->size) { + if (f->writer) { + if (f->writer(f) == -1) return -1; + } else if (f->beg == f->size) { + f->beg = 0; + } + } + return c; + } else { + return __fseteof(f); + } +} + +/** + * Writes byte to stream. + * + * @return c (as unsigned char) if written or -1 w/ errno + */ +noinstrument int fputcfb(int c, FILE *f) { + if (f->beg + 1 < f->size) { + c &= 0xff; + f->buf[f->beg++] = c; + return c; + } else { + return slowpath(c, f); + } +} diff --git a/libc/stdio/fputs.c b/libc/stdio/fputs.c index 124d206a..0488d126 100644 --- a/libc/stdio/fputs.c +++ b/libc/stdio/fputs.c @@ -38,7 +38,7 @@ int fputs(const char *s, FILE *f) { int i, n, m; n = strlen(s); for (i = 0; i < n; ++i) { - if (fputc(s[i], f) == -1) { + if (putc(s[i], f) == -1) { if (ferror(f) == EINTR) continue; if (feof(f)) errno = f->state = EPIPE; return -1; diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c index e4c7f989..a316d7e7 100644 --- a/libc/stdio/fread.c +++ b/libc/stdio/fread.c @@ -25,7 +25,7 @@ #include "libc/str/internal.h" /** - * Reads data to stream. + * Reads data from stream. * * @param stride specifies the size of individual items * @param count is the number of strides to fetch @@ -36,7 +36,7 @@ size_t fread(void *buf, size_t stride, size_t count, FILE *f) { size_t i, n; unsigned char *p; for (n = stride * count, p = buf, i = 0; i < n; ++i) { - if ((c = fgetc(f)) != -1) { + if ((c = getc(f)) != -1) { p[i] = c & 0xff; } else if (!(i % stride)) { return i / stride; diff --git a/libc/stdio/g_stdbuf.c b/libc/stdio/g_stdbuf.c index e1fc72eb..1d17829e 100644 --- a/libc/stdio/g_stdbuf.c +++ b/libc/stdio/g_stdbuf.c @@ -19,5 +19,5 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/internal.h" -alignas(PAGESIZE) unsigned char g_stdoutbuf[BUFSIZ]; -alignas(PAGESIZE) unsigned char g_stderrbuf[BUFSIZ]; +_Alignas(PAGESIZE) unsigned char g_stdoutbuf[BUFSIZ]; +_Alignas(PAGESIZE) unsigned char g_stderrbuf[BUFSIZ]; diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index c36cfb03..5c600bc5 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -100,8 +100,8 @@ int vfscanf(FILE *, const char *, va_list); │ cosmopolitan § standard i/o » optimizations ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -#define putc(c, f) fputc(c, f) #define getc(f) (f->beg < f->end ? f->buf[f->beg++] : fgetc(f)) +#define putc(c, f) fputc(c, f) #if defined(__GNUC__) && !defined(__STRICT_ANSI__) #define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__) diff --git a/libc/str/chomp.c b/libc/str/chomp.c index 0d27a21c..c01d998f 100644 --- a/libc/str/chomp.c +++ b/libc/str/chomp.c @@ -25,7 +25,7 @@ * @param line is NULL-propagating * @see getline */ -char *(chomp)(char *line) { +char *chomp(char *line) { size_t i; for (i = strlen(line); i--;) { if (line[i] == '\r' || line[i] == '\n') { diff --git a/libc/str/decodentsutf16.c b/libc/str/decodentsutf16.c index c5c00a6e..af54e274 100644 --- a/libc/str/decodentsutf16.c +++ b/libc/str/decodentsutf16.c @@ -20,6 +20,9 @@ #include "libc/str/str.h" #include "libc/str/utf16.h" +/** + * Helps runtime decode UTF-16 with slightly smaller code size. + */ wint_t DecodeNtsUtf16(const char16_t **s) { wint_t x, y; for (;;) { diff --git a/libc/str/hextoint.c b/libc/str/hextoint.c index 03a9e101..1f35e2ce 100644 --- a/libc/str/hextoint.c +++ b/libc/str/hextoint.c @@ -19,6 +19,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Converts ASCII hexadecimal character to integer case-insensitively. + * @return integer or 0 if c ∉ [0-9A-Fa-f] + */ int hextoint(int c) { if ('0' <= c && c <= '9') { return c - '0'; diff --git a/libc/str/isgraph.c b/libc/str/isgraph.c index 94d0b0c4..9fc9ba29 100644 --- a/libc/str/isgraph.c +++ b/libc/str/isgraph.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Returns nonzero if c is printable ascii that isn't space. + */ int isgraph(int c) { return 0x21 <= c && c <= 0x7E; } diff --git a/libc/str/isprint.c b/libc/str/isprint.c index 5e091465..c2cff39b 100644 --- a/libc/str/isprint.c +++ b/libc/str/isprint.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Returns nonzero if c is printable ascii including space. + */ int isprint(int c) { return 0x20 <= c && c <= 0x7E; } diff --git a/libc/str/ispunct.c b/libc/str/ispunct.c index 7ae42d2c..281eacc2 100644 --- a/libc/str/ispunct.c +++ b/libc/str/ispunct.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Returns nonzero if c ∈ !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ + */ int ispunct(int c) { return (0x21 <= c && c <= 0x7E) && !('0' <= c && c <= '9') && !('A' <= c && c <= 'Z') && !('a' <= c && c <= 'z'); diff --git a/libc/str/mbtowc.c b/libc/str/mbtowc.c index c530b85e..6b8c59f3 100644 --- a/libc/str/mbtowc.c +++ b/libc/str/mbtowc.c @@ -24,7 +24,7 @@ compatfn int mbtowc(wchar_t *wc, const char *s, size_t n) { if (!s) return 0; - alignas(8) char alt[ROUNDUP(MB_CUR_MAX, 8)]; + _Alignas(8) char alt[ROUNDUP(MB_CUR_MAX, 8)]; if (n < MB_CUR_MAX) { memset(alt, 0, sizeof(alt)); memcpy(alt, s, n); diff --git a/libc/str/str.h b/libc/str/str.h index 3085ff40..e210491d 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -233,37 +233,21 @@ int iswctype(wint_t, wctype_t) pureconst; char *strsignal(int) returnsnonnull libcesque; +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § strings » optimizations ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define explicit_bzero(STR, BYTES) \ - do { \ - void *Str; \ - size_t Bytes; \ - asm volatile("call\texplicit_bzero" \ - : "=D"(Str), "=S"(Bytes) \ - : "0"(STR), "1"(BYTES) \ - : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", \ - "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"); \ - } while (0) +#define __memcpy_isgoodsize(SIZE) \ + (__builtin_constant_p(SIZE) && ((SIZE) <= __BIGGEST_ALIGNMENT__ * 2 && \ + __builtin_popcountl((unsigned)(SIZE)) == 1)) -#ifdef UNBLOAT_STDARG -#define __STR_XMM_CLOBBER -#else -#define __STR_XMM_CLOBBER "xmm3", "xmm4", -#endif - -#define __memcpy_isgoodsize(SIZE) \ - (isconstant(SIZE) && ((SIZE) <= __BIGGEST_ALIGNMENT__ * 2 && \ - __builtin_popcountl((unsigned)(SIZE)) == 1)) - -#define __memset_isgoodsize(SIZE) \ - (isconstant(SIZE) && (((SIZE) <= __BIGGEST_ALIGNMENT__ && \ - __builtin_popcountl((unsigned)(SIZE)) == 1) || \ - ((SIZE) % __BIGGEST_ALIGNMENT__ == 0 && \ - (SIZE) / __BIGGEST_ALIGNMENT__ <= 3))) +#define __memset_isgoodsize(SIZE) \ + (__builtin_constant_p(SIZE) && \ + (((SIZE) <= __BIGGEST_ALIGNMENT__ && \ + __builtin_popcountl((unsigned)(SIZE)) == 1) || \ + ((SIZE) % __BIGGEST_ALIGNMENT__ == 0 && \ + (SIZE) / __BIGGEST_ALIGNMENT__ <= 3))) #define memcpy(DEST, SRC, SIZE) \ (__memcpy_isgoodsize(SIZE) ? __builtin_memcpy(DEST, SRC, SIZE) \ @@ -273,7 +257,18 @@ char *strsignal(int) returnsnonnull libcesque; (__memset_isgoodsize(SIZE) ? __builtin_memset(DEST, BYTE, SIZE) \ : __memset(DEST, BYTE, SIZE)) -#if defined(__STDC_HOSTED__) && (defined(__SSE2__) || defined(UNBLOAT_STDARG)) +#if defined(__STDC_HOSTED__) && defined(__SSE2__) + +#define strlen(STR) \ + (__builtin_constant_p(STR) ? __builtin_strlen(STR) : ({ \ + size_t LeN; \ + const char *StR = (STR); \ + asm("call\tstrlen" \ + : "=a"(LeN) \ + : "D"(StR), "m"(*(char(*)[0x7fffffff])StR) \ + : "rcx", "rdx", "xmm3", "xmm4", "cc"); \ + LeN; \ + })) #define memmove(DEST, SRC, SIZE) __memcpy("MemMove", (DEST), (SRC), (SIZE)) @@ -291,7 +286,7 @@ char *strsignal(int) returnsnonnull libcesque; asm("call\t" FN \ : "=m"(*(char(*)[SiZe])(DeSt)) \ : "D"(DeSt), "S"(SrC), "d"(SiZe), "m"(*(const char(*)[SiZe])(SrC)) \ - : __STR_XMM_CLOBBER "rcx", "cc"); \ + : "xmm3", "xmm4", "rcx", "cc"); \ DeSt; \ }) @@ -302,11 +297,22 @@ char *strsignal(int) returnsnonnull libcesque; asm("call\tMemSet" \ : "=m"(*(char(*)[SiZe])(DeSt)) \ : "D"(DeSt), "S"(BYTE), "d"(SiZe) \ - : __STR_XMM_CLOBBER "rcx", "cc"); \ + : "xmm3", "xmm4", "rcx", "cc"); \ DeSt; \ }) -#else /* hosted/sse2/unbloat */ +#define explicit_bzero(STR, BYTES) \ + do { \ + void *Str; \ + size_t Bytes; \ + asm volatile("call\texplicit_bzero" \ + : "=D"(Str), "=S"(Bytes) \ + : "0"(STR), "1"(BYTES) \ + : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", \ + "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"); \ + } while (0) + +#else /* hosted+sse2 */ #define mempcpy(DEST, SRC, SIZE) \ ({ \ @@ -347,7 +353,6 @@ char *strsignal(int) returnsnonnull libcesque; }) #endif /* hosted/sse2/unbloat */ - #endif /* __GNUC__ && !__STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/str/strlcpy.c b/libc/str/strlcpy.c index fcbef23e..31936077 100644 --- a/libc/str/strlcpy.c +++ b/libc/str/strlcpy.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/macros.h" #include "libc/str/str.h" /** @@ -34,7 +35,7 @@ size_t strlcpy(char *d, const char *s, size_t n) { size_t slen, actual; slen = strlen(s); if (n) { - actual = min(n, slen); + actual = MIN(n, slen); memcpy(d, s, actual); d[actual] = '\0'; } diff --git a/libc/str/strsignal.c b/libc/str/strsignal.c index 06f00cc5..bef925cc 100644 --- a/libc/str/strsignal.c +++ b/libc/str/strsignal.c @@ -24,7 +24,7 @@ static const char kSig[4] = "SIG"; static const char kUnknown[8] = "UNKNOWN"; -alignas(1) static const char kStrSignals[][8] = { +_Alignas(char) static const char kStrSignals[][8] = { "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", diff --git a/libc/str/tpdecodecb.internal.h b/libc/str/tpdecodecb.internal.h index 97208ee9..6ec04a38 100644 --- a/libc/str/tpdecodecb.internal.h +++ b/libc/str/tpdecodecb.internal.h @@ -3,6 +3,8 @@ #include "libc/nexgen32e/bsr.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) +/* TODO(jart): DELETE? */ + /** * Generic Thompson-Pike Varint Decoder. * @return number of bytes successfully consumed or -1 w/ errno @@ -12,11 +14,11 @@ forceinline int tpdecodecb(wint_t *out, int first, int get(void *arg, uint32_t i), void *arg) { uint32_t wc, cb, need, msb, j, i = 1; - if (unlikely((wc = first) == -1)) return -1; - while (unlikely((wc & 0b11000000) == 0b10000000)) { + if (__builtin_expect((wc = first) == -1, 0)) return -1; + while (__builtin_expect((wc & 0b11000000) == 0b10000000, 0)) { if ((wc = get(arg, i++)) == -1) return -1; } - if (unlikely(!(0 <= wc && wc <= 0x7F))) { + if (__builtin_expect(!(0 <= wc && wc <= 0x7F), 0)) { msb = wc < 252 ? bsr(~wc & 0xff) : 1; need = 7 - msb; wc &= ((1u << msb) - 1) | 0b00000011; @@ -30,7 +32,7 @@ forceinline int tpdecodecb(wint_t *out, int first, } } } - if (likely(out)) *out = (wint_t)wc; + if (__builtin_expect(!!out, 1)) *out = (wint_t)wc; return i; } diff --git a/libc/str/tpencode.ncabi.c b/libc/str/tpencode.ncabi.c index 53acadb7..f3d31c5b 100644 --- a/libc/str/tpencode.ncabi.c +++ b/libc/str/tpencode.ncabi.c @@ -21,6 +21,8 @@ #include "libc/str/tpenc.h" #include "libc/str/tpencode.internal.h" +/* TODO: DELETE */ + /** * Thompson-Pike Varint Encoder. * diff --git a/libc/str/utf16.h b/libc/str/utf16.h index 8042c021..7bc999f7 100644 --- a/libc/str/utf16.h +++ b/libc/str/utf16.h @@ -11,13 +11,14 @@ COSMOPOLITAN_C_START_ #define IsUcs2(wc) (((wc)&UTF16_MASK) != UTF16_MOAR) #define IsUtf16Cont(wc) (((wc)&UTF16_MASK) == UTF16_CONT) #define MergeUtf16(lo, hi) ((((lo)-0xD800) << 10) + ((hi)-0xDC00) + 0x10000) -#define EncodeUtf16(wc) \ - (likely((0x0000 <= (wc) && (wc) <= 0xFFFF) || \ - (0xE000 <= (wc) && (wc) <= 0xFFFF)) \ - ? (wc) \ - : 0x10000 <= (wc) && (wc) <= 0x10FFFF \ - ? (((((wc)-0x10000) >> 10) + 0xD800) | \ - ((((wc)-0x10000) & 1023) + 0xDC00) << 16) \ +#define EncodeUtf16(wc) \ + (__builtin_expect(((0x0000 <= (wc) && (wc) <= 0xFFFF) || \ + (0xE000 <= (wc) && (wc) <= 0xFFFF)), \ + 1) \ + ? (wc) \ + : 0x10000 <= (wc) && (wc) <= 0x10FFFF \ + ? (((((wc)-0x10000) >> 10) + 0xD800) | \ + ((((wc)-0x10000) & 1023) + 0xDC00) << 16) \ : 0xFFFD) COSMOPOLITAN_C_END_ diff --git a/libc/testlib/ugly.h b/libc/testlib/ugly.h index 241a65cd..1f7ed9f0 100644 --- a/libc/testlib/ugly.h +++ b/libc/testlib/ugly.h @@ -14,9 +14,9 @@ #define __BENCH_ARRAY(S) \ _Section(".piro.relo.sort.bench.2." #S ",\"aw\",@init_array #") -#define __TEST_PROTOTYPE(S, N, A, K) \ - testonly void S##_##N(void); \ - alignas(8) const void *const S##_##N##_ptr[] A(S) = {S##_##N}; \ +#define __TEST_PROTOTYPE(S, N, A, K) \ + testonly void S##_##N(void); \ + _Alignas(long) const void *const S##_##N##_ptr[] A(S) = {S##_##N}; \ testonly K void S##_##N(void) #define __TEST_SECTION(NAME, CONTENT) \ diff --git a/libc/x/x.h b/libc/x/x.h index 087b7ccb..a3ecdaf5 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -45,6 +45,7 @@ char *xstrdup(const char *) _XPNN _XMAL; char *xstrndup(const char *, size_t) _XPNN _XMAL; char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL; char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL; +char *xdirname(const char *) paramsnonnull() _XMAL; char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL; char *xinet_ntop(int, const void *) _XPNN _XMAL; diff --git a/libc/x/xdirname.c b/libc/x/xdirname.c new file mode 100644 index 00000000..142a13d5 --- /dev/null +++ b/libc/x/xdirname.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/x/x.h" + +/** + * Returns directory portion of path. + */ +char *xdirname(const char *path) { + return dirname(xstrdup(path)); +} diff --git a/libc/x/xjoinpaths.c b/libc/x/xjoinpaths.c index be1d8c50..dac89ba1 100644 --- a/libc/x/xjoinpaths.c +++ b/libc/x/xjoinpaths.c @@ -22,14 +22,25 @@ #include "libc/x/x.h" /** - * Joins paths. + * Joins paths, e.g. + * + * "a" + "b" → "a/b" + * "a/" + "b" → "a/b" + * "a" + "b/" → "a/b/" + * "a" + "/b" → "/b" + * "." + "b" → "b" + * "" + "b" → "b" + * + * @return newly allocated string of resulting path */ char *xjoinpaths(const char *path, const char *other) { if (!*other) { return xstrdup(path); - } else if (startswith(other, "/") || strcmp(path, ".") == 0) { + } else if (!*path) { return xstrdup(other); - } else if (endswith(other, "/")) { + } else if (startswith(other, "/") || !strcmp(path, ".")) { + return xstrdup(other); + } else if (endswith(path, "/")) { return xstrcat(path, other); } else { return xstrcat(path, '/', other); diff --git a/test/libc/fmt/basename_test.c b/test/libc/fmt/basename_test.c index f6310697..01510048 100644 --- a/test/libc/fmt/basename_test.c +++ b/test/libc/fmt/basename_test.c @@ -44,13 +44,3 @@ TEST(basename, testWindows_isGrantedRespect) { EXPECT_STREQ("there", basename("hello\\there")); EXPECT_STREQ("yo", basename("hello\\there\\yo")); } - -TEST(dirname, test) { - EXPECT_STREQ("/usr", dirname(strdup("/usr/lib"))); - EXPECT_STREQ("usr", dirname(strdup("usr/lib"))); - EXPECT_STREQ("/", dirname(strdup("/usr/"))); - EXPECT_STREQ("/", dirname(strdup("/"))); - EXPECT_STREQ(".", dirname(strdup("hello"))); - EXPECT_STREQ(".", dirname(strdup("."))); - EXPECT_STREQ(".", dirname(strdup(".."))); -} diff --git a/test/libc/fmt/dirname_test.c b/test/libc/fmt/dirname_test.c new file mode 100644 index 00000000..12718c25 --- /dev/null +++ b/test/libc/fmt/dirname_test.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/testlib/testlib.h" + +TEST(dirname, test) { + EXPECT_STREQ("/usr", dirname(gc(strdup("/usr/lib")))); + EXPECT_STREQ("usr", dirname(gc(strdup("usr/lib")))); + EXPECT_STREQ("/", dirname(gc(strdup("/usr/")))); + EXPECT_STREQ("/", dirname(gc(strdup("/")))); + EXPECT_STREQ(".", dirname(gc(strdup("hello")))); + EXPECT_STREQ(".", dirname(gc(strdup(".")))); + EXPECT_STREQ(".", dirname(gc(strdup("..")))); +} diff --git a/test/libc/stdio/fputs_test.c b/test/libc/stdio/fputs_test.c new file mode 100644 index 00000000..36807c73 --- /dev/null +++ b/test/libc/stdio/fputs_test.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" + +BENCH(fputs, bench) { + FILE *f; + char *buf; + buf = gc(malloc(kHyperionSize)); + EZBENCH2("fputs", f = fmemopen(buf, kHyperionSize, "r+"), + fputs(kHyperion, f)); +} diff --git a/test/libc/stdio/fread_test.c b/test/libc/stdio/fread_test.c new file mode 100644 index 00000000..9690ccfe --- /dev/null +++ b/test/libc/stdio/fread_test.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" + +BENCH(fputs, bench) { + FILE *f; + char *buf = gc(malloc(kHyperionSize)); + char *buf2 = gc(malloc(kHyperionSize)); + buf2 = gc(malloc(kHyperionSize)); + f = fmemopen(buf, kHyperionSize, "r+"); + ASSERT_EQ(kHyperionSize, fread(buf2, 1, kHyperionSize, f)); + EZBENCH2("fread", f = fmemopen(buf, kHyperionSize, "r+"), + fread(buf2, 1, kHyperionSize, f)); +} diff --git a/libc/str/strlen.c b/test/libc/str/bsr_test.c similarity index 74% rename from libc/str/strlen.c rename to test/libc/str/bsr_test.c index 0acd9642..47df74fa 100644 --- a/libc/str/strlen.c +++ b/test/libc/str/bsr_test.c @@ -17,34 +17,25 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/intrin/pcmpeqb.h" -#include "libc/intrin/pmovmskb.h" -#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" #include "libc/str/str.h" +#include "libc/testlib/testlib.h" -/** - * Returns length of NUL-terminated string. - * - * @param s is non-null NUL-terminated string pointer - * @return number of bytes (excluding NUL) - * @asyncsignalsafe - */ -size_t strlen(const char *s) { - const char *p; - unsigned k, m; - uint8_t v1[16], vz[16]; - k = (uintptr_t)s & 15; - p = (const char *)((uintptr_t)s & -16); - memset(vz, 0, 16); - memcpy(v1, p, 16); - pcmpeqb(v1, v1, vz); - m = pmovmskb(v1) >> k << k; - while (!m) { - p += 16; - memcpy(v1, p, 16); - pcmpeqb(v1, v1, vz); - m = pmovmskb(v1); - } - return p + bsf(m) - s; +static unsigned Bsr(unsigned x) { + static const char kDebruijn[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31, + }; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x *= 0x07c4acdd; + x >>= 27; + return kDebruijn[x]; +} + +TEST(bsr, test) { + ASSERT_EQ(bsr(0xffffffff), Bsr(0xffffffff)); } diff --git a/test/libc/str/strlen_test.c b/test/libc/str/strlen_test.c index 773aeae2..c06361c7 100644 --- a/test/libc/str/strlen_test.c +++ b/test/libc/str/strlen_test.c @@ -71,11 +71,15 @@ TEST(strnlen, testconst) { } TEST(strlen, testnonconst) { + /* this test case is a great example of why we need: + "m"(*(char(*)[0x7fffffff])StR) + rather than: + "m"(*StR) */ char buf[256]; unsigned i; - for (i = 0; i < 255; ++i) buf[i] = i + 1; + for (i = 0; i < 250; ++i) buf[i] = i + 1; buf[i] = '\0'; - ASSERT_EQ(255, strlen(buf)); + ASSERT_EQ(250, strlen(buf)); } TEST(strnlen_s, null_ReturnsZero) { diff --git a/test/libc/x/xjoinpaths_test.c b/test/libc/x/xjoinpaths_test.c new file mode 100644 index 00000000..701269aa --- /dev/null +++ b/test/libc/x/xjoinpaths_test.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(xjoinpaths, test) { + EXPECT_STREQ("", gc(xjoinpaths("", ""))); + EXPECT_STREQ("b", gc(xjoinpaths("", "b"))); + EXPECT_STREQ("a/b", gc(xjoinpaths("a", "b"))); + EXPECT_STREQ("a/b", gc(xjoinpaths("a/", "b"))); + EXPECT_STREQ("a/b/", gc(xjoinpaths("a", "b/"))); + EXPECT_STREQ("/b", gc(xjoinpaths("a", "/b"))); + EXPECT_STREQ("b", gc(xjoinpaths(".", "b"))); +} diff --git a/test/net/http/uricspn_test.c b/test/net/http/uricspn_test.c index 015aa6d8..27cf7f6f 100644 --- a/test/net/http/uricspn_test.c +++ b/test/net/http/uricspn_test.c @@ -22,7 +22,7 @@ #include "libc/testlib/testlib.h" #include "net/http/uri.h" -alignas(32) const char kWinsockIcoPngBase64[] = "\ +_Alignas(32) const char kWinsockIcoPngBase64[] = "\ base64,iVBORw0KGgoAAAANSUhEUgAAAJcAAACXCAYAAAAYn8l5AAAABmJLR0QA/\ wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4woLByMP6uwgW\ QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAMeSURBVHja7\ diff --git a/third_party/chibicc/README.cosmo b/third_party/chibicc/README.cosmo index ab1115f0..56971eec 100644 --- a/third_party/chibicc/README.cosmo +++ b/third_party/chibicc/README.cosmo @@ -30,6 +30,7 @@ local bug fixes - fix 64-bit bug in generated code for struct bitfields - fix bug where last statement in statement expression couldn't have label - print_tokens (chibicc -E) now works in the case of adjacent string literals +- make enums unsigned (like gcc) so we don't suffer the msvc enum bitfield bug local changes diff --git a/third_party/chibicc/as.c b/third_party/chibicc/as.c index 4bda3aca..09b8d6df 100644 --- a/third_party/chibicc/as.c +++ b/third_party/chibicc/as.c @@ -16,75 +16,172 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/bits/popcnt.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/elf/def.h" #include "libc/fmt/conv.h" +#include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/macros.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/bsr.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" -#include "libc/x/x.h" #include "third_party/gdtoa/gdtoa.h" #include "tool/build/lib/elfwriter.h" -/* WORK IN PROGRESS */ +/** + * @fileoverview Assembler + * + * This program turns assembly into relocatable NexGen32e ELF objects. + * That process is normally an implementation detail of your compiler, + * which can embed this program or launch it as a subprocess. Much GNU + * style syntax is supported. Your code that gets embedded in an asm() + * statement will ultimately end up here. This implementation, has the + * advantage of behaving the same across platforms, in a simple single + * file implementation that compiles down to a 100kilo ape executable. + * + * Your assembler supports the following flags: + * + * -o FILE output path [default: a.out] + * -I DIR append include path [default: .] + * -W inhibit .warning + * -Z inhibit .error and .err + * + * Your assembler supports the following directives: + * + * .zero INT... emits int8 + * .word INT... emits int16 + * .long INT... emits int32 + * .quad INT... emits int64 + * .ascii STR... emits string + * .asciz STR... emits string and 0 byte + * .ident STR emits string to .comment section + * .float NUMBER... emits binary32 + * .double NUMBER... emits binary64 + * .float80 NUMBER... emits x86 float (10 bytes) + * .ldbl NUMBER... emits x86 float (16 bytes) + * .sleb128 NUMBER... emits LEB-128 signed varint + * .uleb128 NUMBER... emits LEB-128 unsigned varint + * .align BYTES [FILL [MAXSKIP]] emits fill bytes to boundary + * .end halts tokenization + * .abort crashes assembler + * .err aborts (ignorable w/ -Z) + * .error STR aborts (ignorable w/ -Z) + * .warning STR whines (ignorable w/ -W) + * .text enters text section (default) + * .data enters data section + * .bss enters bss section + * .section NAME [SFLG SHT] enters section + * .previous enters previous section + * .pushsection NAME [SFLG SHT] pushes section + * .popsection pops section + * .type SYM TYPE sets type of symbol + * .size SYM EXPR sets size of symbol + * .internal SYM... marks symbol STV_INTERNAL + * .hidden SYM... marks symbol STV_HIDDEN + * .protected SYM... marks symbol STV_PROTECTED + * .globl SYM... marks symbol STB_GLOBAL + * .local SYM... marks symbol STB_LOCAL + * .weak SYM... marks symbol STB_WEAK + * .include FILE assembles file source + * .incbin FILE emits file content + * .file FILENO PATH dwarf file define + * .loc FILENO LINENO dwarf source line + * + * TYPE can be one of the following: + * + * - @notype STT_NOTYPE (default) + * - @object STT_OBJECT + * - @function STT_FUNC + * - @common STT_COMMON + * - @tls_object STT_TLS + * + * SHT can be one of the following: + * + * - @progbits SHT_PROGBITS + * - @note SHT_NOTE + * - @nobits SHT_NOBITS + * - @preinit_array SHT_PREINIT_ARRAY + * - @init_array SHT_INIT_ARRAY + * - @fini_array SHT_FINI_ARRAY + * + * SFLG is a string which may have the following characters: + * + * - a SHF_ALLOC + * - w SHF_WRITE + * - x SHF_EXECINSTR + * - g SHF_GROUP + * - M SHF_MERGE + * - S SHF_STRINGS + * - T SHF_TLS + */ + +#define OSZ 0x66 +#define ASZ 0x67 +#define REX 0x40 // byte +#define REXB 0x41 // src +#define REXX 0x42 // index +#define REXR 0x44 // dest +#define REXW 0x48 // quad -#define OSZ 0x66 -#define ASZ 0x67 -#define REX 0x40 // byte -#define REXB 0x41 // src -#define REXX 0x42 // index -#define REXR 0x44 // dest -#define REXW 0x48 // quad #define HASASZ 0x00010000 #define HASBASE 0x00020000 #define HASINDEX 0x00040000 #define ISRIP 0x00080000 #define ISREG 0x00100000 -#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p)) -#define IS(P, N, S) (N == strlen(S) && !strncasecmp(P, S, strlen(S))) +#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p)) +#define IS(P, N, S) (N == sizeof(S) - 1 && !strncasecmp(P, S, sizeof(S) - 1)) +#define BSR(I) (__builtin_clz(I) ^ 31) +#define ROUNDUP(X, K) (((X) + (K)-1) & -(K)) +#define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) +#define LOAD128BE(S) ((unsigned __int128)LOAD64BE(S) << 64 | LOAD64BE((S) + 8)) +#define LOAD64BE(S) \ + ((unsigned long)((unsigned char *)(S))[0] << 070 | \ + (unsigned long)((unsigned char *)(S))[1] << 060 | \ + (unsigned long)((unsigned char *)(S))[2] << 050 | \ + (unsigned long)((unsigned char *)(S))[3] << 040 | \ + (unsigned long)((unsigned char *)(S))[4] << 030 | \ + (unsigned long)((unsigned char *)(S))[5] << 020 | \ + (unsigned long)((unsigned char *)(S))[6] << 010 | \ + (unsigned long)((unsigned char *)(S))[7] << 000) +#define ARRAYLEN(A) \ + ((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A))))) -struct Strings { - size_t n; - char **p; -}; - -struct Assembler { - int i; - int section; - int previous; +struct As { + int i; // things + int section; // sections + int previous; // sections + int inpath; // strings + int outpath; // strings + int counter; + bool inhibiterr; + bool inhibitwarn; struct Ints { - size_t n; + unsigned long n; long *p; } ints; struct Floats { - size_t n; + unsigned long n; long double *p; } floats; struct Slices { - size_t n; + unsigned long n; struct Slice { - size_t n; + unsigned long n; char *p; } * p; } slices; struct Sauces { - size_t n; + unsigned long n; struct Sauce { - const char *path; - int line; + int path; // strings + int line; // 1-indexed } * p; } sauces; struct Things { - size_t n; + unsigned long n; struct Thing { enum ThingType { TT_INT, @@ -93,15 +190,15 @@ struct Assembler { TT_PUNCT, TT_FORWARD, TT_BACKWARD, - } t; - int i; - int s; + } t : 4; + int s : 28; // sauces + int i; // identity,ints,floats,slices } * p; } things; struct Sections { - size_t n; + unsigned long n; struct Section { - const char *name; + int name; // strings int flags; int type; int align; @@ -109,42 +206,110 @@ struct Assembler { } * p; } sections; struct Symbols { - size_t n; + unsigned long n; struct Symbol { - int name; - int section; - int stb; - int stv; - int type; - long location; + bool isused; + int name; // slices + int section; // sections + int stb; // STB_* + int stv; // STV_* + int type; // STT_* + long offset; long size; + struct ElfWriterSymRef ref; } * p; } symbols; + struct HashTable { + unsigned i, n; + struct HashEntry { + int h; + int i; + } * p; + } symbolindex; struct Labels { - size_t n; + unsigned long n; struct Label { - int s; int id; - int section; - long location; + int tok; // things + int symbol; // symbols } * p; } labels; + struct Relas { + unsigned long n; + struct Rela { + bool isdead; + int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...} + int expr; // exprs + int section; // sections + long offset; + long addend; + } * p; + } relas; + struct Exprs { + unsigned long n; + struct Expr { + enum ExprKind { + EX_INT, // integer + EX_SYM, // slice, forward, backward, then symbol + EX_NEG, // unary - + EX_NOT, // unary ! + EX_BITNOT, // unary ~ + EX_ADD, // + + EX_SUB, // - + EX_MUL, // * + EX_DIV, // / + EX_REM, // % + EX_AND, // & + EX_OR, // | + EX_XOR, // ^ + EX_SHL, // << + EX_SHR, // >> + EX_EQ, // == + EX_NE, // != + EX_LT, // < + EX_LE, // <= + } kind; + enum ExprMod { + EM_NORMAL, + EM_GOTPCREL, + EM_DTPOFF, + EM_TPOFF, + } em; + int tok; + int lhs; + int rhs; + long x; + bool isvisited; + bool isevaluated; + } * p; + } exprs; + struct Strings { + unsigned long n; + char **p; + } strings, incpaths; + struct SectionStack { + unsigned long n; + int *p; + } sectionstack; }; static const char kPrefixByte[30] = { - 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf0, 0x26, 0x2e, 0x36, 0x3e, - 0x64, 0x65, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x66, 0x67, + 0x67, 0x2E, 0x66, 0x3E, 0x26, 0x64, 0x65, 0xF0, 0xF3, 0xF3, + 0xF2, 0xF2, 0xF3, 0x40, 0x41, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4C, 0x4D, 0x4E, 0x4F, 0x4A, 0x4B, 0x42, 0x43, 0x36, }; static const char kPrefix[30][8] = { - "rep", "repz", "repe", "repne", "repnz", "lock", - "es", "cs", "ss", "ds", "fs", "gs", - "rex", "rex.b", "rex.x", "rex.xb", "rex.r", "rex.rb", - "rex.rx", "rex.rxb", "rex.w", "rex.wb", "rex.wx", "rex.wxb", - "rex.wr", "rex.wrb", "rex.wrx", "rex.wrxb", "data16", "addr32", + "addr32", "cs", "data16", "ds", "es", "fs", + "gs", "lock", "rep", "repe", "repne", "repnz", + "repz", "rex", "rex.b", "rex.r", "rex.rb", "rex.rx", + "rex.rxb", "rex.w", "rex.wb", "rex.wr", "rex.wrb", "rex.wrx", + "rex.wrxb", "rex.wx", "rex.wxb", "rex.x", "rex.xb", "ss", }; +static const char kSegmentByte[6] = {0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36}; +static const char kSegment[6][2] = {"cs", "ds", "es", "fs", "gs", "ss"}; + /** * Context-sensitive register encoding information. * @@ -183,8 +348,19 @@ static const struct Reg { {"ecx", 1 | 2<<3, 1 | 2<<3, 1 | 2<<3, 1 | 2<<3 }, {"edi", 7 | 2<<3, 7 | 2<<3, 7 | 2<<3, 7 | 2<<3 }, {"edx", 2 | 2<<3, 2 | 2<<3, 2 | 2<<3, 2 | 2<<3 }, + {"eiz", -1, -1, -1, 4 | 2<<3 }, {"esi", 6 | 2<<3, 6 | 2<<3, 6 | 2<<3, 6 | 2<<3 }, {"esp", 4 | 2<<3, 4 | 2<<3, 4 | 2<<3, 4 | 2<<3 }, + {"mm0", 0 | 3<<3, 0 | 3<<3, -1, -1 }, + {"mm1", 1 | 3<<3, 1 | 3<<3, -1, -1 }, + {"mm2", 2 | 3<<3, 2 | 3<<3, -1, -1 }, + {"mm3", 3 | 3<<3, 3 | 3<<3, -1, -1 }, + {"mm4", 4 | 3<<3, 4 | 3<<3, -1, -1 }, + {"mm5", 5 | 3<<3, 5 | 3<<3, -1, -1 }, + {"mm6", 6 | 3<<3, 6 | 3<<3, -1, -1 }, + {"mm7", 7 | 3<<3, 7 | 3<<3, -1, -1 }, + {"mm8", 0 | 3<<3 | REXR<<8, 0 | 3<<3 | REXB<<8, -1, -1 }, + {"mm9", 1 | 3<<3 | REXR<<8, 1 | 3<<3 | REXB<<8, -1, -1 }, {"r10", 2 | 3<<3 | REXR<<8 | REXW<<8, 2 | 3<<3 | REXB<<8 | REXW<<8, 2 | 3<<3 | REXB<<8, 2 | 3<<3 | REXX<<8 }, {"r10b", 2 | REXR<<8, 2 | REXB<<8, -1, -1 }, {"r10d", 2 | 2<<3 | REXR<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXX<<8 }, @@ -223,12 +399,22 @@ static const struct Reg { {"rcx", 1 | 3<<3 | REXW<<8, 1 | 3<<3 | REXW<<8, 1 | 3<<3, 1 | 3<<3 }, {"rdi", 7 | 3<<3 | REXW<<8, 7 | 3<<3 | REXW<<8, 7 | 3<<3, 7 | 3<<3 }, {"rdx", 2 | 3<<3 | REXW<<8, 2 | 3<<3 | REXW<<8, 2 | 3<<3, 2 | 3<<3 }, + {"riz", -1, -1, -1, 4 | 3<<3 }, {"rsi", 6 | 3<<3 | REXW<<8, 6 | 3<<3 | REXW<<8, 6 | 3<<3, 6 | 3<<3 }, {"rsp", 4 | 3<<3 | REXW<<8, 4 | 3<<3 | REXW<<8, 4 | 3<<3, 4 | 3<<3 }, {"si", 6 | 1<<3, 6 | 1<<3, 6 | 1<<3, 6 | 1<<3 }, {"sil", 6 | REX<<8, 6 | REX<<8, 6 | REX<<8, 6 | REX<<8 }, {"sp", 4 | 1<<3, 4 | 1<<3, 4 | 1<<3, 4 | 1<<3 }, {"spl", 4 | REX<<8, 4 | REX<<8, 4 | REX<<8, 4 | REX<<8 }, + {"st", 0 | 4<<3, 0 | 4<<3, -1, -1 }, + {"st(0)", 0 | 4<<3, 0 | 4<<3, -1, -1 }, + {"st(1)", 1 | 4<<3, 1 | 4<<3, -1, -1 }, + {"st(2)", 2 | 4<<3, 2 | 4<<3, -1, -1 }, + {"st(3)", 3 | 4<<3, 3 | 4<<3, -1, -1 }, + {"st(4)", 4 | 4<<3, 4 | 4<<3, -1, -1 }, + {"st(5)", 5 | 4<<3, 5 | 4<<3, -1, -1 }, + {"st(6)", 6 | 4<<3, 6 | 4<<3, -1, -1 }, + {"st(7)", 7 | 4<<3, 7 | 4<<3, -1, -1 }, {"xmm0", 0 | 4<<3, 0 | 4<<3, -1, -1 }, {"xmm1", 1 | 4<<3, 1 | 4<<3, -1, -1 }, {"xmm10", 2 | 4<<3 | REXR<<8, 2 | 4<<3 | REXB<<8, -1, -1 }, @@ -247,43 +433,133 @@ static const struct Reg { {"xmm9", 1 | 4<<3 | REXR<<8, 1 | 4<<3 | REXB<<8, -1, -1 }, } /* clang-format on */; -static bool g_ignore_err; -static const char *g_input_path; -static const char *g_output_path; -static struct Strings g_include_paths; +static unsigned Hash(const void *p, unsigned long n) { + unsigned h, i; + for (h = i = 0; i < n; i++) { + h += ((unsigned char *)p)[i]; + h *= 0x9e3779b1; + } + return MAX(1, h); +} + +static bool StartsWith(const char *s, const char *prefix) { + for (;;) { + if (!*prefix) return true; + if (!*s) return false; + if (*s++ != *prefix++) return false; + } +} + +static bool EndsWith(const char *s, const char *suffix) { + size_t n, m; + n = strlen(s); + m = strlen(suffix); + if (m > n) return false; + return memcmp(s + n - m, suffix, m) == 0; +} + +static char *Format(const char *fmt, ...) { + char *res; + va_list va; + va_start(va, fmt); + vasprintf(&res, fmt, va); + va_end(va); + return res; +} + +static char *DirName(const char *path) { + return dirname(strdup(path)); +} + +static char *JoinPaths(const char *path, const char *other) { + if (!*other) { + return strdup(path); + } else if (!*path) { + return strdup(other); + } else if (StartsWith(other, "/") || !strcmp(path, ".")) { + return strdup(other); + } else if (EndsWith(path, "/")) { + return Format("%s%s", path, other); + } else { + return Format("%s/%s", path, other); + } +} + +static bool IsPunctMergeable(int c) { + switch (c) { + case ';': + case '$': + return false; + default: + return true; + } +} + +static char *PunctToStr(int p, char b[4]) { + int c, i, j; + memset(b, 0, 4); + for (j = 0, i = 2; i >= 0; --i) { + if ((c = (p >> (i * 8)) & 0xff)) { + b[j++] = c; + } + } + return b; +} static void PrintSlice(struct Slice s) { fprintf(stderr, "%.*s\n", s.n, s.p); } -static void AppendString(struct Strings *l, const char *p) { - l->p = realloc(l->p, ++l->n * sizeof(*l->p)); +static char *SaveString(struct Strings *l, char *p) { + APPEND((*l)); l->p[l->n - 1] = p; + return p; } -static void ReadFlags(int argc, char *argv[]) { - int i; - g_input_path = "-"; - g_output_path = "a.out"; - for (i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "-o")) { - g_output_path = argv[++i]; - } else if (startswith(argv[i], "-o")) { - g_output_path = argv[i] + 2; - } else if (!strcmp(argv[i], "-I")) { - AppendString(&g_include_paths, argv[++i]); - } else if (startswith(argv[i], "-I")) { - AppendString(&g_include_paths, argv[i] + 2); - } else if (!strcmp(argv[i], "-Z")) { - g_ignore_err = true; - } else if (argv[i][0] != '-') { - g_input_path = argv[i]; - } +static int StrDup(struct As *a, const char *s) { + SaveString(&a->strings, strdup(s)); + return a->strings.n - 1; +} + +static int SliceDup(struct As *a, struct Slice s) { + SaveString(&a->strings, strndup(s.p, s.n)); + return a->strings.n - 1; +} + +static int AppendSauce(struct As *a, int path, int line) { + if (!a->sauces.n || (line != a->sauces.p[a->sauces.n - 1].line || + path != a->sauces.p[a->sauces.n - 1].path)) { + APPEND(a->sauces); + a->sauces.p[a->sauces.n - 1].path = path; + a->sauces.p[a->sauces.n - 1].line = line; } + return a->sauces.n - 1; } -static int AppendSection(struct Assembler *a, const char *name, int flags, - int type) { +static void AppendExpr(struct As *a) { + APPEND(a->exprs); + memset(a->exprs.p + a->exprs.n - 1, 0, sizeof(*a->exprs.p)); + a->exprs.p[a->exprs.n - 1].tok = a->i; + a->exprs.p[a->exprs.n - 1].lhs = -1; + a->exprs.p[a->exprs.n - 1].rhs = -1; +} + +static void AppendThing(struct As *a) { + APPEND(a->things); + memset(a->things.p + a->things.n - 1, 0, sizeof(*a->things.p)); +} + +static void AppendRela(struct As *a) { + APPEND(a->relas); + memset(a->relas.p + a->relas.n - 1, 0, sizeof(*a->relas.p)); +} + +static void AppendSlice(struct As *a) { + APPEND(a->slices); + memset(a->slices.p + a->slices.n - 1, 0, sizeof(*a->slices.p)); +} + +static int AppendSection(struct As *a, int name, int flags, int type) { int i; APPEND(a->sections); i = a->sections.n - 1; @@ -296,27 +572,60 @@ static int AppendSection(struct Assembler *a, const char *name, int flags, return i; } -static struct Assembler *NewAssembler(void) { - struct Assembler *a = calloc(1, sizeof(struct Assembler)); - APPEND(a->slices); - a->slices.p[0].p = NULL; - a->slices.p[0].n = 0; - AppendSection(a, "", 0, SHT_NULL); - AppendSection(a, ".text", SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS); - AppendSection(a, ".data", SHF_ALLOC | SHF_WRITE, SHT_PROGBITS); - AppendSection(a, ".bss", SHF_ALLOC | SHF_WRITE, SHT_NOBITS); +static struct As *NewAssembler(void) { + struct As *a = calloc(1, sizeof(struct As)); + AppendSlice(a); + AppendSection(a, StrDup(a, ""), 0, SHT_NULL); + AppendSection(a, StrDup(a, ".text"), SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS); + AppendSection(a, StrDup(a, ".data"), SHF_ALLOC | SHF_WRITE, SHT_PROGBITS); + AppendSection(a, StrDup(a, ".bss"), SHF_ALLOC | SHF_WRITE, SHT_NOBITS); a->section = 1; return a; } -static int AppendLine(struct Assembler *a, char *path, int line) { - if (!a->sauces.n || (line != a->sauces.p[a->sauces.n - 1].line || - path != a->sauces.p[a->sauces.n - 1].path)) { - APPEND(a->sauces); - a->sauces.p[a->sauces.n - 1].path = path; - a->sauces.p[a->sauces.n - 1].line = line; +static void FreeAssembler(struct As *a) { + int i; + for (i = 0; i < a->sections.n; ++i) free(a->sections.p[i].binary.p); + for (i = 0; i < a->strings.n; ++i) free(a->strings.p[i]); + for (i = 0; i < a->incpaths.n; ++i) free(a->incpaths.p[i]); + free(a->ints.p); + free(a->floats.p); + free(a->slices.p); + free(a->sauces.p); + free(a->things.p); + free(a->sections.p); + free(a->symbols.p); + free(a->symbolindex.p); + free(a->labels.p); + free(a->relas.p); + free(a->exprs.p); + free(a->strings.p); + free(a->incpaths.p); + free(a->sectionstack.p); + free(a); +} + +static void ReadFlags(struct As *a, int argc, char *argv[]) { + int i; + a->inpath = StrDup(a, "-"); + a->outpath = StrDup(a, "a.out"); + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-o")) { + a->outpath = StrDup(a, argv[++i]); + } else if (StartsWith(argv[i], "-o")) { + a->outpath = StrDup(a, argv[i] + 2); + } else if (!strcmp(argv[i], "-I")) { + SaveString(&a->incpaths, strdup(argv[++i])); + } else if (StartsWith(argv[i], "-I")) { + SaveString(&a->incpaths, strdup(argv[i] + 2)); + } else if (!strcmp(argv[i], "-Z")) { + a->inhibiterr = true; + } else if (!strcmp(argv[i], "-W")) { + a->inhibitwarn = true; + } else if (argv[i][0] != '-') { + a->inpath = StrDup(a, argv[i]); + } } - return a->sauces.n - 1; } static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) { @@ -346,7 +655,14 @@ static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) { } } return c; - case '0' ... '7': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': c -= '0'; if ('0' <= p[*i] && p[*i] <= '7') { c = c * 8 + (p[(*i)++] - '0'); @@ -397,7 +713,7 @@ static char *ReadFile(const char *path) { char *p; FILE *fp; int buflen, nread, end, n; - if (strcmp(path, "-") == 0) { + if (!strcmp(path, "-")) { fp = stdin; } else { fp = fopen(path, "r"); @@ -426,26 +742,74 @@ static char *ReadFile(const char *path) { return p; } -static void Tokenize(struct Assembler *a, char *path) { - bool bol; +static void PrintLocation(struct As *a) { + fprintf(stderr, + "%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[a->i].s].path], + a->sauces.p[a->things.p[a->i].s].line); +} + +static wontreturn void Fail(struct As *a, const char *fmt, ...) { + va_list va; + PrintLocation(a); + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + fputc('\n', stderr); + __die(); +} + +static wontreturn void InvalidRegister(struct As *a) { + Fail(a, "invalid register"); +} + +static char *FindInclude(struct As *a, const char *file) { + int i; + char *path; + struct stat st; + for (i = 0; i < a->incpaths.n; ++i) { + path = JoinPaths(a->incpaths.p[i], file); + if (stat(path, &st) != -1 && S_ISREG(st.st_mode)) return path; + free(path); + } + return NULL; +} + +static void Tokenize(struct As *a, int path) { int c, i, line; char *p, *path2; struct Slice buf; - if (!(p = ReadFile(path))) return; + bool bol, isfloat, isfpu; + p = SaveString(&a->strings, ReadFile(a->strings.p[path])); if (!memcmp(p, "\357\273\277", 3)) p += 3; CanonicalizeNewline(p); RemoveBackslashNewline(p); line = 1; bol = true; while ((c = *p)) { + if (c == '/' && p[1] == '*') { + for (i = 2; p[i]; ++i) { + if (p[i] == '\n') { + ++line; + bol = true; + } else { + bol = false; + if (p[i] == '*' && p[i + 1] == '/') { + i += 2; + break; + } + } + } + p += i; + continue; + } if (c == '#' || (c == '/' && bol) || (c == '/' && p[1] == '/')) { p = strchr(p, '\n'); continue; } if (c == '\n') { - APPEND(a->things); + AppendThing(a); a->things.p[a->things.n - 1].t = TT_PUNCT; - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); a->things.p[a->things.n - 1].i = ';'; ++p; bol = true; @@ -458,27 +822,34 @@ static void Tokenize(struct Assembler *a, char *path) { ++p; continue; } - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_' || - c == '%' || c == '@' || (c == '.' && !('0' <= p[1] && p[1] <= '9'))) { + if ((c & 0x80) || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || + c == '_' || c == '%' || c == '@' || + (c == '.' && !('0' <= p[1] && p[1] <= '9'))) { + isfpu = false; for (i = 1;; ++i) { - if (!(('a' <= p[i] && p[i] <= 'z') || ('A' <= p[i] && p[i] <= 'Z') || - ('0' <= p[i] && p[i] <= '9') || p[i] == '.' || p[i] == '_' || - p[i] == '$')) { + if (!((p[i] & 0x80) || ('a' <= p[i] && p[i] <= 'z') || + ('A' <= p[i] && p[i] <= 'Z') || ('0' <= p[i] && p[i] <= '9') || + p[i] == '.' || p[i] == '_' || p[i] == '$' || + (isfpu && (p[i] == '(' || p[i] == ')')))) { break; } + if (i == 2 && p[i - 2] == '%' && p[i - 1] == 's' && p[i] == 't') { + isfpu = true; + } } - APPEND(a->things); + if (i == 4 && !strncasecmp(p, ".end", 4)) break; + AppendThing(a); a->things.p[a->things.n - 1].t = TT_SLICE; - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); a->things.p[a->things.n - 1].i = a->slices.n; - APPEND(a->slices); + AppendSlice(a); a->slices.p[a->slices.n - 1].p = p; a->slices.p[a->slices.n - 1].n = i; p += i; continue; } if (('0' <= c && c <= '9') || (c == '.' && '0' <= p[1] && p[1] <= '9')) { - bool isfloat = c == '.'; + isfloat = c == '.'; if (c == '0' && p[1] != '.') { if (p[1] == 'x' || p[1] == 'X') { for (i = 2;; ++i) { @@ -488,7 +859,8 @@ static void Tokenize(struct Assembler *a, char *path) { break; } } - } else if (p[1] == 'b' || p[1] == 'B') { + } else if ((p[1] == 'b' || p[1] == 'B') && + ('0' <= p[2] && p[2] <= '9')) { for (i = 2;; ++i) { if (!(p[i] == '0' || p[i] == '1')) break; } @@ -508,7 +880,7 @@ static void Tokenize(struct Assembler *a, char *path) { break; } } - APPEND(a->things); + AppendThing(a); if (isfloat) { APPEND(a->floats); a->floats.p[a->floats.n - 1] = strtold(p, NULL); @@ -520,13 +892,15 @@ static void Tokenize(struct Assembler *a, char *path) { a->things.p[a->things.n - 1].i = a->ints.n - 1; if (p[i] == 'f' || p[i] == 'F') { a->things.p[a->things.n - 1].t = TT_FORWARD; + ++i; } else if (p[i] == 'b' || p[i] == 'B') { a->things.p[a->things.n - 1].t = TT_BACKWARD; + ++i; } else { a->things.p[a->things.n - 1].t = TT_INT; } } - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); p += i; continue; } @@ -536,9 +910,9 @@ static void Tokenize(struct Assembler *a, char *path) { c = ReadCharLiteral(&buf, c, p, &i); if (p[i] == '\'') ++i; p += i; - APPEND(a->things); + AppendThing(a); a->things.p[a->things.n - 1].t = TT_INT; - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); a->things.p[a->things.n - 1].i = a->ints.n; APPEND(a->ints); a->ints.p[a->ints.n - 1] = c; @@ -560,132 +934,482 @@ static void Tokenize(struct Assembler *a, char *path) { APPEND(buf); buf.p[buf.n - 1] = '\0'; --a->things.n; - if (fileexists(buf.p)) { - Tokenize(a, buf.p); + if ((path2 = FindInclude(a, buf.p))) { + Tokenize(a, StrDup(a, path2)); + free(path2); + free(buf.p); } else { - for (i = 0; i < g_include_paths.n; ++i) { - path2 = xstrcat(g_include_paths.p[i], '/', buf.p); - if (fileexists(path2)) { - Tokenize(a, path2); - free(path2); - break; - } else { - free(path2); - } - } + Fail(a, "not found: %s", buf.p); } - free(buf.p); } else { - APPEND(a->things); + SaveString(&a->strings, buf.p); + AppendThing(a); a->things.p[a->things.n - 1].t = TT_SLICE; - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); a->things.p[a->things.n - 1].i = a->slices.n; - APPEND(a->slices); + AppendSlice(a); a->slices.p[a->slices.n - 1] = buf; } continue; } - - APPEND(a->things); - a->things.p[a->things.n - 1].t = TT_PUNCT; - a->things.p[a->things.n - 1].s = AppendLine(a, path, line); - a->things.p[a->things.n - 1].i = c; + if (IsPunctMergeable(c) && a->things.n && + a->things.p[a->things.n - 1].t == TT_PUNCT && + IsPunctMergeable(a->things.p[a->things.n - 1].i)) { + a->things.p[a->things.n - 1].i = a->things.p[a->things.n - 1].i << 8 | c; + } else { + AppendThing(a); + a->things.p[a->things.n - 1].t = TT_PUNCT; + a->things.p[a->things.n - 1].s = AppendSauce(a, path, line); + a->things.p[a->things.n - 1].i = c; + } ++p; } } -static bool IsLocal(struct Assembler *a, int name) { - if (name < 0) return true; - return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2); -} - -static int GetSymbol(struct Assembler *a, int name) { - int i; - for (i = 0; i < a->symbols.n; ++i) { - if (a->slices.p[a->symbols.p[i].name].n == a->slices.p[name].n && - !memcmp(a->slices.p[a->symbols.p[i].name].p, a->slices.p[name].p, - a->slices.p[name].n)) { - return i; +static int GetSymbol(struct As *a, int name) { + struct HashEntry *p; + unsigned i, j, k, n, m, h, n2; + h = Hash(a->slices.p[name].p, a->slices.p[name].n); + n = a->symbolindex.n; + i = 0; + if (n) { + k = 0; + do { + i = (h + k + ((k + 1) >> 1)) & (n - 1); + if (a->symbolindex.p[i].h == h && + a->slices.p[a->symbols.p[a->symbolindex.p[i].i].name].n == + a->slices.p[name].n && + !memcmp(a->slices.p[a->symbols.p[a->symbolindex.p[i].i].name].p, + a->slices.p[name].p, a->slices.p[name].n)) { + return a->symbolindex.p[i].i; + } + ++k; + } while (a->symbolindex.p[i].h); + } + if (++a->symbolindex.i >= (n >> 1)) { + m = n ? n << 1 : 16; + p = calloc(m, sizeof(struct HashEntry)); + for (j = 0; j < n; ++j) { + if (a->symbolindex.p[j].h) { + k = 0; + do { + i = (a->symbolindex.p[j].h + k + ((k + 1) >> 1)) & (m - 1); + ++k; + } while (p[i].h); + p[i].h = a->symbolindex.p[j].h; + p[i].i = a->symbolindex.p[j].i; + } } + k = 0; + do { + i = (h + k + ((k + 1) >> 1)) & (m - 1); + ++k; + } while (p[i].h); + free(a->symbolindex.p); + a->symbolindex.p = p; + a->symbolindex.n = m; } APPEND(a->symbols); - i = a->symbols.n - 1; - memset(&a->symbols.p[i], 0, sizeof(a->symbols.p[i])); - a->symbols.p[i].name = name; - return i; + memset(a->symbols.p + a->symbols.n - 1, 0, sizeof(*a->symbols.p)); + a->symbolindex.p[i].h = h; + a->symbolindex.p[i].i = a->symbols.n - 1; + a->symbols.p[a->symbols.n - 1].name = name; + return a->symbols.n - 1; } -static wontreturn void Fail(struct Assembler *a, const char *fmt, ...) { - va_list va; - fprintf(stderr, "%s:%d:: ", a->sauces.p[a->things.p[a->i].s].path, - a->sauces.p[a->things.p[a->i].s].line); - va_start(va, fmt); - vfprintf(stderr, fmt, va); - va_end(va); - fputc('\n', stderr); - __die(); -} - -static void Label(struct Assembler *a, int name) { +static void OnSymbol(struct As *a, int name) { int i = GetSymbol(a, name); if (a->symbols.p[i].section) { Fail(a, "already defined: %.*s", a->slices.p[name].n, a->slices.p[name].p); } a->symbols.p[i].section = a->section; - a->symbols.p[i].location = a->sections.p[a->section].binary.n; + a->symbols.p[i].offset = a->sections.p[a->section].binary.n; a->i += 2; } -static void LocalLabel(struct Assembler *a, int id) { +static void OnLocalLabel(struct As *a, int id) { + int i; + char *name; + name = Format(".Label.%d", a->counter++); + SaveString(&a->strings, name); + AppendSlice(a); + a->slices.p[a->slices.n - 1].p = name; + a->slices.p[a->slices.n - 1].n = strlen(name); + i = GetSymbol(a, a->slices.n - 1); + a->symbols.p[i].section = a->section; + a->symbols.p[i].offset = a->sections.p[a->section].binary.n; APPEND(a->labels); - a->labels.p[a->labels.n - 1].s = a->things.p[a->i].s; a->labels.p[a->labels.n - 1].id = id; - a->labels.p[a->labels.n - 1].section = a->section; - a->labels.p[a->labels.n - 1].location = a->sections.p[a->section].binary.n; + a->labels.p[a->labels.n - 1].tok = a->i; + a->labels.p[a->labels.n - 1].symbol = i; a->i += 2; } -static void SetSection(struct Assembler *a, int section) { +static void SetSection(struct As *a, int section) { a->previous = a->section; a->section = section; } -static bool IsInt(struct Assembler *a, int i) { +static bool IsInt(struct As *a, int i) { return a->things.p[i].t == TT_INT; } - -static bool IsFloat(struct Assembler *a, int i) { +static bool IsFloat(struct As *a, int i) { return a->things.p[i].t == TT_FLOAT; } - -static bool IsSlice(struct Assembler *a, int i) { +static bool IsSlice(struct As *a, int i) { return a->things.p[i].t == TT_SLICE; } - -static bool IsPunct(struct Assembler *a, int i, int c) { +static bool IsPunct(struct As *a, int i, int c) { return a->things.p[i].t == TT_PUNCT && a->things.p[i].i == c; } +static bool IsForward(struct As *a, int i) { + return a->things.p[i].t == TT_FORWARD; +} +static bool IsBackward(struct As *a, int i) { + return a->things.p[i].t == TT_BACKWARD; +} -static void ConsumePunct(struct Assembler *a, int c) { +static bool IsRegister(struct As *a, int i) { + return IsSlice(a, i) && (a->slices.p[a->things.p[i].i].n && + *a->slices.p[a->things.p[i].i].p == '%'); +} + +static void ConsumePunct(struct As *a, int c) { + char pb[4]; if (IsPunct(a, a->i, c)) { ++a->i; } else { - Fail(a, "expected %`'c", c); + Fail(a, "expected %s", PunctToStr(c, pb)); } } -static long GetInt(struct Assembler *a) { - long res; - if (IsInt(a, a->i)) { - res = a->ints.p[a->things.p[a->i].i]; - ++a->i; - return res; +static int NewPrimary(struct As *a, enum ExprKind k, long x) { + AppendExpr(a); + a->exprs.p[a->exprs.n - 1].kind = k; + a->exprs.p[a->exprs.n - 1].x = x; + return a->exprs.n - 1; +} + +static int NewUnary(struct As *a, enum ExprKind k, int lhs) { + AppendExpr(a); + a->exprs.p[a->exprs.n - 1].kind = k; + a->exprs.p[a->exprs.n - 1].lhs = lhs; + return a->exprs.n - 1; +} + +static int NewBinary(struct As *a, enum ExprKind k, int lhs, int rhs) { + AppendExpr(a); + a->exprs.p[a->exprs.n - 1].kind = k; + a->exprs.p[a->exprs.n - 1].lhs = lhs; + a->exprs.p[a->exprs.n - 1].rhs = rhs; + return a->exprs.n - 1; +} + +// primary = int +// | symbol +// | reference +static int ParsePrimary(struct As *a, int *rest, int i) { + int e; + if (IsInt(a, i)) { + *rest = i + 1; + return NewPrimary(a, EX_INT, a->ints.p[a->things.p[i].i]); + } else if (IsForward(a, i) || IsBackward(a, i) || + (IsSlice(a, i) && (a->slices.p[a->things.p[i].i].n && + a->slices.p[a->things.p[i].i].p[0] != '%' && + a->slices.p[a->things.p[i].i].p[0] != '@'))) { + *rest = i + 1; + return NewPrimary(a, EX_SYM, i); } else { - Fail(a, "expected int"); + Fail(a, "expected int or label"); } } -static long double GetFloat(struct Assembler *a) { +// postfix = primary "@gotpcrel" +// | primary "@dtpoff" +// | primary "@tpoff" +// | primary +static int ParsePostfix(struct As *a, int *rest, int i) { + int x; + struct Slice suffix; + x = ParsePrimary(a, &i, i); + if (IsSlice(a, i)) { + suffix = a->slices.p[a->things.p[i].i]; + if (suffix.n && suffix.p[0] == '@') { + if (IS(suffix.p, suffix.n, "@gotpcrel")) { + a->exprs.p[x].em = EM_GOTPCREL; + ++i; + } else if (IS(suffix.p, suffix.n, "@dtpoff")) { + a->exprs.p[x].em = EM_DTPOFF; + ++i; + } else if (IS(suffix.p, suffix.n, "@tpoff")) { + a->exprs.p[x].em = EM_TPOFF; + ++i; + } + } + } + *rest = i; + return x; +} + +// unary = ("+" | "-" | "!" | "~") unary +// | postfix +static int ParseUnary(struct As *a, int *rest, int i) { + int x; + if (IsPunct(a, i, '+')) { + x = ParseUnary(a, rest, i + 1); + } else if (IsPunct(a, i, '-')) { + x = ParseUnary(a, rest, i + 1); + if (a->exprs.p[x].kind == EX_INT) { + a->exprs.p[x].x = -a->exprs.p[x].x; + } else { + x = NewPrimary(a, EX_NEG, x); + } + } else if (IsPunct(a, i, '!')) { + x = ParseUnary(a, rest, i + 1); + if (a->exprs.p[x].kind == EX_INT) { + a->exprs.p[x].x = !a->exprs.p[x].x; + } else { + x = NewPrimary(a, EX_NOT, x); + } + } else if (IsPunct(a, i, '~')) { + x = ParseUnary(a, rest, i + 1); + if (a->exprs.p[x].kind == EX_INT) { + a->exprs.p[x].x = ~a->exprs.p[x].x; + } else { + x = NewPrimary(a, EX_BITNOT, x); + } + } else { + x = ParsePostfix(a, rest, i); + } + return x; +} + +// mul = unary ("*" unary | "/" unary | "%" unary)* +static int ParseMul(struct As *a, int *rest, int i) { + int x, y; + x = ParseUnary(a, &i, i); + for (;;) { + if (IsPunct(a, i, '*')) { + y = ParseUnary(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x *= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_MUL, x, y); + } + } else if (IsPunct(a, i, '/')) { + y = ParseUnary(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x /= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_DIV, x, y); + } + } else if (IsPunct(a, i, '%')) { + y = ParseUnary(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x %= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_REM, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// add = mul ("+" mul | "-" mul)* +static int ParseAdd(struct As *a, int *rest, int i) { + int x, y; + x = ParseMul(a, &i, i); + for (;;) { + if (IsPunct(a, i, '+')) { + y = ParseMul(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x += a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_ADD, x, y); + } + } else if (IsPunct(a, i, '-')) { + y = ParseMul(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x -= a->exprs.p[y].x; + } else if (a->exprs.p[y].kind == EX_INT) { + a->exprs.p[y].x = -a->exprs.p[y].x; + x = NewBinary(a, EX_ADD, x, y); + } else { + x = NewBinary(a, EX_SUB, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// shift = add ("<<" add | ">>" add)* +static int ParseShift(struct As *a, int *rest, int i) { + int x, y; + x = ParseAdd(a, &i, i); + for (;;) { + if (IsPunct(a, i, '<' << 8 | '<')) { + y = ParseAdd(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x <<= a->exprs.p[y].x & 63; + } else { + x = NewBinary(a, EX_SHL, x, y); + } + } else if (IsPunct(a, i, '>' << 8 | '>')) { + y = ParseAdd(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x >>= a->exprs.p[y].x & 63; + } else { + x = NewBinary(a, EX_SHR, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)* +static int ParseRelational(struct As *a, int *rest, int i) { + int x, y; + x = ParseShift(a, &i, i); + for (;;) { + if (IsPunct(a, i, '<')) { + y = ParseShift(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[x].x < a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_LT, x, y); + } + } else if (IsPunct(a, i, '>')) { + y = ParseShift(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[y].x < a->exprs.p[x].x; + } else { + x = NewBinary(a, EX_LT, y, x); + } + } else if (IsPunct(a, i, '<' << 8 | '=')) { + y = ParseShift(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[x].x <= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_LE, x, y); + } + } else if (IsPunct(a, i, '>' << 8 | '=')) { + y = ParseShift(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[y].x <= a->exprs.p[x].x; + } else { + x = NewBinary(a, EX_LE, y, x); + } + } else { + *rest = i; + return x; + } + } +} + +// equality = relational ("==" relational | "!=" relational)* +static int ParseEquality(struct As *a, int *rest, int i) { + int x, y; + x = ParseRelational(a, &i, i); + for (;;) { + if (IsPunct(a, i, '=' << 8 | '=')) { + y = ParseRelational(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[x].x == a->exprs.p[y].x & 63; + } else { + x = NewBinary(a, EX_EQ, x, y); + } + } else if (IsPunct(a, i, '!' << 8 | '=')) { + y = ParseRelational(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x = a->exprs.p[x].x != a->exprs.p[y].x & 63; + } else { + x = NewBinary(a, EX_NE, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// and = equality ("&" equality)* +static int ParseAnd(struct As *a, int *rest, int i) { + int x, y; + x = ParseEquality(a, &i, i); + for (;;) { + if (IsPunct(a, i, '&')) { + y = ParseEquality(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x &= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_AND, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// xor = and ("^" and)* +static int ParseXor(struct As *a, int *rest, int i) { + int x, y; + x = ParseAnd(a, &i, i); + for (;;) { + if (IsPunct(a, i, '^')) { + y = ParseAnd(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x ^= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_XOR, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +// or = xor ("|" xor)* +static int ParseOr(struct As *a, int *rest, int i) { + int x, y; + x = ParseXor(a, &i, i); + for (;;) { + if (IsPunct(a, i, '|')) { + y = ParseXor(a, &i, i + 1); + if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { + a->exprs.p[x].x |= a->exprs.p[y].x; + } else { + x = NewBinary(a, EX_OR, x, y); + } + } else { + *rest = i; + return x; + } + } +} + +static int Parse(struct As *a) { + return ParseOr(a, &a->i, a->i); +} + +static long GetInt(struct As *a) { + int x; + x = Parse(a); + if (a->exprs.p[x].kind == EX_INT) { + return a->exprs.p[x].x; + } else { + Fail(a, "expected constexpr int"); + } +} + +static long double GetFloat(struct As *a) { long double res; if (IsFloat(a, a->i)) { res = a->floats.p[a->things.p[a->i].i]; @@ -696,7 +1420,7 @@ static long double GetFloat(struct Assembler *a) { } } -static struct Slice GetString(struct Assembler *a) { +static struct Slice GetSlice(struct As *a) { struct Slice res; if (IsSlice(a, a->i)) { res = a->slices.p[a->things.p[a->i].i]; @@ -707,168 +1431,278 @@ static struct Slice GetString(struct Assembler *a) { } } -static void Emit(struct Assembler *a, int c) { - APPEND(a->sections.p[a->section].binary); - a->sections.p[a->section].binary.p[a->sections.p[a->section].binary.n - 1] = - c; +static void EmitData(struct As *a, const void *p, unsigned long n) { + struct Slice *s; + s = &a->sections.p[a->section].binary; + s->p = realloc(s->p, s->n + n); + memcpy(s->p + s->n, p, n); + s->n += n; } -static void EmitWord(struct Assembler *a, long x) { - Emit(a, x >> 000); - Emit(a, x >> 010); +static void EmitByte(struct As *a, unsigned long x) { + unsigned char b[1]; + b[0] = x >> 000; + EmitData(a, b, 1); } -static void EmitLong(struct Assembler *a, long x) { - Emit(a, x >> 000); - Emit(a, x >> 010); - Emit(a, x >> 020); - Emit(a, x >> 030); +static void EmitWord(struct As *a, unsigned long x) { + unsigned char b[2]; + b[0] = x >> 000; + b[1] = x >> 010; + EmitData(a, b, 2); } -static void EmitQuad(struct Assembler *a, long x) { - EmitLong(a, x >> 000); - EmitLong(a, x >> 040); +static void EmitLong(struct As *a, unsigned long x) { + unsigned char b[4]; + b[0] = x >> 000; + b[1] = x >> 010; + b[2] = x >> 020; + b[3] = x >> 030; + EmitData(a, b, 4); } -static void OnZero(struct Assembler *a) { - long i, n; +void EmitQuad(struct As *a, unsigned long x) { + unsigned char b[8]; + b[0] = x >> 000; + b[1] = x >> 010; + b[2] = x >> 020; + b[3] = x >> 030; + b[4] = x >> 040; + b[5] = x >> 050; + b[6] = x >> 060; + b[7] = x >> 070; + EmitData(a, b, 8); +} + +static void EmitVarword(struct As *a, unsigned long x) { + if (x > 255) EmitVarword(a, x >> 8); + EmitByte(a, x); +} + +static void OnSleb128(struct As *a, struct Slice s) { + int c; + long x; + while (!IsPunct(a, a->i, ';')) { + x = GetInt(a); + for (;;) { + c = x & 0x7f; + x >>= 7; + if ((x == 0 && !(c & 0x40)) || (x == -1 && (c & 0x40))) { + break; + } else { + c |= 0x80; + } + EmitByte(a, c); + } + } +} + +static void OnUleb128(struct As *a, struct Slice s) { + int c; + unsigned long x; + while (!IsPunct(a, a->i, ';')) { + x = GetInt(a); + do { + c = x & 0x7f; + x >>= 7; + if (x) c |= 0x80; + EmitByte(a, c); + } while (x); + } +} + +static void OnZero(struct As *a, struct Slice s) { + long n; + char *p; while (IsInt(a, a->i)) { n = GetInt(a); - for (i = 0; i < n; ++i) { - Emit(a, 0); - } + p = calloc(n, 1); + EmitData(a, p, n); + free(p); } } -static void OnFill(struct Assembler *a) { - int x; - long i, n; - n = GetInt(a); - if (IsInt(a, a->i)) { - x = GetInt(a); +static void OnSpace(struct As *a, struct Slice s) { + long n; + char *p; + p = malloc((n = GetInt(a))); + memset(p, IsInt(a, a->i) ? GetInt(a) : 0, n); + EmitData(a, p, n); + free(p); +} + +static long GetRelaAddend(int kind) { + switch (kind) { + case R_X86_64_PC8: + return -1; + case R_X86_64_PC16: + return -2; + case R_X86_64_PC32: + case R_X86_64_PLT32: + case R_X86_64_GOTPCRELX: + return -4; + default: + return 0; + } +} + +static void EmitExpr(struct As *a, int expr, int kind, + void emitter(struct As *, unsigned long)) { + if (expr == -1) { + emitter(a, 0); + } else if (a->exprs.p[expr].kind == EX_INT) { + emitter(a, a->exprs.p[expr].x); } else { - x = 0; - } - for (i = 0; i < n; ++i) { - Emit(a, x); + AppendRela(a); + a->relas.p[a->relas.n - 1].kind = kind; + a->relas.p[a->relas.n - 1].expr = expr; + a->relas.p[a->relas.n - 1].section = a->section; + a->relas.p[a->relas.n - 1].offset = a->sections.p[a->section].binary.n; + a->relas.p[a->relas.n - 1].addend = GetRelaAddend(kind); + emitter(a, 0); } } -static void OnByte(struct Assembler *a) { - while (IsInt(a, a->i)) { - Emit(a, GetInt(a)); - } +static void OnByte(struct As *a, struct Slice s) { + do { + EmitExpr(a, Parse(a), R_X86_64_8, EmitByte); + } while (!IsPunct(a, a->i, ';')); } -static void OnWord(struct Assembler *a) { - long x; - while (IsInt(a, a->i)) { - x = GetInt(a); - Emit(a, x >> 000); - Emit(a, x >> 010); - } +static void OnWord(struct As *a, struct Slice s) { + do { + EmitExpr(a, Parse(a), R_X86_64_16, EmitWord); + } while (!IsPunct(a, a->i, ';')); } -static void OnLong(struct Assembler *a) { - while (IsInt(a, a->i)) { - EmitLong(a, GetInt(a)); - } +static void OnLong(struct As *a, struct Slice s) { + do { + EmitExpr(a, Parse(a), R_X86_64_32, EmitLong); + } while (!IsPunct(a, a->i, ';')); } -static void OnQuad(struct Assembler *a) { - while (IsInt(a, a->i)) { - EmitQuad(a, GetInt(a)); - } +static void OnQuad(struct As *a, struct Slice s) { + do { + EmitExpr(a, Parse(a), R_X86_64_64, EmitQuad); + } while (!IsPunct(a, a->i, ';')); } -static void OnFloat(struct Assembler *a) { +static void OnFloat(struct As *a, struct Slice s) { float f; char b[4]; - while (IsFloat(a, a->i)) { - f = GetFloat(a); + for (;;) { + if (IsFloat(a, a->i)) { + f = GetFloat(a); + } else if (IsInt(a, a->i)) { + f = GetInt(a); + } else { + break; + } memcpy(b, &f, 4); - Emit(a, b[0]); - Emit(a, b[1]); - Emit(a, b[2]); - Emit(a, b[3]); + EmitData(a, b, 4); } } -static void OnDouble(struct Assembler *a) { +static void OnDouble(struct As *a, struct Slice s) { double f; char b[8]; - while (IsFloat(a, a->i)) { - f = GetFloat(a); + for (;;) { + if (IsFloat(a, a->i)) { + f = GetFloat(a); + } else if (IsInt(a, a->i)) { + f = GetInt(a); + } else { + break; + } memcpy(b, &f, 8); - Emit(a, b[0]); - Emit(a, b[1]); - Emit(a, b[2]); - Emit(a, b[3]); - Emit(a, b[4]); - Emit(a, b[5]); - Emit(a, b[6]); - Emit(a, b[7]); + EmitData(a, b, 8); } } -static void OnAscii(struct Assembler *a) { - size_t i; +static void OnLongDouble(struct As *a, int n) { + char b[16]; + long double f; + for (;;) { + if (IsFloat(a, a->i)) { + f = GetFloat(a); + } else if (IsInt(a, a->i)) { + f = GetInt(a); + } else { + break; + } + memset(b, 0, 16); + memcpy(b, &f, sizeof(f)); + EmitData(a, b, n); + } +} + +static void OnFloat80(struct As *a, struct Slice s) { + OnLongDouble(a, 10); +} + +static void OnLdbl(struct As *a, struct Slice s) { + OnLongDouble(a, 16); +} + +static void OnAscii(struct As *a, struct Slice s) { struct Slice arg; while (IsSlice(a, a->i)) { - arg = GetString(a); - for (i = 0; i < arg.n; ++i) { - Emit(a, arg.p[i]); - } + arg = GetSlice(a); + EmitData(a, arg.p, arg.n); } } -static void OnAsciz(struct Assembler *a) { - size_t i; +static void OnAsciz(struct As *a, struct Slice s) { struct Slice arg; while (IsSlice(a, a->i)) { - arg = GetString(a); - for (i = 0; i < arg.n; ++i) { - Emit(a, arg.p[i]); - } - Emit(a, 0); + arg = GetSlice(a); + EmitData(a, arg.p, arg.n); + EmitByte(a, 0); } } -static void OnAbort(struct Assembler *a) { +static void OnAbort(struct As *a, struct Slice s) { Fail(a, "aborted"); } -static void OnErr(struct Assembler *a) { - if (g_ignore_err) return; +static void OnErr(struct As *a, struct Slice s) { + if (a->inhibiterr) return; Fail(a, "error"); } -static void OnError(struct Assembler *a) { - struct Slice msg = GetString(a); - if (g_ignore_err) return; +static void OnError(struct As *a, struct Slice s) { + struct Slice msg = GetSlice(a); + if (a->inhibiterr) return; Fail(a, "%.*s", msg.n, msg.p); } -static void OnText(struct Assembler *a) { - SetSection(a, 0); +static void OnWarning(struct As *a, struct Slice s) { + struct Slice msg = GetSlice(a); + if (a->inhibitwarn) return; + PrintLocation(a); + fprintf(stderr, "%.*s\n", msg.n, msg.p); } -static void OnData(struct Assembler *a) { +static void OnText(struct As *a, struct Slice s) { SetSection(a, 1); } -static void OnBss(struct Assembler *a) { +static void OnData(struct As *a, struct Slice s) { SetSection(a, 2); } -static void OnPrevious(struct Assembler *a) { +static void OnBss(struct As *a, struct Slice s) { + SetSection(a, 3); +} + +static void OnPrevious(struct As *a, struct Slice s) { SetSection(a, a->previous); } -static void OnAlign(struct Assembler *a) { +static void OnAlign(struct As *a, struct Slice s) { long i, n, align, fill, maxskip; align = GetInt(a); - if (popcnt(align) != 1) Fail(a, "alignment not power of 2"); + if (__builtin_popcountl(align) != 1) Fail(a, "alignment not power of 2"); fill = (a->sections.p[a->section].flags & SHF_EXECINSTR) ? 0x90 : 0; maxskip = 268435456; if (IsInt(a, a->i)) { @@ -881,12 +1715,10 @@ static void OnAlign(struct Assembler *a) { n = ROUNDUP(i, align) - i; if (n > maxskip) return; a->sections.p[a->section].align = MAX(a->sections.p[a->section].align, align); - for (i = 0; i < n; ++i) { - Emit(a, fill); - } + for (i = 0; i < n; ++i) EmitByte(a, fill); } -static int SectionFlag(struct Assembler *a, int c) { +static int SectionFlag(struct As *a, int c) { switch (c) { case 'a': return SHF_ALLOC; @@ -907,7 +1739,7 @@ static int SectionFlag(struct Assembler *a, int c) { } } -static int SectionFlags(struct Assembler *a, struct Slice s) { +static int SectionFlags(struct As *a, struct Slice s) { int i, flags; for (flags = i = 0; i < s.n; ++i) { flags |= SectionFlag(a, s.p[i]); @@ -915,74 +1747,26 @@ static int SectionFlags(struct Assembler *a, struct Slice s) { return flags; } -static int SectionType(struct Assembler *a, struct Slice s) { - if (IS(s.p, s.n, "@progbits")) { +static int SectionType(struct As *a, struct Slice s) { + if (IS(s.p, s.n, "@progbits") || IS(s.p, s.n, "SHT_PROGBITS")) { return SHT_PROGBITS; - } else if (IS(s.p, s.n, "@note")) { + } else if (IS(s.p, s.n, "@note") || IS(s.p, s.n, "SHT_NOTE")) { return SHT_NOTE; - } else if (IS(s.p, s.n, "@nobits")) { + } else if (IS(s.p, s.n, "@nobits") || IS(s.p, s.n, "SHT_NOBITS")) { return SHT_NOBITS; - } else if (IS(s.p, s.n, "@preinit_array")) { + } else if (IS(s.p, s.n, "@preinit_array") || + IS(s.p, s.n, "SHT_PREINIT_ARRAY")) { return SHT_PREINIT_ARRAY; - } else if (IS(s.p, s.n, "@init_array")) { + } else if (IS(s.p, s.n, "@init_array") || IS(s.p, s.n, "SHT_INIT_ARRAY")) { return SHT_INIT_ARRAY; - } else if (IS(s.p, s.n, "@fini_array")) { + } else if (IS(s.p, s.n, "@fini_array") || IS(s.p, s.n, "SHT_FINI_ARRAY")) { return SHT_FINI_ARRAY; } else { Fail(a, "unknown section type: %.*s", s.n, s.p); } } -static void OnSection(struct Assembler *a) { - char *name; - int flags, type; - struct Slice arg; - arg = GetString(a); - name = strndup(arg.p, arg.n); - if (startswith(name, ".text")) { - flags = SHF_ALLOC | SHF_EXECINSTR; - type = SHT_PROGBITS; - } else if (startswith(name, ".data")) { - flags = SHF_ALLOC | SHF_WRITE; - type = SHT_PROGBITS; - } else if (startswith(name, ".bss")) { - flags = SHF_ALLOC | SHF_WRITE; - type = SHT_NOBITS; - } else { - flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE; - type = SHT_PROGBITS; - } - if (IsSlice(a, a->i)) { - flags = SectionFlags(a, GetString(a)); - if (IsSlice(a, a->i)) { - type = SectionType(a, GetString(a)); - } - } - SetSection(a, AppendSection(a, name, flags, type)); -} - -static void OnIncbin(struct Assembler *a) { - int fd; - char *path; - struct stat st; - struct Slice arg; - arg = GetString(a); - path = strndup(arg.p, arg.n); - if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1) { - Fail(a, "open failed: %s", path); - } - a->sections.p[a->section].binary.p = - realloc(a->sections.p[a->section].binary.p, - a->sections.p[a->section].binary.n + st.st_size); - if (read(fd, a->sections.p[a->section].binary.p, st.st_size) != st.st_size) { - Fail(a, "read failed: %s", path); - } - a->sections.p[a->section].binary.n += st.st_size; - close(fd); - free(path); -} - -static int SymbolType(struct Assembler *a, struct Slice s) { +static int SymbolType(struct As *a, struct Slice s) { if (IS(s.p, s.n, "@object") || IS(s.p, s.n, "STT_OBJECT")) { return STT_OBJECT; } else if (IS(s.p, s.n, "@function") || IS(s.p, s.n, "STT_FUNC")) { @@ -998,19 +1782,103 @@ static int SymbolType(struct Assembler *a, struct Slice s) { } } -static void OnType(struct Assembler *a) { +static int GrabSection(struct As *a, int name, int flags, int type) { int i; - i = GetSymbol(a, a->things.p[a->i++].i); - a->symbols.p[i].type = SymbolType(a, GetString(a)); + for (i = 0; i < a->sections.n; ++i) { + if (!strcmp(a->strings.p[name], a->strings.p[a->sections.p[i].name])) { + return i; + } + } + return AppendSection(a, name, flags, type); } -static void OnSize(struct Assembler *a) { +static void OnSection(struct As *a, struct Slice s) { + int name, flags, type; + name = SliceDup(a, GetSlice(a)); + if (StartsWith(a->strings.p[name], ".text")) { + flags = SHF_ALLOC | SHF_EXECINSTR; + type = SHT_PROGBITS; + } else if (StartsWith(a->strings.p[name], ".data")) { + flags = SHF_ALLOC | SHF_WRITE; + type = SHT_PROGBITS; + } else if (StartsWith(a->strings.p[name], ".bss")) { + flags = SHF_ALLOC | SHF_WRITE; + type = SHT_NOBITS; + } else { + flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE; + type = SHT_PROGBITS; + } + if (IsSlice(a, a->i)) { + flags = SectionFlags(a, GetSlice(a)); + if (IsSlice(a, a->i)) { + type = SectionType(a, GetSlice(a)); + } + } + SetSection(a, GrabSection(a, name, flags, type)); +} + +static void OnPushsection(struct As *a, struct Slice s) { + APPEND(a->sectionstack); + a->sectionstack.p[a->sectionstack.n - 1] = a->section; + OnSection(a, s); +} + +static void OnPopsection(struct As *a, struct Slice s) { + if (!a->sectionstack.n) Fail(a, "stack smashed"); + a->section = a->sectionstack.p[--a->sectionstack.n]; +} + +static void OnIdent(struct As *a, struct Slice s) { + struct Slice arg; + int comment, oldsection; + comment = GrabSection(a, StrDup(a, ".comment"), SHF_MERGE | SHF_STRINGS, + SHT_PROGBITS); + oldsection = a->section; + a->section = comment; + arg = GetSlice(a); + EmitData(a, arg.p, arg.n); + EmitByte(a, 0); + a->section = oldsection; +} + +static void OnIncbin(struct As *a, struct Slice s) { + int fd; + struct stat st; + char *path, *path2; + struct Slice *data, arg; + arg = GetSlice(a); + path = strndup(arg.p, arg.n); + if ((path2 = FindInclude(a, path))) { + if ((fd = open(path2, O_RDONLY)) == -1 || fstat(fd, &st) == -1) { + Fail(a, "open failed: %s", path2); + } + data = &a->sections.p[a->section].binary; + data->p = realloc(data->p, data->n + st.st_size); + if (read(fd, data->p, st.st_size) != st.st_size) { + Fail(a, "read failed: %s", path2); + } + data->n += st.st_size; + close(fd); + free(path2); + } else { + Fail(a, "not found: %s", path); + } + free(path); +} + +static void OnType(struct As *a, struct Slice s) { + int i; + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].type = SymbolType(a, GetSlice(a)); +} + +static void OnSize(struct As *a, struct Slice s) { int i; i = GetSymbol(a, a->things.p[a->i++].i); a->symbols.p[i].size = GetInt(a); } -static void OnInternal(struct Assembler *a) { +static void OnInternal(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1018,7 +1886,7 @@ static void OnInternal(struct Assembler *a) { } } -static void OnHidden(struct Assembler *a) { +static void OnHidden(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1026,7 +1894,7 @@ static void OnHidden(struct Assembler *a) { } } -static void OnProtected(struct Assembler *a) { +static void OnProtected(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1034,7 +1902,7 @@ static void OnProtected(struct Assembler *a) { } } -static void OnLocal(struct Assembler *a) { +static void OnLocal(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1042,7 +1910,7 @@ static void OnLocal(struct Assembler *a) { } } -static void OnWeak(struct Assembler *a) { +static void OnWeak(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1050,7 +1918,7 @@ static void OnWeak(struct Assembler *a) { } } -static void OnGlobal(struct Assembler *a) { +static void OnGlobal(struct As *a, struct Slice s) { int i; while (IsSlice(a, a->i)) { i = GetSymbol(a, a->things.p[a->i++].i); @@ -1059,7 +1927,7 @@ static void OnGlobal(struct Assembler *a) { } static bool IsSizableOp(const char *op, struct Slice s) { - size_t n = strlen(op); + int n = strlen(op); if (n == s.n) return !memcmp(op, s.p, n); if (n + 1 == s.n && !memcmp(op, s.p, n)) { switch (s.p[n]) { @@ -1079,52 +1947,83 @@ static bool IsSizableOp(const char *op, struct Slice s) { return false; } -static int GetOpSize(struct Assembler *a, struct Slice s) { - switch (s.p[s.n - 1]) { - case 'b': - case 'B': - return 0; - case 'w': - case 'W': - return 1; - case 'l': - case 'L': - return 2; - case 'q': - case 'Q': - return 3; - default: - Fail(a, "could not size instruction"); +static int GetOpSize(struct As *a, struct Slice s, int modrm, int i) { + if (modrm & ISREG) { + return (modrm & 070) >> 3; + } else { + switch (s.p[s.n - i]) { + case 'b': + case 'B': + return 0; + case 'w': + case 'W': + return 1; + case 'l': + case 'L': + return 2; + case 'q': + case 'Q': + return 3; + default: + Fail(a, "could not size instruction"); + } } } -static int CompareString8(const char a[8], const char b[8]) { - uint64_t x, y; - x = READ64BE(a); - y = READ64BE(b); - return x > y ? 1 : x < y ? -1 : 0; +static bool ConsumeSegment(struct As *a) { + int i; + struct Slice s; + if (IsSlice(a, a->i)) { + s = a->slices.p[a->things.p[a->i].i]; + if (s.n == 3 && *s.p == '%') { + for (i = 0; i < ARRAYLEN(kSegment); ++i) { + if (s.p[1] == kSegment[i][0] && s.p[2] == kSegment[i][1]) { + ++a->i; + EmitByte(a, kSegmentByte[i]); + ConsumePunct(a, ':'); + return true; + } + } + } + } + return false; } -static bool FindReg(const char *p, size_t n, struct Reg *out_reg) { - char key[8]; - int i, c, m, l, r; +static void CopyLower(char *k, const char *p, int n) { + int i; + for (i = 0; i < n; ++i) { + k[i] = tolower(p[i]); + } +} + +static unsigned long MakeKey64(const char *p, int n) { + char k[8] = {0}; + CopyLower(k, p, n); + return LOAD64BE(k); +} + +static unsigned __int128 MakeKey128(const char *p, int n) { + char k[16] = {0}; + CopyLower(k, p, n); + return LOAD128BE(k); +} + +static bool Prefix(struct As *a, const char *p, int n) { + int m, l, r; + unsigned long x, y; if (n && n <= 8) { - if (*p == '%') ++p, --n; - memset(key, 0, sizeof(key)); - for (i = 0; i < n; ++i) { - key[i] = tolower(p[i]); - } + x = MakeKey64(p, n); l = 0; - r = ARRAYLEN(kRegs) - 1; + r = ARRAYLEN(kPrefix) - 1; while (l <= r) { m = (l + r) >> 1; - c = CompareString8(kRegs[m].s, key); - if (c < 0) { - l = m + 1; - } else if (c > 0) { + y = LOAD64BE(kPrefix[m]); + if (x < y) { r = m - 1; + } else if (x > y) { + l = m + 1; } else { - *out_reg = kRegs[m]; + EmitByte(a, kPrefixByte[m]); return true; } } @@ -1132,11 +2031,29 @@ static bool FindReg(const char *p, size_t n, struct Reg *out_reg) { return false; } -static int RemoveRexw(int x) { - if (x == -1) return x; - x &= ~0x0800; - if (((x & 0xff00) >> 8) == REX) x &= ~0xff00; - return x; +static bool FindReg(const char *p, int n, struct Reg *out_reg) { + int m, l, r; + unsigned long x, y; + if (n && n <= 8 && *p == '%') { + ++p; + --n; + x = MakeKey64(p, n); + l = 0; + r = ARRAYLEN(kRegs) - 1; + while (l <= r) { + m = (l + r) >> 1; + y = LOAD64BE(kRegs[m].s); + if (x < y) { + r = m - 1; + } else if (x > y) { + l = m + 1; + } else { + *out_reg = kRegs[m]; + return true; + } + } + } + return false; } static int FindRegReg(struct Slice s) { @@ -1163,356 +2080,1584 @@ static int FindRegIndex(struct Slice s) { return reg.index; } -static wontreturn void InvalidRegister(struct Assembler *a) { - Fail(a, "invalid register"); +static int RemoveRexw(int x) { + if (x == -1) return x; + x &= ~0x0800; + if (((x & 0xff00) >> 8) == REX) x &= ~0xff00; + return x; } -static int GetRegisterReg(struct Assembler *a) { +static int GetRegisterReg(struct As *a) { int reg; struct Slice wut; - if ((reg = FindRegReg(GetString(a))) == -1) InvalidRegister(a); + if ((reg = FindRegReg(GetSlice(a))) == -1) InvalidRegister(a); return reg; } -static int GetRegisterRm(struct Assembler *a) { +static int GetRegisterRm(struct As *a) { int reg; struct Slice wut; - if ((reg = FindRegRm(GetString(a))) == -1) InvalidRegister(a); + if ((reg = FindRegRm(GetSlice(a))) == -1) InvalidRegister(a); return reg; } -static int ParseMemory(struct Assembler *a, long *disp) { - /* ┌isrip - │┌hasindex - ││┌hasbase - │││┌hasasz - ││││┌rex - │││││ ┌scale - │││││ │ ┌index - │││││ │ │ ┌base - ││││├──────┐├┐├─┐├─┐ +static int ParseModrm(struct As *a, int *disp) { + /* ┌isreg + │┌isrip + ││┌hasindex + │││┌hasbase + ││││┌hasasz + │││││┌rex + ││││││ ┌scale + ││││││ │ ┌index or size + ││││││ │ │ ┌base or reg + │││││├──────┐├┐├─┐├─┐ 0b00000000000000000000000000000000*/ struct Slice str; - int reg, scale, mem = 0; - *disp = IsInt(a, a->i) ? GetInt(a) : 0; - if (IsPunct(a, a->i, '(')) { - ++a->i; - if ((str = GetString(a)).n) { - mem |= HASBASE; - if (!strncasecmp(str.p, "%rip", str.n)) { - mem |= ISRIP; - } else { - reg = FindRegBase(str); - if (reg == -1) InvalidRegister(a); - mem |= reg & 007; // reg - mem |= reg & 0xff00; // rex - if (((reg & 070) >> 3) == 2) mem |= HASASZ; // asz - } - } - if (!IsPunct(a, a->i, ')')) { - mem |= HASINDEX; - reg = FindRegIndex(GetString(a)); - if (reg == -1) InvalidRegister(a); - mem |= (reg & 007) << 3; // index - mem |= reg & 0xff00; // rex - if (((reg & 070) >> 3) == 2) mem |= HASASZ; // asz - if (!IsPunct(a, a->i, ')')) { - mem |= (bsr(GetInt(a)) & 3) << 6; - } + int reg, scale, modrm = 0; + if (!ConsumeSegment(a) && IsRegister(a, a->i)) { + *disp = 0; + modrm = GetRegisterRm(a) | ISREG; + } else { + if (!IsPunct(a, a->i, '(')) { + *disp = Parse(a); } else { - mem |= 4 << 3; // rsp index (hint: there is none) + *disp = -1; + } + if (IsPunct(a, a->i, '(')) { + ++a->i; + if ((str = GetSlice(a)).n) { + modrm |= HASBASE; + if (!strncasecmp(str.p, "%rip", str.n)) { + modrm |= ISRIP; + } else { + reg = FindRegBase(str); + if (reg == -1) InvalidRegister(a); + modrm |= reg & 007; // reg + modrm |= reg & 0xff00; // rex + if (((reg & 070) >> 3) == 2) modrm |= HASASZ; // asz + } + } + if (!IsPunct(a, a->i, ')')) { + modrm |= HASINDEX; + reg = FindRegIndex(GetSlice(a)); + if (reg == -1) InvalidRegister(a); + modrm |= (reg & 007) << 3; // index + modrm |= reg & 0xff00; // rex + if (((reg & 070) >> 3) == 2) modrm |= HASASZ; // asz + if (!IsPunct(a, a->i, ')')) { + modrm |= (BSR(GetInt(a)) & 3) << 6; + } + } else { + modrm |= 4 << 3; // puttin' on the riz + } + ConsumePunct(a, ')'); + } + if (modrm & HASASZ) { + EmitByte(a, ASZ); } - ConsumePunct(a, ')'); } - return mem; + return modrm; } -static void EncodeModrm(struct Assembler *a, int reg, int mem, long disp) { +static void EmitImm(struct As *a, int reg, int imm) { + switch ((reg & 030) >> 3) { + case 0: + EmitExpr(a, imm, R_X86_64_8, EmitByte); + break; + case 1: + EmitExpr(a, imm, R_X86_64_16, EmitWord); + break; + case 2: + case 3: + EmitExpr(a, imm, R_X86_64_32S, EmitLong); + break; + default: + abort(); + } +} + +static void EmitModrm(struct As *a, int reg, int modrm, int disp) { reg &= 7; reg <<= 3; - if (mem & (HASBASE | HASINDEX)) { - if (mem & ISRIP) { - Emit(a, 005 | reg); - } else { - Emit(a, 0204 | reg); // suboptimal - Emit(a, mem); - } + if (modrm & ISREG) { + EmitByte(a, 0300 | reg | modrm & 7); } else { - Emit(a, 004 | reg); - Emit(a, 045); + if (modrm & (HASBASE | HASINDEX)) { + if (modrm & ISRIP) { + EmitByte(a, 005 | reg); + } else { + EmitByte(a, 0204 | reg); // suboptimal + EmitByte(a, modrm); + } + } else { + EmitByte(a, 004 | reg); + EmitByte(a, 045); + } + EmitExpr(a, disp, R_X86_64_32S, EmitLong); } - EmitLong(a, disp); } -static void OnMov(struct Assembler *a, struct Slice op) { - long imm, disp; - int reg, rm, mem, modrm; +static void EmitRex(struct As *a, int x) { + if (x & 0xff00) { + EmitByte(a, x >> 8); + } +} + +static void EmitOpModrm(struct As *a, long op, int reg, int modrm, int disp, + int skew) { + switch ((reg & 070) >> 3) { + case 0: + EmitVarword(a, op); + EmitModrm(a, reg, modrm, disp); + break; + case 1: + EmitByte(a, OSZ); + EmitVarword(a, op + skew); + EmitModrm(a, reg, modrm, disp); + break; + case 2: + case 3: + case 4: + EmitVarword(a, op + skew); + EmitModrm(a, reg, modrm, disp); + break; + default: + abort(); + } +} + +static void EmitRexOpModrm(struct As *a, long op, int reg, int modrm, int disp, + int skew) { + EmitRex(a, reg | modrm); + EmitOpModrm(a, op, reg, modrm, disp, skew); +} + +static void OnLea(struct As *a, struct Slice s) { + int modrm, reg, disp; + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, 0x8D, reg, modrm, disp, 0); +} + +static void OnMov(struct As *a, struct Slice s) { + int reg, modrm, disp, imm; if (IsPunct(a, a->i, '$')) { ++a->i; - imm = GetInt(a); + imm = Parse(a); if (IsSlice(a, a->i)) { // imm -> reg reg = GetRegisterRm(a); - if (reg & 0xff00) { - Emit(a, reg >> 8); - } switch ((reg & 070) >> 3) { case 0: - Emit(a, 0xb0 + (reg & 7)); - Emit(a, imm); + EmitRex(a, reg); + EmitByte(a, 0xb0 + (reg & 7)); + EmitExpr(a, imm, R_X86_64_8, EmitByte); break; case 1: - Emit(a, OSZ); - Emit(a, 0xb8 + (reg & 7)); - EmitWord(a, imm); + EmitRex(a, reg); + EmitByte(a, OSZ); + EmitByte(a, 0xb8 + (reg & 7)); + EmitExpr(a, imm, R_X86_64_16, EmitWord); break; case 2: - Emit(a, 0xb8 + (reg & 7)); - EmitLong(a, imm); + EmitRex(a, reg); + EmitByte(a, 0xb8 + (reg & 7)); + EmitExpr(a, imm, R_X86_64_32, EmitLong); break; case 3: - Emit(a, 0xb8 + (reg & 7)); // suboptimal - EmitQuad(a, imm); + EmitRex(a, reg); + EmitByte(a, 0xb8 + (reg & 7)); // suboptimal + EmitExpr(a, imm, R_X86_64_64, EmitQuad); break; default: Fail(a, "todo movd/movq"); } - } else { // imm -> mem - mem = ParseMemory(a, &disp); - if (mem & 0xff00) { - Emit(a, mem >> 8); - } - switch (GetOpSize(a, op)) { + } else { // imm -> modrm + modrm = ParseModrm(a, &disp); + switch (GetOpSize(a, s, modrm, 1)) { case 0: - Emit(a, 0xc6); - EncodeModrm(a, 0, mem, disp); - Emit(a, imm); + EmitRex(a, modrm); + EmitByte(a, 0xc6); + EmitModrm(a, 0, modrm, disp); + EmitExpr(a, imm, R_X86_64_8, EmitByte); break; case 1: - Emit(a, OSZ); - Emit(a, 0xc7); - EncodeModrm(a, 0, mem, disp); - EmitWord(a, imm); + EmitByte(a, OSZ); + EmitRex(a, modrm); + EmitByte(a, 0xc7); + EmitModrm(a, 0, modrm, disp); + EmitExpr(a, imm, R_X86_64_16, EmitWord); break; case 2: - Emit(a, 0xc7); - EncodeModrm(a, 0, mem, disp); - EmitLong(a, imm); + EmitRex(a, modrm); + EmitByte(a, 0xc7); + EmitModrm(a, 0, modrm, disp); + EmitExpr(a, imm, R_X86_64_32, EmitLong); break; case 3: - Emit(a, 0xc7); // suboptimal - EncodeModrm(a, 0, mem, disp); - EmitQuad(a, imm); + EmitRex(a, modrm | REXW << 8); + EmitByte(a, 0xc7); // suboptimal + EmitModrm(a, 0, modrm, disp); + EmitExpr(a, imm, R_X86_64_32, EmitLong); break; default: - unreachable; + abort(); } } - } else if (IsSlice(a, a->i)) { + } else if (IsSlice(a, a->i)) { // reg -> reg/modrm reg = GetRegisterReg(a); - if (IsSlice(a, a->i)) { // reg -> reg - rm = GetRegisterRm(a); - if (((reg & 070) >> 3) != ((rm & 070) >> 3)) { - Fail(a, "size mismatch"); - } - if ((reg | rm) & 0xff00) { - Emit(a, (reg | rm) >> 8); - } - modrm = 0300 | (reg & 7) << 3 | rm & 7; - switch ((reg & 070) >> 3) { - case 0: - Emit(a, 0x88); - Emit(a, modrm); - break; - case 1: - Emit(a, OSZ); - Emit(a, 0x89); - Emit(a, modrm); - break; - case 2: - case 3: - Emit(a, 0x89); - Emit(a, modrm); - break; - case 4: - Emit(a, 0x66); - Emit(a, 0x0F); - Emit(a, 0x6F); - Emit(a, modrm); - break; - default: - unreachable; - } - } else { // reg -> mem - mem = ParseMemory(a, &disp); - if ((reg | mem) & 0xff00) { - Emit(a, (reg | mem) >> 8); - } - modrm = 0300 | (reg & 7) << 3 | rm & 7; - switch ((reg & 070) >> 3) { - case 0: - Emit(a, 0x88); - EncodeModrm(a, reg, mem, 0); - break; - case 1: - Emit(a, OSZ); - Emit(a, 0x89); - EncodeModrm(a, reg, mem, 0); - break; - case 2: - case 3: - Emit(a, 0x89); - EncodeModrm(a, reg, mem, 0); - break; - default: - Fail(a, "todo movdqu"); - } - } - } else { // mem -> reg - mem = ParseMemory(a, &disp); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x88, reg, modrm, disp, 1); + } else { // modrm -> reg + modrm = ParseModrm(a, &disp); reg = GetRegisterReg(a); - if ((reg | mem) & 0xff00) { - Emit(a, (reg | mem) >> 8); - } - modrm = 0300 | (reg & 7) << 3 | rm & 7; - switch ((reg & 070) >> 3) { - case 0: - Emit(a, 0x8A); - EncodeModrm(a, reg, mem, 0); - break; - case 1: - Emit(a, OSZ); - Emit(a, 0x8B); - EncodeModrm(a, reg, mem, 0); - break; - case 2: - case 3: - Emit(a, 0x8B); - EncodeModrm(a, reg, mem, 0); - break; - default: - Fail(a, "todo movdqu"); - } + EmitRexOpModrm(a, 0x8A, reg, modrm, disp, 1); } } -static void OnPush(struct Assembler *a) { - long x; - int reg; +static void EmitMovx(struct As *a, struct Slice opname, int op) { + int reg, modrm, disp; + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRex(a, reg); + if (((reg & 070) >> 3) == 1) EmitByte(a, OSZ); + EmitVarword(a, op + !!GetOpSize(a, opname, modrm, 2)); + EmitModrm(a, reg, modrm, disp); +} + +static void OnMovzbwx(struct As *a, struct Slice s) { + EmitMovx(a, s, 0x0FB6); +} + +static void OnMovsbwx(struct As *a, struct Slice s) { + EmitMovx(a, s, 0x0FBE); +} + +static void OnMovslq(struct As *a, struct Slice s) { + int reg, modrm, disp; + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitByte(a, REXW); + EmitByte(a, 0x63); + EmitModrm(a, reg, modrm, disp); +} + +static noinline void OpAluImpl(struct As *a, struct Slice opname, int op) { + int reg, modrm, imm, disp; + if (IsPunct(a, a->i, '$')) { // imm -> reg/modrm + ++a->i; + imm = Parse(a); + modrm = ParseModrm(a, &disp); + reg = GetOpSize(a, opname, modrm, 1) << 3 | op; + EmitRexOpModrm(a, 0x80, reg, modrm, disp, 1); // suboptimal + EmitImm(a, reg, imm); + } else if (IsSlice(a, a->i)) { // reg -> reg/modrm + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, op << 3, reg, modrm, disp, 1); + } else { // modrm -> reg + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, op << 3 | 2, reg, modrm, disp, 1); + } +} + +static noinline void OpAlu(struct As *a, struct Slice opname, int op) { + OpAluImpl(a, opname, op); +} + +static noinline void OpBsuImpl(struct As *a, struct Slice opname, int op) { + int reg, modrm, imm, disp; if (IsPunct(a, a->i, '$')) { ++a->i; - x = GetInt(a); - if (-128 <= x && x <= 127) { - Emit(a, 0x6A); - Emit(a, x); - } else { - Emit(a, 0x68); - EmitLong(a, x); - } + imm = Parse(a); + } else if (IsSlice(a, a->i) && + (a->slices.p[a->things.p[a->i].i].n == 3 && + !strncasecmp(a->slices.p[a->things.p[a->i].i].p, "%cl", 3)) && + !IsPunct(a, a->i + 1, ';')) { + ++a->i; + modrm = ParseModrm(a, &disp); + reg = GetOpSize(a, opname, modrm, 1) << 3 | op; + EmitRexOpModrm(a, 0xC0, reg, modrm, disp, 1); + return; } else { - reg = RemoveRexw(GetRegisterRm(a)); - if (reg & 0xff00) Emit(a, reg >> 8); - if (((reg & 070) >> 3) == 1) Emit(a, OSZ); - Emit(a, 0x50 + (reg & 7)); + AppendExpr(a); + a->exprs.p[a->exprs.n - 1].kind = EX_INT; + a->exprs.p[a->exprs.n - 1].tok = a->i; + a->exprs.p[a->exprs.n - 1].x = 1; + imm = a->exprs.n - 1; + } + modrm = ParseModrm(a, &disp); + reg = GetOpSize(a, opname, modrm, 1) << 3 | op; + EmitRexOpModrm(a, 0xC0, reg, modrm, disp, 1); // suboptimal + EmitByte(a, imm); +} + +static noinline void OpBsu(struct As *a, struct Slice opname, int op) { + OpBsuImpl(a, opname, op); +} + +static int OpF6(struct As *a, struct Slice s, int reg) { + int modrm, imm, disp; + modrm = ParseModrm(a, &disp); + reg |= GetOpSize(a, s, modrm, 1) << 3; + EmitRexOpModrm(a, 0xF6, reg, modrm, disp, 1); + return reg; +} + +static void OnTest(struct As *a, struct Slice s) { + int reg, modrm, imm, disp; + if (IsPunct(a, a->i, '$')) { + ++a->i; + imm = Parse(a); + reg = OpF6(a, s, 0); // suboptimal + EmitImm(a, reg, imm); + } else { + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x84, reg, modrm, disp, 1); } } -static void OnPop(struct Assembler *a) { - int reg; - reg = RemoveRexw(GetRegisterRm(a)); - if (reg & 0xff00) Emit(a, reg >> 8); - if (((reg & 070) >> 3) == 1) Emit(a, OSZ); - Emit(a, 0x58 + (reg & 7)); -} - -static void OnRet(struct Assembler *a) { +static void OnImul(struct As *a, struct Slice s) { + int reg, modrm, imm, disp; if (IsPunct(a, a->i, '$')) { ++a->i; - Emit(a, 0xC2); + imm = Parse(a); + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, 0x69, reg, modrm, disp, 0); + EmitImm(a, reg, imm); + } else { + modrm = ParseModrm(a, &disp); + if (IsPunct(a, a->i, ';')) { + reg = GetOpSize(a, s, modrm, 1) << 3 | 5; + EmitRexOpModrm(a, 0xF6, reg, modrm, disp, 1); + } else { + reg = GetRegisterReg(a); + EmitRexOpModrm(a, 0x0FAF, reg, modrm, disp, 0); + } + } +} + +static void OpBit(struct As *a, int op) { + int reg, modrm, disp; + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, op, reg, modrm, disp, 0); +} + +static void OnXchg(struct As *a, struct Slice s) { + int reg, modrm, disp; + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x86, reg, modrm, disp, 1); +} + +static void OpBump(struct As *a, struct Slice s, int reg) { + int modrm, disp; + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0xFE, GetOpSize(a, s, modrm, 1) << 3 | reg, modrm, disp, 1); +} + +static void OpShrld(struct As *a, struct Slice s, int op) { + int imm, reg, modrm, disp, skew; + if (IsSlice(a, a->i) && + (a->slices.p[a->things.p[a->i].i].n == 3 && + !strncasecmp(a->slices.p[a->things.p[a->i].i].p, "%cl", 3))) { + skew = 1; + ++a->i; + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x0F00 | op | skew, reg, modrm, disp, 0); + } else { + skew = 0; + ConsumePunct(a, '$'); + imm = GetInt(a); + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x0F00 | op | skew, reg, modrm, disp, 0); + EmitByte(a, imm); + } +} + +static void OnShrd(struct As *a, struct Slice s) { + OpShrld(a, s, 0xAC); +} + +static void OnShld(struct As *a, struct Slice s) { + OpShrld(a, s, 0xA4); +} + +static void OpSseMov(struct As *a, int opWsdVsd, int opVsdWsd) { + int reg, modrm, disp; + if (IsRegister(a, a->i)) { + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, opWsdVsd, reg, modrm, disp, 0); + } else { + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, opVsdWsd, reg, modrm, disp, 0); + } +} + +static void OpMovdqx(struct As *a, int op) { + OpSseMov(a, op + 0x10, op); +} + +static void OnMovdqu(struct As *a, struct Slice s) { + EmitByte(a, 0xF3); + OpMovdqx(a, 0x0F6F); +} + +static void OnMovups(struct As *a, struct Slice s) { + OpSseMov(a, 0x0F11, 0x0F10); +} + +static void OnMovupd(struct As *a, struct Slice s) { + EmitByte(a, 0x66); + OnMovups(a, s); +} + +static void OnMovdqa(struct As *a, struct Slice s) { + EmitByte(a, 0x66); + OpMovdqx(a, 0x0F6F); +} + +static void OnMovaps(struct As *a, struct Slice s) { + OpSseMov(a, 0x0F29, 0x0F28); +} + +static void OnMovapd(struct As *a, struct Slice s) { + EmitByte(a, 0x66); + OpSseMov(a, 0x0F29, 0x0F28); +} + +static void OnMovdq(struct As *a, struct Slice s) { + int reg, modrm, boop, disp; + EmitByte(a, 0x66); // todo mmx + if (IsRegister(a, a->i)) { + reg = GetRegisterReg(a); + modrm = ParseModrm(a, &disp); + boop = ((reg & 070) >> 3) == 4 ? 0x10 : 0; + } else { + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + boop = ((reg & 070) >> 3) == 4 ? 0x10 : 0; + } + EmitRexOpModrm(a, 0x0F6E + boop, reg, modrm, disp, 0); +} + +static void OnMovss(struct As *a, struct Slice s) { + OpSseMov(a, 0xF30F11, 0xF30F10); +} + +static void OnMovsd(struct As *a, struct Slice s) { + OpSseMov(a, 0xF20F11, 0xF20F10); +} + +static bool IsSsePrefix(int c) { + return c == 0x66 || c == 0xF2 || c == 0xF3; // must come before rex +} + +static noinline void OpSseImpl(struct As *a, int op) { + int reg, modrm, disp; + if (IsSsePrefix((op & 0xff000000) >> 24)) { + EmitByte(a, (op & 0xff000000) >> 24); + op &= 0xffffff; + } + if (IsSsePrefix((op & 0x00ff0000) >> 16)) { + EmitByte(a, (op & 0x00ff0000) >> 16); + op &= 0xffff; + } + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, op, reg, modrm, disp, 0); +} + +static noinline void OpSse(struct As *a, int op) { + OpSseImpl(a, op); +} + +static noinline void OpSseIbImpl(struct As *a, int op) { + int imm; + ConsumePunct(a, '$'); + imm = Parse(a); + OpSse(a, op); + EmitExpr(a, imm, R_X86_64_8, EmitByte); +} + +static noinline void OpSseIb(struct As *a, int op) { + OpSseIbImpl(a, op); +} + +static bool HasXmmOnLine(struct As *a) { + int i; + for (i = 0; !IsPunct(a, a->i + i, ';'); ++i) { + if (IsSlice(a, a->i + i) && a->slices.p[a->things.p[a->i + i].i].n >= 4 && + (StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") || + StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) { + return true; + } + } + return false; +} + +static void OnMovq(struct As *a, struct Slice s) { + if (HasXmmOnLine(a)) { + OnMovdq(a, s); + } else { + OnMov(a, s); + } +} + +static void OnPush(struct As *a, struct Slice s) { + int modrm, disp; + if (IsPunct(a, a->i, '$')) { + ++a->i; + EmitByte(a, 0x68); + EmitLong(a, GetInt(a)); + } else { + modrm = RemoveRexw(ParseModrm(a, &disp)); + EmitRexOpModrm(a, 0xFF, 6, modrm, disp, 0); // suboptimal + } +} + +static void OnPop(struct As *a, struct Slice s) { + int modrm, disp; + modrm = RemoveRexw(ParseModrm(a, &disp)); + EmitRexOpModrm(a, 0x8F, 0, modrm, disp, 0); // suboptimal +} + +static void OnNop(struct As *a, struct Slice opname) { + int modrm, disp; + if (IsPunct(a, a->i, ';')) { + EmitByte(a, 0x90); + } else { + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x0F1F, 6, modrm, disp, 0); + } +} + +static void OnRet(struct As *a, struct Slice s) { + if (IsPunct(a, a->i, '$')) { + ++a->i; + EmitByte(a, 0xC2); EmitWord(a, GetInt(a)); } else { - Emit(a, 0xC3); + EmitByte(a, 0xC3); } } -static void OnLeave(struct Assembler *a) { - Emit(a, 0xC9); -} -static void OnHlt(struct Assembler *a) { - Emit(a, 0xF4); -} -static void OnCmc(struct Assembler *a) { - Emit(a, 0xF5); -} -static void OnClc(struct Assembler *a) { - Emit(a, 0xF8); -} -static void OnStc(struct Assembler *a) { - Emit(a, 0xF9); -} -static void OnCli(struct Assembler *a) { - Emit(a, 0xFA); -} -static void OnSti(struct Assembler *a) { - Emit(a, 0xFB); -} -static void OnCld(struct Assembler *a) { - Emit(a, 0xFC); -} -static void OnStd(struct Assembler *a) { - Emit(a, 0xFD); -} -static void OnLodsb(struct Assembler *a) { - Emit(a, 0xAC); -} -static void OnLodsw(struct Assembler *a) { - Emit(a, OSZ); - Emit(a, 0xAD); -} -static void OnLodsl(struct Assembler *a) { - Emit(a, 0xAD); -} -static void OnLodsq(struct Assembler *a) { - Emit(a, REXW); - Emit(a, 0xAD); -} -static void OnStosb(struct Assembler *a) { - Emit(a, 0xAA); -} -static void OnStosw(struct Assembler *a) { - Emit(a, OSZ); - Emit(a, 0xAB); -} -static void OnStosl(struct Assembler *a) { - Emit(a, 0xAB); -} -static void OnStosq(struct Assembler *a) { - Emit(a, REXW); - Emit(a, 0xAB); -} -static void OnMovsb(struct Assembler *a) { - Emit(a, 0xA4); -} -static void OnMovsw(struct Assembler *a) { - Emit(a, OSZ); - Emit(a, 0xA5); -} -static void OnMovsl(struct Assembler *a) { - Emit(a, 0xA5); -} -static void OnMovsq(struct Assembler *a) { - Emit(a, REXW); - Emit(a, 0xA5); +static noinline void OpCmovccImpl(struct As *a, int cc) { + int reg, modrm, disp; + modrm = ParseModrm(a, &disp); + reg = GetRegisterReg(a); + EmitRexOpModrm(a, 0x0F40 | cc, reg, modrm, disp, 0); } -static bool Prefix(struct Assembler *a, const char *s, size_t n) { - int i; - char key[8]; - if (n <= 8) { - memset(key, 0, 8); - for (i = 0; i < n; ++i) key[i] = tolower(s[i]); - for (i = 0; i < sizeof(kPrefix) / sizeof(*kPrefix); i++) { - if (!memcmp(key, kPrefix[i], 8)) { - Emit(a, kPrefixByte[i]); +static noinline void OpCmovcc(struct As *a, int cc) { + OpCmovccImpl(a, cc); +} + +static noinline void OpSetccImpl(struct As *a, int cc) { + int modrm, disp; + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x0F90 | cc, 6, modrm, disp, 0); +} + +static noinline void OpSetcc(struct As *a, int cc) { + OpSetccImpl(a, cc); +} + +static void OnFile(struct As *a, struct Slice s) { + int fileno; + struct Slice path; + fileno = GetInt(a); + path = GetSlice(a); + // TODO: DWARF +} + +static void OnLoc(struct As *a, struct Slice s) { + int fileno, lineno; + fileno = GetInt(a); + lineno = GetInt(a); + // TODO: DWARF +} + +static void OnCall(struct As *a, struct Slice s) { + int modrm, disp; + if (IsPunct(a, a->i, '*')) ++a->i; + modrm = RemoveRexw(ParseModrm(a, &disp)); + if (modrm & (ISREG | ISRIP | HASINDEX | HASBASE)) { + EmitRexOpModrm(a, 0xFF, 2, modrm, disp, 0); + } else { + EmitByte(a, 0xE8); + EmitExpr(a, disp, R_X86_64_PC32, EmitLong); + } +} + +static noinline void OpJmpImpl(struct As *a, int cc) { + int modrm, disp; + if (IsPunct(a, a->i, '*')) ++a->i; + modrm = RemoveRexw(ParseModrm(a, &disp)); + if (cc == -1) { + EmitRexOpModrm(a, 0xFF, 4, modrm, disp, 0); + } else { + EmitByte(a, 0x0F); + EmitByte(a, 0x80 + cc); + EmitExpr(a, disp, R_X86_64_PC32, EmitLong); + } +} + +static noinline void OpJmp(struct As *a, int cc) { + OpJmpImpl(a, cc); +} + +static noinline void OpFpu1Impl(struct As *a, int op, int reg) { + int modrm, disp; + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, op, reg, modrm, disp, 0); +} + +static noinline void OpFpu1(struct As *a, int op, int reg) { + OpFpu1Impl(a, op, reg); +} + +static void OnFucomi(struct As *a, struct Slice s) { + int reg, rm; + rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1; + reg = !IsPunct(a, a->i, ';') ? GetRegisterReg(a) : 0; + if (reg & 7) Fail(a, "bad register"); + EmitByte(a, 0xDB); + EmitByte(a, 0350 | rm & 7); +} + +static void OnFxch(struct As *a, struct Slice s) { + int rm; + rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1; + EmitByte(a, 0xD9); + EmitByte(a, 0310 | rm & 7); +} + +static void OnBswap(struct As *a, struct Slice s) { + int srm; + srm = GetRegisterRm(a); + EmitRex(a, srm); + EmitByte(a, 0x0F); + EmitByte(a, 0310 | srm & 7); +} + +// clang-format off +static void OnAdc(struct As *a, struct Slice s) { OpAlu(a, s, 2); } +static void OnAdd(struct As *a, struct Slice s) { OpAlu(a, s, 0); } +static void OnAddpd(struct As *a, struct Slice s) { OpSse(a, 0x660F58); } +static void OnAddps(struct As *a, struct Slice s) { OpSse(a, 0x0F58); } +static void OnAddsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F58); } +static void OnAddss(struct As *a, struct Slice s) { OpSse(a, 0xF30F58); } +static void OnAddsubpd(struct As *a, struct Slice s) { OpSse(a, 0x660FD0); } +static void OnAddsubps(struct As *a, struct Slice s) { OpSse(a, 0xF20FD0); } +static void OnAnd(struct As *a, struct Slice s) { OpAlu(a, s, 4); } +static void OnAndnpd(struct As *a, struct Slice s) { OpSse(a, 0x660F55); } +static void OnAndnps(struct As *a, struct Slice s) { OpSse(a, 0x0F55); } +static void OnAndpd(struct As *a, struct Slice s) { OpSse(a, 0x660F54); } +static void OnAndps(struct As *a, struct Slice s) { OpSse(a, 0x0F54); } +static void OnBlendpd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0D); } +static void OnBlendvpd(struct As *a, struct Slice s) { OpSse(a, 0x660F3815); } +static void OnBsf(struct As *a, struct Slice s) { OpBit(a, 0x0FBC); } +static void OnBsr(struct As *a, struct Slice s) { OpBit(a, 0x0FBD); } +static void OnCbtw(struct As *a, struct Slice s) { EmitVarword(a, 0x6698); } +static void OnClc(struct As *a, struct Slice s) { EmitByte(a, 0xF8); } +static void OnCld(struct As *a, struct Slice s) { EmitByte(a, 0xFC); } +static void OnCli(struct As *a, struct Slice s) { EmitByte(a, 0xFA); } +static void OnCltd(struct As *a, struct Slice s) { EmitByte(a, 0x99); } +static void OnCltq(struct As *a, struct Slice s) { EmitVarword(a, 0x4898); } +static void OnCmc(struct As *a, struct Slice s) { EmitByte(a, 0xF5); } +static void OnCmovb(struct As *a, struct Slice s) { OpCmovcc(a, 2); } +static void OnCmovbe(struct As *a, struct Slice s) { OpCmovcc(a, 6); } +static void OnCmovl(struct As *a, struct Slice s) { OpCmovcc(a, 12); } +static void OnCmovle(struct As *a, struct Slice s) { OpCmovcc(a, 14); } +static void OnCmovnb(struct As *a, struct Slice s) { OpCmovcc(a, 3); } +static void OnCmovnbe(struct As *a, struct Slice s) { OpCmovcc(a, 7); } +static void OnCmovnl(struct As *a, struct Slice s) { OpCmovcc(a, 13); } +static void OnCmovnle(struct As *a, struct Slice s) { OpCmovcc(a, 15); } +static void OnCmovno(struct As *a, struct Slice s) { OpCmovcc(a, 1); } +static void OnCmovnp(struct As *a, struct Slice s) { OpCmovcc(a, 11); } +static void OnCmovns(struct As *a, struct Slice s) { OpCmovcc(a, 9); } +static void OnCmovnz(struct As *a, struct Slice s) { OpCmovcc(a, 5); } +static void OnCmovo(struct As *a, struct Slice s) { OpCmovcc(a, 0); } +static void OnCmovp(struct As *a, struct Slice s) { OpCmovcc(a, 10); } +static void OnCmovs(struct As *a, struct Slice s) { OpCmovcc(a, 8); } +static void OnCmovz(struct As *a, struct Slice s) { OpCmovcc(a, 4); } +static void OnCmp(struct As *a, struct Slice s) { OpAlu(a, s, 7); } +static void OnCmppd(struct As *a, struct Slice s) { OpSseIb(a, 0x660FC2); } +static void OnCmpps(struct As *a, struct Slice s) { OpSseIb(a, 0x0FC2); } +static void OnCmpsd(struct As *a, struct Slice s) { OpSseIb(a, 0xF20FC2); } +static void OnCmpss(struct As *a, struct Slice s) { OpSseIb(a, 0xF30FC2); } +static void OnComisd(struct As *a, struct Slice s) { OpSse(a, 0x660F2F); } +static void OnComiss(struct As *a, struct Slice s) { OpSse(a, 0x0F2F); } +static void OnCqto(struct As *a, struct Slice s) { EmitVarword(a, 0x4899); } +static void OnCvtdq2pd(struct As *a, struct Slice s) { OpSse(a, 0xF30FE6); } +static void OnCvtdq2ps(struct As *a, struct Slice s) { OpSse(a, 0xF5B); } +static void OnCvtpd2dq(struct As *a, struct Slice s) { OpSse(a, 0xF20FE6); } +static void OnCvtpd2ps(struct As *a, struct Slice s) { OpSse(a, 0x660F5A); } +static void OnCvtps2dq(struct As *a, struct Slice s) { OpSse(a, 0x660F5B); } +static void OnCvtps2pd(struct As *a, struct Slice s) { OpSse(a, 0x0F5A); } +static void OnCvtsd2ss(struct As *a, struct Slice s) { OpSse(a, 0xF20F5A); } +static void OnCvtsi2sd(struct As *a, struct Slice s) { OpSse(a, 0xF20F2A); } +static void OnCvtsi2ss(struct As *a, struct Slice s) { OpSse(a, 0xF30F2A); } +static void OnCvtss2sd(struct As *a, struct Slice s) { OpSse(a, 0xF30F5A); } +static void OnCvttpd2dq(struct As *a, struct Slice s) { OpSse(a, 0x660FE6); } +static void OnCvttps2dq(struct As *a, struct Slice s) { OpSse(a, 0xF30F5B); } +static void OnCvttsd2si(struct As *a, struct Slice s) { OpSse(a, 0xF20F2C); } +static void OnCvttss2si(struct As *a, struct Slice s) { OpSse(a, 0xF30F2C); } +static void OnCwtd(struct As *a, struct Slice s) { EmitVarword(a, 0x6699); } +static void OnCwtl(struct As *a, struct Slice s) { EmitByte(a, 0x98); } +static void OnDec(struct As *a, struct Slice s) { OpBump(a, s, 1); } +static void OnDiv(struct As *a, struct Slice s) { OpF6(a, s, 6); } +static void OnDivpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5E); } +static void OnDivps(struct As *a, struct Slice s) { OpSse(a, 0x0F5E); } +static void OnDivsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5E); } +static void OnDivss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5E); } +static void OnDppd(struct As *a, struct Slice s) { OpSse(a, 0x660F3A41); } +static void OnFabs(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e1); } +static void OnFaddl(struct As *a, struct Slice s) { OpFpu1(a, 0xDC, 0); } +static void OnFaddp(struct As *a, struct Slice s) { EmitVarword(a, 0xdec1); } +static void OnFadds(struct As *a, struct Slice s) { OpFpu1(a, 0xD8, 0); } +static void OnFchs(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e0); } +static void OnFcomip(struct As *a, struct Slice s) { EmitVarword(a, 0xdff1); } +static void OnFdivrp(struct As *a, struct Slice s) { EmitVarword(a, 0xdef9); } +static void OnFildl(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 0); } +static void OnFildll(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); } +static void OnFildq(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); } +static void OnFilds(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 0); } +static void OnFisttpq(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 1); } +static void OnFisttps(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 1); } +static void OnFld(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 0); } +static void OnFld1(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e8); } +static void OnFldcw(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 5); } +static void OnFldl(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 0); } +static void OnFldl2e(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ea); } +static void OnFldl2t(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e9); } +static void OnFldlg2(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ec); } +static void OnFldln2(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ed); } +static void OnFldpi(struct As *a, struct Slice s) { EmitVarword(a, 0xd9eb); } +static void OnFlds(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 0); } +static void OnFldt(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 0); } +static void OnFldz(struct As *a, struct Slice s) { EmitVarword(a, 0xd9ee); } +static void OnFmulp(struct As *a, struct Slice s) { EmitVarword(a, 0xdec9); } +static void OnFnstcw(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 7); } +static void OnFnstsw(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 4); } +static void OnFstp(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 3); } +static void OnFstpl(struct As *a, struct Slice s) { OpFpu1(a, 0xDD, 3); } +static void OnFstps(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 3); } +static void OnFstpt(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 7); } +static void OnFsubrp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEE9); } +static void OnFtst(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E4); } +static void OnFucomip(struct As *a, struct Slice s) { EmitVarword(a, 0xDFE9); } +static void OnFwait(struct As *a, struct Slice s) { EmitByte(a, 0x9B); } +static void OnFxam(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e5); } +static void OnHaddpd(struct As *a, struct Slice s) { OpSse(a, 0x660F7C); } +static void OnHaddps(struct As *a, struct Slice s) { OpSse(a, 0xF20F7C); } +static void OnHlt(struct As *a, struct Slice s) { EmitByte(a, 0xF4); } +static void OnHsubpd(struct As *a, struct Slice s) { OpSse(a, 0x660F7D); } +static void OnHsubps(struct As *a, struct Slice s) { OpSse(a, 0xF20F7D); } +static void OnIdiv(struct As *a, struct Slice s) { OpF6(a, s, 7); } +static void OnInc(struct As *a, struct Slice s) { OpBump(a, s, 0); } +static void OnInt1(struct As *a, struct Slice s) { EmitByte(a, 0xF1); } +static void OnInt3(struct As *a, struct Slice s) { EmitByte(a, 0xCC); } +static void OnJb(struct As *a, struct Slice s) { OpJmp(a, 2); } +static void OnJbe(struct As *a, struct Slice s) { OpJmp(a, 6); } +static void OnJl(struct As *a, struct Slice s) { OpJmp(a, 12); } +static void OnJle(struct As *a, struct Slice s) { OpJmp(a, 14); } +static void OnJmp(struct As *a, struct Slice s) { OpJmp(a, -1); } +static void OnJnb(struct As *a, struct Slice s) { OpJmp(a, 3); } +static void OnJnbe(struct As *a, struct Slice s) { OpJmp(a, 7); } +static void OnJnl(struct As *a, struct Slice s) { OpJmp(a, 13); } +static void OnJnle(struct As *a, struct Slice s) { OpJmp(a, 15); } +static void OnJno(struct As *a, struct Slice s) { OpJmp(a, 1); } +static void OnJnp(struct As *a, struct Slice s) { OpJmp(a, 11); } +static void OnJns(struct As *a, struct Slice s) { OpJmp(a, 9); } +static void OnJnz(struct As *a, struct Slice s) { OpJmp(a, 5); } +static void OnJo(struct As *a, struct Slice s) { OpJmp(a, 0); } +static void OnJp(struct As *a, struct Slice s) { OpJmp(a, 10); } +static void OnJs(struct As *a, struct Slice s) { OpJmp(a, 8); } +static void OnJz(struct As *a, struct Slice s) { OpJmp(a, 4); } +static void OnLeave(struct As *a, struct Slice s) { EmitByte(a, 0xC9); } +static void OnLodsb(struct As *a, struct Slice s) { EmitByte(a, 0xAC); } +static void OnLodsl(struct As *a, struct Slice s) { EmitByte(a, 0xAD); } +static void OnLodsq(struct As *a, struct Slice s) { EmitVarword(a, 0x48AD); } +static void OnLodsw(struct As *a, struct Slice s) { EmitVarword(a, 0x66AD); } +static void OnMaxpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5F); } +static void OnMaxps(struct As *a, struct Slice s) { OpSse(a, 0x0F5F); } +static void OnMaxsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5F); } +static void OnMaxss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5F); } +static void OnMinpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5D); } +static void OnMinps(struct As *a, struct Slice s) { OpSse(a, 0x0F5D); } +static void OnMinsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5D); } +static void OnMinss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5D); } +static void OnMovmskpd(struct As *a, struct Slice s) { OpSse(a, 0x660F50); } +static void OnMovmskps(struct As *a, struct Slice s) { OpSse(a, 0x0F50); } +static void OnMovsb(struct As *a, struct Slice s) { EmitByte(a, 0xA4); } +static void OnMovsl(struct As *a, struct Slice s) { EmitByte(a, 0xA5); } +static void OnMovsq(struct As *a, struct Slice s) { EmitVarword(a, 0x48A5); } +static void OnMovsw(struct As *a, struct Slice s) { EmitVarword(a, 0x66A5); } +static void OnMpsadbw(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A42); } +static void OnMul(struct As *a, struct Slice s) { OpF6(a, s, 4); } +static void OnMulpd(struct As *a, struct Slice s) { OpSse(a, 0x660F59); } +static void OnMulps(struct As *a, struct Slice s) { OpSse(a, 0x0F59); } +static void OnMulsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F59); } +static void OnMulss(struct As *a, struct Slice s) { OpSse(a, 0xF30F59); } +static void OnNeg(struct As *a, struct Slice s) { OpF6(a, s, 3); } +static void OnNot(struct As *a, struct Slice s) { OpF6(a, s, 2); } +static void OnOr(struct As *a, struct Slice s) { OpAlu(a, s, 1); } +static void OnOrpd(struct As *a, struct Slice s) { OpSse(a, 0x660F56); } +static void OnOrps(struct As *a, struct Slice s) { OpSse(a, 0x0F56); } +static void OnPabsb(struct As *a, struct Slice s) { OpSse(a, 0x660F381C); } +static void OnPabsd(struct As *a, struct Slice s) { OpSse(a, 0x660F381E); } +static void OnPabsw(struct As *a, struct Slice s) { OpSse(a, 0x660F381D); } +static void OnPackssdw(struct As *a, struct Slice s) { OpSse(a, 0x660F6B); } +static void OnPacksswb(struct As *a, struct Slice s) { OpSse(a, 0x660F63); } +static void OnPackusdw(struct As *a, struct Slice s) { OpSse(a, 0x660F382B); } +static void OnPackuswb(struct As *a, struct Slice s) { OpSse(a, 0x660F67); } +static void OnPaddb(struct As *a, struct Slice s) { OpSse(a, 0x660FFC); } +static void OnPaddd(struct As *a, struct Slice s) { OpSse(a, 0x660FFE); } +static void OnPaddq(struct As *a, struct Slice s) { OpSse(a, 0x660FD4); } +static void OnPaddsb(struct As *a, struct Slice s) { OpSse(a, 0x660FEC); } +static void OnPaddsw(struct As *a, struct Slice s) { OpSse(a, 0x660FED); } +static void OnPaddusb(struct As *a, struct Slice s) { OpSse(a, 0x660FDC); } +static void OnPaddusw(struct As *a, struct Slice s) { OpSse(a, 0x660FDD); } +static void OnPaddw(struct As *a, struct Slice s) { OpSse(a, 0x660FFD); } +static void OnPalignr(struct As *a, struct Slice s) { OpSse(a, 0x660F3A0F); } +static void OnPand(struct As *a, struct Slice s) { OpSse(a, 0x660FDB); } +static void OnPandn(struct As *a, struct Slice s) { OpSse(a, 0x660FDF); } +static void OnPause(struct As *a, struct Slice s) { EmitVarword(a, 0xF390); } +static void OnPavgb(struct As *a, struct Slice s) { OpSse(a, 0x660FE0); } +static void OnPavgw(struct As *a, struct Slice s) { OpSse(a, 0x660FE3); } +static void OnPblendvb(struct As *a, struct Slice s) { OpSse(a, 0x660F3810); } +static void OnPblendw(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0E); } +static void OnPcmpeqb(struct As *a, struct Slice s) { OpSse(a, 0x660F74); } +static void OnPcmpeqd(struct As *a, struct Slice s) { OpSse(a, 0x660F76); } +static void OnPcmpeqq(struct As *a, struct Slice s) { OpSse(a, 0x660F3829); } +static void OnPcmpeqw(struct As *a, struct Slice s) { OpSse(a, 0x660F75); } +static void OnPcmpgtb(struct As *a, struct Slice s) { OpSse(a, 0x660F64); } +static void OnPcmpgtd(struct As *a, struct Slice s) { OpSse(a, 0x660F66); } +static void OnPcmpgtq(struct As *a, struct Slice s) { OpSse(a, 0x660F3837); } +static void OnPcmpgtw(struct As *a, struct Slice s) { OpSse(a, 0x660F65); } +static void OnPcmpistri(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A63); } +static void OnPcmpistrm(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A62); } +static void OnPhaddd(struct As *a, struct Slice s) { OpSse(a, 0x660F3802); } +static void OnPhaddsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3803); } +static void OnPhaddw(struct As *a, struct Slice s) { OpSse(a, 0x660F3801); } +static void OnPhminposuw(struct As *a, struct Slice s) { OpSse(a, 0x660F3841); } +static void OnPhsubd(struct As *a, struct Slice s) { OpSse(a, 0x660F3806); } +static void OnPhsubsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3807); } +static void OnPhsubw(struct As *a, struct Slice s) { OpSse(a, 0x660F3805); } +static void OnPmaddubsw(struct As *a, struct Slice s) { OpSse(a, 0x660F3804); } +static void OnPmaddwd(struct As *a, struct Slice s) { OpSse(a, 0x660FF5); } +static void OnPmaxsb(struct As *a, struct Slice s) { OpSse(a, 0x660F383C); } +static void OnPmaxsd(struct As *a, struct Slice s) { OpSse(a, 0x660F383D); } +static void OnPmaxsw(struct As *a, struct Slice s) { OpSse(a, 0x660FEE); } +static void OnPmaxub(struct As *a, struct Slice s) { OpSse(a, 0x660FDE); } +static void OnPmaxud(struct As *a, struct Slice s) { OpSse(a, 0x660F383F); } +static void OnPmaxuw(struct As *a, struct Slice s) { OpSse(a, 0x660F383E); } +static void OnPminsb(struct As *a, struct Slice s) { OpSse(a, 0x660F3838); } +static void OnPminsd(struct As *a, struct Slice s) { OpSse(a, 0x660F3839); } +static void OnPminsw(struct As *a, struct Slice s) { OpSse(a, 0x660FEA); } +static void OnPminub(struct As *a, struct Slice s) { OpSse(a, 0x660FDA); } +static void OnPminud(struct As *a, struct Slice s) { OpSse(a, 0x660F383B); } +static void OnPminuw(struct As *a, struct Slice s) { OpSse(a, 0x660F383A); } +static void OnPmovmskb(struct As *a, struct Slice s) { OpSse(a, 0x660FD7); } +static void OnPmuldq(struct As *a, struct Slice s) { OpSse(a, 0x660F3828); } +static void OnPmulhrsw(struct As *a, struct Slice s) { OpSse(a, 0x660F380B); } +static void OnPmulhuw(struct As *a, struct Slice s) { OpSse(a, 0x660FE4); } +static void OnPmulhw(struct As *a, struct Slice s) { OpSse(a, 0x660FE5); } +static void OnPmulld(struct As *a, struct Slice s) { OpSse(a, 0x660F3840); } +static void OnPmullw(struct As *a, struct Slice s) { OpSse(a, 0x660FD5); } +static void OnPmuludq(struct As *a, struct Slice s) { OpSse(a, 0x660FF4); } +static void OnPopcnt(struct As *a, struct Slice s) { OpBit(a, 0xF30FB8); } +static void OnPor(struct As *a, struct Slice s) { OpSse(a, 0x660FEB); } +static void OnPsadbw(struct As *a, struct Slice s) { OpSse(a, 0x660FF6); } +static void OnPshufb(struct As *a, struct Slice s) { OpSse(a, 0x660F3800); } +static void OnPshufd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F70); } +static void OnPshufhw(struct As *a, struct Slice s) { OpSseIb(a, 0xF30F70); } +static void OnPshuflw(struct As *a, struct Slice s) { OpSseIb(a, 0xF20F70); } +static void OnPsignb(struct As *a, struct Slice s) { OpSse(a, 0x660F3808); } +static void OnPsignd(struct As *a, struct Slice s) { OpSse(a, 0x660F380A); } +static void OnPsignw(struct As *a, struct Slice s) { OpSse(a, 0x660F3809); } +static void OnPslld(struct As *a, struct Slice s) { OpSse(a, 0x660FF2); } +static void OnPsllq(struct As *a, struct Slice s) { OpSse(a, 0x660FF3); } +static void OnPsllw(struct As *a, struct Slice s) { OpSse(a, 0x660FF1); } +static void OnPsrad(struct As *a, struct Slice s) { OpSse(a, 0x660FE2); } +static void OnPsraw(struct As *a, struct Slice s) { OpSse(a, 0x660FE1); } +static void OnPsrld(struct As *a, struct Slice s) { OpSse(a, 0x660FD2); } +static void OnPsrlq(struct As *a, struct Slice s) { OpSse(a, 0x660FD3); } +static void OnPsrlw(struct As *a, struct Slice s) { OpSse(a, 0x660FD1); } +static void OnPsubb(struct As *a, struct Slice s) { OpSse(a, 0x660FF8); } +static void OnPsubd(struct As *a, struct Slice s) { OpSse(a, 0x660FFA); } +static void OnPsubq(struct As *a, struct Slice s) { OpSse(a, 0x660FFB); } +static void OnPsubsb(struct As *a, struct Slice s) { OpSse(a, 0x660FE8); } +static void OnPsubsw(struct As *a, struct Slice s) { OpSse(a, 0x660FE9); } +static void OnPsubusb(struct As *a, struct Slice s) { OpSse(a, 0x660FD8); } +static void OnPsubusw(struct As *a, struct Slice s) { OpSse(a, 0x660FD9); } +static void OnPsubw(struct As *a, struct Slice s) { OpSse(a, 0x660FF9); } +static void OnPtest(struct As *a, struct Slice s) { OpSse(a, 0x660F3817); } +static void OnPunpckhbw(struct As *a, struct Slice s) { OpSse(a, 0x660F68); } +static void OnPunpckhdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6A); } +static void OnPunpckhqdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6D); } +static void OnPunpckhwd(struct As *a, struct Slice s) { OpSse(a, 0x660F69); } +static void OnPunpcklbw(struct As *a, struct Slice s) { OpSse(a, 0x660F60); } +static void OnPunpckldq(struct As *a, struct Slice s) { OpSse(a, 0x660F62); } +static void OnPunpcklqdq(struct As *a, struct Slice s) { OpSse(a, 0x660F6C); } +static void OnPunpcklwd(struct As *a, struct Slice s) { OpSse(a, 0x660F61); } +static void OnPxor(struct As *a, struct Slice s) { OpSse(a, 0x660FEF); } +static void OnRcl(struct As *a, struct Slice s) { OpBsu(a, s, 2); } +static void OnRcpps(struct As *a, struct Slice s) { OpSse(a, 0x0F53); } +static void OnRcpss(struct As *a, struct Slice s) { OpSse(a, 0xF30F53); } +static void OnRcr(struct As *a, struct Slice s) { OpBsu(a, s, 3); } +static void OnRol(struct As *a, struct Slice s) { OpBsu(a, s, 0); } +static void OnRor(struct As *a, struct Slice s) { OpBsu(a, s, 1); } +static void OnRoundsd(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0B); } +static void OnRoundss(struct As *a, struct Slice s) { OpSseIb(a, 0x660F3A0A); } +static void OnRsqrtps(struct As *a, struct Slice s) { OpSse(a, 0x0F52); } +static void OnRsqrtss(struct As *a, struct Slice s) { OpSse(a, 0xF30F52); } +static void OnSal(struct As *a, struct Slice s) { OpBsu(a, s, 6); } +static void OnSar(struct As *a, struct Slice s) { OpBsu(a, s, 7); } +static void OnSbb(struct As *a, struct Slice s) { OpAlu(a, s, 3); } +static void OnSetb(struct As *a, struct Slice s) { OpSetcc(a, 2); } +static void OnSetbe(struct As *a, struct Slice s) { OpSetcc(a, 6); } +static void OnSetl(struct As *a, struct Slice s) { OpSetcc(a, 12); } +static void OnSetle(struct As *a, struct Slice s) { OpSetcc(a, 14); } +static void OnSetnb(struct As *a, struct Slice s) { OpSetcc(a, 3); } +static void OnSetnbe(struct As *a, struct Slice s) { OpSetcc(a, 7); } +static void OnSetnl(struct As *a, struct Slice s) { OpSetcc(a, 13); } +static void OnSetnle(struct As *a, struct Slice s) { OpSetcc(a, 15); } +static void OnSetno(struct As *a, struct Slice s) { OpSetcc(a, 1); } +static void OnSetnp(struct As *a, struct Slice s) { OpSetcc(a, 11); } +static void OnSetns(struct As *a, struct Slice s) { OpSetcc(a, 9); } +static void OnSetnz(struct As *a, struct Slice s) { OpSetcc(a, 5); } +static void OnSeto(struct As *a, struct Slice s) { OpSetcc(a, 0); } +static void OnSetp(struct As *a, struct Slice s) { OpSetcc(a, 10); } +static void OnSets(struct As *a, struct Slice s) { OpSetcc(a, 8); } +static void OnSetz(struct As *a, struct Slice s) { OpSetcc(a, 4); } +static void OnShl(struct As *a, struct Slice s) { OpBsu(a, s, 4); } +static void OnShr(struct As *a, struct Slice s) { OpBsu(a, s, 5); } +static void OnShufpd(struct As *a, struct Slice s) { OpSseIb(a, 0x660FC6); } +static void OnShufps(struct As *a, struct Slice s) { OpSseIb(a, 0x0FC6); } +static void OnSqrtpd(struct As *a, struct Slice s) { OpSse(a, 0x660F51); } +static void OnSqrtps(struct As *a, struct Slice s) { OpSse(a, 0x0F51); } +static void OnSqrtsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F51); } +static void OnSqrtss(struct As *a, struct Slice s) { OpSse(a, 0xF30F51); } +static void OnStc(struct As *a, struct Slice s) { EmitByte(a, 0xF9); } +static void OnStd(struct As *a, struct Slice s) { EmitByte(a, 0xFD); } +static void OnSti(struct As *a, struct Slice s) { EmitByte(a, 0xFB); } +static void OnStosb(struct As *a, struct Slice s) { EmitByte(a, 0xAA); } +static void OnStosl(struct As *a, struct Slice s) { EmitByte(a, 0xAB); } +static void OnStosq(struct As *a, struct Slice s) { EmitVarword(a, 0x48AB); } +static void OnStosw(struct As *a, struct Slice s) { EmitVarword(a, 0x66AB); } +static void OnSub(struct As *a, struct Slice s) { OpAlu(a, s, 5); } +static void OnSubpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5C); } +static void OnSubps(struct As *a, struct Slice s) { OpSse(a, 0x0F5C); } +static void OnSubsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5C); } +static void OnSubss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5C); } +static void OnUcomisd(struct As *a, struct Slice s) { OpSse(a, 0x660F2E); } +static void OnUcomiss(struct As *a, struct Slice s) { OpSse(a, 0x0F2E); } +static void OnUd2(struct As *a, struct Slice s) { EmitVarword(a, 0x0F0B); } +static void OnUnpckhpd(struct As *a, struct Slice s) { OpSse(a, 0x660F15); } +static void OnUnpcklpd(struct As *a, struct Slice s) { OpSse(a, 0x660F14); } +static void OnXor(struct As *a, struct Slice s) { OpAlu(a, s, 6); } +static void OnXorpd(struct As *a, struct Slice s) { OpSse(a, 0x660F57); } +static void OnXorps(struct As *a, struct Slice s) { OpSse(a, 0x0F57); } +// clang-format on + +static const struct Directive8 { + char s[8]; + void (*f)(struct As *, struct Slice); +} kDirective8[] = { + {".abort", OnAbort}, // + {".align", OnAlign}, // + {".ascii", OnAscii}, // + {".asciz", OnAsciz}, // + {".balign", OnAlign}, // + {".bss", OnBss}, // + {".byte", OnByte}, // + {".data", OnData}, // + {".double", OnDouble}, // + {".err", OnErr}, // + {".error", OnError}, // + {".file", OnFile}, // + {".float", OnFloat}, // + {".float80", OnFloat80}, // + {".global", OnGlobal}, // + {".globl", OnGlobal}, // + {".hidden", OnHidden}, // + {".ident", OnIdent}, // + {".incbin", OnIncbin}, // + {".ldbl", OnLdbl}, // + {".loc", OnLoc}, // + {".local", OnLocal}, // + {".long", OnLong}, // + {".quad", OnQuad}, // + {".section", OnSection}, // + {".short", OnWord}, // + {".size", OnSize}, // + {".sleb128", OnSleb128}, // + {".space", OnSpace}, // + {".text", OnText}, // + {".type", OnType}, // + {".uleb128", OnUleb128}, // + {".warning", OnWarning}, // + {".weak", OnWeak}, // + {".word", OnWord}, // + {".zero", OnZero}, // + {"adc", OnAdc}, // + {"adcb", OnAdc}, // + {"adcl", OnAdc}, // + {"adcq", OnAdc}, // + {"adcw", OnAdc}, // + {"add", OnAdd}, // + {"addb", OnAdd}, // + {"addl", OnAdd}, // + {"addpd", OnAddpd}, // + {"addps", OnAddps}, // + {"addq", OnAdd}, // + {"addsd", OnAddsd}, // + {"addss", OnAddss}, // + {"addsubpd", OnAddsubpd}, // + {"addsubps", OnAddsubps}, // + {"addw", OnAdd}, // + {"and", OnAnd}, // + {"andb", OnAnd}, // + {"andl", OnAnd}, // + {"andnpd", OnAndnpd}, // + {"andnps", OnAndnps}, // + {"andpd", OnAndpd}, // + {"andps", OnAndps}, // + {"andq", OnAnd}, // + {"andw", OnAnd}, // + {"blendpd", OnBlendpd}, // + {"blendvpd", OnBlendvpd}, // + {"bsf", OnBsf}, // + {"bsr", OnBsr}, // + {"bswap", OnBswap}, // + {"call", OnCall}, // + {"callq", OnCall}, // + {"cbtw", OnCbtw}, // + {"cbw", OnCbtw}, // + {"cdq", OnCltd}, // + {"cdqe", OnCltq}, // + {"clc", OnClc}, // + {"cld", OnCld}, // + {"cli", OnCli}, // + {"cltd", OnCltd}, // + {"cltq", OnCltq}, // + {"cmc", OnCmc}, // + {"cmova", OnCmovnbe}, // + {"cmovae", OnCmovnb}, // + {"cmovb", OnCmovb}, // + {"cmovbe", OnCmovbe}, // + {"cmovc", OnCmovb}, // + {"cmove", OnCmovz}, // + {"cmovg", OnCmovnle}, // + {"cmovge", OnCmovnl}, // + {"cmovl", OnCmovl}, // + {"cmovle", OnCmovle}, // + {"cmovna", OnCmovbe}, // + {"cmovnae", OnCmovb}, // + {"cmovnb", OnCmovnb}, // + {"cmovnbe", OnCmovnbe}, // + {"cmovnc", OnCmovnb}, // + {"cmovne", OnCmovnz}, // + {"cmovng", OnCmovle}, // + {"cmovnge", OnCmovl}, // + {"cmovnl", OnCmovnl}, // + {"cmovnle", OnCmovnle}, // + {"cmovno", OnCmovno}, // + {"cmovnp", OnCmovnp}, // + {"cmovns", OnCmovns}, // + {"cmovnz", OnCmovnz}, // + {"cmovo", OnCmovo}, // + {"cmovp", OnCmovp}, // + {"cmovpe", OnCmovp}, // + {"cmovpo", OnCmovnp}, // + {"cmovs", OnCmovs}, // + {"cmovz", OnCmovz}, // + {"cmp", OnCmp}, // + {"cmpb", OnCmp}, // + {"cmpl", OnCmp}, // + {"cmppd", OnCmppd}, // + {"cmpps", OnCmpps}, // + {"cmpq", OnCmp}, // + {"cmpsd", OnCmpsd}, // + {"cmpss", OnCmpss}, // + {"cmpw", OnCmp}, // + {"comisd", OnComisd}, // + {"comiss", OnComiss}, // + {"cqo", OnCqto}, // + {"cqto", OnCqto}, // + {"cvtdq2pd", OnCvtdq2pd}, // + {"cvtdq2ps", OnCvtdq2ps}, // + {"cvtpd2dq", OnCvtpd2dq}, // + {"cvtpd2ps", OnCvtpd2ps}, // + {"cvtps2dq", OnCvtps2dq}, // + {"cvtps2pd", OnCvtps2pd}, // + {"cvtsd2ss", OnCvtsd2ss}, // + {"cvtsd2ss", OnCvtsd2ss}, // + {"cvtsi2sd", OnCvtsi2sd}, // + {"cvtsi2sd", OnCvtsi2sd}, // + {"cvtsi2ss", OnCvtsi2ss}, // + {"cvtsi2ss", OnCvtsi2ss}, // + {"cvtss2sd", OnCvtss2sd}, // + {"cwd", OnCwtd}, // + {"cwde", OnCwtl}, // + {"cwtd", OnCwtd}, // + {"cwtl", OnCwtl}, // + {"dec", OnDec}, // + {"decb", OnDec}, // + {"decl", OnDec}, // + {"decq", OnDec}, // + {"decw", OnDec}, // + {"div", OnDiv}, // + {"divpd", OnDivpd}, // + {"divps", OnDivps}, // + {"divsd", OnDivsd}, // + {"divss", OnDivss}, // + {"dppd", OnDppd}, // + {"fabs", OnFabs}, // + {"faddl", OnFaddl}, // + {"faddp", OnFaddp}, // + {"fadds", OnFadds}, // + {"fchs", OnFchs}, // + {"fcomip", OnFcomip}, // + {"fdivrp", OnFdivrp}, // + {"fildl", OnFildl}, // + {"fildll", OnFildll}, // + {"fildq", OnFildq}, // + {"filds", OnFilds}, // + {"fisttpll", OnFisttpq}, // + {"fisttpq", OnFisttpq}, // + {"fisttps", OnFisttps}, // + {"fld", OnFld}, // + {"fld1", OnFld1}, // + {"fldcw", OnFldcw}, // + {"fldl", OnFldl}, // + {"fldl2e", OnFldl2e}, // + {"fldl2t", OnFldl2t}, // + {"fldlg2", OnFldlg2}, // + {"fldln2", OnFldln2}, // + {"fldpi", OnFldpi}, // + {"flds", OnFlds}, // + {"fldt", OnFldt}, // + {"fldz", OnFldz}, // + {"fmulp", OnFmulp}, // + {"fnstcw", OnFnstcw}, // + {"fnstsw", OnFnstsw}, // + {"fstp", OnFstp}, // + {"fstpl", OnFstpl}, // + {"fstps", OnFstps}, // + {"fstpt", OnFstpt}, // + {"fsubrp", OnFsubrp}, // + {"ftst", OnFtst}, // + {"fucomi", OnFucomi}, // + {"fucomip", OnFucomip}, // + {"fwait", OnFwait}, // + {"fxam", OnFxam}, // + {"fxch", OnFxch}, // + {"haddpd", OnHaddpd}, // + {"haddps", OnHaddps}, // + {"hlt", OnHlt}, // + {"hsubpd", OnHsubpd}, // + {"hsubps", OnHsubps}, // + {"icebp", OnInt1}, // + {"idiv", OnIdiv}, // + {"imul", OnImul}, // + {"inc", OnInc}, // + {"incb", OnInc}, // + {"incl", OnInc}, // + {"incq", OnInc}, // + {"incw", OnInc}, // + {"int1", OnInt1}, // + {"int3", OnInt3}, // + {"ja", OnJnbe}, // + {"jae", OnJnb}, // + {"jb", OnJb}, // + {"jbe", OnJbe}, // + {"jc", OnJb}, // + {"je", OnJz}, // + {"jg", OnJnle}, // + {"jge", OnJnl}, // + {"jl", OnJl}, // + {"jle", OnJle}, // + {"jmp", OnJmp}, // + {"jmpq", OnJmp}, // + {"jna", OnJbe}, // + {"jnae", OnJb}, // + {"jnb", OnJnb}, // + {"jnbe", OnJnbe}, // + {"jnc", OnJnb}, // + {"jne", OnJnz}, // + {"jng", OnJle}, // + {"jnge", OnJl}, // + {"jnl", OnJnl}, // + {"jnle", OnJnle}, // + {"jno", OnJno}, // + {"jnp", OnJnp}, // + {"jns", OnJns}, // + {"jnz", OnJnz}, // + {"jo", OnJo}, // + {"jp", OnJp}, // + {"jpe", OnJp}, // + {"jpo", OnJnp}, // + {"js", OnJs}, // + {"jz", OnJz}, // + {"lea", OnLea}, // + {"leave", OnLeave}, // + {"lodsb", OnLodsb}, // + {"lodsl", OnLodsl}, // + {"lodsq", OnLodsq}, // + {"lodsw", OnLodsw}, // + {"maxpd", OnMaxpd}, // + {"maxps", OnMaxps}, // + {"maxsd", OnMaxsd}, // + {"maxss", OnMaxss}, // + {"minpd", OnMinpd}, // + {"minps", OnMinps}, // + {"minsd", OnMinsd}, // + {"minss", OnMinss}, // + {"mov", OnMov}, // + {"movabs", OnMov}, // + {"movapd", OnMovapd}, // + {"movaps", OnMovaps}, // + {"movb", OnMov}, // + {"movd", OnMovdq}, // + {"movdqa", OnMovdqa}, // + {"movdqa", OnMovdqa}, // + {"movdqu", OnMovdqu}, // + {"movdqu", OnMovdqu}, // + {"movl", OnMov}, // + {"movmskpd", OnMovmskpd}, // + {"movmskps", OnMovmskps}, // + {"movq", OnMovq}, // + {"movsb", OnMovsb}, // + {"movsbl", OnMovsbwx}, // + {"movsbq", OnMovsbwx}, // + {"movsbw", OnMovsbwx}, // + {"movsd", OnMovsd}, // + {"movsd", OnMovsd}, // + {"movsl", OnMovsl}, // + {"movslq", OnMovslq}, // + {"movsq", OnMovsq}, // + {"movss", OnMovss}, // + {"movss", OnMovss}, // + {"movsw", OnMovsw}, // + {"movswl", OnMovsbwx}, // + {"movswq", OnMovsbwx}, // + {"movupd", OnMovupd}, // + {"movups", OnMovups}, // + {"movw", OnMov}, // + {"movzbl", OnMovzbwx}, // + {"movzbq", OnMovzbwx}, // + {"movzbw", OnMovzbwx}, // + {"movzwl", OnMovzbwx}, // + {"movzwq", OnMovzbwx}, // + {"mpsadbw", OnMpsadbw}, // + {"mul", OnMul}, // + {"mulpd", OnMulpd}, // + {"mulps", OnMulps}, // + {"mulsd", OnMulsd}, // + {"mulss", OnMulss}, // + {"neg", OnNeg}, // + {"negb", OnNeg}, // + {"negl", OnNeg}, // + {"negq", OnNeg}, // + {"negw", OnNeg}, // + {"nop", OnNop}, // + {"nopb", OnNop}, // + {"nopl", OnNop}, // + {"nopq", OnNop}, // + {"nopw", OnNop}, // + {"not", OnNot}, // + {"notb", OnNot}, // + {"notl", OnNot}, // + {"notq", OnNot}, // + {"notw", OnNot}, // + {"or", OnOr}, // + {"orb", OnOr}, // + {"orl", OnOr}, // + {"orpd", OnOrpd}, // + {"orps", OnOrps}, // + {"orq", OnOr}, // + {"orw", OnOr}, // + {"pabsb", OnPabsb}, // + {"pabsd", OnPabsd}, // + {"pabsw", OnPabsw}, // + {"packssdw", OnPackssdw}, // + {"packsswb", OnPacksswb}, // + {"packusdw", OnPackusdw}, // + {"packuswb", OnPackuswb}, // + {"paddb", OnPaddb}, // + {"paddd", OnPaddd}, // + {"paddq", OnPaddq}, // + {"paddsb", OnPaddsb}, // + {"paddsw", OnPaddsw}, // + {"paddusb", OnPaddusb}, // + {"paddusw", OnPaddusw}, // + {"paddw", OnPaddw}, // + {"palignr", OnPalignr}, // + {"pand", OnPand}, // + {"pandn", OnPandn}, // + {"pause", OnPause}, // + {"pavgb", OnPavgb}, // + {"pavgw", OnPavgw}, // + {"pblendvb", OnPblendvb}, // + {"pblendw", OnPblendw}, // + {"pcmpeqb", OnPcmpeqb}, // + {"pcmpeqd", OnPcmpeqd}, // + {"pcmpeqq", OnPcmpeqq}, // + {"pcmpeqw", OnPcmpeqw}, // + {"pcmpgtb", OnPcmpgtb}, // + {"pcmpgtd", OnPcmpgtd}, // + {"pcmpgtq", OnPcmpgtq}, // + {"pcmpgtw", OnPcmpgtw}, // + {"phaddd", OnPhaddd}, // + {"phaddsw", OnPhaddsw}, // + {"phaddw", OnPhaddw}, // + {"phsubd", OnPhsubd}, // + {"phsubsw", OnPhsubsw}, // + {"phsubw", OnPhsubw}, // + {"pmaddwd", OnPmaddwd}, // + {"pmaxsb", OnPmaxsb}, // + {"pmaxsd", OnPmaxsd}, // + {"pmaxsw", OnPmaxsw}, // + {"pmaxub", OnPmaxub}, // + {"pmaxud", OnPmaxud}, // + {"pmaxuw", OnPmaxuw}, // + {"pminsb", OnPminsb}, // + {"pminsd", OnPminsd}, // + {"pminsw", OnPminsw}, // + {"pminub", OnPminub}, // + {"pminud", OnPminud}, // + {"pminuw", OnPminuw}, // + {"pmovmskb", OnPmovmskb}, // + {"pmuldq", OnPmuldq}, // + {"pmulhrsw", OnPmulhrsw}, // + {"pmulhuw", OnPmulhuw}, // + {"pmulhw", OnPmulhw}, // + {"pmulld", OnPmulld}, // + {"pmullw", OnPmullw}, // + {"pmuludq", OnPmuludq}, // + {"pop", OnPop}, // + {"popcnt", OnPopcnt}, // + {"por", OnPor}, // + {"psadbw", OnPsadbw}, // + {"pshufb", OnPshufb}, // + {"pshufd", OnPshufd}, // + {"pshufhw", OnPshufhw}, // + {"pshuflw", OnPshuflw}, // + {"psignb", OnPsignb}, // + {"psignd", OnPsignd}, // + {"psignw", OnPsignw}, // + {"pslld", OnPslld}, // + {"psllq", OnPsllq}, // + {"psllw", OnPsllw}, // + {"psrad", OnPsrad}, // + {"psraw", OnPsraw}, // + {"psrld", OnPsrld}, // + {"psrlq", OnPsrlq}, // + {"psrlw", OnPsrlw}, // + {"psubb", OnPsubb}, // + {"psubd", OnPsubd}, // + {"psubq", OnPsubq}, // + {"psubsb", OnPsubsb}, // + {"psubsw", OnPsubsw}, // + {"psubusb", OnPsubusb}, // + {"psubusw", OnPsubusw}, // + {"psubw", OnPsubw}, // + {"ptest", OnPtest}, // + {"push", OnPush}, // + {"pxor", OnPxor}, // + {"rcl", OnRcl}, // + {"rclb", OnRcl}, // + {"rcll", OnRcl}, // + {"rclq", OnRcl}, // + {"rclw", OnRcl}, // + {"rcpps", OnRcpps}, // + {"rcpss", OnRcpss}, // + {"rcr", OnRcr}, // + {"rcrb", OnRcr}, // + {"rcrl", OnRcr}, // + {"rcrq", OnRcr}, // + {"rcrw", OnRcr}, // + {"ret", OnRet}, // + {"rol", OnRol}, // + {"rolb", OnRol}, // + {"roll", OnRol}, // + {"rolq", OnRol}, // + {"rolw", OnRol}, // + {"ror", OnRor}, // + {"rorb", OnRor}, // + {"rorl", OnRor}, // + {"rorq", OnRor}, // + {"rorw", OnRor}, // + {"roundsd", OnRoundsd}, // + {"roundss", OnRoundss}, // + {"rsqrtps", OnRsqrtps}, // + {"rsqrtss", OnRsqrtss}, // + {"sal", OnSal}, // + {"salb", OnSal}, // + {"sall", OnSal}, // + {"salq", OnSal}, // + {"salw", OnSal}, // + {"sar", OnSar}, // + {"sarb", OnSar}, // + {"sarl", OnSar}, // + {"sarq", OnSar}, // + {"sarw", OnSar}, // + {"sbb", OnSbb}, // + {"sbbb", OnSbb}, // + {"sbbl", OnSbb}, // + {"sbbq", OnSbb}, // + {"sbbw", OnSbb}, // + {"seta", OnSetnbe}, // + {"setae", OnSetnb}, // + {"setb", OnSetb}, // + {"setbe", OnSetbe}, // + {"setc", OnSetb}, // + {"sete", OnSetz}, // + {"setg", OnSetnle}, // + {"setge", OnSetnl}, // + {"setl", OnSetl}, // + {"setle", OnSetle}, // + {"setna", OnSetbe}, // + {"setnae", OnSetb}, // + {"setnb", OnSetnb}, // + {"setnbe", OnSetnbe}, // + {"setnc", OnSetnb}, // + {"setne", OnSetnz}, // + {"setng", OnSetle}, // + {"setnge", OnSetl}, // + {"setnl", OnSetnl}, // + {"setnle", OnSetnle}, // + {"setno", OnSetno}, // + {"setnp", OnSetnp}, // + {"setns", OnSetns}, // + {"setnz", OnSetnz}, // + {"seto", OnSeto}, // + {"setp", OnSetp}, // + {"setpe", OnSetp}, // + {"setpo", OnSetnp}, // + {"sets", OnSets}, // + {"setz", OnSetz}, // + {"shl", OnShl}, // + {"shlb", OnShl}, // + {"shld", OnShld}, // + {"shll", OnShl}, // + {"shlq", OnShl}, // + {"shlw", OnShl}, // + {"shr", OnShr}, // + {"shrb", OnShr}, // + {"shrd", OnShrd}, // + {"shrl", OnShr}, // + {"shrq", OnShr}, // + {"shrw", OnShr}, // + {"shufpd", OnShufpd}, // + {"shufps", OnShufps}, // + {"sqrtpd", OnSqrtpd}, // + {"sqrtps", OnSqrtps}, // + {"sqrtsd", OnSqrtsd}, // + {"sqrtss", OnSqrtss}, // + {"stc", OnStc}, // + {"std", OnStd}, // + {"sti", OnSti}, // + {"stosb", OnStosb}, // + {"stosl", OnStosl}, // + {"stosq", OnStosq}, // + {"stosw", OnStosw}, // + {"sub", OnSub}, // + {"subb", OnSub}, // + {"subl", OnSub}, // + {"subpd", OnSubpd}, // + {"subps", OnSubps}, // + {"subq", OnSub}, // + {"subsd", OnSubsd}, // + {"subss", OnSubss}, // + {"subw", OnSub}, // + {"test", OnTest}, // + {"testb", OnTest}, // + {"testl", OnTest}, // + {"testq", OnTest}, // + {"testw", OnTest}, // + {"ucomisd", OnUcomisd}, // + {"ucomiss", OnUcomiss}, // + {"ud2", OnUd2}, // + {"unpckhpd", OnUnpckhpd}, // + {"unpcklpd", OnUnpcklpd}, // + {"wait", OnFwait}, // + {"xchg", OnXchg}, // + {"xor", OnXor}, // + {"xorb", OnXor}, // + {"xorl", OnXor}, // + {"xorpd", OnXorpd}, // + {"xorps", OnXorps}, // + {"xorq", OnXor}, // + {"xorw", OnXor}, // +}; + +static const struct Directive16 { + char s[16]; + void (*f)(struct As *, struct Slice); +} kDirective16[] = { + {".internal", OnInternal}, // + {".popsection", OnPopsection}, // + {".previous", OnPrevious}, // + {".protected", OnProtected}, // + {".pushsection", OnPushsection}, // + {"cvtsd2ssl", OnCvtsd2ss}, // + {"cvtsd2ssq", OnCvtsd2ss}, // + {"cvtsi2sdl", OnCvtsi2sd}, // + {"cvtsi2sdq", OnCvtsi2sd}, // + {"cvtsi2sdq", OnCvtsi2sd}, // + {"cvtsi2ssl", OnCvtsi2ss}, // + {"cvtsi2ssq", OnCvtsi2ss}, // + {"cvttpd2dq", OnCvttpd2dq}, // + {"cvttps2dq", OnCvttps2dq}, // + {"cvttsd2si", OnCvttsd2si}, // + {"cvttsd2sil", OnCvttsd2si}, // + {"cvttsd2siq", OnCvttsd2si}, // + {"cvttss2si", OnCvttss2si}, // + {"cvttss2sil", OnCvttss2si}, // + {"cvttss2siq", OnCvttss2si}, // + {"pcmpistri", OnPcmpistri}, // + {"pcmpistrm", OnPcmpistrm}, // + {"phminposuw", OnPhminposuw}, // + {"pmaddubsw", OnPmaddubsw}, // + {"punpckhbw", OnPunpckhbw}, // + {"punpckhdq", OnPunpckhdq}, // + {"punpckhqdq", OnPunpckhqdq}, // + {"punpckhwd", OnPunpckhwd}, // + {"punpcklbw", OnPunpcklbw}, // + {"punpckldq", OnPunpckldq}, // + {"punpcklqdq", OnPunpcklqdq}, // + {"punpcklwd", OnPunpcklwd}, // +}; + +static bool OnDirective8(struct As *a, struct Slice s) { + int m, l, r; + uint64_t x, y; + if (s.n && s.n <= 8) { + x = MakeKey64(s.p, s.n); + l = 0; + r = ARRAYLEN(kDirective8) - 1; + while (l <= r) { + m = (l + r) >> 1; + y = LOAD64BE(kDirective8[m].s); + if (x < y) { + r = m - 1; + } else if (x > y) { + l = m + 1; + } else { + kDirective8[m].f(a, s); return true; } } @@ -1520,129 +3665,50 @@ static bool Prefix(struct Assembler *a, const char *s, size_t n) { return false; } -static void Directive(struct Assembler *a) { +static bool OnDirective16(struct As *a, struct Slice s) { + int m, l, r; + unsigned __int128 x, y; + if (s.n && s.n <= 16) { + x = MakeKey128(s.p, s.n); + l = 0; + r = ARRAYLEN(kDirective16) - 1; + while (l <= r) { + m = (l + r) >> 1; + y = LOAD128BE(kDirective16[m].s); + if (x < y) { + r = m - 1; + } else if (x > y) { + l = m + 1; + } else { + kDirective16[m].f(a, s); + return true; + } + } + } + return false; +} + +static void OnDirective(struct As *a) { struct Slice s; for (;;) { - s = GetString(a); - if (!Prefix(a, s.p, s.n)) break; - } - if (s.n >= 1 && s.p[0] == '.') { - if (IS(s.p, s.n, ".zero")) { - OnZero(a); - } else if (IS(s.p, s.n, ".align") || IS(s.p, s.n, ".balign")) { - OnAlign(a); - } else if (IS(s.p, s.n, ".byte")) { - OnByte(a); - } else if (IS(s.p, s.n, ".word") || IS(s.p, s.n, ".short")) { - OnWord(a); - } else if (IS(s.p, s.n, ".long")) { - OnLong(a); - } else if (IS(s.p, s.n, ".quad")) { - OnQuad(a); - } else if (IS(s.p, s.n, ".float")) { - OnFloat(a); - } else if (IS(s.p, s.n, ".double")) { - OnDouble(a); - } else if (IS(s.p, s.n, ".ascii")) { - OnAscii(a); - } else if (IS(s.p, s.n, ".asciz")) { - OnAsciz(a); - } else if (IS(s.p, s.n, ".text")) { - OnText(a); - } else if (IS(s.p, s.n, ".data")) { - OnData(a); - } else if (IS(s.p, s.n, ".bss")) { - OnBss(a); - } else if (IS(s.p, s.n, ".previous")) { - OnPrevious(a); - } else if (IS(s.p, s.n, ".section")) { - OnSection(a); - } else if (IS(s.p, s.n, ".abort")) { - OnAbort(a); - } else if (IS(s.p, s.n, ".err")) { - OnErr(a); - } else if (IS(s.p, s.n, ".error")) { - OnError(a); - } else if (IS(s.p, s.n, ".fill") || IS(s.p, s.n, ".space")) { - OnFill(a); - } else if (IS(s.p, s.n, ".type")) { - OnType(a); - } else if (IS(s.p, s.n, ".size")) { - OnSize(a); - } else if (IS(s.p, s.n, ".local")) { - OnLocal(a); - } else if (IS(s.p, s.n, ".internal")) { - OnInternal(a); - } else if (IS(s.p, s.n, ".weak")) { - OnWeak(a); - } else if (IS(s.p, s.n, ".hidden")) { - OnHidden(a); - } else if (IS(s.p, s.n, ".globl") || IS(s.p, s.n, ".global")) { - OnGlobal(a); - } else if (IS(s.p, s.n, ".protected")) { - OnProtected(a); - } else if (IS(s.p, s.n, ".incbin")) { - OnIncbin(a); + s = GetSlice(a); + if (Prefix(a, s.p, s.n)) { + if (IsPunct(a, a->i, ';')) { + return; + } } else { - Fail(a, "unexpected directive: %.*s", s.n, s.p); + break; + } + } + if (!OnDirective8(a, s)) { + if (!OnDirective16(a, s)) { + Fail(a, "unexpected op: %.*s", s.n, s.p); } - } else if (IS(s.p, s.n, "ret")) { - OnRet(a); - } else if (IS(s.p, s.n, "leave")) { - OnLeave(a); - } else if (IS(s.p, s.n, "push")) { - OnPush(a); - } else if (IS(s.p, s.n, "pop")) { - OnPop(a); - } else if (IS(s.p, s.n, "hlt")) { - OnHlt(a); - } else if (IS(s.p, s.n, "cmc")) { - OnCmc(a); - } else if (IS(s.p, s.n, "clc")) { - OnClc(a); - } else if (IS(s.p, s.n, "stc")) { - OnStc(a); - } else if (IS(s.p, s.n, "cli")) { - OnCli(a); - } else if (IS(s.p, s.n, "sti")) { - OnSti(a); - } else if (IS(s.p, s.n, "cld")) { - OnCld(a); - } else if (IS(s.p, s.n, "std")) { - OnStd(a); - } else if (IS(s.p, s.n, "stosb")) { - OnStosb(a); - } else if (IS(s.p, s.n, "stosw")) { - OnStosw(a); - } else if (IS(s.p, s.n, "stosl")) { - OnStosl(a); - } else if (IS(s.p, s.n, "stosq")) { - OnStosq(a); - } else if (IS(s.p, s.n, "lodsb")) { - OnLodsb(a); - } else if (IS(s.p, s.n, "lodsw")) { - OnLodsw(a); - } else if (IS(s.p, s.n, "lodsl")) { - OnLodsl(a); - } else if (IS(s.p, s.n, "lodsq")) { - OnLodsq(a); - } else if (IS(s.p, s.n, "movsb")) { - OnMovsb(a); - } else if (IS(s.p, s.n, "movsw")) { - OnMovsw(a); - } else if (IS(s.p, s.n, "movsl")) { - OnMovsl(a); - } else if (IS(s.p, s.n, "movsq")) { - OnMovsq(a); - } else if (IsSizableOp("mov", s)) { - OnMov(a, s); - } else { - Fail(a, "unexpected op: %.*s", s.n, s.p); } ConsumePunct(a, ';'); } -static void Assemble(struct Assembler *a) { +static void Assemble(struct As *a) { while (a->i < a->things.n) { if (IsPunct(a, a->i, ';')) { ++a->i; @@ -1651,38 +3717,176 @@ static void Assemble(struct Assembler *a) { switch (a->things.p[a->i].t) { case TT_SLICE: if (IsPunct(a, a->i + 1, ':')) { - Label(a, a->things.p[a->i].i); + OnSymbol(a, a->things.p[a->i].i); } else { - Directive(a); + OnDirective(a); } break; case TT_INT: if (IsPunct(a, a->i + 1, ':')) { - LocalLabel(a, a->ints.p[a->things.p[a->i].i]); + OnLocalLabel(a, a->ints.p[a->things.p[a->i].i]); + break; } - // fallthrough + Fail(a, "unexpected token"); default: Fail(a, "unexpected token"); } } } -static void Objectify(struct Assembler *a, const char *path) { - size_t i, j; +static int FindLabelForward(struct As *a, int id) { + int i; + for (i = 0; i < a->labels.n; ++i) { + if (a->labels.p[i].id == id && a->labels.p[i].tok > a->i) { + return a->labels.p[i].symbol; + } + } + Fail(a, "label not found"); +} + +static int FindLabelBackward(struct As *a, int id) { + int i; + for (i = a->labels.n; i--;) { + if (a->labels.p[i].id == id && a->labels.p[i].tok < a->i) { + return a->labels.p[i].symbol; + } + } + Fail(a, "label not found"); +} + +static int ResolveSymbol(struct As *a, int i) { + switch (a->things.p[i].t) { + case TT_SLICE: + return GetSymbol(a, a->things.p[i].i); + case TT_BACKWARD: + return FindLabelBackward(a, a->ints.p[a->things.p[i].i]); + case TT_FORWARD: + return FindLabelForward(a, a->ints.p[a->things.p[i].i]); + default: + DebugBreak(); + Fail(a, "this corruption"); + } +} + +static void EvaluateExpr(struct As *a, int i) { + if (i == -1) return; + if (a->exprs.p[i].isevaluated) return; + if (a->exprs.p[i].isvisited) Fail(a, "circular expr"); + a->exprs.p[i].isvisited = true; + EvaluateExpr(a, a->exprs.p[i].lhs); + EvaluateExpr(a, a->exprs.p[i].rhs); + a->i = a->exprs.p[i].tok; + switch (a->exprs.p[i].kind) { + case EX_INT: + break; + case EX_SYM: + a->exprs.p[i].x = ResolveSymbol(a, a->exprs.p[i].x); + break; + default: + break; + } + a->exprs.p[i].isevaluated = true; +} + +static void Write32(char b[4], int x) { + b[0] = x >> 000; + b[1] = x >> 010; + b[2] = x >> 020; + b[3] = x >> 030; +} + +static void MarkUsedSymbols(struct As *a, int i) { + if (i == -1) return; + MarkUsedSymbols(a, a->exprs.p[i].lhs); + MarkUsedSymbols(a, a->exprs.p[i].rhs); + if (a->exprs.p[i].kind == EX_SYM) { + a->symbols.p[a->exprs.p[i].x].isused = true; + } +} + +static void Evaluate(struct As *a) { + int i; + struct Expr *e; + for (i = 0; i < a->relas.n; ++i) { + EvaluateExpr(a, a->relas.p[i].expr); + if (a->relas.p[i].kind == R_X86_64_PC32) { + e = a->exprs.p + a->relas.p[i].expr; + if (e->kind == EX_SYM && a->symbols.p[e->x].stb == STB_LOCAL && + a->symbols.p[e->x].section == a->relas.p[i].section) { + a->relas.p[i].isdead = true; + Write32((a->sections.p[a->relas.p[i].section].binary.p + + a->relas.p[i].offset), + (a->symbols.p[e->x].offset - a->relas.p[i].offset + + a->relas.p[i].addend)); + } + } + } + for (i = 0; i < a->relas.n; ++i) { + if (a->relas.p[i].isdead) continue; + MarkUsedSymbols(a, a->relas.p[i].expr); + } +} + +static bool IsLocal(struct As *a, int name) { + if (name < 0) return true; + return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2); +} + +static bool IsLiveSymbol(struct As *a, int i) { + return !(!a->symbols.p[i].isused && a->symbols.p[i].stb == STB_LOCAL && + a->symbols.p[i].section && IsLocal(a, a->symbols.p[i].name)); +} + +static void Objectify(struct As *a, int path) { + int i, j, s; struct ElfWriter *elf; - elf = elfwriter_open(path, 0644); + elf = elfwriter_open(a->strings.p[path], 0644); + for (i = 0; i < a->symbols.n; ++i) { + if (!IsLiveSymbol(a, i)) continue; + a->symbols.p[i].ref = elfwriter_appendsym( + elf, + strndup(a->slices.p[a->symbols.p[i].name].p, + a->slices.p[a->symbols.p[i].name].n), + ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type), + a->symbols.p[i].stv, a->symbols.p[i].offset, a->symbols.p[i].size); + } for (i = 0; i < a->sections.n; ++i) { elfwriter_align(elf, a->sections.p[i].align, 0); - elfwriter_startsection(elf, a->sections.p[i].name, a->sections.p[i].type, - a->sections.p[i].flags); + s = elfwriter_startsection(elf, a->strings.p[a->sections.p[i].name], + a->sections.p[i].type, a->sections.p[i].flags); for (j = 0; j < a->symbols.n; ++j) { + if (!IsLiveSymbol(a, j)) continue; if (a->symbols.p[j].section != i) continue; - elfwriter_appendsym( - elf, - strndup(a->slices.p[a->symbols.p[j].name].p, - a->slices.p[a->symbols.p[j].name].n), - ELF64_ST_INFO(a->symbols.p[j].stb, a->symbols.p[j].type), - a->symbols.p[j].stv, a->symbols.p[j].location, a->symbols.p[j].size); + elfwriter_setsection(elf, a->symbols.p[j].ref, s); + } + for (j = 0; j < a->relas.n; ++j) { + if (a->relas.p[j].isdead) continue; + if (a->relas.p[j].section != i) continue; + a->i = a->exprs.p[a->relas.p[j].expr].tok; + switch (a->exprs.p[a->relas.p[j].expr].kind) { + case EX_INT: + break; + case EX_SYM: + elfwriter_appendrela( + elf, a->relas.p[j].offset, + a->symbols.p[a->exprs.p[a->relas.p[j].expr].x].ref, + a->relas.p[j].kind, a->relas.p[j].addend); + break; + case EX_ADD: + if (a->exprs.p[a->exprs.p[j].lhs].kind == EX_SYM && + a->exprs.p[a->exprs.p[j].rhs].kind == EX_INT) { + elfwriter_appendrela( + elf, a->relas.p[j].offset, + a->symbols.p[a->exprs.p[a->exprs.p[j].lhs].x].ref, + a->relas.p[j].kind, + a->relas.p[j].addend + a->exprs.p[a->exprs.p[j].rhs].x); + } else { + Fail(a, "bad addend"); + } + break; + default: + Fail(a, "unsupported relocation type"); + } } memcpy(elfwriter_reserve(elf, a->sections.p[i].binary.n), a->sections.p[i].binary.p, a->sections.p[i].binary.n); @@ -1692,11 +3896,11 @@ static void Objectify(struct Assembler *a, const char *path) { elfwriter_close(elf); } -static void PrintThings(struct Assembler *a) { +static void PrintThings(struct As *a) { int i; - char fbuf[32]; + char pbuf[4], fbuf[32]; for (i = 0; i < a->things.n; ++i) { - printf("%s:%d:: ", a->sauces.p[a->things.p[i].s].path, + printf("%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[i].s].path], a->sauces.p[a->things.p[i].s].line); switch (a->things.p[i].t) { case TT_INT: @@ -1707,35 +3911,45 @@ static void PrintThings(struct Assembler *a) { printf("TT_FLOAT %s\n", fbuf); break; case TT_SLICE: - printf("TT_SLICE %`'.*s\n", a->slices.p[a->things.p[i].i].n, + printf("TT_SLICE %.*s\n", a->slices.p[a->things.p[i].i].n, a->slices.p[a->things.p[i].i].p); break; case TT_PUNCT: - printf("TT_PUNCT %`'c\n", a->things.p[i].i); + printf("TT_PUNCT %s\n", PunctToStr(a->things.p[i].i, pbuf)); + break; + case TT_BACKWARD: + printf("TT_BACKWARD %d\n", a->ints.p[a->things.p[i].i]); + break; + case TT_FORWARD: + printf("TT_FORWARD %d\n", a->ints.p[a->things.p[i].i]); break; default: - unreachable; + abort(); } } } void Assembler(int argc, char *argv[]) { - struct Assembler *a; - showcrashreports(); - if (argc == 1) { - system("o//third_party/chibicc/as.com -o /tmp/o third_party/chibicc/hog.s"); - system("objdump -wxd /tmp/o"); - exit(0); - } - ReadFlags(argc, argv); + struct As *a; a = NewAssembler(); - Tokenize(a, g_input_path); + ReadFlags(a, argc, argv); + SaveString(&a->incpaths, strdup(".")); + SaveString(&a->incpaths, DirName(a->strings.p[a->inpath])); + Tokenize(a, a->inpath); /* PrintThings(a); */ Assemble(a); - Objectify(a, g_output_path); + Evaluate(a); + Objectify(a, a->outpath); + FreeAssembler(a); } int main(int argc, char *argv[]) { + showcrashreports(); + if (argc == 1) { + system("o//third_party/chibicc/as.com -o /tmp/o third_party/chibicc/hog.s"); + system("objdump -xwd /tmp/o"); + exit(0); + } Assembler(argc, argv); return 0; } diff --git a/third_party/chibicc/asm.c b/third_party/chibicc/asm.c index 09ea0834..278d33c6 100644 --- a/third_party/chibicc/asm.c +++ b/third_party/chibicc/asm.c @@ -679,8 +679,7 @@ static void StoreAsmOutputs(Asm *a) { println("\tmov\t%%%s,(%%rax)", kGreg[z][a->ops[i].reg]); } else { println("\tpush\t%%rbx"); - push(); - pop("%rbx"); + println("\tmov\t%%rax,%%rbx"); gen_addr(a->ops[i].node); println("\tmov\t%%%s,(%%rax)", kGreg[z][3]); println("\tpop\t%%rbx"); diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c index 7b6ea99d..829267db 100644 --- a/third_party/chibicc/chibicc.c +++ b/third_party/chibicc/chibicc.c @@ -41,6 +41,7 @@ static bool opt_c; static bool opt_cc1; static bool opt_hash_hash_hash; static bool opt_static; +static bool opt_save_temps; static char *opt_MF; static char *opt_MT; static char *opt_o; @@ -140,6 +141,7 @@ static char *quote_makefile(char *s) { static void PrintMemoryUsage(void) { struct mallinfo mi; + malloc_trim(0); mi = mallinfo(); fprintf(stderr, "\n"); fprintf(stderr, "allocated %,ld bytes of memory\n", mi.arena); @@ -342,7 +344,7 @@ static char *replace_extn(char *tmpl, char *extn) { } static void cleanup(void) { - if (tmpfiles) { + if (tmpfiles && !opt_save_temps) { for (int i = 0; tmpfiles[i]; i++) { unlink(tmpfiles[i]); } @@ -350,7 +352,7 @@ static void cleanup(void) { } static char *create_tmpfile(void) { - char *path = xstrcat(kTmpPath, "chibicc-XXXXXX"); + char *path = xjoinpaths(kTmpPath, "chibicc-XXXXXX"); int fd = mkstemp(path); if (fd == -1) error("mkstemp failed: %s", strerror(errno)); close(fd); @@ -383,6 +385,7 @@ static void run_subprocess(char **argv) { } } if (status != 0) { + opt_save_temps = true; exit(1); } } @@ -545,6 +548,7 @@ static void cc1(void) { static void assemble(char *input, char *output) { char *as = getenv("AS"); if (!as || !*as) as = "as"; + /* as = "o//third_party/chibicc/as.com"; */ StringArray arr = {}; strarray_push(&arr, as); strarray_push(&arr, "-W"); diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h index 3b33a487..86df1cda 100644 --- a/third_party/chibicc/chibicc.h +++ b/third_party/chibicc/chibicc.h @@ -16,6 +16,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" +#include "libc/nexgen32e/crc32.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" @@ -126,8 +127,8 @@ char *read_file(char *); int read_escaped_char(char **, char *); #define UNREACHABLE() error("internal error at %s:%d", __FILE__, __LINE__) -#define EQUAL(T, S) equal(T, S, strlen(S)) -#define CONSUME(R, T, S) consume(R, T, S, strlen(S)) +#define EQUAL(T, S) equal(T, S, sizeof(S) - 1) +#define CONSUME(R, T, S) consume(R, T, S, sizeof(S) - 1) // // preprocess.c diff --git a/third_party/chibicc/codegen.c b/third_party/chibicc/codegen.c index 86a71116..658cf35c 100644 --- a/third_party/chibicc/codegen.c +++ b/third_party/chibicc/codegen.c @@ -616,11 +616,9 @@ static bool has_flonum2(Type *ty) { static void push_struct(Type *ty) { int sz = ROUNDUP(ty->size, 8); println("\tsub\t$%d,%%rsp", sz); + println("\tmov\t%%rsp,%%rdi"); depth += sz / 8; - for (int i = 0; i < ty->size; i++) { - println("\tmov\t%d(%%rax),%%r10b", i); - println("\tmov\t%%r10b,%d(%%rsp)", i); - } + gen_memcpy(ty->size); } static void push_args2(Node *args, bool first_pass) { @@ -781,7 +779,7 @@ static void copy_ret_buffer(Obj *var) { } } else { char *reg1 = (gp == 0) ? "%al" : "%dl"; - char *reg2 = (gp == 0) ? "%rax" : "%rdx"; + char *reg2 = (gp == 0) ? "%rax" : "%rdx"; /* TODO: isn't ax clobbered? */ for (int i = 8; i < MIN(16, ty->size); i++) { println("\tmov\t%s,%d(%%rbp)", reg1, var->offset + i); println("\tshr\t$8,%s", reg2); @@ -2241,7 +2239,7 @@ static void emit_data(Obj *prog) { } print_align(align); println("\t.type\t%s,@object", nameof(var)); - println("\t.size\t%s,%d", nameof(var), var->ty->size); + /* println("\t.size\t%s,%d", nameof(var), var->ty->size); */ println("%s:", nameof(var)); if (var->init_data) { int pos = 0; @@ -2425,7 +2423,7 @@ static void emit_text(Obj *prog) { emitlin("\tleave"); emitlin("\tret"); } - println("\t.size\t%s,.-%s", nameof(fn), nameof(fn)); + /* println("\t.size\t%s,.-%s", nameof(fn), nameof(fn)); */ if (fn->is_constructor) { emitlin("\t.section .ctors,\"aw\",@progbits"); emitlin("\t.align\t8"); @@ -2448,6 +2446,7 @@ static void emit_staticasms(StaticAsm *a) { void codegen(Obj *prog, FILE *out) { output_stream = out; File **files = get_input_files(); + println("# -*- mode:unix-assembly -*-"); for (int i = 0; files[i]; i++) { println("\t.file\t%d %`'s", files[i]->file_no, files[i]->name); } diff --git a/third_party/chibicc/hashmap.c b/third_party/chibicc/hashmap.c index 5a0d5a83..05ec2e77 100644 --- a/third_party/chibicc/hashmap.c +++ b/third_party/chibicc/hashmap.c @@ -35,8 +35,9 @@ static void rehash(HashMap *map) { map2.capacity = cap; for (int i = 0; i < map->capacity; i++) { HashEntry *ent = &map->buckets[i]; - if (ent->key && ent->key != TOMBSTONE) + if (ent->key && ent->key != TOMBSTONE) { hashmap_put2(&map2, ent->key, ent->keylen, ent->val); + } } assert(map2.used == nkeys); *map = map2; diff --git a/third_party/chibicc/hog.s b/third_party/chibicc/hog.s deleted file mode 100644 index d8bd0dda..00000000 --- a/third_party/chibicc/hog.s +++ /dev/null @@ -1,34 +0,0 @@ - .globl _start -_start: rep movsb -/ add $333333,%rcx -/ test %eax,(%rcx) # 85 0001 - push %r15 - pop %r15 - mov %al,%bl - mov %eax,%ecx # 89 0301 - mov %ecx,%eax # 89 0310 - mov %ecx,(%rbx) - mov (%rbx),%ecx - mov %xmm0,%xmm1 - movb $1,(%rax) - movl $1,(%rax) - movl $1,0xffffff(%rax,%rbx,8) - mov $1,%bl - mov $123,%r8d -/ mov %ebx,%r8d -/ mov (%r8),%ebx -/ mov %ebx,(%r8) -/ movd %eax,%xmm0 -/ movdqa %xmm0,%xmm8 -/ movdqa %xmm8,%xmm1 -/ paddw %xmm8,%xmm1 -/ paddw %xmm1,%xmm8 - hlt - ret $1 - -a: .asciz "ho","ggg" - .align 8 - .section .text.yo - .zero 1 - .byte 1 - .hidden doge diff --git a/third_party/chibicc/parse.c b/third_party/chibicc/parse.c index 05bbf301..9866320a 100644 --- a/third_party/chibicc/parse.c +++ b/third_party/chibicc/parse.c @@ -16,6 +16,7 @@ // So it is very easy to lookahead arbitrary number of tokens in this // parser. +#include "libc/testlib/testlib.h" #include "third_party/chibicc/chibicc.h" typedef struct InitDesg InitDesg; @@ -577,6 +578,8 @@ static Token *thing_attributes(Token *tok, void *arg) { error_tok(tok, "unknown function attribute"); } +Token *to; + // declspec = ("void" | "_Bool" | "char" | "short" | "int" | "long" // | "typedef" | "static" | "extern" | "inline" // | "_Thread_local" | "__thread" @@ -641,6 +644,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) { if (attr->is_typedef && attr->is_static + attr->is_extern + attr->is_inline + attr->is_tls > 1) { + to = tok; error_tok(tok, "typedef may not be used together with static," " extern, inline, __thread or _Thread_local"); } @@ -2577,8 +2581,9 @@ static Node *unary(Token **rest, Token *tok) { if (EQUAL(tok, "&")) { Node *lhs = cast(rest, tok->next); add_type(lhs); - if (lhs->kind == ND_MEMBER && lhs->member->is_bitfield) + 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, "*")) { @@ -2995,13 +3000,13 @@ static Node *primary(Token **rest, Token *tok) { 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)) { + if ((EQUAL(tok, "_Alignof") || 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")) { + if (EQUAL(tok, "_Alignof") || EQUAL(tok, "__alignof__")) { Node *node = unary(rest, tok->next); add_type(node); return new_ulong(node->ty->align, tok); @@ -3338,7 +3343,7 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) { fn->asmname = ConsumeStringLiteral(&tok, tok); tok = skip(tok, ')'); } - tok = attribute_list(tok, &attr, thing_attributes); + tok = attribute_list(tok, attr, thing_attributes); if (CONSUME(&tok, tok, ";")) return tok; current_fn = fn; locals = NULL; diff --git a/third_party/chibicc/preprocess.c b/third_party/chibicc/preprocess.c index 4910d2d6..6098b3b9 100644 --- a/third_party/chibicc/preprocess.c +++ b/third_party/chibicc/preprocess.c @@ -686,7 +686,7 @@ static char *detect_include_guard(Token *tok) { char *macro = strndup(tok->loc, tok->len); tok = tok->next; if (!is_hash(tok) || !EQUAL(tok->next, "define") || - !EQUAL(tok->next->next, macro)) + !equal(tok->next->next, macro, strlen(macro))) return NULL; // Read until the end of the file. while (tok->kind != TK_EOF) { diff --git a/third_party/chibicc/test/bitfield_test.c b/third_party/chibicc/test/bitfield_test.c index 33b93cc6..5eb31112 100644 --- a/third_party/chibicc/test/bitfield_test.c +++ b/third_party/chibicc/test/bitfield_test.c @@ -1,5 +1,7 @@ #include "third_party/chibicc/test/test.h" +#define TYPE_SIGNED(type) (((type)-1) < 0) + struct { char a; int b : 5; @@ -7,6 +9,27 @@ struct { } g45 = {1, 2, 3}, g46 = {}; int main() { + + /* NOTE: Consistent w/ GCC (but MSVC would fail this) */ + ASSERT(1, 2 == ({ + struct { + enum { a, b, c } e : 2; + } x = { + .e = 2, + }; + x.e; + })); + + /* NOTE: GCC forbids typeof(bitfield). */ + ASSERT(0, ({ + struct { + enum { a, b, c } e : 2; + } x = { + .e = 2, + }; + TYPE_SIGNED(typeof(x.e)); + })); + ASSERT(4, sizeof(struct { int x : 1; })); ASSERT(8, sizeof(struct { long x : 1; })); diff --git a/third_party/chibicc/test/dce_test.c b/third_party/chibicc/test/dce_test.c index cab6d254..5182efcb 100644 --- a/third_party/chibicc/test/dce_test.c +++ b/third_party/chibicc/test/dce_test.c @@ -1,42 +1,26 @@ #include "third_party/chibicc/test/test.h" -int x; +#define CRASH \ + ({ \ + asm(".err"); \ + 666; \ + }) int main(void) { + if (0) { - asm(".error \"the assembler shall fail\""); + return CRASH; } - x = 1 ? 777 : ({ - asm(".error \"the system is down\""); - 666; - }); - ASSERT(777, x); + if (1) { + } else { + return CRASH; + } - x = 0; - x = 777 ?: ({ - asm(".error \"the system is down\""); - 666; - }); - - x = 0; - x = __builtin_popcount(strlen("hihi")) == 1 ? 777 : ({ - asm(".error \"the system is down\""); - 666; - }); - ASSERT(777, x); - - x = 0; - x = strpbrk("hihi", "ei") ? 777 : ({ - asm(".error \"the system is down!\""); - 666; - }); - ASSERT(777, x); - - x = 0; - x = !__builtin_strpbrk("HELLO\n", "bxdinupo") ? 777 : ({ - asm(".error \"the system is down\""); - 666; - }); - ASSERT(777, x); + ASSERT(777, 777 ?: CRASH); + ASSERT(777, 1 ? 777 : CRASH); + ASSERT(777, 0 ? CRASH : 777); + ASSERT(777, __builtin_popcount(__builtin_strlen("hihi")) == 1 ? 777 : CRASH); + ASSERT(777, !__builtin_strpbrk("HELLO\n", "bxdinupo") ? 777 : CRASH); + ASSERT(777, strpbrk("hihi", "ei") ? 777 : CRASH); } diff --git a/third_party/chibicc/test/initializer_test.c b/third_party/chibicc/test/initializer_test.c deleted file mode 100644 index c8914f2c..00000000 --- a/third_party/chibicc/test/initializer_test.c +++ /dev/null @@ -1,802 +0,0 @@ -#include "third_party/chibicc/test/test.h" - -char g3 = 3; -short g4 = 4; -int g5 = 5; -long g6 = 6; -int g9[3] = {0, 1, 2}; -struct { - char a; - int b; -} g11[2] = {{1, 2}, {3, 4}}; -struct { - int a[2]; -} g12[2] = {{{1, 2}}}; -union { - int a; - char b[8]; -} g13[2] = {0x01020304, 0x05060708}; -char g17[] = "foobar"; -char g18[10] = "foobar"; -char g19[3] = "foobar"; -char *g20 = g17 + 0; -char *g21 = g17 + 3; -char *g22 = &g17 - 3; -char *g23[] = {g17 + 0, g17 + 3, g17 - 3}; -int g24 = 3; -int *g25 = &g24; -int g26[3] = {1, 2, 3}; -int *g27 = g26 + 1; -int *g28 = &g11[1].a; -long g29 = (long)(long)g26; -struct { - struct { - int a[3]; - } a; -} g30 = {{{1, 2, 3}}}; -int *g31 = g30.a.a; -struct { - int a[2]; -} g40[2] = {{1, 2}, 3, 4}; -struct { - int a[2]; -} g41[2] = {1, 2, 3, 4}; -char g43[][4] = {'f', 'o', 'o', 0, 'b', 'a', 'r', 0}; -char *g44 = {"foo"}; -union { - int a; - char b[4]; -} g50 = {.b[2] = 0x12}; -union { - int a; -} g51[2] = {}; - -typedef char T60[]; -T60 g60 = {1, 2, 3}; -T60 g61 = {1, 2, 3, 4, 5, 6}; - -typedef struct { - char a, b[]; -} T65; -T65 g65 = {'f', 'o', 'o', 0}; -T65 g66 = {'f', 'o', 'o', 'b', 'a', 'r', 0}; - -int main() { - ASSERT(1, ({ - int x[3] = {1, 2, 3}; - x[0]; - })); - ASSERT(2, ({ - int x[3] = {1, 2, 3}; - x[1]; - })); - ASSERT(3, ({ - int x[3] = {1, 2, 3}; - x[2]; - })); - ASSERT(3, ({ - int x[3] = {1, 2, 3}; - x[2]; - })); - - ASSERT(2, ({ - int x[2][3] = {{1, 2, 3}, {4, 5, 6}}; - x[0][1]; - })); - ASSERT(4, ({ - int x[2][3] = {{1, 2, 3}, {4, 5, 6}}; - x[1][0]; - })); - ASSERT(6, ({ - int x[2][3] = {{1, 2, 3}, {4, 5, 6}}; - x[1][2]; - })); - - ASSERT(0, ({ - int x[3] = {}; - x[0]; - })); - ASSERT(0, ({ - int x[3] = {}; - x[1]; - })); - ASSERT(0, ({ - int x[3] = {}; - x[2]; - })); - - ASSERT(2, ({ - int x[2][3] = {{1, 2}}; - x[0][1]; - })); - ASSERT(0, ({ - int x[2][3] = {{1, 2}}; - x[1][0]; - })); - ASSERT(0, ({ - int x[2][3] = {{1, 2}}; - x[1][2]; - })); - - ASSERT('a', ({ - char x[4] = "abc"; - x[0]; - })); - ASSERT('c', ({ - char x[4] = "abc"; - x[2]; - })); - ASSERT(0, ({ - char x[4] = "abc"; - x[3]; - })); - ASSERT('a', ({ - char x[2][4] = {"abc", "def"}; - x[0][0]; - })); - ASSERT(0, ({ - char x[2][4] = {"abc", "def"}; - x[0][3]; - })); - ASSERT('d', ({ - char x[2][4] = {"abc", "def"}; - x[1][0]; - })); - ASSERT('f', ({ - char x[2][4] = {"abc", "def"}; - x[1][2]; - })); - - ASSERT(4, ({ - int x[] = {1, 2, 3, 4}; - x[3]; - })); - ASSERT(16, ({ - int x[] = {1, 2, 3, 4}; - sizeof(x); - })); - ASSERT(4, ({ - char x[] = "foo"; - sizeof(x); - })); - - ASSERT(4, ({ - typedef char T[]; - T x = "foo"; - T y = "x"; - sizeof(x); - })); - ASSERT(2, ({ - typedef char T[]; - T x = "foo"; - T y = "x"; - sizeof(y); - })); - ASSERT(2, ({ - typedef char T[]; - T x = "x"; - T y = "foo"; - sizeof(x); - })); - ASSERT(4, ({ - typedef char T[]; - T x = "x"; - T y = "foo"; - sizeof(y); - })); - - ASSERT(1, ({ - struct { - int a; - int b; - int c; - } x = {1, 2, 3}; - x.a; - })); - ASSERT(2, ({ - struct { - int a; - int b; - int c; - } x = {1, 2, 3}; - x.b; - })); - ASSERT(3, ({ - struct { - int a; - int b; - int c; - } x = {1, 2, 3}; - x.c; - })); - ASSERT(1, ({ - struct { - int a; - int b; - int c; - } x = {1}; - x.a; - })); - ASSERT(0, ({ - struct { - int a; - int b; - int c; - } x = {1}; - x.b; - })); - ASSERT(0, ({ - struct { - int a; - int b; - int c; - } x = {1}; - x.c; - })); - - ASSERT(1, ({ - struct { - int a; - int b; - } x[2] = {{1, 2}, {3, 4}}; - x[0].a; - })); - ASSERT(2, ({ - struct { - int a; - int b; - } x[2] = {{1, 2}, {3, 4}}; - x[0].b; - })); - ASSERT(3, ({ - struct { - int a; - int b; - } x[2] = {{1, 2}, {3, 4}}; - x[1].a; - })); - ASSERT(4, ({ - struct { - int a; - int b; - } x[2] = {{1, 2}, {3, 4}}; - x[1].b; - })); - - ASSERT(0, ({ - struct { - int a; - int b; - } x[2] = {{1, 2}}; - x[1].b; - })); - - ASSERT(0, ({ - struct { - int a; - int b; - } x = {}; - x.a; - })); - ASSERT(0, ({ - struct { - int a; - int b; - } x = {}; - x.b; - })); - - ASSERT(5, ({ - typedef struct { - int a, b, c, d, e, f; - } T; - T x = {1, 2, 3, 4, 5, 6}; - T y; - y = x; - y.e; - })); - ASSERT(2, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y, z; - z = y = x; - z.b; - })); - - ASSERT(1, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y = x; - y.a; - })); - - ASSERT(4, ({ - union { - int a; - char b[4]; - } x = {0x01020304}; - x.b[0]; - })); - ASSERT(3, ({ - union { - int a; - char b[4]; - } x = {0x01020304}; - x.b[1]; - })); - - ASSERT(0x01020304, ({ - union { - struct { - char a, b, c, d; - } e; - int f; - } x = {{4, 3, 2, 1}}; - x.f; - })); - - ASSERT(3, g3); - ASSERT(4, g4); - ASSERT(5, g5); - ASSERT(6, g6); - - ASSERT(0, g9[0]); - ASSERT(1, g9[1]); - ASSERT(2, g9[2]); - - ASSERT(1, g11[0].a); - ASSERT(2, g11[0].b); - ASSERT(3, g11[1].a); - ASSERT(4, g11[1].b); - - ASSERT(1, g12[0].a[0]); - ASSERT(2, g12[0].a[1]); - ASSERT(0, g12[1].a[0]); - ASSERT(0, g12[1].a[1]); - - ASSERT(4, g13[0].b[0]); - ASSERT(3, g13[0].b[1]); - ASSERT(8, g13[1].b[0]); - ASSERT(7, g13[1].b[1]); - - ASSERT(7, sizeof(g17)); - ASSERT(10, sizeof(g18)); - ASSERT(3, sizeof(g19)); - - ASSERT(0, memcmp(g17, "foobar", 7)); - ASSERT(0, memcmp(g18, "foobar\0\0\0", 10)); - ASSERT(0, memcmp(g19, "foo", 3)); - - ASSERT(0, strcmp(g20, "foobar")); - ASSERT(0, strcmp(g21, "bar")); - ASSERT(0, strcmp(g22 + 3, "foobar")); - - ASSERT(0, strcmp(g23[0], "foobar")); - ASSERT(0, strcmp(g23[1], "bar")); - ASSERT(0, strcmp(g23[2] + 3, "foobar")); - - ASSERT(3, g24); - ASSERT(3, *g25); - ASSERT(2, *g27); - ASSERT(3, *g28); - ASSERT(1, *(int *)g29); - - ASSERT(1, g31[0]); - ASSERT(2, g31[1]); - ASSERT(3, g31[2]); - - ASSERT(1, g40[0].a[0]); - ASSERT(2, g40[0].a[1]); - ASSERT(3, g40[1].a[0]); - ASSERT(4, g40[1].a[1]); - - ASSERT(1, g41[0].a[0]); - ASSERT(2, g41[0].a[1]); - ASSERT(3, g41[1].a[0]); - ASSERT(4, g41[1].a[1]); - - ASSERT(0, ({ - int x[2][3] = {0, 1, 2, 3, 4, 5}; - x[0][0]; - })); - ASSERT(3, ({ - int x[2][3] = {0, 1, 2, 3, 4, 5}; - x[1][0]; - })); - - ASSERT(0, ({ - struct { - int a; - int b; - } x[2] = {0, 1, 2, 3}; - x[0].a; - })); - ASSERT(2, ({ - struct { - int a; - int b; - } x[2] = {0, 1, 2, 3}; - x[1].a; - })); - - ASSERT(0, strcmp(g43[0], "foo")); - ASSERT(0, strcmp(g43[1], "bar")); - ASSERT(0, strcmp(g44, "foo")); - - ASSERT(3, ({ - int a[] = { - 1, - 2, - 3, - }; - a[2]; - })); - ASSERT(1, ({ - struct { - int a, b, c; - } x = { - 1, - 2, - 3, - }; - x.a; - })); - ASSERT(1, ({ - union { - int a; - char b; - } x = { - 1, - }; - x.a; - })); - ASSERT(2, ({ - enum { - x, - y, - z, - }; - z; - })); - - ASSERT(3, sizeof(g60)); - ASSERT(6, sizeof(g61)); - - ASSERT(4, sizeof(g65)); - ASSERT(7, sizeof(g66)); - ASSERT(0, strcmp(g65.b, "oo")); - ASSERT(0, strcmp(g66.b, "oobar")); - - ASSERT(4, ({ - int x[3] = {1, 2, 3, [0] = 4, 5}; - x[0]; - })); - ASSERT(5, ({ - int x[3] = {1, 2, 3, [0] = 4, 5}; - x[1]; - })); - ASSERT(3, ({ - int x[3] = {1, 2, 3, [0] = 4, 5}; - x[2]; - })); - - ASSERT(10, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[0][0]; - })); - ASSERT(11, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[0][1]; - })); - ASSERT(8, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[0][2]; - })); - ASSERT(12, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[1][0]; - })); - ASSERT(5, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[1][1]; - })); - ASSERT(6, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0][1] = 7, 8, [0] = 9, [0] = 10, 11, [1][0] = 12}; - x[1][2]; - })); - - ASSERT(7, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[0][0]; - })); - ASSERT(8, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[0][1]; - })); - ASSERT(3, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[0][2]; - })); - ASSERT(9, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[1][0]; - })); - ASSERT(10, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[1][1]; - })); - ASSERT(6, ({ - int x[2][3] = {1, 2, 3, 4, 5, 6, [0] = {7, 8}, 9, 10}; - x[1][2]; - })); - - ASSERT(7, ((int[10]){[3] = 7})[3]); - ASSERT(0, ((int[10]){[3] = 7})[4]); - - ASSERT(10, ({ - char x[] = {[10 - 3] = 1, 2, 3}; - sizeof(x); - })); - ASSERT(20, ({ - char x[][2] = {[8][1] = 1, 2}; - sizeof(x); - })); - - ASSERT(3, sizeof(g60)); - ASSERT(6, sizeof(g61)); - - ASSERT(4, sizeof(g65)); - ASSERT(7, sizeof(g66)); - ASSERT(0, strcmp(g65.b, "oo")); - ASSERT(0, strcmp(g66.b, "oobar")); - - ASSERT(7, ((int[10]){[3] 7})[3]); - ASSERT(0, ((int[10]){[3] 7})[4]); - - ASSERT(4, ({ - struct { - int a, b; - } x = {1, 2, .b = 3, .a = 4}; - x.a; - })); - ASSERT(3, ({ - struct { - int a, b; - } x = {1, 2, .b = 3, .a = 4}; - x.b; - })); - - ASSERT(1, ({ - struct { - struct { - int a, b; - } c; - } x = {.c = 1, 2}; - x.c.a; - })); - ASSERT(2, ({ - struct { - struct { - int a, b; - } c; - } x = {.c = 1, 2}; - x.c.b; - })); - - ASSERT(0, ({ - struct { - struct { - int a, b; - } c; - } x = {.c.b = 1}; - x.c.a; - })); - ASSERT(1, ({ - struct { - struct { - int a, b; - } c; - } x = {.c.b = 1}; - x.c.b; - })); - - ASSERT(1, ({ - struct { - int a[2]; - } x = {.a = 1, 2}; - x.a[0]; - })); - ASSERT(2, ({ - struct { - int a[2]; - } x = {.a = 1, 2}; - x.a[1]; - })); - - ASSERT(0, ({ - struct { - int a[2]; - } x = {.a[1] = 1}; - x.a[0]; - })); - ASSERT(1, ({ - struct { - int a[2]; - } x = {.a[1] = 1}; - x.a[1]; - })); - - ASSERT(3, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[0].a; - })); - ASSERT(4, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[0].b; - })); - ASSERT(0, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[1].a; - })); - ASSERT(1, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[1].b; - })); - ASSERT(2, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[2].a; - })); - ASSERT(0, ({ - struct { - int a, b; - } x[] = { - [1].b = 1, - 2, - [0] = 3, - 4, - }; - x[2].b; - })); - - ASSERT(1, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y[] = {x}; - y[0].a; - })); - ASSERT(2, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y[] = {x}; - y[0].b; - })); - ASSERT(0, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y[] = {x, [0].b = 3}; - y[0].a; - })); - ASSERT(3, ({ - typedef struct { - int a, b; - } T; - T x = {1, 2}; - T y[] = {x, [0].b = 3}; - y[0].b; - })); - - ASSERT(5, ((struct { int a, b, c; }){.c = 5}).c); - ASSERT(0, ((struct { int a, b, c; }){.c = 5}).a); - - ASSERT(0x00ff, ({ - union { - unsigned short a; - char b[2]; - } x = {.b[0] = 0xff}; - x.a; - })); - ASSERT(0xff00, ({ - union { - unsigned short a; - char b[2]; - } x = {.b[1] = 0xff}; - x.a; - })); - - ASSERT(0x00120000, g50.a); - ASSERT(0, g51[0].a); - ASSERT(0, g51[1].a); - - ASSERT(1, ({ - struct { - struct { - int a; - struct { - int b; - }; - }; - int c; - } x = {1, 2, 3, .b = 4, 5}; - x.a; - })); - ASSERT(4, ({ - struct { - struct { - int a; - struct { - int b; - }; - }; - int c; - } x = {1, 2, 3, .b = 4, 5}; - x.b; - })); - ASSERT(5, ({ - struct { - struct { - int a; - struct { - int b; - }; - }; - int c; - } x = {1, 2, 3, .b = 4, 5}; - x.c; - })); - - ASSERT(16, ({ - char x[] = {[2 ... 10] = 'a', [7] = 'b', [15 ... 15] = 'c', [3 ... 5] = 'd'}; - sizeof(x); - })); - ASSERT(0, ({ - char x[] = {[2 ... 10] = 'a', [7] = 'b', [15 ... 15] = 'c', [3 ... 5] = 'd'}; - memcmp(x, "\0\0adddabaaa\0\0\0\0c", 16); - })); - - return 0; -} diff --git a/third_party/chibicc/test/sizeof_test.c b/third_party/chibicc/test/sizeof_test.c index 71468111..d41e9bf4 100644 --- a/third_party/chibicc/test/sizeof_test.c +++ b/third_party/chibicc/test/sizeof_test.c @@ -109,5 +109,9 @@ int main() { ASSERT(1, sizeof(main)); + ASSERT(1, sizeof("")); + ASSERT(2, sizeof("h")); + ASSERT(6, sizeof("hello")); + return 0; } diff --git a/third_party/chibicc/tokenize.c b/third_party/chibicc/tokenize.c index a4b44fb2..c20507fc 100644 --- a/third_party/chibicc/tokenize.c +++ b/third_party/chibicc/tokenize.c @@ -1,6 +1,6 @@ #include "third_party/chibicc/chibicc.h" -#define LOOKINGAT(TOK, OP) (!memcmp(TOK, OP, strlen(OP))) +#define LOOKINGAT(TOK, OP) (!memcmp(TOK, OP, sizeof(OP) - 1)) // Input file static File *current_file; diff --git a/third_party/chibicc/type.c b/third_party/chibicc/type.c index fb8f8538..9e948c1d 100644 --- a/third_party/chibicc/type.c +++ b/third_party/chibicc/type.c @@ -113,7 +113,9 @@ Type *vla_of(Type *base, Node *len) { } Type *enum_type(void) { - return new_type(TY_ENUM, 4, 4); + Type *ty = new_type(TY_ENUM, 4, 4); + ty->is_unsigned = true; + return ty; } Type *struct_type(void) { diff --git a/third_party/compiler_rt/clear_cache.c b/third_party/compiler_rt/clear_cache.c deleted file mode 100644 index e139f29e..00000000 --- a/third_party/compiler_rt/clear_cache.c +++ /dev/null @@ -1,183 +0,0 @@ -/* clang-format off */ -/* ===-- clear_cache.c - Implement __clear_cache ---------------------------=== - * - * The LLVM Compiler Infrastructure - * - * This file is dual licensed under the MIT and the University of Illinois Open - * Source Licenses. See LICENSE.TXT for details. - * - * ===----------------------------------------------------------------------=== - */ - -STATIC_YOINK("huge_compiler_rt_license"); - -#include "third_party/compiler_rt/int_lib.h" - -#if __APPLE__ - #include -#endif - -#if defined(_WIN32) -/* Forward declare Win32 APIs since the GCC mode driver does not handle the - newer SDKs as well as needed. */ -uint32_t FlushInstructionCache(uintptr_t hProcess, void *lpBaseAddress, - uintptr_t dwSize); -uintptr_t GetCurrentProcess(void); -#endif - -#if defined(__linux__) && defined(__mips__) - #if defined(__ANDROID__) && defined(__LP64__) - /* - * clear_mips_cache - Invalidates instruction cache for Mips. - */ - static void clear_mips_cache(const void* Addr, size_t Size) { - __asm__ volatile ( - ".set push\n" - ".set noreorder\n" - ".set noat\n" - "beq %[Size], $zero, 20f\n" /* If size == 0, branch around. */ - "nop\n" - "daddu %[Size], %[Addr], %[Size]\n" /* Calculate end address + 1 */ - "rdhwr $v0, $1\n" /* Get step size for SYNCI. - $1 is $HW_SYNCI_Step */ - "beq $v0, $zero, 20f\n" /* If no caches require - synchronization, branch - around. */ - "nop\n" - "10:\n" - "synci 0(%[Addr])\n" /* Synchronize all caches around - address. */ - "daddu %[Addr], %[Addr], $v0\n" /* Add step size. */ - "sltu $at, %[Addr], %[Size]\n" /* Compare current with end - address. */ - "bne $at, $zero, 10b\n" /* Branch if more to do. */ - "nop\n" - "sync\n" /* Clear memory hazards. */ - "20:\n" - "bal 30f\n" - "nop\n" - "30:\n" - "daddiu $ra, $ra, 12\n" /* $ra has a value of $pc here. - Add offset of 12 to point to the - instruction after the last nop. - */ - "jr.hb $ra\n" /* Return, clearing instruction - hazards. */ - "nop\n" - ".set pop\n" - : [Addr] "+r"(Addr), [Size] "+r"(Size) - :: "at", "ra", "v0", "memory" - ); - } - #endif -#endif - -/* - * The compiler generates calls to __clear_cache() when creating - * trampoline functions on the stack for use with nested functions. - * It is expected to invalidate the instruction cache for the - * specified range. - */ - -void __clear_cache(void *start, void *end) { -#if __i386__ || __x86_64__ || defined(_M_IX86) || defined(_M_X64) -/* - * Intel processors have a unified instruction and data cache - * so there is nothing to do - */ -#elif defined(_WIN32) && (defined(__arm__) || defined(__aarch64__)) - FlushInstructionCache(GetCurrentProcess(), start, end - start); -#elif defined(__arm__) && !defined(__APPLE__) - #if defined(__FreeBSD__) || defined(__NetBSD__) - struct arm_sync_icache_args arg; - - arg.addr = (uintptr_t)start; - arg.len = (uintptr_t)end - (uintptr_t)start; - - sysarch(ARM_SYNC_ICACHE, &arg); - #elif defined(__linux__) - /* - * We used to include asm/unistd.h for the __ARM_NR_cacheflush define, but - * it also brought many other unused defines, as well as a dependency on - * kernel headers to be installed. - * - * This value is stable at least since Linux 3.13 and should remain so for - * compatibility reasons, warranting it's re-definition here. - */ - #define __ARM_NR_cacheflush 0x0f0002 - register int start_reg __asm("r0") = (int) (intptr_t) start; - const register int end_reg __asm("r1") = (int) (intptr_t) end; - const register int flags __asm("r2") = 0; - const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush; - __asm __volatile("svc 0x0" - : "=r"(start_reg) - : "r"(syscall_nr), "r"(start_reg), "r"(end_reg), - "r"(flags)); - assert(start_reg == 0 && "Cache flush syscall failed."); - #else - compilerrt_abort(); - #endif -#elif defined(__linux__) && defined(__mips__) - const uintptr_t start_int = (uintptr_t) start; - const uintptr_t end_int = (uintptr_t) end; - #if defined(__ANDROID__) && defined(__LP64__) - // Call synci implementation for short address range. - const uintptr_t address_range_limit = 256; - if ((end_int - start_int) <= address_range_limit) { - clear_mips_cache(start, (end_int - start_int)); - } else { - syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); - } - #else - syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); - #endif -#elif defined(__mips__) && defined(__OpenBSD__) - cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); -#elif defined(__aarch64__) && !defined(__APPLE__) - uint64_t xstart = (uint64_t)(uintptr_t) start; - uint64_t xend = (uint64_t)(uintptr_t) end; - uint64_t addr; - - // Get Cache Type Info - uint64_t ctr_el0; - __asm __volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); - - /* - * dc & ic instructions must use 64bit registers so we don't use - * uintptr_t in case this runs in an IPL32 environment. - */ - const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15); - for (addr = xstart & ~(dcache_line_size - 1); addr < xend; - addr += dcache_line_size) - __asm __volatile("dc cvau, %0" :: "r"(addr)); - __asm __volatile("dsb ish"); - - const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15); - for (addr = xstart & ~(icache_line_size - 1); addr < xend; - addr += icache_line_size) - __asm __volatile("ic ivau, %0" :: "r"(addr)); - __asm __volatile("isb sy"); -#elif defined (__powerpc64__) - const size_t line_size = 32; - const size_t len = (uintptr_t)end - (uintptr_t)start; - - const uintptr_t mask = ~(line_size - 1); - const uintptr_t start_line = ((uintptr_t)start) & mask; - const uintptr_t end_line = ((uintptr_t)start + len + line_size - 1) & mask; - - for (uintptr_t line = start_line; line < end_line; line += line_size) - __asm__ volatile("dcbf 0, %0" : : "r"(line)); - __asm__ volatile("sync"); - - for (uintptr_t line = start_line; line < end_line; line += line_size) - __asm__ volatile("icbi 0, %0" : : "r"(line)); - __asm__ volatile("isync"); -#else - #if __APPLE__ - /* On Darwin, sys_icache_invalidate() provides this functionality */ - sys_icache_invalidate(start, end-start); - #else - compilerrt_abort(); - #endif -#endif -} diff --git a/third_party/compiler_rt/trampoline_setup.c b/third_party/compiler_rt/trampoline_setup.c deleted file mode 100644 index d6798b93..00000000 --- a/third_party/compiler_rt/trampoline_setup.c +++ /dev/null @@ -1,51 +0,0 @@ -/* clang-format off */ -/* ===----- trampoline_setup.c - Implement __trampoline_setup -------------=== - * - * The LLVM Compiler Infrastructure - * - * This file is dual licensed under the MIT and the University of Illinois Open - * Source Licenses. See LICENSE.TXT for details. - * - * ===----------------------------------------------------------------------=== - */ - -STATIC_YOINK("huge_compiler_rt_license"); - -#include "third_party/compiler_rt/int_lib.h" - -extern void __clear_cache(void* start, void* end); - -/* - * The ppc compiler generates calls to __trampoline_setup() when creating - * trampoline functions on the stack for use with nested functions. - * This function creates a custom 40-byte trampoline function on the stack - * which loads r11 with a pointer to the outer function's locals - * and then jumps to the target nested function. - */ - -#if __ppc__ && !defined(__powerpc64__) -COMPILER_RT_ABI void -__trampoline_setup(uint32_t* trampOnStack, int trampSizeAllocated, - const void* realFunc, void* localsPtr) -{ - /* should never happen, but if compiler did not allocate */ - /* enough space on stack for the trampoline, abort */ - if ( trampSizeAllocated < 40 ) - compilerrt_abort(); - - /* create trampoline */ - trampOnStack[0] = 0x7c0802a6; /* mflr r0 */ - trampOnStack[1] = 0x4800000d; /* bl Lbase */ - trampOnStack[2] = (uint32_t)realFunc; - trampOnStack[3] = (uint32_t)localsPtr; - trampOnStack[4] = 0x7d6802a6; /* Lbase: mflr r11 */ - trampOnStack[5] = 0x818b0000; /* lwz r12,0(r11) */ - trampOnStack[6] = 0x7c0803a6; /* mtlr r0 */ - trampOnStack[7] = 0x7d8903a6; /* mtctr r12 */ - trampOnStack[8] = 0x816b0004; /* lwz r11,4(r11) */ - trampOnStack[9] = 0x4e800420; /* bctr */ - - /* clear instruction cache */ - __clear_cache(trampOnStack, &trampOnStack[10]); -} -#endif /* __ppc__ && !defined(__powerpc64__) */ diff --git a/third_party/duktape/duk_config.h b/third_party/duktape/duk_config.h index 27ce9553..5ccf4663 100644 --- a/third_party/duktape/duk_config.h +++ b/third_party/duktape/duk_config.h @@ -800,8 +800,8 @@ * because of bugs in gcc-4.4 * (http://lists.debian.org/debian-gcc/2010/04/msg00000.html) */ -#define DUK_LIKELY(x) likely(x) -#define DUK_UNLIKELY(x) unlikely(x) +#define DUK_LIKELY(x) __builtin_expect(!!(x), 1) +#define DUK_UNLIKELY(x) __builtin_expect(!!(x), 0) #endif /* XXX: equivalent of clang __builtin_unpredictable? */ diff --git a/tool/build/build.mk b/tool/build/build.mk index e60120f4..34cc49c8 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -48,7 +48,6 @@ TOOL_BUILD_DIRECTDEPS = \ LIBC_STUBS \ LIBC_SYSV \ LIBC_SYSV_CALLS \ - LIBC_TESTLIB \ LIBC_TIME \ LIBC_TINYMATH \ LIBC_UNICODE \ diff --git a/tool/build/lib/elfwriter.c b/tool/build/lib/elfwriter.c index 25bd7182..2405437f 100644 --- a/tool/build/lib/elfwriter.c +++ b/tool/build/lib/elfwriter.c @@ -190,7 +190,10 @@ void elfwriter_close(struct ElfWriter *elf) { freeinterner(elf->strtab); free(elf->shdrs->p); free(elf->relas->p); - for (i = 0; i < ARRAYLEN(elf->syms); ++i) free(elf->syms[i]->p); + free(elf->path); + for (i = 0; i < ARRAYLEN(elf->syms); ++i) { + free(elf->syms[i]->p); + } free(elf); } @@ -237,6 +240,14 @@ void elfwriter_finishsection(struct ElfWriter *elf) { if (elf->relas->j < elf->relas->i) MakeRelaSection(elf, section); } +/** + * Appends symbol. + * + * This function should be called between elfwriter_startsection() and + * elfwriter_finishsection(). If that's not possible, then this can be + * called after elfwriter_open() and then elfwriter_setsection() can be + * called later to fix-up the section id. + */ struct ElfWriterSymRef elfwriter_appendsym(struct ElfWriter *elf, const char *name, int st_info, int st_other, size_t st_value, @@ -247,6 +258,11 @@ struct ElfWriterSymRef elfwriter_appendsym(struct ElfWriter *elf, : kElfWriterSymGlobal); } +void elfwriter_setsection(struct ElfWriter *elf, struct ElfWriterSymRef sym, + uint16_t st_shndx) { + elf->syms[sym.slg]->p[sym.sym].st_shndx = st_shndx; +} + struct ElfWriterSymRef elfwriter_linksym(struct ElfWriter *elf, const char *name, int st_info, int st_other) { diff --git a/tool/build/lib/elfwriter.h b/tool/build/lib/elfwriter.h index f8505daf..a9baaa94 100644 --- a/tool/build/lib/elfwriter.h +++ b/tool/build/lib/elfwriter.h @@ -61,6 +61,7 @@ struct ElfWriterSymRef elfwriter_linksym(struct ElfWriter *, const char *, int, struct ElfWriterSymRef elfwriter_appendsym(struct ElfWriter *, const char *, int, int, size_t, size_t); void elfwriter_yoink(struct ElfWriter *, const char *); +void elfwriter_setsection(struct ElfWriter *, struct ElfWriterSymRef, uint16_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/interner.c b/tool/build/lib/interner.c index ee41819a..ab92bd60 100644 --- a/tool/build/lib/interner.c +++ b/tool/build/lib/interner.c @@ -47,7 +47,7 @@ static void rehash(struct InternerObject *it) { if (!p[i].hash) continue; step = 0; do { - j = (p[i].hash + step * (step + 1) / 2) & (it->n - 1); + j = (p[i].hash + step * ((step + 1) >> 1)) & (it->n - 1); step++; } while (it->p[j].hash); memcpy(&it->p[j], &p[i], sizeof(p[i])); @@ -103,7 +103,7 @@ size_t internobj(struct Interner *t, const void *data, size_t size) { hash = max(1, KnuthMultiplicativeHash32(data, size)); do { /* it is written that triangle probe halts iff in - 1); + i = (hash + step * ((step + 1) >> 1)) & (it->n - 1); if (it->p[i].hash == hash && it->p[i].index + size <= it->pool.n && memcmp(item, &it->pool.p[it->p[i].index], size) == 0) { return it->p[i].index; @@ -114,7 +114,7 @@ size_t internobj(struct Interner *t, const void *data, size_t size) { rehash(it); step = 0; do { - i = (hash + step * (step + 1) / 2) & (it->n - 1); + i = (hash + step * ((step + 1) >> 1)) & (it->n - 1); step++; } while (it->p[i].hash); } diff --git a/tool/build/lib/iovs.c b/tool/build/lib/iovs.c index 50ec67a9..b6eb42cf 100644 --- a/tool/build/lib/iovs.c +++ b/tool/build/lib/iovs.c @@ -33,7 +33,7 @@ int AppendIovs(struct Iovs *ib, void *base, size_t len) { if (i && (intptr_t)base == (intptr_t)p[i - 1].iov_base + p[i - 1].iov_len) { p[i - 1].iov_len += len; } else { - if (unlikely(i == n)) { + if (__builtin_expect(i == n, 0)) { n += n >> 1; if (p == ib->init) { if (!(p = malloc(sizeof(struct iovec) * n))) return -1; diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 9685117b..728f5533 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -66,7 +66,7 @@ * Only the first 64kb of each source file is considered. */ -alignas(16) const char kIncludePrefix[] = "include \""; +_Alignas(16) const char kIncludePrefix[] = "include \""; const char kSourceExts[][5] = {".s", ".S", ".c", ".cc", ".cpp"}; diff --git a/tool/decode/elf.c b/tool/decode/elf.c index 866244f6..8f2b7dbf 100644 --- a/tool/decode/elf.c +++ b/tool/decode/elf.c @@ -32,6 +32,7 @@ #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/x/x.h" #include "tool/decode/lib/asmcodegen.h" #include "tool/decode/lib/elfidnames.h" #include "tool/decode/lib/flagger.h" @@ -249,10 +250,10 @@ static char *getelfsymbolname(const Elf64_Ehdr *elf, size_t mapsize, static void printelfrelocations(void) { int sym; - size_t i, j; + size_t i, j, count; const Elf64_Sym *syms; const Elf64_Rela *rela; - const Elf64_Shdr *shdr, *boop; + const Elf64_Shdr *shdr, *symtab; char *strtab, *shstrtab, *symbolname; strtab = GetElfStringTable(elf, st->st_size); shstrtab = GetElfSectionNameStringTable(elf, st->st_size); @@ -266,11 +267,16 @@ static void printelfrelocations(void) { min((uintptr_t)elf + st->st_size, (uintptr_t)elf + shdr->sh_offset + shdr->sh_size)); ++rela, ++j) { - boop = GetElfSectionHeaderAddress(elf, st->st_size, shdr->sh_link); - syms = GetElfSectionAddress(elf, st->st_size, boop); + symtab = GetElfSectionHeaderAddress(elf, st->st_size, shdr->sh_link); + count = symtab->sh_size / symtab->sh_entsize; + syms = GetElfSectionAddress(elf, st->st_size, symtab); sym = ELF64_R_SYM(rela->r_info); - symbolname = - getelfsymbolname(elf, st->st_size, strtab, shstrtab, &syms[sym]); + if (0 <= sym && sym < count) { + symbolname = + getelfsymbolname(elf, st->st_size, strtab, shstrtab, syms + sym); + } else { + symbolname = xasprintf("bad-sym-%d", sym); + } printf("/\t%s+%#lx → %s%c%#lx\n", GetElfString( elf, st->st_size, shstrtab, @@ -297,7 +303,7 @@ static void printelfrelocations(void) { int main(int argc, char *argv[]) { showcrashreports(); if (argc != 2) { - fprintf(stderr, "usage: %`s FILE: %s\n", argv[0]); + fprintf(stderr, "usage: %s FILE\n", argv[0]); return 1; } path = argv[1]; diff --git a/tool/emacs/c.lang b/tool/emacs/c.lang index 8f74affb..e1a88a57 100644 --- a/tool/emacs/c.lang +++ b/tool/emacs/c.lang @@ -1665,7 +1665,6 @@ Keywords={ "COSMOPOLITAN_C_END_", "MACHINE_CODE_ANALYSIS_BEGIN_", "MACHINE_CODE_ANALYSIS_END_", -"typescompatible", "DebugBreak", "VEIL", "CONCEAL", @@ -1674,8 +1673,6 @@ Keywords={ "STATIC_YOINK", "STATIC_YOINK_SOURCE", "STRINGIFY", -"isconstant", -"chooseexpr", "likely", "unlikely", "assume", diff --git a/tool/emacs/cosmo-c-builtins.el b/tool/emacs/cosmo-c-builtins.el index c4175dec..6f61d094 100644 --- a/tool/emacs/cosmo-c-builtins.el +++ b/tool/emacs/cosmo-c-builtins.el @@ -1280,8 +1280,7 @@ "MACHINE_CODE_ANALYSIS_END_")) (cosmopolitan-builtin-functions - '("typescompatible" - "DebugBreak" + '("DebugBreak" "VEIL" "CONCEAL" "EXPROPRIATE" @@ -1289,8 +1288,6 @@ "STATIC_YOINK" "STATIC_YOINK_SOURCE" "STRINGIFY" - "isconstant" - "chooseexpr" "likely" "unlikely")) diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 1210a690..6601622f 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -441,7 +441,7 @@ (cond ((not (eq 0 (logand 8 arg))) (cosmo--assembly (setq arg (logand (lognot 8))) "SILENT=0 COPTS='-Os'")) - (t (cosmo--assembly arg "SILENT=0 COPTS='-Os' TARGET_ARCH='-march=znver2 -mdispatch-scheduler' CPPFLAGS='-DSTACK_FRAME_UNLIMITED'")))) + (t (cosmo--assembly arg "SILENT=0 COPTS='-Os' TARGET_ARCH='-mdispatch-scheduler' CPPFLAGS='-DSTACK_FRAME_UNLIMITED'")))) (defun cosmo-assembly-native (arg) (interactive "P")