diff --git a/Makefile b/Makefile index d147fc6ed..5e5e4245a 100644 --- a/Makefile +++ b/Makefile @@ -116,6 +116,7 @@ include libc/mem/mem.mk # │ include libc/ohmyplus/ohmyplus.mk # │ include libc/zipos/zipos.mk # │ include third_party/dtoa/dtoa.mk # │ +include third_party/gdtoa/gdtoa.mk # │ include libc/time/time.mk # │ include libc/alg/alg.mk # │ include libc/calls/hefty/hefty.mk # │ @@ -138,6 +139,7 @@ include dsp/tty/tty.mk # ├──online include libc/dns/dns.mk # │ include libc/crypto/crypto.mk # │ include net/http/http.mk #─┘ +include third_party/chibicc/chibicc.mk include third_party/lemon/lemon.mk include third_party/linenoise/linenoise.mk include third_party/editline/editline.mk diff --git a/ape/ape.S b/ape/ape.S index e24e7b3cb..c60c8dfbc 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -35,7 +35,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/config.h" #include "ape/lib/pc.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" #include "ape/relocations.h" #include "libc/elf/def.h" @@ -785,8 +785,8 @@ ape.pe: .ascin "PE",4 .long 0 # Checksum .short v_ntsubsystem # Subsystem: 0=Neutral,2=GUI,3=Console .short .LDLLEXE # DllCharacteristics - .quad 0x0000000000080000 # StackReserve - .quad 0x0000000000080000 # StackCommit + .quad 0x0000000000020000 # StackReserve + .quad 0x0000000000020000 # StackCommit .quad 0 # HeapReserve .quad 0 # HeapCommit .long 0 # LoaderFlags diff --git a/ape/ape.lds b/ape/ape.lds index 4feef395b..2c4905631 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -175,7 +175,7 @@ Until then, we can build for those platforms using Linux or WSL. */ #ifdef __LINKER__ -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/config.h" #include "libc/nt/pedef.internal.h" #include "libc/zip.h" diff --git a/ape/lib/bootdr.S b/ape/lib/bootdr.S index caf05dfb8..47082108b 100644 --- a/ape/lib/bootdr.S +++ b/ape/lib/bootdr.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" .section .real,"ax",@progbits .source __FILE__ diff --git a/ape/lib/e820map.S b/ape/lib/e820map.S index a80dcfbe3..7be808c71 100644 --- a/ape/lib/e820map.S +++ b/ape/lib/e820map.S @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/lib/pc.h" #include "ape/config.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" .section .real,"ax",@progbits .source __FILE__ diff --git a/ape/lib/flattenhighmemory.c b/ape/lib/flattenhighmemory.c index 54b0045e5..13244a53f 100644 --- a/ape/lib/flattenhighmemory.c +++ b/ape/lib/flattenhighmemory.c @@ -20,7 +20,7 @@ #include "ape/config.h" #include "ape/lib/pc.h" #include "libc/bits/bits.h" -#include "libc/bits/safemacros.internal.h" +#include "libc/macros.h" /** * Virtualizes physical memory. @@ -33,27 +33,24 @@ */ textreal void flattenhighmemory(struct SmapEntry *e820, struct PageTable *pml4t, uint64_t *ptsp) { - struct SmapEntry *smap = e820; - struct SmapEntry *hole = e820; - uint64_t paddr = IMAGE_BASE_PHYSICAL; - uint64_t vaddr = IMAGE_BASE_VIRTUAL; - while (smap->size) { + uint64_t *entry, paddr, vaddr; + struct SmapEntry *smap, *hole; + for (smap = hole = e820, vaddr = IMAGE_BASE_VIRTUAL; smap->size; ++smap) { while (smap->size && smap->type != kMemoryUsable) smap++; - paddr = roundup(max(paddr, smap->addr), PAGESIZE); - while (paddr < rounddown(smap->addr + smap->size, PAGESIZE)) { + paddr = ROUNDUP(MAX(IMAGE_BASE_PHYSICAL, smap->addr), PAGESIZE); + while (paddr < ROUNDDOWN(smap->addr + smap->size, PAGESIZE)) { while (hole->size && (hole->type == kMemoryUsable || hole->addr + hole->size < paddr)) { hole++; } if (paddr >= hole->addr && paddr < hole->addr + hole->size) { - paddr = roundup(hole->addr + hole->size, PAGESIZE); + paddr = ROUNDUP(hole->addr + hole->size, PAGESIZE); } else { - uint64_t *entry = getpagetableentry(vaddr, 3, pml4t, ptsp); + entry = __getpagetableentry(vaddr, 3, pml4t, ptsp); *entry = paddr | PAGE_V | PAGE_RW; vaddr += 0x1000; paddr += 0x1000; } } - smap++; } } diff --git a/ape/lib/g_pml4t.S b/ape/lib/g_pml4t.S index a2554934e..8a8f6e40e 100644 --- a/ape/lib/g_pml4t.S +++ b/ape/lib/g_pml4t.S @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/lib/pc.h" #include "ape/config.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" .section .real,"ax",@progbits .source __FILE__ diff --git a/ape/lib/g_ptsp.S b/ape/lib/g_ptsp.S index c6725d582..dc297ec63 100644 --- a/ape/lib/g_ptsp.S +++ b/ape/lib/g_ptsp.S @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/lib/pc.h" #include "ape/config.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" .section .real,"ax",@progbits .source __FILE__ diff --git a/ape/lib/getpagetableentry.c b/ape/lib/getpagetableentry.c index c5a4f06a2..71dc7714c 100644 --- a/ape/lib/getpagetableentry.c +++ b/ape/lib/getpagetableentry.c @@ -20,12 +20,13 @@ #include "ape/lib/pc.h" #include "libc/assert.h" -textreal static uint64_t pushpagetable(uint64_t *ptsp) { +static textreal uint64_t __pushpagetable(uint64_t *ptsp) { return (*ptsp -= PAGESIZE) | PAGE_V | PAGE_RW; } -textreal uint64_t *getpagetableentry(int64_t vaddr, unsigned depth, - struct PageTable *pml4t, uint64_t *ptsp) { +textreal uint64_t *__getpagetableentry(int64_t vaddr, unsigned depth, + struct PageTable *pml4t, + uint64_t *ptsp) { uint64_t *entry; unsigned char shift; assert(depth <= 3); @@ -36,7 +37,7 @@ textreal uint64_t *getpagetableentry(int64_t vaddr, unsigned depth, entry = &pml4t->p[(vaddr >> shift) & 511]; if (!depth--) return entry; shift -= 9; - if (!*entry) *entry = pushpagetable(ptsp); + if (!*entry) *entry = __pushpagetable(ptsp); pml4t = (void *)(*entry & PAGE_TA); } } diff --git a/ape/lib/kbiosdataarea.S b/ape/lib/kbiosdataarea.S index c2048b0db..018beaa5a 100644 --- a/ape/lib/kbiosdataarea.S +++ b/ape/lib/kbiosdataarea.S @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/lib/pc.h" #include "ape/config.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/notice.inc" .section .real,"ax",@progbits .source __FILE__ diff --git a/ape/lib/mapimage.c b/ape/lib/mapimage.c index 0961fa5bc..05a2655f5 100644 --- a/ape/lib/mapimage.c +++ b/ape/lib/mapimage.c @@ -24,7 +24,7 @@ textreal static void __map_segment(uint64_t k, uint64_t a, uint64_t b) { uint64_t *e; for (; a < b; a += 0x1000) { - e = getpagetableentry(IMAGE_BASE_VIRTUAL + a, 3, &g_pml4t, &g_ptsp_xlm); + e = __getpagetableentry(IMAGE_BASE_VIRTUAL + a, 3, &g_pml4t, &g_ptsp_xlm); *e = (IMAGE_BASE_REAL + a) | k; } } diff --git a/ape/lib/pageunmap.c b/ape/lib/pageunmap.c index 2e9980d9f..6e39676cb 100644 --- a/ape/lib/pageunmap.c +++ b/ape/lib/pageunmap.c @@ -22,7 +22,7 @@ textreal void pageunmap(int64_t vaddr) { uint64_t *entry; - entry = getpagetableentry(vaddr, 3, &g_pml4t, &g_ptsp_xlm); + entry = __getpagetableentry(vaddr, 3, &g_pml4t, &g_ptsp_xlm); *entry &= ~PAGE_V; invlpg(vaddr); } diff --git a/ape/lib/pc.h b/ape/lib/pc.h index e0d6cac91..991bc7c29 100644 --- a/ape/lib/pc.h +++ b/ape/lib/pc.h @@ -214,7 +214,8 @@ extern uint64_t g_ptsp_xlm; void bootdr(char drive) noreturn; void smapsort(struct SmapEntry *); -uint64_t *getpagetableentry(int64_t, unsigned, struct PageTable *, uint64_t *); +uint64_t *__getpagetableentry(int64_t, unsigned, struct PageTable *, + uint64_t *); void flattenhighmemory(struct SmapEntry *, struct PageTable *, uint64_t *); void pageunmap(int64_t); diff --git a/ape/macros.h b/ape/macros.internal.h similarity index 100% rename from ape/macros.h rename to ape/macros.internal.h diff --git a/dsp/tty/ttyraw.c b/dsp/tty/ttyraw.c index ffa7c4092..ab8d6808e 100644 --- a/dsp/tty/ttyraw.c +++ b/dsp/tty/ttyraw.c @@ -29,7 +29,6 @@ #include "libc/log/log.h" #include "libc/macros.h" #include "libc/runtime/gc.h" -#include "libc/runtime/rbx.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" diff --git a/examples/acid2.c b/examples/acid2.c deleted file mode 100644 index a7300d83a..000000000 --- a/examples/acid2.c +++ /dev/null @@ -1,136 +0,0 @@ -#if 0 -/*─────────────────────────────────────────────────────────────────╗ -│ To the extent possible under law, Justine Tunney has waived │ -│ all copyright and related or neighboring rights to this file, │ -│ as it is written in the following disclaimers: │ -│ • http://unlicense.org/ │ -│ • http://creativecommons.org/publicdomain/zero/1.0/ │ -╚─────────────────────────────────────────────────────────────────*/ -#endif -#include "libc/calls/calls.h" -#include "libc/calls/ioctl.h" -#include "libc/calls/struct/termios.h" -#include "libc/fmt/fmt.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/termios.h" - -int yn2, xn2; -int yn3, xn3; -char b[128], inbuf[128]; -int y, x, yn, xn, my, mx; -struct termios term, oldterm; - -int main(int argc, char *argv[]) { - int i; - setvbuf(stdout, inbuf, _IONBF, 128); /* make things slower */ - - /* raw mode */ - ioctl(1, TCGETS, &oldterm); - memcpy(&term, &oldterm, sizeof(term)); - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 1; - term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON); - term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL); - term.c_cflag &= ~(CSIZE | PARENB); - term.c_oflag &= ~OPOST; - term.c_cflag |= CS8; - term.c_iflag |= IUTF8; - ioctl(1, TCSETSF, &term); - - /* get cursor position and display dimensions */ - printf("\e7\e[6n\e[9979;9979H\e[6n\e8"); - read(0, b, sizeof(b)); - sscanf(b, "\e[%d;%dR\e[%d;%dR", &y, &x, &yn, &xn); - - printf("\e[1q"); /* turn on led one */ - printf("\e[2J"); /* clear display */ - printf("\e#8"); /* fill display with E's */ - printf("\e[H"); - - /* clear display again */ - printf("\e[2q"); /* turn on led two */ - for (i = 0; i < yn; ++i) { - if (i) printf("\n"); - printf(" "); - printf("\e[0K"); - } - for (i = 0; i < yn - 1; ++i) { - if (i) printf("\eM"); - printf("\e[1K"); - } - - printf("\e(0"); /* line drawing mode */ - printf("\e[3q"); /* turn on led three */ - printf("\e[H"); - - /* move to center */ - my = yn / 2; - mx = xn / 2 - 7; - if (y > my) { - printf("\e[%dA", y - my); - } else if (y < my) { - printf("\e[%dB", my - y); - } - if (x > mx) { - printf("\e[%dD", x - mx); - } else if (x < mx) { - printf("\e[%dC", mx - x); - } - - printf("\e[90;103m"); /* black on yellow */ - printf("\e[90;103ma ` a"); /* draw nose */ - - printf("\e[0m"); /* reset style */ - printf("\e(B"); /* ascii mode */ - - /* draw corners */ - printf("\e[H"); /* top left */ - printf("A"); - printf("\e[9979C"); /* rightmost */ - printf("B"); - printf("\e[9979;9979H"); /* bottom right corner */ - printf("\e[C"); /* move right gets clamped */ - printf("D"); /* write, set redzone flag */ - printf("\e[2A"); /* move up, unsets redzone */ - - /* gnu screen now reports out of bounds position */ - /* kitty hasnt got a redzone reporting next line */ - printf("\e[6n"); - read(0, b, sizeof(b)); - sscanf(b, "\e[%d;%dR", &yn2, &xn2); - - /* writes to (yn-3,xn-1) normally and (yn-2,0) in gnu screen */ - printf("!"); - - /* draw ruler on top */ - printf("\e[H"); - for (i = 8; i + 1 < xn; i += 8) { - printf("\e[%dG%d", i + 1, i); /* set column */ - } - - printf("\e[9979;9979H"); /* bottom right */ - printf("\e[9979D"); /* leftmost */ - printf("C"); - - /* let's break gnu screen again with multimonospace redzone */ - printf("\e[%d;9979H", yn / 2); /* right middle */ - printf("\e[D"); /* left */ - printf("A"); - printf("\e[6n"); - read(0, b, sizeof(b)); - sscanf(b, "\e[%d;%dR", &yn2, &xn2); - - printf("\e[%dH", yn / 2); - printf("%d %d vs. %d %d\r\n", yn, xn, yn2, xn2); - printf("%d %d vs. %d %d\r\n", yn / 2, xn, yn2, xn2); - printf("\e#6double width\e#5\r\n"); - printf("\e[3mthis text is so \e[1mitalic\e[0m\r\n"); - printf("\e[1;20mpress any fraktur exit\e[0m"); - printf("\a"); - - read(0, b, sizeof(b)); - printf("\r\n"); - ioctl(1, TCSETS, &oldterm); - return 0; -} diff --git a/examples/auto-launch-gdb-on-crash.c b/examples/auto-launch-gdb-on-crash.c index e91cda4c3..734b870ef 100644 --- a/examples/auto-launch-gdb-on-crash.c +++ b/examples/auto-launch-gdb-on-crash.c @@ -33,7 +33,6 @@ * - libc/nexgen32e/bsf.h * - libc/nexgen32e/tzcnt.h * - libc/nexgen32e/cpuid4.internal.h - * - libc/nexgen32e/tinystrcmp.internal.h * - https://gist.github.com/jart/fe8d104ef93149b5ba9b72912820282c */ diff --git a/examples/forkrand.c b/examples/forkrand.c index a6d810dc4..1123643c9 100644 --- a/examples/forkrand.c +++ b/examples/forkrand.c @@ -16,13 +16,13 @@ #include "libc/stdio/stdio.h" #include "libc/time/time.h" -noinline void dostuff(void) { +noinline void dostuff(const char *s) { int i, us; srand(rand64()); /* seeds rand() w/ intel rdrnd, auxv, etc. */ for (i = 0; i < 5; ++i) { us = rand() % 500000; usleep(us); - printf("%s%u%s%u [%s=%d]\n", "hello no. ", i, " from ", getpid(), "us", us); + printf("hello no. %u from %s %u [us=%d]\n", i, s, getpid(), us); fflush(stdout); } } @@ -32,13 +32,14 @@ int main(int argc, char *argv[]) { CHECK_NE(-1, (child = fork())); if (!child) { /* child process */ - dostuff(); + dostuff("child"); return 0; } else { /* parent process */ - dostuff(); + dostuff("parent"); /* note: abandoned children become zombies */ CHECK_NE(-1, (rc = wait(&wstatus))); - return WEXITSTATUS(wstatus); + CHECK_EQ(0, WEXITSTATUS(wstatus)); + return 0; } } diff --git a/examples/tiny-raw-linux-tutorial.S b/examples/tiny-raw-linux-tutorial.S index 81801c401..9ca5de468 100644 --- a/examples/tiny-raw-linux-tutorial.S +++ b/examples/tiny-raw-linux-tutorial.S @@ -42,7 +42,6 @@ / @see also glibc static binaries which start at 800kb!!! / @see also go where interfaces sadly disempower ld prune / @see also the stl where bad linkage is due to tech debt -/ @see libc/macros-cpp.inc forthe getstr macro definition / @note libc/elf/elf.lds can be tinier with page align off / @note gas is more powerful than nasm due to rms notation / @noreturn diff --git a/libc/bits/morton.h b/libc/bits/morton.h index 67bf743db..f749573d4 100644 --- a/libc/bits/morton.h +++ b/libc/bits/morton.h @@ -6,18 +6,17 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -unsigned long morton(unsigned long, unsigned long) libcesque pureconst; -axdx_t unmorton(unsigned long) libcesque pureconst; +unsigned long morton(unsigned long, unsigned long) libcesque; +axdx_t unmorton(unsigned long) libcesque; #ifndef __STRICT_ANSI__ -#define morton(Y, X) \ - (X86_NEED(BMI2) \ - ? pdep(X, 0x5555555555555555ul) | pdep(Y, 0xAAAAAAAAAAAAAAAAul) \ - : morton(Y, X)) -#define unmorton(I) \ - (X86_NEED(BMI2) ? (axdx_t){pext(I, 0xAAAAAAAAAAAAAAAAul), \ - pext(I, 0x5555555555555555ul)} \ - : unmorton(I)) +#define morton(Y, X) \ + (X86_NEED(BMI2) ? pdep(X, 0x5555555555555555) | pdep(Y, 0xAAAAAAAAAAAAAAAA) \ + : morton(Y, X)) +#define unmorton(I) \ + (X86_NEED(BMI2) \ + ? (axdx_t){pext(I, 0xAAAAAAAAAAAAAAAA), pext(I, 0x5555555555555555)} \ + : unmorton(I)) #endif COSMOPOLITAN_C_END_ diff --git a/libc/calls/access.c b/libc/calls/access.c index 94e3c43ec..02a584bff 100644 --- a/libc/calls/access.c +++ b/libc/calls/access.c @@ -32,12 +32,12 @@ * @asyncsignalsafe */ int access(const char *path, int mode) { + char16_t path16[PATH_MAX]; if (!path) return efault(); if (!IsWindows()) { return faccessat$sysv(AT_FDCWD, path, mode, 0); } else { - char16_t path16[PATH_MAX]; - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; return ntaccesscheck(path16, mode); } } diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index d3d4bfc23..9af593b9e 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -25,7 +25,7 @@ textwindows int chdir$nt(const char *path) { int len; char16_t path16[PATH_MAX]; - if ((len = mkntpath(path, path16)) == -1) return -1; + if ((len = __mkntpath(path, path16)) == -1) return -1; if (path16[len - 1] != u'/' && path16[len - 1] != u'\\') { path16[len + 0] = u'/'; path16[len + 1] = u'\0'; diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index 0f7a333d5..09e24475e 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -24,22 +24,15 @@ textwindows int close$nt(int fd) { bool32 ok; - if (__isfdopen(fd)) { - if (g_fds.p[fd].kind == kFdFile) { - /* - * Like Linux, closing a file on Windows doesn't guarantee it's - * immediately synced to disk. But unlike Linux, this could cause - * subsequent operations, e.g. unlink() to break w/ access error. - */ - FlushFileBuffers(g_fds.p[fd].handle); - } - ok = CloseHandle(g_fds.p[fd].handle); - if (g_fds.p[fd].kind == kFdConsole) { - ok &= CloseHandle(g_fds.p[fd].extra); - } - __removefd(fd); - return ok ? 0 : __winerr(); - } else { - return ebadf(); + if (g_fds.p[fd].kind == kFdFile) { + /* + * Like Linux, closing a file on Windows doesn't guarantee it's + * immediately synced to disk. But unlike Linux, this could cause + * subsequent operations, e.g. unlink() to break w/ access error. + */ + FlushFileBuffers(g_fds.p[fd].handle); } + ok = CloseHandle(g_fds.p[fd].handle); + if (g_fds.p[fd].kind == kFdConsole) ok &= CloseHandle(g_fds.p[fd].extra); + return ok ? 0 : __winerr(); } diff --git a/libc/calls/close.c b/libc/calls/close.c index 1f8eec35e..2754a29c7 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -43,7 +43,8 @@ int close(int fd) { } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSocket) { rc = weaken(closesocket$nt)(fd); } else if (fd < g_fds.n && - (g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole)) { + (g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole || + g_fds.p[fd].kind == kFdProcess)) { rc = close$nt(fd); } else { rc = ebadf(); diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index e5290a735..9d1c7e95c 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -25,6 +25,6 @@ int faccessat$nt(int dirfd, const char *path, int mode, uint32_t flags) { char16_t path16[PATH_MAX]; if (dirfd != AT_FDCWD || flags) return einval(); - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; return ntaccesscheck(path16, mode); } diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index 2835e1736..7e47e429a 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -18,18 +18,16 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/macros.h" #include "libc/nt/files.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" textwindows char *getcwd$nt(char *buf, size_t size) { - uint16_t name16[PATH_MAX]; - if (GetCurrentDirectory(PATH_MAX, name16)) { - if (tprecode16to8(buf, size, name16) < size - 1) { - return buf; - } else { - erange(); - } + uint16_t name16[PATH_MAX + 1]; + if (GetCurrentDirectory(ARRAYLEN(name16), name16)) { + tprecode16to8(buf, size, name16); + return buf; } else { __winerr(); } diff --git a/libc/calls/getdomainname.c b/libc/calls/getdomainname.c index 3d96355d9..6105539a4 100644 --- a/libc/calls/getdomainname.c +++ b/libc/calls/getdomainname.c @@ -31,8 +31,8 @@ int getdomainname(char *name, size_t len) { uint32_t nSize; - char16_t name16[256]; struct utsname u; + char16_t name16[256]; if (len < 1) return einval(); if (!name) return efault(); if (!IsWindows()) { @@ -44,10 +44,11 @@ int getdomainname(char *name, size_t len) { return 0; } else { nSize = ARRAYLEN(name16); - if (!GetComputerNameEx(kNtComputerNameDnsFullyQualified, name16, &nSize)) { + if (GetComputerNameEx(kNtComputerNameDnsFullyQualified, name16, &nSize)) { + tprecode16to8(name, len, name16); + return 0; + } else { return __winerr(); } - tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16); - return 0; } } diff --git a/libc/calls/gethostname.c b/libc/calls/gethostname.c index 6e301aaba..ae0f3ed6c 100644 --- a/libc/calls/gethostname.c +++ b/libc/calls/gethostname.c @@ -49,10 +49,11 @@ int gethostname(char *name, size_t len) { return 0; } else { nSize = ARRAYLEN(name16); - if (!GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) { + if (GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) { + tprecode16to8(name, len, name16); + return 0; + } else { return __winerr(); } - tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16); - return 0; } } diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index c747970b8..97c93521a 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -23,7 +23,7 @@ #include "libc/macros.h" #include "libc/nt/accounting.h" #include "libc/runtime/runtime.h" -#include "libc/str/knuthmultiplicativehash.h" +#include "libc/str/knuthmultiplicativehash.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" diff --git a/libc/calls/hefty/copyfile.c b/libc/calls/hefty/copyfile.c index 6ad973e69..ec7841862 100644 --- a/libc/calls/hefty/copyfile.c +++ b/libc/calls/hefty/copyfile.c @@ -38,8 +38,8 @@ static textwindows int copyfile$nt(const char *src, const char *dst, int64_t fhsrc, fhdst; struct NtFileTime accessed, modified; char16_t src16[PATH_MAX], dst16[PATH_MAX]; - if (mkntpath(src, src16) == -1) return -1; - if (mkntpath(dst, dst16) == -1) return -1; + if (__mkntpath(src, src16) == -1) return -1; + if (__mkntpath(dst, dst16) == -1) return -1; if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) { if (flags & COPYFILE_PRESERVE_TIMESTAMPS) { fhsrc = CreateFile(src16, kNtFileReadAttributes, kNtFileShareRead, NULL, diff --git a/libc/calls/hefty/dirstream.c b/libc/calls/hefty/dirstream.c index 67552d757..1b085706b 100644 --- a/libc/calls/hefty/dirstream.c +++ b/libc/calls/hefty/dirstream.c @@ -62,7 +62,7 @@ static textwindows noinline DIR *opendir$nt(const char *name) { int len; DIR *res; char16_t name16[PATH_MAX]; - if ((len = mkntpath(name, name16)) == -1) return NULL; + if ((len = __mkntpath(name, name16)) == -1) return NULL; if (len + 2 + 1 > PATH_MAX) return PROGN(enametoolong(), NULL); if (name16[len - 1] == u'/' || name16[len - 1] == u'\\') { name16[--len] = u'\0'; @@ -87,7 +87,8 @@ static textwindows noinline struct dirent *readdir$nt(DIR *dir) { dir->ent.d_off = dir->tell++; dir->ent.d_reclen = sizeof(dir->ent) + tprecode16to8(dir->ent.d_name, sizeof(dir->ent.d_name), - dir->windata.cFileName) + + dir->windata.cFileName) + .ax + 1; switch (dir->windata.dwFileType) { case kNtFileTypeDisk: diff --git a/libc/calls/hefty/execve-nt.c b/libc/calls/hefty/execve-nt.c index b957c5617..345859e05 100644 --- a/libc/calls/hefty/execve-nt.c +++ b/libc/calls/hefty/execve-nt.c @@ -20,36 +20,49 @@ #include "libc/calls/hefty/internal.h" #include "libc/calls/hefty/ntspawn.h" #include "libc/calls/internal.h" +#include "libc/nt/accounting.h" #include "libc/nt/enum/startf.h" +#include "libc/nt/enum/status.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/startupinfo.h" +#include "libc/nt/synchronization.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sock.h" -static textwindows int64_t passstdhand$nt(int fd) { - if (g_fds.p[fd].kind != kFdEmpty && - !(g_fds.p[fd].flags & - (g_fds.p[fd].kind == kFdSocket ? SOCK_CLOEXEC : O_CLOEXEC))) { - return g_fds.p[fd].handle; - } else { - return -1; - } -} - textwindows int execve$nt(const char *program, char *const argv[], char *const envp[]) { + int i; + uint32_t dwExitCode; struct NtStartupInfo startinfo; + struct NtProcessInformation procinfo; memset(&startinfo, 0, sizeof(startinfo)); startinfo.cb = sizeof(struct NtStartupInfo); startinfo.dwFlags = kNtStartfUsestdhandles; - startinfo.hStdInput = passstdhand$nt(STDIN_FILENO); - startinfo.hStdOutput = passstdhand$nt(STDOUT_FILENO); - startinfo.hStdError = passstdhand$nt(STDERR_FILENO); - if (ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo, - NULL) != -1) { - for (;;) TerminateProcess(GetCurrentProcess(), 0); + startinfo.hStdInput = g_fds.p[0].handle; + startinfo.hStdOutput = g_fds.p[1].handle; + startinfo.hStdError = g_fds.p[2].handle; + for (i = 2; i < g_fds.n; ++i) { + if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) { + close(i); + } } - return -1; + if (ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo, + &procinfo) == -1) { + return -1; + } + CloseHandle(procinfo.hThread); + for (i = 0; i < g_fds.n; ++i) { + if (g_fds.p[i].kind != kFdEmpty) { + close(i); + } + } + do { + WaitForSingleObject(procinfo.hProcess, -1); + dwExitCode = kNtStillActive; + GetExitCodeProcess(procinfo.hProcess, &dwExitCode); + } while (dwExitCode == kNtStillActive); + ExitProcess(dwExitCode); } diff --git a/libc/calls/hefty/fork-nt.c b/libc/calls/hefty/fork-nt.c index b6a6a7e43..e5bd3ad94 100644 --- a/libc/calls/hefty/fork-nt.c +++ b/libc/calls/hefty/fork-nt.c @@ -17,20 +17,26 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/hefty/ntspawn.h" #include "libc/calls/hefty/spawn.h" #include "libc/calls/internal.h" #include "libc/conv/itoa.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" +#include "libc/nt/enum/startf.h" #include "libc/nt/ipc.h" #include "libc/nt/memory.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" static textwindows int64_t ParseInt(char16_t **p) { @@ -42,8 +48,8 @@ static textwindows int64_t ParseInt(char16_t **p) { return x; } -static noinline textwindows void DoAll(int64_t h, void *buf, size_t n, - bool32 (*f)()) { +static noinline textwindows void ForkIo(int64_t h, void *buf, size_t n, + bool32 (*f)()) { char *p; size_t i; uint32_t x; @@ -53,11 +59,11 @@ static noinline textwindows void DoAll(int64_t h, void *buf, size_t n, } static noinline textwindows void WriteAll(int64_t h, void *buf, size_t n) { - DoAll(h, buf, n, WriteFile); + ForkIo(h, buf, n, WriteFile); } static noinline textwindows void ReadAll(int64_t h, void *buf, size_t n) { - DoAll(h, buf, n, ReadFile); + ForkIo(h, buf, n, ReadFile); } textwindows void WinMainForked(void) { @@ -79,7 +85,7 @@ textwindows void WinMainForked(void) { ReadAll(h, &_mmi.p[i], sizeof(_mmi.p[i])); addr = (void *)((uint64_t)_mmi.p[i].x << 16); size = ((uint64_t)(_mmi.p[i].y - _mmi.p[i].x) << 16) + FRAMESIZE; - switch (_mmi.p[i].prot) { + switch (_mmi.p[i].prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) { case PROT_READ | PROT_WRITE | PROT_EXEC: protect = kNtPageExecuteReadwrite; access = kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute; @@ -117,9 +123,12 @@ textwindows void WinMainForked(void) { textwindows int fork$nt(void) { jmp_buf jb; + int i, rc, pid; int64_t reader, writer; - int i, rc, pid, fds[3]; char *p, buf[21 + 1 + 21 + 1]; + struct NtStartupInfo startinfo; + struct NtProcessInformation procinfo; + if ((pid = __getemptyfd()) == -1) return -1; if (!setjmp(jb)) { if (CreatePipe(&reader, &writer, &kNtIsInheritable, 0)) { p = buf; @@ -127,12 +136,19 @@ textwindows int fork$nt(void) { *p++ = ' '; p += uint64toarray_radix10(writer, p); setenv("_FORK", buf, true); - fds[0] = 0; - fds[1] = 1; - fds[2] = 2; - /* TODO: CloseHandle(g_fds.p[pid].h) if SIGCHLD is SIG_IGN */ - if ((pid = spawnve(0, fds, g_argv[0], g_argv, environ)) != -1) { + memset(&startinfo, 0, sizeof(startinfo)); + startinfo.cb = sizeof(struct NtStartupInfo); + startinfo.dwFlags = kNtStartfUsestdhandles; + startinfo.hStdInput = g_fds.p[0].handle; + startinfo.hStdOutput = g_fds.p[1].handle; + startinfo.hStdError = g_fds.p[2].handle; + if (ntspawn(g_argv[0], g_argv, environ, &kNtIsInheritable, NULL, true, 0, + NULL, &startinfo, &procinfo) != -1) { CloseHandle(reader); + CloseHandle(procinfo.hThread); + g_fds.p[pid].kind = kFdProcess; + g_fds.p[pid].handle = procinfo.hProcess; + g_fds.p[pid].flags = O_CLOEXEC; WriteAll(writer, jb, sizeof(jb)); WriteAll(writer, &_mmi.i, sizeof(_mmi.i)); for (i = 0; i < _mmi.i; ++i) { @@ -144,11 +160,11 @@ textwindows int fork$nt(void) { } WriteAll(writer, _edata, _end - _edata); CloseHandle(writer); - rc = pid; } else { rc = -1; } unsetenv("_FORK"); + rc = pid; } else { rc = __winerr(); } diff --git a/libc/calls/hefty/mkntcmdline.c b/libc/calls/hefty/mkntcmdline.c index b5eb3a22c..b764f9453 100644 --- a/libc/calls/hefty/mkntcmdline.c +++ b/libc/calls/hefty/mkntcmdline.c @@ -96,5 +96,3 @@ error: free(cmdline_p); return NULL; } - -#undef APPENDCHAR diff --git a/libc/calls/hefty/ntspawn.c b/libc/calls/hefty/ntspawn.c index 6e8d4fa7d..238b7bf57 100644 --- a/libc/calls/hefty/ntspawn.c +++ b/libc/calls/hefty/ntspawn.c @@ -68,8 +68,8 @@ textwindows int ntspawn( char16_t program16[PATH_MAX], *lpCommandLine, *lpEnvironment; lpCommandLine = NULL; lpEnvironment = NULL; - if (mkntpath(program, program16) != -1 && - (lpCommandLine = mkntcmdline(argv)) && + if (__mkntpath(program, program16) != -1 && + (lpCommandLine = mkntcmdline(&argv[1])) && (lpEnvironment = mkntenvblock(envp))) { if (CreateProcess(program16, lpCommandLine, opt_lpProcessAttributes, opt_lpThreadAttributes, bInheritHandles, diff --git a/libc/calls/hefty/sortenvp.c b/libc/calls/hefty/sortenvp.c index 59b44e3c0..9da0664a5 100644 --- a/libc/calls/hefty/sortenvp.c +++ b/libc/calls/hefty/sortenvp.c @@ -21,7 +21,6 @@ #include "libc/alg/arraylist.internal.h" #include "libc/calls/hefty/ntspawn.h" #include "libc/dce.h" -#include "libc/nexgen32e/tinystrcmp.internal.h" #include "libc/str/str.h" static int CompareStrings(const char *l, const char *r) { diff --git a/libc/calls/hefty/spawnve-nt.c b/libc/calls/hefty/spawnve-nt.c index 0c14c4ba5..cca352dc3 100644 --- a/libc/calls/hefty/spawnve-nt.c +++ b/libc/calls/hefty/spawnve-nt.c @@ -94,6 +94,6 @@ textwindows int spawnve$nt(unsigned flags, int stdiofds[3], const char *program, g_fds.p[pid].kind = kFdProcess; g_fds.p[pid].handle = handle; - g_fds.p[pid].flags = flags; + g_fds.p[pid].flags = O_CLOEXEC; return pid; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 790f34728..4bfb7fc52 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -252,6 +252,7 @@ int nanosleep$nt(const struct timespec *, struct timespec *) hidden; │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +int64_t ntreturn(uint32_t); void WinMainForked(void) hidden; void *GetProcAddressModule(const char *, const char *) hidden; int getsetpriority$nt(int, unsigned, int, int (*)(int)); @@ -261,19 +262,9 @@ bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; bool32 onntconsoleevent$nt(u32) hidden; void __winalarm(void *, uint32_t, uint32_t) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; -i64 ntreturn(u32); -i64 __winerr(void) nocallback privileged; - -#define mkntpath(PATH, PATH16) mkntpath2(PATH, -1u, PATH16) -#define mkntpath2(PATH, FLAGS, PATH16) \ - ({ \ - int Count; \ - asm("call\tmkntpath" \ - : "=a"(Count), "=m"(*PATH16) \ - : "D"(PATH), "S"(FLAGS), "d"(&PATH16[0]), "m"((PATH)[0]) \ - : "cc"); \ - Count; \ - }) +int64_t __winerr(void) nocallback privileged; +int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden; +int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » drivers ─╬─│┼ diff --git a/libc/calls/link-nt.c b/libc/calls/link-nt.c index 118fe195b..152eadfc7 100644 --- a/libc/calls/link-nt.c +++ b/libc/calls/link-nt.c @@ -25,8 +25,8 @@ textwindows int link$nt(const char *existingpath, const char *newpath) { char16_t newpath16[PATH_MAX]; char16_t existingpath16[PATH_MAX]; - if (mkntpath(existingpath, existingpath16) != -1 && - mkntpath(newpath, newpath16) != -1) { + if (__mkntpath(existingpath, existingpath16) != -1 && + __mkntpath(newpath, newpath16) != -1) { if (CreateHardLink(newpath16, existingpath16, NULL)) { return 0; } else { diff --git a/libc/calls/mkdir.c b/libc/calls/mkdir.c index 83994bf9c..e5fb61587 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -27,7 +27,7 @@ static textwindows noinline int mkdir$nt(const char *path, uint32_t mode) { uint16_t path16[PATH_MAX]; - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; if (CreateDirectory(path16, NULL)) { return 0; } else { diff --git a/libc/calls/mkntpath.ncabi.c b/libc/calls/mkntpath.c similarity index 77% rename from libc/calls/mkntpath.ncabi.c rename to libc/calls/mkntpath.c index 436598404..f0a9637e7 100644 --- a/libc/calls/mkntpath.ncabi.c +++ b/libc/calls/mkntpath.c @@ -17,8 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" -#include "libc/nexgen32e/tinystrcmp.internal.h" #include "libc/str/oldutf16.internal.h" #include "libc/str/str.h" #include "libc/str/tpdecode.internal.h" @@ -30,19 +30,24 @@ textwindows static const char *FixNtMagicPath(const char *path, const struct NtMagicPaths *mp = &kNtMagicPaths; asm("" : "+r"(mp)); if (path[0] != '/') return path; - if (tinystrcmp(path, mp->devtty) == 0) { + if (strcmp(path, mp->devtty) == 0) { if ((flags & O_ACCMODE) == O_RDONLY) { return mp->conin; } else if ((flags & O_ACCMODE) == O_WRONLY) { return mp->conout; } } - if (tinystrcmp(path, mp->devnull) == 0) return mp->nul; - if (tinystrcmp(path, mp->devstdin) == 0) return mp->conin; - if (tinystrcmp(path, mp->devstdout) == 0) return mp->conout; + if (strcmp(path, mp->devnull) == 0) return mp->nul; + if (strcmp(path, mp->devstdin) == 0) return mp->conin; + if (strcmp(path, mp->devstdout) == 0) return mp->conout; return path; } +textwindows int __mkntpath(const char *path, + char16_t path16[hasatleast PATH_MAX - 16]) { + return __mkntpath2(path, path16, -1); +} + /** * Copies path for Windows NT. * @@ -56,9 +61,9 @@ textwindows static const char *FixNtMagicPath(const char *path, * @return short count excluding NUL on success, or -1 w/ errno * @error ENAMETOOLONG */ -forcealignargpointer textwindows int mkntpath( - const char *path, unsigned flags, - char16_t path16[hasatleast PATH_MAX - 16]) { +textwindows int __mkntpath2(const char *path, + char16_t path16[hasatleast PATH_MAX - 16], + int flags) { /* * 1. Reserve +1 for NUL-terminator * 2. Reserve +1 for UTF-16 overflow @@ -66,31 +71,14 @@ forcealignargpointer textwindows int mkntpath( * 4. Reserve ≥10 for CreateNamedPipe "\\.\pipe\" prefix requirement * 5. Reserve ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0" */ - int rc; - wint_t wc; - size_t i, j; + size_t i, n; path = FixNtMagicPath(path, flags); - i = 0; - j = 0; - for (;;) { - if ((rc = tpdecode(&path[i], &wc)) == -1) { - path16[0] = u'\0'; - return -1; - } - if (!wc) break; - i += (size_t)rc; - if (wc == '/') wc = '\\'; - if (j + 1 /* utf-16 */ + 1 /* chdir() */ + 1 /* NUL */ < PATH_MAX - 16) { - if ((rc = pututf16(&path16[j], 2, wc, false)) == -1) { - path16[0] = u'\0'; - return -1; - } - j += (size_t)rc; - } else { - path16[0] = u'\0'; - return enametoolong(); + n = tprecode8to16(path16, PATH_MAX - 16, path).ax; + if (n == PATH_MAX - 16 - 1) return enametoolong(); + for (i = 0; i < n; ++i) { + if (path16[i] == '/') { + path16[i] = '\\'; } } - path16[j] = u'\0'; - return j; + return n; } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 1ab9d3754..494f589a7 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -20,7 +20,6 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" -#include "libc/nexgen32e/tinystrcmp.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -31,6 +30,7 @@ #include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -40,7 +40,7 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags, uint32_t br; int64_t handle; char16_t file16[PATH_MAX]; - if (mkntpath2(file, flags, file16) == -1) return -1; + if (__mkntpath2(file, file16, flags) == -1) return -1; if ((handle = CreateFile( file16, (flags & 0xf000000f) | (/* this is needed if we mmap(rwx+cow) @@ -119,7 +119,7 @@ textwindows ssize_t open$nt(const char *file, uint32_t flags, int32_t mode) { size_t fd; if ((fd = __getemptyfd()) == -1) return -1; if ((flags & O_ACCMODE) == O_RDWR && - tinystrcmp(file, kNtMagicPaths.devtty) == 0) { + strcmp(file, kNtMagicPaths.devtty) == 0) { return open$nt$console(&kNtMagicPaths, flags, mode, fd); } else { return open$nt$file(file, flags, mode, fd); diff --git a/libc/calls/rename-nt.c b/libc/calls/rename-nt.c index b2cda5b3f..4595f0d3a 100644 --- a/libc/calls/rename-nt.c +++ b/libc/calls/rename-nt.c @@ -27,8 +27,8 @@ textwindows int rename$nt(const char *oldpath, const char *newpath) { char16_t oldpath16[PATH_MAX]; char16_t newpath16[PATH_MAX]; - if (mkntpath(oldpath, oldpath16) == -1 || - mkntpath(newpath, newpath16) == -1) { + if (__mkntpath(oldpath, oldpath16) == -1 || + __mkntpath(newpath, newpath16) == -1) { return -1; } if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { diff --git a/libc/calls/rmdir-nt.c b/libc/calls/rmdir-nt.c index aa9d06f1e..dbd2cb7d7 100644 --- a/libc/calls/rmdir-nt.c +++ b/libc/calls/rmdir-nt.c @@ -23,7 +23,7 @@ textwindows int rmdir$nt(const char *path) { uint16_t path16[PATH_MAX]; - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; if (RemoveDirectory(path16)) { return 0; } else { diff --git a/libc/calls/stat-nt.c b/libc/calls/stat-nt.c index ddc631e64..b77a0805e 100644 --- a/libc/calls/stat-nt.c +++ b/libc/calls/stat-nt.c @@ -31,7 +31,7 @@ textwindows int stat$nt(const char *path, struct stat *st) { int rc; int64_t fh; uint16_t path16[PATH_MAX]; - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile( path16, kNtFileReadAttributes, kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, NULL, diff --git a/libc/calls/symlink-nt.c b/libc/calls/symlink-nt.c index 2aa6eaa75..16f1434c5 100644 --- a/libc/calls/symlink-nt.c +++ b/libc/calls/symlink-nt.c @@ -24,8 +24,8 @@ textwindows int symlink$nt(const char *target, const char *linkpath) { char16_t linkpath16[PATH_MAX], target16[PATH_MAX]; uint32_t flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0; - if (mkntpath(linkpath, linkpath16) == -1) return -1; - if (mkntpath(target, target16) == -1) return -1; + if (__mkntpath(linkpath, linkpath16) == -1) return -1; + if (__mkntpath(target, target16) == -1) return -1; if (CreateSymbolicLink(linkpath16, target16, flags)) { return 0; } else { diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index 4b8c82c9a..39a3da521 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -29,7 +29,7 @@ textwindows int truncate$nt(const char *path, uint64_t length) { bool32 ok; int64_t fh; uint16_t path16[PATH_MAX]; - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtGenericWrite, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) { ok = ftruncate$nt(fh, length); diff --git a/libc/calls/unlink-nt.c b/libc/calls/unlink-nt.c index 37db5279e..f31a5ebc3 100644 --- a/libc/calls/unlink-nt.c +++ b/libc/calls/unlink-nt.c @@ -23,7 +23,7 @@ textwindows int unlink$nt(const char *name) { uint16_t name16[PATH_MAX]; - if (mkntpath(name, name16) == -1) return -1; + if (__mkntpath(name, name16) == -1) return -1; if (DeleteFile(name16)) { return 0; } else { diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 0cca9110c..9faa6735a 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -42,7 +42,7 @@ textwindows int utimensat$nt(int dirfd, const char *path, if (flags) return einval(); if (path) { if (dirfd == AT_FDCWD) { - if (mkntpath(path, path16) == -1) return -1; + if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) { diff --git a/libc/complex.h b/libc/complex.h index cbb98cdbb..51f7dd322 100644 --- a/libc/complex.h +++ b/libc/complex.h @@ -2,7 +2,7 @@ #define COSMOPOLITAN_LIBC_COMPLEX_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#if __STDC_VERSION__ + 0 >= 201112 +#if __STDC_VERSION__ + 0 >= 201112 && !defined(__STDC_NO_COMPLEX__) #define complex _Complex #define imaginary _Imaginary diff --git a/libc/conv/basename_n.c b/libc/conv/basename_n.c index 9be71c0d8..1800523b6 100644 --- a/libc/conv/basename_n.c +++ b/libc/conv/basename_n.c @@ -36,7 +36,7 @@ textstartup char *basename_n(const char *path, size_t size) { if (size) { if (isslash(path[size - 1])) { l = size - 1; - while (isslash(path[l - 1])) --l; + while (l && isslash(path[l - 1])) --l; if (!l) return (/*unconst*/ char *)&path[size - 1]; size = l; } diff --git a/libc/conv/conv.h b/libc/conv/conv.h index a099c3138..0f842ddae 100644 --- a/libc/conv/conv.h +++ b/libc/conv/conv.h @@ -51,6 +51,7 @@ long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect; │ cosmopolitan § conversion » manipulation ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +char *dirname(char *); char *basename(const char *) nosideeffect; char *basename_n(const char *, size_t) nosideeffect; bool isabspath(const char *) paramsnonnull() nosideeffect; @@ -83,7 +84,7 @@ div_t div(int, int) pureconst; ldiv_t ldiv(long, long) pureconst; lldiv_t lldiv(long long, long long) pureconst; imaxdiv_t imaxdiv(intmax_t, intmax_t) pureconst; -double RoundDecimalPlaces(double, double, double(double)); +double RoundDecimalPlaces(double, double, double (*)(double)); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § conversion » optimizations ─╬─│┼ diff --git a/libc/conv/dirname.c b/libc/conv/dirname.c new file mode 100644 index 000000000..f0c7a0517 --- /dev/null +++ b/libc/conv/dirname.c @@ -0,0 +1,42 @@ +/*-*- 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/conv/conv.h" +#include "libc/str/str.h" + +#define ISDELIM(c) (c == '/' || c == '\\' || c == '.') + +char *dirname(char *s) { + size_t i, n; + if (!(n = strlen(s))) return s; + while (n && ISDELIM(s[n - 1])) --n; + if (n) { + while (n && !ISDELIM(s[n - 1])) --n; + if (n) { + while (n && ISDELIM(s[n - 1])) --n; + if (!n) ++n; + } else { + s[n++] = '.'; + } + } else { + ++n; + } + s[n] = '\0'; + return s; +} diff --git a/libc/dns/getntnameservers.c b/libc/dns/getntnameservers.c index 6ab7ab64e..35d7e47ae 100644 --- a/libc/dns/getntnameservers.c +++ b/libc/dns/getntnameservers.c @@ -41,21 +41,24 @@ */ textwindows int getntnameservers(struct ResolvConf *resolv) { int rc; - int64_t hkInterfaces = kNtInvalidHandleValue; - uint32_t keycount = 0; + char value8[128]; + int64_t hkInterfaces; + struct sockaddr_in nameserver; + char16_t value[128], ifaceuuid[64]; + uint32_t i, keycount, valuebytes, ifaceuuidlen; + keycount = 0; + hkInterfaces = kNtInvalidHandleValue; if (!RegOpenKeyEx( kNtHkeyLocalMachine, u"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", 0, kNtKeyRead, &hkInterfaces) && !RegQueryInfoKey(hkInterfaces, NULL, NULL, NULL, &keycount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { - struct sockaddr_in nameserver; nameserver.sin_family = AF_INET; nameserver.sin_port = htons(DNS_PORT); rc = 0; - for (uint32_t i = 0; i < keycount; ++i) { - char16_t value[128], ifaceuuid[64]; - uint32_t valuebytes, ifaceuuidlen = sizeof(ifaceuuid); + for (i = 0; i < keycount; ++i) { + ifaceuuidlen = sizeof(ifaceuuid); if (!RegEnumKeyEx(hkInterfaces, i, ifaceuuid, &ifaceuuidlen, NULL, NULL, NULL, NULL) && ((!RegGetValue(hkInterfaces, ifaceuuid, u"DhcpIpAddress", @@ -74,7 +77,6 @@ textwindows int getntnameservers(struct ResolvConf *resolv) { kNtRrfRtRegSz | kNtRrfRtRegMultiSz, NULL, value, ((valuebytes = sizeof(value)), &valuebytes)) && valuebytes > 2 * sizeof(char16_t)))) { - char value8[128]; tprecode16to8(value8, sizeof(value8), value); if (inet_pton(AF_INET, value8, &nameserver.sin_addr.s_addr) == 1) { if (append(&resolv->nameservers, &nameserver) != -1) ++rc; diff --git a/libc/dns/getresolvconf.c b/libc/dns/getresolvconf.c index c6dafb9b5..175837c8e 100644 --- a/libc/dns/getresolvconf.c +++ b/libc/dns/getresolvconf.c @@ -38,7 +38,8 @@ static struct ResolvConfInitialStaticMemory { const struct ResolvConf *getresolvconf(void) { int rc; FILE *f; - struct ResolvConfInitialStaticMemory *init = &g_resolvconf_init; + struct ResolvConfInitialStaticMemory *init; + init = &g_resolvconf_init; if (!g_resolvconf) { g_resolvconf = &init->rv; pushmov(&init->rv.nameservers.n, ARRAYLEN(init->nameservers)); diff --git a/libc/dns/newaddrinfo.c b/libc/dns/newaddrinfo.c index 9cf6c48ac..8344da30d 100644 --- a/libc/dns/newaddrinfo.c +++ b/libc/dns/newaddrinfo.c @@ -23,19 +23,18 @@ #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" +#define SIZE ROUNDUP(sizeof(struct addrinfo), sizeof(void *)) +#define ADDRLEN sizeof(struct sockaddr_in) + struct addrinfo *newaddrinfo(uint16_t port) { - void *mem; - struct addrinfo *ai = NULL; - /* shoehorning is ok since this'll never be realloc()'d */ - uint32_t size = ROUNDUP(sizeof(struct addrinfo), sizeof(void *)); - uint32_t addrlen = sizeof(struct sockaddr_in); - if ((ai = mem = calloc(1, size + addrlen + DNS_NAME_MAX + 1))) { + struct addrinfo *ai; + if ((ai = calloc(1, SIZE + ADDRLEN + DNS_NAME_MAX + 1))) { ai->ai_family = AF_INET; - ai->ai_addrlen = addrlen; - ai->ai_addr4 = (struct sockaddr_in *)((char *)mem + size); + ai->ai_addrlen = ADDRLEN; + ai->ai_addr4 = (struct sockaddr_in *)((char *)ai + SIZE); ai->ai_addr4->sin_family = AF_INET; ai->ai_addr4->sin_port = htons(port); - ai->ai_canonname = (char *)mem + size + addrlen; + ai->ai_canonname = (char *)ai + SIZE + ADDRLEN; } return ai; } diff --git a/libc/dns/parsehoststxt.c b/libc/dns/parsehoststxt.c index 89c410e16..423e35eae 100644 --- a/libc/dns/parsehoststxt.c +++ b/libc/dns/parsehoststxt.c @@ -44,9 +44,10 @@ * @see hoststxtsort() which is the logical next step */ int parsehoststxt(struct HostsTxt *ht, FILE *f) { - int rc = 0; + int rc; char *line; size_t linesize; + rc = 0; line = NULL; linesize = 0; while ((getline(&line, &linesize, f)) != -1) { diff --git a/libc/dns/parseresolvconf.c b/libc/dns/parseresolvconf.c index 4085fcbf4..6e898b18b 100644 --- a/libc/dns/parseresolvconf.c +++ b/libc/dns/parseresolvconf.c @@ -44,16 +44,17 @@ */ int parseresolvconf(struct ResolvConf *resolv, struct FILE *f) { /* TODO(jart): options ndots:5 */ - int rc = 0; + int rc; char *line; size_t linesize; struct sockaddr_in nameserver; + char *directive, *value, *tok, *comment; + rc = 0; line = NULL; linesize = 0; nameserver.sin_family = AF_INET; nameserver.sin_port = htons(DNS_PORT); while (getline(&line, &linesize, f) != -1) { - char *directive, *value, *tok, *comment; if ((comment = strchr(line, '#'))) *comment = '\0'; if ((directive = strtok_r(line, " \t\r\n\v", &tok)) && (value = strtok_r(NULL, " \t\r\n\v", &tok))) { diff --git a/libc/dns/pascalifydnsname.c b/libc/dns/pascalifydnsname.c index dd5dc5abd..802a5c6ae 100644 --- a/libc/dns/pascalifydnsname.c +++ b/libc/dns/pascalifydnsname.c @@ -32,15 +32,14 @@ * @return bytes written (excluding NUL) or -1 w/ errno */ int pascalifydnsname(uint8_t *buf, size_t size, const char *name) { - size_t i = 0; - size_t namelen = strlen(name); - if (namelen > DNS_NAME_MAX) return enametoolong(); + size_t i, j, k, namelen; + if ((namelen = strlen(name)) > DNS_NAME_MAX) return enametoolong(); + i = 0; if (size || namelen) { if (namelen + 1 > size) return enospc(); buf[0] = '\0'; - size_t j = 0; + j = 0; for (;;) { - size_t k; for (k = 0; name[j + k] && name[j + k] != '.'; ++k) { buf[i + k + 1] = name[j + k]; } diff --git a/libc/dns/resolvconf.h b/libc/dns/resolvconf.h index e6dff80ae..ee2fd3fef 100644 --- a/libc/dns/resolvconf.h +++ b/libc/dns/resolvconf.h @@ -18,7 +18,7 @@ struct ResolvConf { const struct ResolvConf *getresolvconf(void) returnsnonnull; int parseresolvconf(struct ResolvConf *, struct FILE *) paramsnonnull(); void freeresolvconf(struct ResolvConf **) paramsnonnull(); -int getntnameservers(struct ResolvConf *resolv) paramsnonnull(); +int getntnameservers(struct ResolvConf *) paramsnonnull(); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/dns/resolvedns.c b/libc/dns/resolvedns.c index 2a9aa74ad..de3d1d857 100644 --- a/libc/dns/resolvedns.c +++ b/libc/dns/resolvedns.c @@ -34,6 +34,8 @@ #include "libc/sysv/consts/sock.h" #include "libc/sysv/errfuns.h" +#define kMsgMax 512 + /** * Queries Domain Name System for address associated with name. * @@ -48,10 +50,15 @@ */ int resolvedns(const struct ResolvConf *resolvconf, int af, const char *name, struct sockaddr *addr, uint32_t addrsize) { + size_t msgsize; + int res, fd, rc, rc2; + struct sockaddr_in *addr4; + struct DnsQuestion question; + uint16_t rtype, rclass, rdlength; + uint8_t *p, *pe, *outmsg, *inmsg; + struct DnsHeader header, response; if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); if (!resolvconf->nameservers.i) return 0; - struct DnsHeader header; - struct DnsQuestion question; memset(&header, 0, sizeof(header)); header.id = rand32(); header.bf1 = 1; /* recursion desired */ @@ -59,28 +66,21 @@ int resolvedns(const struct ResolvConf *resolvconf, int af, const char *name, question.qname = name; question.qtype = DNS_TYPE_A; question.qclass = DNS_CLASS_IN; - const size_t kMsgMax = 512; - uint8_t *outmsg = NULL; - uint8_t *inmsg = NULL; - size_t msgsize; - int res = -1; - int rc, rc2; + res = -1; if ((outmsg = malloc(kMsgMax)) && (inmsg = malloc(kMsgMax)) && (rc = serializednsheader(outmsg, kMsgMax, header)) != -1 && (rc2 = serializednsquestion(outmsg + rc, kMsgMax - rc, question)) != -1) { msgsize = rc + rc2; - int fd; if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1 && sendto(fd, outmsg, msgsize, 0, (void *)&resolvconf->nameservers.p[0], sizeof(resolvconf->nameservers.p[0])) == msgsize) { - struct DnsHeader response; if ((rc = recv(fd, inmsg, kMsgMax, 0)) != -1 && (rc2 = deserializednsheader(&response, inmsg, rc)) != -1 && response.id == header.id) { res = 0; if (response.ancount) { - uint8_t *p = inmsg + rc2; - uint8_t *pe = inmsg + rc; + p = inmsg + rc2; + pe = inmsg + rc; while (p < pe && response.qdcount) { p += strnlen((char *)p, pe - p) + 1 + 4; response.qdcount--; @@ -92,7 +92,6 @@ int resolvedns(const struct ResolvConf *resolvconf, int af, const char *name, p += strnlen((char *)p, pe - p) + 1; } if (p + 2 + 2 + 4 + 2 < pe) { - uint16_t rtype, rclass, rdlength; rtype = READ16BE(p), p += 2; rclass = READ16BE(p), p += 2; /* ttl */ p += 4; @@ -102,7 +101,7 @@ int resolvedns(const struct ResolvConf *resolvconf, int af, const char *name, res = 1; if (addrsize) { if (addrsize >= kMinSockaddr4Size) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + addr4 = (struct sockaddr_in *)addr; addr4->sin_family = AF_INET; memcpy(&addr4->sin_addr.s_addr, p, 4); } else { diff --git a/libc/dns/resolvehoststxt.c b/libc/dns/resolvehoststxt.c index ba0d87e81..75f06901e 100644 --- a/libc/dns/resolvehoststxt.c +++ b/libc/dns/resolvehoststxt.c @@ -23,8 +23,8 @@ #include "libc/dns/hoststxt.h" #include "libc/sock/sock.h" #include "libc/str/str.h" -#include "libc/sysv/errfuns.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/errfuns.h" static int hoststxtgetcmp(const char *node, const struct HostsTxtEntry *entry, const char *strings) { @@ -50,14 +50,15 @@ static int hoststxtgetcmp(const char *node, const struct HostsTxtEntry *entry, int resolvehoststxt(const struct HostsTxt *ht, int af, const char *name, struct sockaddr *addr, uint32_t addrsize, const char **canon) { + struct sockaddr_in *addr4; + struct HostsTxtEntry *entry; if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); - struct HostsTxtEntry *entry = bsearch_r( - name, ht->entries.p, ht->entries.i, sizeof(struct HostsTxtEntry), - (void *)hoststxtgetcmp, ht->strings.p); - if (entry) { + if ((entry = bsearch_r(name, ht->entries.p, ht->entries.i, + sizeof(struct HostsTxtEntry), (void *)hoststxtgetcmp, + ht->strings.p))) { if (addr) { if (addrsize < kMinSockaddr4Size) return einval(); - struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + addr4 = (struct sockaddr_in *)addr; addr4->sin_family = AF_INET; memcpy(&addr4->sin_addr.s_addr, &entry->ip[0], 4); } diff --git a/libc/elf/elf.mk b/libc/elf/elf.mk index 0e910c125..5b41af4cb 100644 --- a/libc/elf/elf.mk +++ b/libc/elf/elf.mk @@ -26,6 +26,7 @@ LIBC_ELF_A_CHECKS = \ LIBC_ELF_A_DIRECTDEPS = \ LIBC_NEXGEN32E \ + LIBC_STR \ LIBC_STUBS LIBC_ELF_A_DEPS := \ diff --git a/libc/integral/c.inc b/libc/integral/c.inc index d1cd9a466..9f74143e7 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -2,7 +2,7 @@ #define __attribute__(x) #endif -#ifdef __STRICT_ANSI__ +#if defined(__STRICT_ANSI__) && __STDC_VERSION__ + 0 < 201112 #define asm __asm__ #endif @@ -16,7 +16,8 @@ */ #if !defined(__GNUC__) && __cplusplus + 0 >= 201103L #define typeof(x) decltype(x) -#elif defined(__STRICT_ANSI__) || !defined(__GNUC__) +#elif (defined(__STRICT_ANSI__) || !defined(__GNUC__)) && \ + __STDC_VERSION__ + 0 < 201112 #define typeof(x) __typeof(x) #endif @@ -123,7 +124,7 @@ typedef _Bool bool; #endif #endif -#if !defined(__cplusplus) && !defined(__STRICT_ANSI__) +#ifndef __cplusplus typedef __WCHAR_TYPE__ wchar_t; typedef __CHAR16_TYPE__ char16_t; typedef __CHAR32_TYPE__ char32_t; @@ -159,7 +160,7 @@ typedef __UINT64_TYPE__ uint64_t; * @see LISP primitives CONS[CAR,CDR] w/ IBM 704 naming * @see int128_t */ -typedef struct axdx_t { +typedef struct { intptr_t ax, dx; } axdx_t; @@ -194,11 +195,15 @@ typedef int64_t intmax_t; typedef uint64_t uintmax_t; #endif +#ifdef __GNUC__ #define va_list __builtin_va_list #define va_arg(ap, type) __builtin_va_arg(ap, type) #define va_copy(dest, src) __builtin_va_copy(dest, src) #define va_end(ap) __builtin_va_end(ap) #define va_start(ap, last) __builtin_va_start(ap, last) +#else +#include "libc/integral/lp64arg.inc" +#endif #define libcesque nothrow nocallback #define memcpyesque libcesque @@ -366,7 +371,7 @@ typedef uint64_t uintmax_t; #elif defined(_MSC_VER) #define forceinline __forceinline #else -#define forceinline static +#define forceinline static inline #endif /* !ANSI && GCC >= 3.2 */ #endif /* __cplusplus */ #endif /* forceinline */ @@ -1046,7 +1051,7 @@ typedef uint64_t uintmax_t; * Pulls source file into ZIP portion of binary. * @see build/rules.mk which defines the wildcard build rule %.zip.o */ -#ifndef IM_FEELING_NAUGHTY +#if !defined(IM_FEELING_NAUGHTY) && !defined(__STRICT_ANSI__) #define STATIC_YOINK_SOURCE(PATH) STATIC_YOINK(PATH) #else #define STATIC_YOINK_SOURCE(PATH) diff --git a/libc/integral/lp64.inc b/libc/integral/lp64.inc index 8c490036b..b60bbda86 100644 --- a/libc/integral/lp64.inc +++ b/libc/integral/lp64.inc @@ -46,6 +46,9 @@ #define __CHAR16_TYPE__ short unsigned int #define __CHAR32_TYPE__ unsigned int #define __WINT_TYPE__ unsigned int +#define __CHAR16_TYPE__ short unsigned int +#define __WCHAR_TYPE__ int +#define __CHAR32_TYPE__ unsigned int #define __INT_LEAST8_TYPE__ __INT8_TYPE__ #define __UINT_LEAST8_TYPE__ __UINT8_TYPE__ diff --git a/libc/integral/lp64arg.inc b/libc/integral/lp64arg.inc new file mode 100644 index 000000000..daa91e41c --- /dev/null +++ b/libc/integral/lp64arg.inc @@ -0,0 +1,50 @@ +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + void *overflow_arg_area; + void *reg_save_area; +} __va_elem; + +typedef __va_elem va_list[1]; + +#define va_start(ap, last) \ + do { \ + *(ap) = *(__va_elem *)__va_area__; \ + } while (0) + +#define va_end(ap) + +static inline void *__va_arg_mem(__va_elem *ap, int sz, int align) { + void *p = ap->overflow_arg_area; + if (align > 8) p = (void *)(((unsigned long)p + 15) / 16 * 16); + ap->overflow_arg_area = (void *)(((unsigned long)p + sz + 7) / 8 * 8); + return p; +} + +static inline void *__va_arg_gp(__va_elem *ap, int sz, int align) { + if (ap->gp_offset >= 48) return __va_arg_mem(ap, sz, align); + void *r = ap->reg_save_area + ap->gp_offset; + ap->gp_offset += 8; + return r; +} + +static inline void *__va_arg_fp(__va_elem *ap, int sz, int align) { + if (ap->fp_offset >= 112) return __va_arg_mem(ap, sz, align); + void *r = ap->reg_save_area + ap->fp_offset; + ap->fp_offset += 8; + return r; +} + +#define va_arg(ap, ty) \ + ({ \ + int klass = __builtin_reg_class(ty); \ + *(ty *)(klass == 0 \ + ? __va_arg_gp(ap, sizeof(ty), _Alignof(ty)) \ + : klass == 1 ? __va_arg_fp(ap, sizeof(ty), _Alignof(ty)) \ + : __va_arg_mem(ap, sizeof(ty), _Alignof(ty))); \ + }) + +#define va_copy(dest, src) ((dest)[0] = (src)[0]) + +#define __GNUC_VA_LIST 1 +typedef va_list __gnuc_va_list; diff --git a/libc/str/getkvlin.c b/libc/intrin/pdep.c similarity index 83% rename from libc/str/getkvlin.c rename to libc/intrin/pdep.c index 2b5d26601..7f9adce48 100644 --- a/libc/str/getkvlin.c +++ b/libc/intrin/pdep.c @@ -1,4 +1,4 @@ -/*-*- mode:c; indent-tabs-mode:nil; tab-width:2; coding:utf-8 -*-│ +/*-*- 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 │ @@ -17,17 +17,18 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" +#include "libc/intrin/pdep.h" -int getkvlin(const char *name, const char *const unsorted[]) { - unsigned i, n; - if (unsorted) { - n = strlen(name); - for (i = 0; unsorted[i]; ++i) { - if (strncmp(unsorted[i], name, n) == 0 && unsorted[i][n] == '=') { - return i; - } +/** + * Parallel bit deposit. + */ +uint64_t(pdep)(uint64_t x, uint64_t mask) { + uint64_t r, b; + for (r = 0, b = 1; mask; mask >>= 1, b <<= 1) { + if (mask & 1) { + if (x & 1) r |= b; + x >>= 1; } } - return -1; + return r; } diff --git a/libc/intrin/pdep.h b/libc/intrin/pdep.h index 1ae356724..41472a8e6 100644 --- a/libc/intrin/pdep.h +++ b/libc/intrin/pdep.h @@ -1,16 +1,21 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_PDEP_H_ #define COSMOPOLITAN_LIBC_INTRIN_PDEP_H_ +#include "libc/nexgen32e/x86feature.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/* TODO(jart): Implement polyfill. */ -#define pdep(NUMBER, BITMASK) \ +uint64_t pdep(uint64_t, uint64_t) pureconst; + +#define PDEP(NUMBER, BITMASK) \ ({ \ typeof(BITMASK) ShuffledBits, Number = (NUMBER); \ asm("pdep\t%2,%1,%0" : "=r"(ShuffledBits) : "r"(Number), "rm"(BITMASK)); \ ShuffledBits; \ }) +#define pdep(NUMBER, BITMASK) \ + (!X86_HAVE(BMI2) ? pdep(NUMBER, BITMASK) : PDEP(NUMBER, BITMASK)) + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_INTRIN_PDEP_H_ */ diff --git a/libc/nexgen32e/towlower.S b/libc/intrin/pext.c similarity index 81% rename from libc/nexgen32e/towlower.S rename to libc/intrin/pext.c index 98cf11014..98482e58d 100644 --- a/libc/nexgen32e/towlower.S +++ b/libc/intrin/pext.c @@ -1,5 +1,5 @@ -/*-*- 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│ +/*-*- 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 │ │ │ @@ -17,16 +17,18 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" +#include "libc/intrin/pext.h" -towlower: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kToLower,cx - movzbl (%rcx,%rax),%eax -1: .leafepilogue - .endfn towlower,globl - .source __FILE__ +/** + * Parallel bit extract. + */ +uint64_t(pext)(uint64_t x, uint64_t mask) { + uint64_t r, b; + for (r = 0, b = 1; mask; mask >>= 1, x >>= 1) { + if (mask & 1) { + if (x & 1) r |= b; + b <<= 1; + } + } + return r; +} diff --git a/libc/intrin/pext.h b/libc/intrin/pext.h index 00dcf15aa..a73322380 100644 --- a/libc/intrin/pext.h +++ b/libc/intrin/pext.h @@ -3,14 +3,18 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/* TODO(jart): Implement polyfill. */ -#define pext(NUMBER, BITMASK) \ +uint64_t pext(uint64_t, uint64_t) pureconst; + +#define PEXT(NUMBER, BITMASK) \ ({ \ typeof(BITMASK) ShuffledBits, Number = (NUMBER); \ asm("pext\t%2,%1,%0" : "=r"(ShuffledBits) : "r"(Number), "rm"(BITMASK)); \ ShuffledBits; \ }) +#define pext(NUMBER, BITMASK) \ + (!X86_HAVE(BMI2) ? pext(NUMBER, BITMASK) : PEXT(NUMBER, BITMASK)) + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_INTRIN_PEXT_H_ */ diff --git a/libc/log/log.h b/libc/log/log.h index b2b407b73..5285c75de 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -52,6 +52,7 @@ bool isrunningundermake(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § liblog » logging ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +#ifndef __STRICT_ANSI__ extern unsigned g_loglevel; /* log level for runtime check */ @@ -223,6 +224,7 @@ void vffatalf(ARGS, va_list) asm("vflogf") ATTRV relegated noreturn libcesque; #undef ATTR #undef ATTRV +#endif /* __STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_LOG_LOG_H_ */ diff --git a/libc/macros-cpp.inc b/libc/macros-cpp.internal.inc similarity index 100% rename from libc/macros-cpp.inc rename to libc/macros-cpp.internal.inc diff --git a/libc/macros.h b/libc/macros.h index bb3fb8d5b..d3b242a84 100644 --- a/libc/macros.h +++ b/libc/macros.h @@ -33,7 +33,7 @@ #define __STRINGIFY(A) #A #define __PASTE(A, B) A##B #ifdef __ASSEMBLER__ -#include "libc/macros-cpp.inc" -#include "libc/macros.inc" +#include "libc/macros-cpp.internal.inc" +#include "libc/macros.internal.inc" #endif /* __ASSEMBLER__ */ #endif /* COSMOPOLITAN_LIBC_MACROS_H_ */ diff --git a/libc/macros.inc b/libc/macros.internal.inc similarity index 100% rename from libc/macros.inc rename to libc/macros.internal.inc diff --git a/libc/math.h b/libc/math.h index 041647d8a..bfaa82597 100644 --- a/libc/math.h +++ b/libc/math.h @@ -46,7 +46,7 @@ #define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ #define FLT_MIN_EXP __FLT_MIN_EXP__ #define HLF_MAX 6.50e4f -#define HLF_MIN 3.10e–5f +#define HLF_MIN 3.10e-5f #define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__ #define LDBL_DIG __LDBL_DIG__ #define LDBL_EPSILON __LDBL_EPSILON__ diff --git a/libc/nexgen32e/atomicislockfree.S b/libc/nexgen32e/atomicislockfree.S deleted file mode 100644 index 4b2102010..000000000 --- a/libc/nexgen32e/atomicislockfree.S +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- 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 true if architecture guarantees atomicity. -/ -/ @param rdi is byte width of type -/ @param rsi is optional pointer for alignment check -/ @see Intel's Six Thousand Page Manual V.3A §8.2.3.1 -__atomic_is_lock_free: - .leafprologue - .profilable - xor %ecx,%ecx - pushpop 1,%rax - cmp $7,%rdi - cmova %ecx,%eax - dec %edi - and %edi,%esi - cmovnz %ecx,%eax - .leafepilogue - .endfn __atomic_is_lock_free,globl,hidden - .source __FILE__ diff --git a/libc/nexgen32e/bcmp.S b/libc/nexgen32e/bcmp.S deleted file mode 100644 index ab8131991..000000000 --- a/libc/nexgen32e/bcmp.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- 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" - -/ Compares memory. -/ -/ This API was thought to be nearly extinct until recent versions -/ of Clang (c. 2019) started generating synthetic calls to it. -/ -/ @param edi first string -/ @param esi second string -/ @param edx byte size -/ @return 0 if equal or nonzero -bcmp: jmp *__memcmp(%rip) - .endfn bcmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/bcopy.S b/libc/nexgen32e/bcopy.S deleted file mode 100644 index a7986bb77..000000000 --- a/libc/nexgen32e/bcopy.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- 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" - -/ Copies memory. -/ -/ DEST and SRC may overlap. -/ -/ @param rdi is dest -/ @param rsi is src -/ @param rdx is number of bytes -bcopy: jmp memmove - .endfn bcopy,globl - .source __FILE__ diff --git a/libc/nexgen32e/ktoupper.S b/libc/nexgen32e/ktoupper.S deleted file mode 100644 index cb0886543..000000000 --- a/libc/nexgen32e/ktoupper.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- 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 "libc/macros.h" - - .initbss 300,_init_kToUpper -kToUpper: - .zero 256 - .endobj kToUpper,globl,hidden - .previous - - .init.start 300,_init_kToUpper - lea 'a(%rdi),%r8 - call imapxlatab - pushpop 'z-'a,%rcx -0: subb $0x20,(%r8,%rcx) - .loop 0b - .init.end 300,_init_kToUpper - .source __FILE__ diff --git a/libc/nexgen32e/macros.h b/libc/nexgen32e/macros.h index acb066cf4..97c394f91 100644 --- a/libc/nexgen32e/macros.h +++ b/libc/nexgen32e/macros.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_NEXGEN32E_MACROS_H_ #define COSMOPOLITAN_LIBC_NEXGEN32E_MACROS_H_ #ifdef __ASSEMBLER__ -#include "libc/nexgen32e/macros.inc" +#include "libc/nexgen32e/macros.internal.inc" #else /* let's give auto-import tooling a helping hand */ diff --git a/libc/nexgen32e/macros.inc b/libc/nexgen32e/macros.internal.inc similarity index 100% rename from libc/nexgen32e/macros.inc rename to libc/nexgen32e/macros.internal.inc diff --git a/libc/nexgen32e/memcmp-avx2.S b/libc/nexgen32e/memcmp-avx2.S deleted file mode 100644 index 91e1940d3..000000000 --- a/libc/nexgen32e/memcmp-avx2.S +++ /dev/null @@ -1,65 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares memory bytes. -/ -/ @param rdi is first uint8_t array -/ @param rsi is second uint8_t array -/ @param rdx is max bytes to consider -/ @return unsigned char subtraction at stop index -/ @note AVX2 requires Haswell (2014+) or Excavator (2015+) -/ @see libc/nexgen32e/memcmp.S (for benchmarks) -/ @asyncsignalsafe - .align 16 -memcmp$avx2: - .leafprologue - .profilable - cmp %rsi,%rdi - je 7f - test %rdx,%rdx - jz 7f - mov %rdx,%r8 - shr $5,%r8 - add $1,%r8 - mov $-32,%rcx -1: add $32,%rcx - sub $1,%r8 - jz 5f - vmovdqu (%rdi,%rcx),%ymm0 - vpcmpeqb (%rsi,%rcx),%ymm0,%ymm0 - vpmovmskb %ymm0,%eax - sub $0xffffffff,%eax - jz 1b - tzcnt %eax,%eax - add %rax,%rcx -5: cmp %rcx,%rdx - je 7f - inc %rcx - movzbl -1(%rdi,%rcx),%eax - movzbl -1(%rsi,%rcx),%r8d - sub %r8d,%eax - jz 5b - jmp 8f -7: xor %eax,%eax -8: vpxor %ymm0,%ymm0,%ymm0 - .leafepilogue - .endfn memcmp$avx2,globl,hidden - .source __FILE__ diff --git a/libc/nexgen32e/memcmp-hook.S b/libc/nexgen32e/memcmp-hook.S deleted file mode 100644 index b3a2fc580..000000000 --- a/libc/nexgen32e/memcmp-hook.S +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- 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 "libc/nexgen32e/x86feature.h" -#include "libc/dce.h" -#include "libc/macros.h" - -/ Dispatches to fastest memcmp() implementation. -/ -/ @param edi first string -/ @param esi second string -/ @param edx byte size -/ @return unsigned char subtraction at stop index -/ @asyncsignalsafe - .initbss 300,_init_memcmp -__memcmp: - .quad 0 - .endobj __memcmp,globl,hidden - .previous - - .init.start 300,_init_memcmp -#if !IsTiny() - ezlea memcmp$avx2,ax -#if !X86_NEED(AVX2) - ezlea memcmp$sse2,dx - testb X86_HAVE(AVX2)+kCpuids(%rip) - cmovz %rdx,%rax -#endif /* AVX */ -#else - ezlea memcmp$sse2,ax -#endif /* TINY */ - stosq - .init.end 300,_init_memcmp - .source __FILE__ diff --git a/libc/nexgen32e/memcmp-sse2.S b/libc/nexgen32e/memcmp-sse2.S deleted file mode 100644 index 452aec851..000000000 --- a/libc/nexgen32e/memcmp-sse2.S +++ /dev/null @@ -1,62 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares memory. -/ -/ @param rdi is first uint8_t array -/ @param rsi is second uint8_t array -/ @param rdx is max bytes to consider -/ @return unsigned char subtraction at stop index -/ @asyncsignalsafe -memcmp$sse2: - .leafprologue - .profilable - cmp %rsi,%rdi - je 7f - test %rdx,%rdx - jz 7f - mov %rdx,%r8 - shr $4,%r8 - add $1,%r8 - mov $-16,%rcx -1: add $16,%rcx - sub $1,%r8 - jz 5f - movdqu (%rdi,%rcx),%xmm0 - movdqu (%rsi,%rcx),%xmm1 - pcmpeqb %xmm1,%xmm0 - pmovmskb %xmm0,%eax - sub $0xffff,%eax - jz 1b - bsf %eax,%eax - add %rax,%rcx -5: cmp %rcx,%rdx - je 7f - inc %rcx - movzbl -1(%rdi,%rcx),%eax - movzbl -1(%rsi,%rcx),%r8d - sub %r8d,%eax - jz 5b - jmp 8f -7: xor %eax,%eax -8: .leafepilogue - .endfn memcmp$sse2,globl,hidden - .source __FILE__ diff --git a/libc/nexgen32e/memcmp.S b/libc/nexgen32e/memcmp.S deleted file mode 100644 index 1a9b18e93..000000000 --- a/libc/nexgen32e/memcmp.S +++ /dev/null @@ -1,206 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares memory. -/ -/ @param edi first string -/ @param esi second string -/ @param edx byte size -/ @return unsigned char subtraction at stop index -/ @asyncsignalsafe -memcmp: jmp *__memcmp(%rip) - .endfn memcmp,globl - .source __FILE__ - -/* cosmo memcmp() avx2 for #c per n where c ≈ 0.273ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 61.000 39.375 36.984 95 - 1 37.000 37.625 37.391 94 - 2 28.500 19.688 19.930 175 - 3 20.333 13.625 14.411 243 - 4 30.250 10.656 10.426 335 - 7 15.000 7.304 6.136 570 - 8 10.125 6.234 5.525 633 - 15 9.133 3.542 3.570 980 - 16 6.062 4.398 3.577 977 - 31 4.548 2.931 2.340 1494 - 32 2.594 1.520 1.492 2344 - 63 3.444 1.240 1.221 2864 - 64 1.328 0.736 0.742 4713 - 127 1.661 0.710 0.605 5778 - 128 0.820 0.452 0.396 8822 - 255 0.639 0.360 0.347 10080 - 256 0.434 0.250 0.220 15874 - 511 0.413 0.218 0.199 17612 - 512 0.201 0.176 0.138 25377 - 1023 0.216 0.142 0.125 28031 - 1024 0.132 0.097 0.096 36276 - 2047 0.125 0.091 0.091 38466 - 2048 0.093 0.079 0.075 46365 - 4095 0.084 0.081 0.078 44705 - 4096 0.069 0.069 0.069 50819 - 8191 0.070 0.068 0.067 51841 - 8192 0.063 0.062 0.062 56633 - 16383 0.066 0.063 0.061 56994 - 16384 0.059 0.058 0.058 60021 - 32767 0.131 0.104 0.100 34909 - 32768 0.120 0.084 0.079 44282 - - cosmo memcmp() sse2 (old cpu) for #c per n where c ≈ 0.273ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 59.000 37.125 37.328 94 - 1 35.000 37.375 36.359 96 - 2 28.500 18.938 20.461 171 - 3 19.000 12.875 13.234 264 - 4 29.250 10.906 10.348 338 - 7 11.571 6.304 6.404 546 - 8 8.125 5.672 5.713 612 - 15 11.533 4.492 3.759 930 - 16 5.812 3.227 2.876 1216 - 31 5.516 2.367 1.797 1946 - 32 2.969 1.816 1.481 2361 - 63 3.413 0.990 0.929 3763 - 64 1.703 0.850 0.763 4580 - 127 1.614 0.531 0.533 6556 - 128 0.961 0.438 0.426 8205 - 255 0.922 0.378 0.325 10745 - 256 0.457 0.322 0.268 13035 - 511 0.331 0.253 0.216 16223 - 512 0.287 0.212 0.189 18460 - 1023 0.220 0.172 0.164 21378 - 1024 0.198 0.159 0.150 23357 - 2047 0.161 0.152 0.150 23271 - 2048 0.147 0.139 0.136 25732 - 4095 0.135 0.130 0.129 27157 - 4096 0.129 0.123 0.123 28499 - 8191 0.122 0.116 0.116 30110 - 8192 0.116 0.113 0.113 30863 - 16383 0.117 0.112 0.112 31311 - 16384 0.111 0.110 0.110 31802 - 32767 0.157 0.138 0.136 25653 - 32768 0.144 0.121 0.118 29590 - - glibc memcmp() for #c per n where c ≈ 0.273ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 6875.000 39.125 35.141 100 - 1 33.000 35.375 35.078 100 - 2 138.500 20.312 18.570 188 - 3 26.333 13.958 12.536 279 - 4 53.250 12.094 9.512 368 - 7 13.571 5.554 5.708 613 - 8 19.625 5.328 5.057 691 - 15 6.867 3.075 2.801 1248 - 16 9.062 2.555 2.526 1384 - 31 4.484 1.319 1.313 2663 - 32 3.906 1.285 1.299 2691 - 63 2.143 0.863 0.719 4867 - 64 1.234 0.814 0.718 4873 - 127 2.071 0.493 0.428 8174 - 128 0.523 0.427 0.421 8310 - 255 0.882 0.302 0.250 13983 - 256 0.465 0.258 0.266 13143 - 511 0.417 0.189 0.164 21339 - 512 0.209 0.170 0.160 21862 - 1023 0.320 0.120 0.111 31391 - 1024 0.128 0.115 0.112 31106 - 2047 0.110 0.092 0.088 39803 - 2048 0.098 0.088 0.086 40837 - 4095 0.093 0.078 0.076 46281 - 4096 0.081 0.076 0.075 46400 - 8191 0.080 0.071 0.069 50984 - 8192 0.075 0.069 0.069 50970 - 16383 0.083 0.071 0.068 51591 - 16384 0.072 0.071 0.068 51736 - 32767 0.145 0.136 0.121 28805 - 32768 0.145 0.139 0.137 25469 - - musl memcmp() for #c per n where c ≈ 0.273ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 55.000 37.625 34.484 101 - 1 35.000 33.625 34.203 102 - 2 37.500 24.562 18.648 188 - 3 20.333 13.625 12.766 274 - 4 32.750 11.531 9.527 367 - 7 12.714 8.482 5.828 600 - 8 13.125 6.234 5.330 656 - 15 9.000 4.892 3.391 1031 - 16 5.188 4.102 3.335 1048 - 31 4.806 2.899 2.295 1524 - 32 4.406 2.801 2.208 1584 - 63 3.794 1.808 1.689 2070 - 64 2.672 1.994 1.675 2088 - 127 1.961 1.739 1.648 2122 - 128 2.055 1.610 1.614 2167 - 255 1.463 1.381 1.401 2496 - 256 1.457 1.362 1.385 2525 - 511 1.286 1.351 1.226 2853 - 512 1.256 1.255 1.253 2791 - 1023 1.207 1.184 1.180 2964 - 1024 1.204 1.146 1.174 2978 - 2047 1.134 1.126 1.152 3036 - 2048 1.134 1.123 1.149 3044 - 4095 1.124 1.108 1.138 3074 - 4096 1.117 1.107 1.136 3077 - 8191 1.106 1.103 1.102 3174 - 8192 1.105 1.102 1.267 2760 - 16383 1.110 1.103 1.099 3182 - 16384 1.108 1.100 1.098 3184 - 32767 1.101 1.097 1.126 3105 - 32768 1.128 1.130 1.126 3105 - - newlib memcmp() for #c per n where c ≈ 0.273ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 73.000 39.625 36.297 96 - 1 35.000 35.375 35.328 99 - 2 41.500 19.438 18.508 189 - 3 29.667 13.542 13.005 269 - 4 22.750 10.656 10.332 338 - 7 14.714 6.875 6.248 560 - 8 18.125 6.453 5.846 598 - 15 11.533 3.575 3.547 986 - 16 8.062 3.461 2.880 1214 - 31 3.839 2.931 2.689 1300 - 32 5.594 1.848 1.589 2200 - 63 3.667 2.387 2.242 1560 - 64 2.078 1.170 0.842 4153 - 127 2.228 2.111 2.126 1644 - 128 1.617 0.669 0.510 6858 - 255 2.059 1.960 1.964 1781 - 256 0.590 0.398 0.335 10452 - 511 1.841 1.814 1.811 1931 - 512 0.373 0.275 0.252 13860 - 1023 1.788 1.748 2.426 1441 - 1024 0.261 0.230 0.226 15474 - 2047 1.745 1.731 1.774 1971 - 2048 0.218 0.199 0.197 17741 - 4095 1.771 1.764 1.763 1983 - 4096 0.187 0.177 0.181 19353 - 8191 1.722 1.714 1.714 2040 - 8192 0.173 0.174 0.173 20252 - 16383 1.754 1.754 1.845 1895 - 16384 0.175 0.171 0.169 20692 - 32767 1.753 1.753 1.753 1995 - 32768 0.186 0.173 0.170 20510 */ diff --git a/libc/nexgen32e/sgetc.S b/libc/nexgen32e/sgetc.S deleted file mode 100644 index 39176fe9f..000000000 --- a/libc/nexgen32e/sgetc.S +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- 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 "ape/macros.h" -#include "libc/nexgen32e/uart.internal.h" -#include "libc/notice.inc" -.real -.code16 # ∩ .code32 ∩ .code64 - -/ Receives byte over serial line. -/ -/ This is both blocking and asynchronous. -/ -/ @param di serial port -/ @return ax character received -/ @mode long,legacy,real -/ @see ttytxr -sgetc: push %bp - mov %sp,%bp - mov %di,%dx - add $UART_LSR,%dx - mov $UART_TTYDA,%ah -1: in %dx,%al - and %ah,%al - rep nop # todo(jart): interrupts are better - jz 1b - mov %di,%dx - xor %ax,%ax - in %dx,%al - pop %bp - ret - .endfn sgetc,globl - .source __FILE__ diff --git a/libc/nexgen32e/strcasecmp.S b/libc/nexgen32e/strcasecmp.S deleted file mode 100644 index a0d815222..000000000 --- a/libc/nexgen32e/strcasecmp.S +++ /dev/null @@ -1,66 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated strings, ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note char is treated as unsigned -strcasecmp: - or $-1,%rdx -/ fallthrough - .endfn strcasecmp,globl - -/ Compares NUL-terminated strings w/ limit ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx max bytes -/ @return 0 if equal, etc. -/ @note char is treated as unsigned -strncasecmp: - .leafprologue - .profilable - push %rbx - cmp %rdi,%rsi - je 3f - ezlea kToLower,bx - or $-1,%rcx -1: add $1,%rcx - cmp %rcx,%rdx - je 3f - movzbl (%rdi,%rcx),%r8d - movzbl (%rsi,%rcx),%eax - xlat - xchg %r8d,%eax - xlat - cmp %r8b,%al - jne 2f - test %al,%al - jnz 1b -2: sub %r8d,%eax - jmp 4f -3: xor %eax,%eax -4: pop %rbx - .leafepilogue - .endfn strncasecmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/strcasecmp16.S b/libc/nexgen32e/strcasecmp16.S deleted file mode 100644 index 712196567..000000000 --- a/libc/nexgen32e/strcasecmp16.S +++ /dev/null @@ -1,72 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated char16_t strings, ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note char16_t is an unsigned type -strcasecmp16: - or $-1,%rdx -/ fallthrough - .endfn strcasecmp16,globl - -/ Compares NUL-terminated char16_t strings w/ limit ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx max shorts -/ @return 0 if equal, etc. -/ @note char16_t is an unsigned type -strncasecmp16: - .leafprologue - .profilable - push %rbx - cmp %rdi,%rsi - je 3f - or $-1,%rcx -1: add $1,%rcx - cmp %rcx,%rdx - je 3f - movzwl (%rsi,%rcx,2),%eax - mov %eax,%ebx - and $0x7f,%ebx - cmp %eax,%ebx - cmove kToLower16(,%rbx,2),%ax - push %rax - movzwl (%rdi,%rcx,2),%eax - mov %eax,%ebx - and $0x7f,%ebx - cmp %eax,%ebx - cmove kToLower16(,%rbx,2),%ax - pop %rbx - cmp %ebx,%eax - jne 2f - test %eax,%eax - jnz 1b -2: sub %ebx,%eax - jmp 4f -3: xor %eax,%eax -4: pop %rbx - .leafepilogue - .endfn strncasecmp16,globl - .source __FILE__ diff --git a/libc/nexgen32e/strcmp-avx.S b/libc/nexgen32e/strcmp-avx.S deleted file mode 100644 index cce891653..000000000 --- a/libc/nexgen32e/strcmp-avx.S +++ /dev/null @@ -1,90 +0,0 @@ -/*-*- 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" - -/ Compares NUL-terminated strings w/ AVX (2011+) -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @asyncsignalsafe -strcmp$avx: - or $-1,%rdx -/ fallthrough - .endfn strcmp$avx,globl,hidden - -/ Compares NUL-terminated strings, with byte limit. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx maximum number of bytes to compare -/ @return 0 if equal, etc. -/ @asyncsignalsafe -strncmp$avx: - .leafprologue - .profilable - cmp %rsi,%rdi - je 1f - mov $-16,%rcx - vpxor %xmm0,%xmm0,%xmm0 - vpcmpeqd %xmm1,%xmm1,%xmm1 -3: add $16,%rcx -4: lea 16(%rcx),%rax - cmp %rdx,%rax - ja 9f - lea (%rdi,%rcx),%eax - and $0xfff,%eax - cmp $0xff0,%eax - ja 9f - lea (%rsi,%rcx),%eax - and $0xfff,%eax - cmp $0xff0,%eax - jbe 7f -9: cmpq %rcx,%rdx - je 1f - movzbl (%rdi,%rcx),%r8d - movzbl (%rsi,%rcx),%r9d - mov %r8d,%eax - sub %r9d,%eax - testl %r8d,%r8d - je 5f - incq %rcx - testl %eax,%eax - je 4b - jmp 5f -1: xor %eax,%eax -5: .leafepilogue -7: vmovdqu (%rsi,%rcx),%xmm3 - vpcmpeqb (%rdi,%rcx),%xmm3,%xmm2 - vpcmpeqb %xmm0,%xmm3,%xmm3 - vpandn %xmm1,%xmm2,%xmm2 - vpor %xmm3,%xmm2,%xmm2 - vpmovmskb %xmm2,%eax - bsf %eax,%eax - jz 3b - mov %eax,%edx - add %rdx,%rdi - movzbl (%rcx,%rdi),%eax - add %rdx,%rsi - movzbl (%rcx,%rsi),%ecx - sub %ecx,%eax - jmp 5b - .endfn strncmp$avx,globl,hidden - .source __FILE__ diff --git a/libc/nexgen32e/strcmp-hook.S b/libc/nexgen32e/strcmp-hook.S deleted file mode 100644 index ab69a5f4e..000000000 --- a/libc/nexgen32e/strcmp-hook.S +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- 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 "libc/nexgen32e/x86feature.h" -#include "libc/dce.h" -#include "libc/macros.h" - -/ Dispatches to fastest strcmp() implementation. -/ -/ @param rdi is first non-null NUL-terminated string pointer -/ @param rsi is second non-null NUL-terminated string pointer -/ @return rax is <0, 0, or >0 based on uint8_t comparison -/ @asyncsignalsafe - .initbss 300,_init_strcmp -hook$strcmp: - .quad 0 - .endobj hook$strcmp,globl,hidden - .previous - - .init.start 300,_init_strcmp -#if !IsTiny() - ezlea strcmp$avx,ax -#if !X86_NEED(AVX) - ezlea tinystrcmp,dx - testb X86_HAVE(AVX)+kCpuids(%rip) - cmovz %rdx,%rax -#endif /* AVX */ -#else - ezlea tinystrcmp,ax -#endif /* TINY */ - stosq - .init.end 300,_init_strcmp - .source __FILE__ diff --git a/libc/nexgen32e/strcmp.S b/libc/nexgen32e/strcmp.S deleted file mode 100644 index 3362f186c..000000000 --- a/libc/nexgen32e/strcmp.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated strings. -/ -/ @param rdi is first non-null NUL-terminated string pointer -/ @param rsi is second non-null NUL-terminated string pointer -/ @return rax is <0, 0, or >0 based on uint8_t comparison -/ @note cosmopolitan headers optimize away this indirection -/ @asyncsignalsafe -strcmp: jmp *hook$strcmp(%rip) - .endfn strcmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/strcmp16-hook.S b/libc/nexgen32e/strcmp16-hook.S deleted file mode 100644 index 1ad789f01..000000000 --- a/libc/nexgen32e/strcmp16-hook.S +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- 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 "libc/macros.h" -#include "libc/notice.inc" - -/ Dispatches to fastest strcmp16() implementation. -/ -/ @param rdi is first non-null NUL-terminated char16_t string -/ @param rsi is second non-null NUL-terminated char16_t string -/ @return rax is <0, 0, or >0 based on uint16_t comparison -/ @asyncsignalsafe - .initbss 300,_init_strcmp16 -hook$strcmp16: - .quad 0 - .endobj hook$strcmp16,globl,hidden - .previous - - .init.start 300,_init_strcmp16 - ezlea strcmp16$k8,ax - stosq - .init.end 300,_init_strcmp16 - .source __FILE__ diff --git a/libc/nexgen32e/strcmp16-k8.S b/libc/nexgen32e/strcmp16-k8.S deleted file mode 100644 index 2f3a2d534..000000000 --- a/libc/nexgen32e/strcmp16-k8.S +++ /dev/null @@ -1,66 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated char16_t strings. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note char16_t is an unsigned type -/ @asyncsignalsafe -strcmp16$k8: - or $-1,%rdx -/ fallthrough - .endfn strcmp16$k8,globl,hidden - -/ Compares NUL-terminated char16_t strings w/ limit. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx max shorts -/ @return 0 if equal, etc. -/ @note char16_t is an unsigned type -/ @asyncsignalsafe -strncmp16$k8: - .leafprologue - .profilable - push %rbx - xor %eax,%eax - xor %ebx,%ebx - xor %ecx,%ecx - cmp %rdi,%rsi - je 1f - test %rdx,%rdx - jz 1f -0: inc %rcx - movzwl -2(%rdi,%rcx,2),%eax - movzwl -2(%rsi,%rcx,2),%ebx - cmp %rcx,%rdx - je 1f - cmp %ebx,%eax - jne 1f - test %eax,%eax - jne 0b -1: sub %ebx,%eax - pop %rbx - .leafepilogue - .endfn strncmp16$k8,globl,hidden - .source __FILE__ diff --git a/libc/nexgen32e/strcmp16.S b/libc/nexgen32e/strcmp16.S deleted file mode 100644 index d8cedf2cf..000000000 --- a/libc/nexgen32e/strcmp16.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated char16_t strings. -/ -/ @param rdi is first non-null NUL-terminated char16_t string -/ @param rsi is second non-null NUL-terminated char16_t string -/ @param rdx is maximum shorts to consider -/ @return rax is <0, 0, or >0 based on uint16_t comparison -/ @note cosmopolitan headers optimize away this indirection -/ @asyncsignalsafe -strcmp16: - jmp *hook$strcmp16(%rip) - .endfn strcmp16,globl - .source __FILE__ diff --git a/libc/nexgen32e/strncmp-hook.S b/libc/nexgen32e/strncmp-hook.S deleted file mode 100644 index c95ca9cbd..000000000 --- a/libc/nexgen32e/strncmp-hook.S +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- 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 "libc/nexgen32e/x86feature.h" -#include "libc/dce.h" -#include "libc/macros.h" - -/ Dispatches to fastest strncmp() implementation. -/ -/ @param rdi is first non-null NUL-terminated string pointer -/ @param rsi is second non-null NUL-terminated string pointer -/ @param rdx is maximum bytes to consider -/ @return rax is <0, 0, or >0 based on uint8_t comparison - .initbss 300,_init_strncmp -hook$strncmp: - .quad 0 - .endobj hook$strncmp,globl,hidden - .previous - - .init.start 300,_init_strncmp -#if !IsTiny() - ezlea strncmp$avx,ax -#if !X86_NEED(AVX) - ezlea tinystrncmp,dx - testb X86_HAVE(AVX)+kCpuids(%rip) - cmovz %rdx,%rax -#endif /* AVX */ -#else - ezlea tinystrncmp,ax -#endif /* TINY */ - stosq - .init.end 300,_init_strncmp - .source __FILE__ diff --git a/libc/nexgen32e/strncmp.S b/libc/nexgen32e/strncmp.S deleted file mode 100644 index 67f8aef50..000000000 --- a/libc/nexgen32e/strncmp.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated strings, with btye limit. -/ -/ @param rdi is first non-null NUL-terminated string pointer -/ @param rsi is second non-null NUL-terminated string pointer -/ @return rax is <0, 0, or >0 based on uint8_t comparison -/ @note cosmopolitan headers optimize away this indirection -/ @asyncsignalsafe -strncmp:jmp *hook$strncmp(%rip) - .endfn strncmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/strncmp16-hook.S b/libc/nexgen32e/strncmp16-hook.S deleted file mode 100644 index 6ad22a5fa..000000000 --- a/libc/nexgen32e/strncmp16-hook.S +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- 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 "libc/macros.h" -#include "libc/notice.inc" - -/ Dispatches to fastest strncmp16() implementation. -/ -/ @param rdi is first non-null NUL-terminated char16_t string -/ @param rsi is second non-null NUL-terminated char16_t string -/ @param rdx is maximum shorts to consider -/ @return rax is <0, 0, or >0 based on uint16_t comparison - .initbss 300,_init_strncmp16 -hook$strncmp16: - .quad 0 - .endobj hook$strncmp16,globl,hidden - .previous - - .init.start 300,_init_strncmp16 - ezlea strncmp16$k8,ax - stosq - .init.end 300,_init_strncmp16 - .source __FILE__ diff --git a/libc/nexgen32e/strncmp16.S b/libc/nexgen32e/strncmp16.S deleted file mode 100644 index 0eb8afecd..000000000 --- a/libc/nexgen32e/strncmp16.S +++ /dev/null @@ -1,32 +0,0 @@ -/*-*- 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" - -/ Compares NUL-terminated char16_t strings w/ limit. -/ -/ @param rdi is first non-null NUL-terminated char16_t string -/ @param rsi is second non-null NUL-terminated char16_t string -/ @param rdx is maximum shorts to consider -/ @return rax is <0, 0, or >0 based on uint16_t comparison -/ @note cosmopolitan headers optimize away this indirection -strncmp16: - jmp *hook$strncmp16(%rip) - .endfn strncmp16,globl - .source __FILE__ diff --git a/libc/nexgen32e/tinystrcmp.S b/libc/nexgen32e/tinystrcmp.S deleted file mode 100644 index 63a96093e..000000000 --- a/libc/nexgen32e/tinystrcmp.S +++ /dev/null @@ -1,51 +0,0 @@ -/*-*- 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" - -/ Compares strings w/ no-clobber greg abi. -/ -/ @param rdi is first non-null NUL-terminated string pointer -/ @param rsi is second non-null NUL-terminated string pointer -/ @return rax is <0, 0, or >0 based on uint8_t comparison -/ @clob flags only -/ @asyncsignalsafe -tinystrcmp: - .leafprologue - push %rcx - push %rdx - xor %eax,%eax - xor %edx,%edx - xor %ecx,%ecx - cmp %rdi,%rsi - je 1f -0: movzbl (%rdi,%rcx,1),%eax - movzbl (%rsi,%rcx,1),%edx - test %al,%al - jz 1f - cmp %dl,%al - jne 1f - inc %rcx - jmp 0b -1: sub %edx,%eax - pop %rdx - pop %rcx - .leafepilogue - .endfn tinystrcmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/tinystrcmp.internal.h b/libc/nexgen32e/tinystrcmp.internal.h deleted file mode 100644 index fd4425d81..000000000 --- a/libc/nexgen32e/tinystrcmp.internal.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_TINYSTRCMP_H_ -#define COSMOPOLITAN_LIBC_NEXGEN32E_TINYSTRCMP_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -int tinystrcmp(const char *, const char *) nothrow nocallback - paramsnonnull() nosideeffect; -int tinystrncmp(const char *, const char *, size_t) nothrow nocallback - paramsnonnull() nosideeffect; - -#define tinystrcmp(s1, s2) \ - ({ \ - int Res; \ - asm("call\ttinystrcmp" \ - : "=a"(Res) \ - : "D"(&(s1)[0]), "S"(&(s2)[0]), "m"(*(s1)), "m"(*(s2)) \ - : "cc"); \ - Res; \ - }) - -#define tinystrncmp(s1, s2, n) \ - ({ \ - int Res; \ - asm("call\ttinystrncmp" \ - : "=a"(Res) \ - : "D"(&(s1)[0]), "S"(&(s2)[0]), "d"(n), "m"(*(s1)), "m"(*(s2)) \ - : "cc"); \ - Res; \ - }) - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_TINYSTRCMP_H_ */ diff --git a/libc/nexgen32e/vcls.S b/libc/nexgen32e/vcls.S index 0d58849be..e05b1dba2 100644 --- a/libc/nexgen32e/vcls.S +++ b/libc/nexgen32e/vcls.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "libc/nexgen32e/vidya.internal.h" #include "libc/notice.inc" .real diff --git a/libc/nexgen32e/wcscasecmp.S b/libc/nexgen32e/wcscasecmp.S deleted file mode 100644 index 4d5b5266d..000000000 --- a/libc/nexgen32e/wcscasecmp.S +++ /dev/null @@ -1,74 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated wchar_t strings, ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -wcscasecmp: - or $-1,%rdx -/ fallthrough - .endfn wcscasecmp,globl - -/ Compares NUL-terminated wchar_t strings w/ limit ignoring ASCII case. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx max shorts -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -wcsncasecmp: - .leafprologue - .profilable - push %rbx - cmp %rdi,%rsi - je 3f - lea kToLower16(%rip),%r8 - or $-1,%rcx -1: add $1,%rcx - cmp %rcx,%rdx - je 3f - mov (%rsi,%rcx,4),%eax - mov %eax,%ebx - and $0x7f,%ebx - cmp %eax,%ebx - cmove (%r8,%rbx,2),%ax - push %rax - mov (%rdi,%rcx,4),%eax - mov %eax,%ebx - and $0x7f,%ebx - cmp %eax,%ebx - cmove (%r8,%rbx,2),%ax - pop %rbx - cmp %ebx,%eax - jne 2f - test %eax,%eax - jnz 1b -2: sub %ebx,%eax - jmp 4f -3: xor %eax,%eax -4: pop %rbx - .leafepilogue - .endfn wcsncasecmp,globl - - .source __FILE__ diff --git a/libc/nexgen32e/wcscmp-hook.S b/libc/nexgen32e/wcscmp-hook.S deleted file mode 100644 index 9029e4af8..000000000 --- a/libc/nexgen32e/wcscmp-hook.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Dispatches to fastest wcscmp() implementation. - .initbss 300,_init_wcscmp -hook$wcscmp: - .quad 0 - .endobj hook$wcscmp,globl,hidden - .previous - - .init.start 300,_init_wcscmp - ezlea wcscmp$k8,ax - stosq - .init.end 300,_init_wcscmp - - .source __FILE__ diff --git a/libc/nexgen32e/wcscmp-k8.S b/libc/nexgen32e/wcscmp-k8.S deleted file mode 100644 index fb0bb09a3..000000000 --- a/libc/nexgen32e/wcscmp-k8.S +++ /dev/null @@ -1,68 +0,0 @@ -/*-*- 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 "libc/macros.h" -.source __FILE__ - -/ Compares NUL-terminated wchar_t strings. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -wcscmp$k8: - or $-1,%rdx -/ fallthrough - .endfn wcscmp$k8,globl - -/ Compares NUL-terminated wchar_t strings w/ limit. -/ -/ @param rdi first string -/ @param rsi second string -/ @param rdx max shorts -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -wcsncmp$k8: - .leafprologue - .profilable - push %rbx - xor %eax,%eax - cmp %rdi,%rsi - je 3f - or $-1,%rcx -1: add $1,%rcx - cmp %rcx,%rdx - je 3f - mov (%rsi,%rcx,4),%ebx - cmp (%rdi,%rcx,4),%ebx - jne 2f - test %ebx,%ebx - jnz 1b -2: cmovl .Lone(%rip),%eax - cmovg .Lneg1(%rip),%eax -3: pop %rbx - .leafepilogue - .endfn wcsncmp$k8,globl - - .rodata.cst4 -.Lone: .long 1 - .endobj .Lone -.Lneg1: .long -1 - .endobj .Lneg1 - .previous diff --git a/libc/nexgen32e/wcscmp.S b/libc/nexgen32e/wcscmp.S deleted file mode 100644 index a61e80b1b..000000000 --- a/libc/nexgen32e/wcscmp.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- 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 "libc/macros.h" - -/ Compares NUL-terminated wchar_t strings. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -/ @note this stub is provided for abi compatibility -wcscmp: jmp *hook$wcscmp(%rip) - .endfn wcscmp,globl - .source __FILE__ diff --git a/libc/nexgen32e/wcsncmp-hook.S b/libc/nexgen32e/wcsncmp-hook.S deleted file mode 100644 index f4053b1a4..000000000 --- a/libc/nexgen32e/wcsncmp-hook.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- 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" - -/ Dispatches to fastest wcscmp() implementation. - .initbss 300,_init_wcsncmp -hook$wcsncmp: - .quad 0 - .endobj hook$wcsncmp,globl,hidden - .previous - - .init.start 300,_init_wcsncmp - ezlea wcsncmp$k8,ax - stosq - .init.end 300,_init_wcsncmp - .source __FILE__ diff --git a/libc/nexgen32e/wcsncmp.S b/libc/nexgen32e/wcsncmp.S deleted file mode 100644 index e6bbfea0e..000000000 --- a/libc/nexgen32e/wcsncmp.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- 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" - -/ Compares NUL-terminated wchar_t strings. -/ -/ @param rdi first string -/ @param rsi second string -/ @return 0 if equal, etc. -/ @note wchar_t is a 32-bit signed type -/ @note this stub is provided for abi compatibility -wcsncmp:jmp *hook$wcsncmp(%rip) - .endfn wcsncmp,globl - .source __FILE__ diff --git a/libc/nt/codegen.h b/libc/nt/codegen.h index 7af391953..0c7a9114e 100644 --- a/libc/nt/codegen.h +++ b/libc/nt/codegen.h @@ -1,5 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_NT_CODEGEN_H_ #define COSMOPOLITAN_LIBC_NT_CODEGEN_H_ #include "ape/idata.internal.h" -#include "ape/macros.h" +#include "ape/macros.internal.h" #endif /* COSMOPOLITAN_LIBC_NT_CODEGEN_H_ */ diff --git a/libc/nt/nt.mk b/libc/nt/nt.mk index 0ff9f3547..2131d6014 100644 --- a/libc/nt/nt.mk +++ b/libc/nt/nt.mk @@ -166,8 +166,8 @@ o/libc/nt/ntdllimport.inc: \ ape/relocations.h \ libc/nt/ntdllimport.h \ libc/macros.h \ - libc/macros.inc \ - libc/macros-cpp.inc + libc/macros.internal.inc \ + libc/macros-cpp.internal.inc #─────────────────────────────────────────────────────────────────────────────── diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 9e73eea52..331e02877 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -32,12 +32,12 @@ COSMOPOLITAN_C_START_ │ cosmopolitan § new technology » synchronization ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -#define InterlockedAdd(PTR, VAL) \ - ({ \ - typeof(*(PTR)) Res; \ - typeof(Res) Val = (VAL); \ - asm volatile("xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \ - Res + Val; \ +#define InterlockedAdd(PTR, VAL) \ + ({ \ + typeof(*(PTR)) Res; \ + typeof(Res) Val = (VAL); \ + asm volatile("lock xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \ + Res + Val; \ }) #define InterlockedExchange(PTR, VAL) \ diff --git a/libc/rand/lcg.h b/libc/rand/lcg.internal.h similarity index 100% rename from libc/rand/lcg.h rename to libc/rand/lcg.internal.h diff --git a/libc/rand/rand.c b/libc/rand/rand.c index e483056b4..91bf7a900 100644 --- a/libc/rand/rand.c +++ b/libc/rand/rand.c @@ -18,7 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/rand/internal.h" -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" /** diff --git a/libc/rand/randf.c b/libc/rand/randf.c index e269fc1dd..f50136c63 100644 --- a/libc/rand/randf.c +++ b/libc/rand/randf.c @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/limits.h" #include "libc/rand/internal.h" -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" float randf(void) { diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index 9916b9810..7890343c0 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -22,7 +22,6 @@ #include "libc/bits/pushpop.h" #include "libc/bits/safemacros.internal.h" #include "libc/runtime/internal.h" -#include "libc/str/appendchar.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" @@ -34,8 +33,31 @@ struct DosArgv { wint_t wc; }; +static textwindows wint_t DecodeDosArgv(const char16_t **s) { + wint_t x, y; + for (;;) { + if (!(x = *(*s)++)) break; + if (IsUtf16Cont(x)) continue; + if (IsUcs2(x)) { + return x; + } else { + if ((y = *(*s)++)) { + return MergeUtf16(x, y); + } else { + return 0; + } + } + } + return x; +} + static textwindows void AppendDosArgv(struct DosArgv *st, wint_t wc) { - AppendChar(&st->p, st->pe, wc); + uint64_t w; + w = tpenc(wc); + do { + if (st->p >= st->pe) break; + *st->p++ = w & 0xff; + } while (w >>= 8); } /** @@ -66,9 +88,9 @@ textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, st.p = buf; st.pe = buf + size; argc = 0; - st.wc = DecodeNtsUtf16(&st.s); + st.wc = DecodeDosArgv(&st.s); while (st.wc) { - while (st.wc && iswspace(st.wc)) st.wc = DecodeNtsUtf16(&st.s); + while (st.wc && isspace(st.wc)) st.wc = DecodeDosArgv(&st.s); if (!st.wc) break; if (++argc < max) { argv[argc - 1] = st.p < st.pe ? st.p : NULL; @@ -79,8 +101,8 @@ textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, if (st.wc == '"' || st.wc == '\\') { slashes = 0; quotes = 0; - while (st.wc == '\\') st.wc = DecodeNtsUtf16(&st.s), slashes++; - while (st.wc == '"') st.wc = DecodeNtsUtf16(&st.s), quotes++; + while (st.wc == '\\') st.wc = DecodeDosArgv(&st.s), slashes++; + while (st.wc == '"') st.wc = DecodeDosArgv(&st.s), quotes++; if (!quotes) { while (slashes--) AppendDosArgv(&st, '\\'); } else { @@ -94,7 +116,7 @@ textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, } } else { AppendDosArgv(&st, st.wc); - st.wc = DecodeNtsUtf16(&st.s); + st.wc = DecodeDosArgv(&st.s); } } AppendDosArgv(&st, '\0'); diff --git a/libc/runtime/getdosenviron.c b/libc/runtime/getdosenviron.c new file mode 100644 index 000000000..de80fca19 --- /dev/null +++ b/libc/runtime/getdosenviron.c @@ -0,0 +1,51 @@ +/*-*- 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/runtime/runtime.h" +#include "libc/str/str.h" + +/** + * Transcodes NT environment variable block from UTF-16 to UTF-8. + * + * @param env is a double NUL-terminated block of key=values + * @param buf is the new environment which gets double-nul'd + * @param size is the byte capacity of buf + * @param envp stores NULL-terminated string pointer list (optional) + * @param max is the pointer count capacity of envp + * @return number of variables decoded, excluding NULL-terminator + */ +textwindows int GetDosEnviron(const char16_t *env, char *buf, size_t size, + char **envp, size_t max) { + int i; + axdx_t r; + i = 0; + if (size) { + --size; + while (*env) { + if (i + 1 < max) envp[i++] = buf; + r = tprecode16to8(buf, size, env); + size -= r.ax + 1; + buf += r.ax + 1; + env += r.dx; + } + *buf = '\0'; + } + if (i < max) envp[i] = NULL; + return i; +} diff --git a/libc/runtime/getdosenviron.internal.h b/libc/runtime/getdosenviron.internal.h deleted file mode 100644 index c805efaca..000000000 --- a/libc/runtime/getdosenviron.internal.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DOSENVIRON_H_ -#define COSMOPOLITAN_LIBC_DOSENVIRON_H_ -#ifndef __STRICT_ANSI__ -#include "libc/bits/safemacros.internal.h" -#include "libc/str/appendchar.h" -#include "libc/str/str.h" -#include "libc/str/utf16.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) - -/** - * Transcodes NT environment variable block from UTF-16 to UTF-8. - * - * @param env is a double NUL-terminated block of key=values - * @param buf is the new environment - * @param size is the byte capacity of buf - * @param envp stores NULL-terminated string pointer list - * @param max is the pointer count capacity of envp - * @return number of variables decoded, excluding NULL-terminator - */ -static inline int GetDosEnviron(const char16_t *env, char *buf, size_t size, - char **envp, size_t max) { - wint_t wc; - size_t envc; - char *p, *pe; - bool endstring; - const char16_t *s; - s = env; - envc = 0; - if (size) { - p = buf; - pe = buf + size - 1; - if (p < pe) { - wc = DecodeNtsUtf16(&s); - while (wc) { - if (++envc < max) { - envp[envc - 1] = p < pe ? p : NULL; - } - do { - AppendChar(&p, pe, wc); - endstring = !wc; - wc = DecodeNtsUtf16(&s); - } while (!endstring); - buf[min(p - buf, size - 2)] = u'\0'; - } - } - AppendChar(&p, pe, '\0'); - buf[min(p - buf, size - 1)] = u'\0'; - } - if (max) envp[min(envc, max - 1)] = NULL; - return envc; -} - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* !ANSI */ -#endif /* COSMOPOLITAN_LIBC_DOSENVIRON_H_ */ diff --git a/libc/runtime/init.S b/libc/runtime/init.S index cb2050945..84bfec2b7 100644 --- a/libc/runtime/init.S +++ b/libc/runtime/init.S @@ -45,7 +45,7 @@ / @param r15 is envp (still callee saved) / @note rdi is __init_bss_start (callee monotonic lockstep) / @note rsi is __init_rodata_start (callee monotonic lockstep) -/ @see .init.start & .init.end (libc/macros.inc) +/ @see .init.start & .init.end (libc/macros.internal.inc) / @see ape/ape.lds .section .initprologue,"ax",@progbits .type _init,@function @@ -78,7 +78,7 @@ _woot: leave / Decentralized section for packed data structures & initializers. / -/ @see .initro (libc/macros.inc) +/ @see .initro (libc/macros.internal.inc) / @see ape/ape.lds .section .initroprologue,"a",@progbits .type __init_rodata_start,@object @@ -100,7 +100,7 @@ __init_rodata_end: / / Data in this section becomes read-only after initialization. / -/ @see .piro.bss.init (libc/macros.inc) +/ @see .piro.bss.init (libc/macros.internal.inc) / @see libc/runtime/piro.c / @see ape/ape.lds .section .piro.bss.init.1,"aw",@nobits diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 41a5b2c79..a5a128665 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -26,6 +26,7 @@ void _jmpstack(void *, void *, ...) hidden noreturn; long _setstack(void *, void *, ...) hidden; int GetDosArgv(const char16_t *, char *, size_t, char **, size_t) hidden; Elf64_Ehdr *MapElfRead(const char *, struct MappedFile *) hidden; +int GetDosEnviron(const char16_t *, char *, size_t, char **, size_t) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/rbx.h b/libc/runtime/rbx.h deleted file mode 100644 index 289e171ea..000000000 --- a/libc/runtime/rbx.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_RUNTIME_RBX_H_ -#define COSMOPOLITAN_LIBC_RUNTIME_RBX_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#if 0 -#ifndef __llvm__ -register uint8_t *__rbx asm("rbx"); -#else -#define __rbx \ - ({ \ - register uint8_t *Rbx asm("rbx"); \ - asm("" : "=r"(Rbx)); \ - Rbx; \ - }) -#endif -#endif - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_RUNTIME_RBX_H_ */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 8f434e150..c5eff6705 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -37,7 +37,6 @@ #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/teb.h" -#include "libc/runtime/getdosenviron.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" @@ -130,8 +129,7 @@ static textwindows noreturn void WinMainNew(void) { NormalizeCmdExe(); *(/*unconst*/ int *)&__hostos = WINDOWS; size = ROUNDUP(STACKSIZE + sizeof(struct WinArgs), FRAMESIZE); - data = 0x777000000000; - data = (intptr_t)AllocateMemory((char *)data, size, &_mmi.p[0].h); + data = (intptr_t)AllocateMemory((char *)0x777000000000, size, &_mmi.p[0].h); _mmi.p[0].x = data >> 16; _mmi.p[0].y = (data >> 16) + ((size >> 16) - 1); _mmi.p[0].prot = PROT_READ | PROT_WRITE; diff --git a/libc/stdio/mkostempsm.c b/libc/stdio/mkostempsm.c index 1690b5265..b623ab63b 100644 --- a/libc/stdio/mkostempsm.c +++ b/libc/stdio/mkostempsm.c @@ -20,7 +20,7 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" #include "libc/stdio/temp.h" #include "libc/str/str.h" diff --git a/libc/stdio/system.c b/libc/stdio/system.c index 203267ab5..de968b261 100644 --- a/libc/stdio/system.c +++ b/libc/stdio/system.c @@ -21,9 +21,11 @@ #include "libc/calls/calls.h" #include "libc/calls/hefty/spawn.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" /** * Launches program with system command interpreter. @@ -33,21 +35,24 @@ * status that can be accessed using macros like WEXITSTATUS(s) */ int system(const char *cmdline) { + char comspec[128]; + int rc, pid, wstatus; const char *prog, *arg; - int rc, wstatus, fds[3]; if (weaken(fflush)) weaken(fflush)(NULL); if (cmdline) { - prog = !IsWindows() ? _PATH_BSHELL : "cmd"; - arg = !IsWindows() ? "-c" : "/C"; - fds[0] = 0; - fds[1] = 1; - fds[2] = 2; - if ((rc = spawnlp(0, fds, prog, prog, arg, cmdline, NULL)) != -1) { - if ((rc = wait4(rc, &wstatus, 0, NULL)) != -1) { - rc = wstatus; - } + if ((pid = fork()) == -1) return -1; + if (!pid) { + strcpy(comspec, kNtSystemDirectory); + strcat(comspec, "cmd.exe"); + prog = !IsWindows() ? _PATH_BSHELL : comspec; + arg = !IsWindows() ? "-c" : "/C"; + execve(prog, (char *const[]){prog, arg, cmdline, NULL}, environ); + _exit(errno); + } else if (wait4(pid, &wstatus, 0, NULL) != -1) { + return wstatus; + } else { + return -1; } - return rc; } else if (IsWindows()) { return true; } else { diff --git a/libc/str/appendchar.h b/libc/str/appendchar.h deleted file mode 100644 index ce2418151..000000000 --- a/libc/str/appendchar.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_RUNTIME_APPENDCHAR_H_ -#define COSMOPOLITAN_LIBC_RUNTIME_APPENDCHAR_H_ -#ifndef __STRICT_ANSI__ -#include "libc/str/str.h" -#include "libc/str/tpenc.h" -#include "libc/str/tpencode.internal.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) - -static inline void AppendChar(char **p, char *pe, wint_t wc) { - uint64_t w; - w = tpenc(wc); - do { - if (*p >= pe) break; - *(*p)++ = w & 0xff; - } while (w >>= 8); -} - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* !ANSI */ -#endif /* COSMOPOLITAN_LIBC_RUNTIME_APPENDCHAR_H_ */ diff --git a/libc/runtime/relocate.c b/libc/str/bcmp.c similarity index 81% rename from libc/runtime/relocate.c rename to libc/str/bcmp.c index 3192e9d54..47da44e31 100644 --- a/libc/runtime/relocate.c +++ b/libc/str/bcmp.c @@ -17,26 +17,17 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/runtime/runtime.h" +#include "libc/str/str.h" /** - * Applies fixups to 64-bit rela addresses in binary. + * Compares memory. + * + * This API was thought to be nearly extinct until recent versions + * of Clang (c. 2019) started generating synthetic calls to it. + * + * @return unsigned char subtraction at stop index + * @asyncsignalsafe */ -void __relocate(void) { - ptrdiff_t skew; - unsigned char **p; - p = __relo_start; - if (p != __relo_end) { - skew = (intptr_t)p - (intptr_t)*p; - do { - if (*p) { - *p += skew; - if (!NoDebug() && !(_base <= *p && *p < _end)) { - asm("int3"); - } - } - } while (++p != __relo_end); - } - return; +int bcmp(const void *a, const void *b, size_t n) { + return memcmp(a, b, n); } diff --git a/libc/bits/max.c b/libc/str/bcopy.c similarity index 92% rename from libc/bits/max.c rename to libc/str/bcopy.c index 6a1318243..fe1a65f83 100644 --- a/libc/bits/max.c +++ b/libc/str/bcopy.c @@ -17,6 +17,11 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" +#include "libc/str/str.h" -long(max)(long x, long y) { return MAX(x, y); } +/** + * Copies memory the legacy way. + */ +void *bcopy(void *dst, const void *src, size_t n) { + return memmove(dst, src, n); +} diff --git a/libc/str/decodentsutf16.c b/libc/str/decodentsutf16.c index 8e2db23c7..c5c00a6e9 100644 --- a/libc/str/decodentsutf16.c +++ b/libc/str/decodentsutf16.c @@ -22,11 +22,17 @@ wint_t DecodeNtsUtf16(const char16_t **s) { wint_t x, y; - if (!IsUcs2((x = *(*s)++))) { - if ((y = *(*s)++)) { - x = MergeUtf16(x, y); + for (;;) { + if (!(x = *(*s)++)) break; + if (IsUtf16Cont(x)) continue; + if (IsUcs2(x)) { + return x; } else { - x = 0; + if ((y = *(*s)++)) { + return MergeUtf16(x, y); + } else { + return 0; + } } } return x; diff --git a/libc/str/getutf16.ncabi.c b/libc/str/getutf16.ncabi.c index 7d8a0648f..ef5bec54b 100644 --- a/libc/str/getutf16.ncabi.c +++ b/libc/str/getutf16.ncabi.c @@ -21,6 +21,8 @@ #include "libc/str/str.h" #include "libc/str/utf16.h" +/* TODO: DELETE */ + /** * Decodes UTF-16 character. * diff --git a/libc/str/knuthmultiplicativehash.h b/libc/str/knuthmultiplicativehash.internal.h similarity index 100% rename from libc/str/knuthmultiplicativehash.h rename to libc/str/knuthmultiplicativehash.internal.h diff --git a/libc/str/memcmp.c b/libc/str/memcmp.c new file mode 100644 index 000000000..d12ade9e0 --- /dev/null +++ b/libc/str/memcmp.c @@ -0,0 +1,54 @@ +/*-*- 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/intrin/pcmpeqb.h" +#include "libc/intrin/pmovmskb.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/str/str.h" + +/** + * Compares memory. + * + * @return unsigned char subtraction at stop index + * @asyncsignalsafe + */ +int memcmp(const void *a, const void *b, size_t n) { + int c; + size_t i; + unsigned m; + uint8_t *p1, *p2; + uint8_t v1[16], v2[16]; + if (n) { + for (p1 = a, p2 = b, i = 0; i + 16 <= n; i += 16) { + memcpy(v1, p1 + i, 16); + memcpy(v2, p2 + i, 16); + pcmpeqb(v1, v1, v2); + if ((m = pmovmskb(v1) - 0xffff)) { + i += bsf(m); + return p1[i] - p2[i]; + } + } + for (; i < n; ++i) { + if ((c = p1[i] - p2[i])) { + return c; + } + } + } + return 0; +} diff --git a/libc/str/str.h b/libc/str/str.h index f3ea0770e..1191ca3dd 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -9,7 +9,6 @@ COSMOPOLITAN_C_START_ extern const uint8_t gperf_downcase[256]; extern const uint8_t kToLower[256]; -extern const uint8_t kToUpper[256]; extern const uint16_t kToLower16[256]; extern const uint8_t kBase36[256]; extern const char16_t kCp437[256]; @@ -170,7 +169,6 @@ bool endswith(const char *, const char *) strlenesque; bool endswith16(const char16_t *, const char16_t *) strlenesque; bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque; const char *IndexDoubleNulString(const char *, unsigned) strlenesque; -int getkvlin(const char *, const char *const[]); wchar_t *wmemset(wchar_t *, wchar_t, size_t) memcpyesque; char16_t *memset16(char16_t *, char16_t, size_t) memcpyesque; compatfn wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; @@ -199,8 +197,8 @@ bool escapedos(char16_t *, unsigned, const char16_t *, unsigned); typedef unsigned mbstate_t; -size_t tprecode8to16(char16_t *, size_t, const char *); -size_t tprecode16to8(char *, size_t, const char16_t *); +axdx_t tprecode8to16(char16_t *, size_t, const char *); +axdx_t tprecode16to8(char *, size_t, const char16_t *); int strcmp8to16(const char *, const char16_t *) strlenesque; int strncmp8to16(const char *, const char16_t *, size_t) strlenesque; int strcasecmp8to16(const char *, const char16_t *) strlenesque; @@ -240,9 +238,6 @@ char *strsignal(int) returnsnonnull libcesque; ╚────────────────────────────────────────────────────────────────────────────│*/ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -extern int (*const __memcmp)(const void *, const void *, size_t); -#define memcmp(a, b, n) __memcmp(a, b, n) - char *_strncpy(char *, const char *, size_t) asm("strncpy") memcpyesque; #define strncpy(DEST, SRC, N) _strncpy(DEST, SRC, N) /* pacify bad warning */ @@ -265,12 +260,12 @@ char *_strncpy(char *, const char *, size_t) asm("strncpy") memcpyesque; #define __memcpy_isgoodsize(SIZE) \ (isconstant(SIZE) && ((SIZE) <= __BIGGEST_ALIGNMENT__ * 2 && \ - __builtin_popcount((unsigned)(SIZE)) == 1)) + __builtin_popcountl((unsigned)(SIZE)) == 1)) -#define __memset_isgoodsize(SIZE) \ - (isconstant(SIZE) && (((SIZE) <= __BIGGEST_ALIGNMENT__ && \ - __builtin_popcount((unsigned)(SIZE)) == 1) || \ - ((SIZE) % __BIGGEST_ALIGNMENT__ == 0 && \ +#define __memset_isgoodsize(SIZE) \ + (isconstant(SIZE) && (((SIZE) <= __BIGGEST_ALIGNMENT__ && \ + __builtin_popcountl((unsigned)(SIZE)) == 1) || \ + ((SIZE) % __BIGGEST_ALIGNMENT__ == 0 && \ (SIZE) / __BIGGEST_ALIGNMENT__ <= 3))) #define memcpy(DEST, SRC, SIZE) \ diff --git a/libc/str/strcasecmp.c b/libc/str/strcasecmp.c new file mode 100644 index 000000000..ac49871d9 --- /dev/null +++ b/libc/str/strcasecmp.c @@ -0,0 +1,38 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated strings case-insensitively. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strcasecmp(const char *a, const char *b) { + int x, y; + size_t i = 0; + if (a == b) return 0; + while ((x = kToLower[a[i] & 0xff]) == (y = kToLower[b[i] & 0xff]) && b[i]) { + ++i; + } + return x - y; +} diff --git a/libc/str/strcasecmp16.c b/libc/str/strcasecmp16.c new file mode 100644 index 000000000..9156f2fa4 --- /dev/null +++ b/libc/str/strcasecmp16.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated UCS-2 strings case-insensitively. + * + * @param a is first non-null NUL-terminated char16 string pointer + * @param b is second non-null NUL-terminated char16 string pointer + * @return is <0, 0, or >0 based on uint16_t comparison + * @asyncsignalsafe + */ +int strcasecmp16(const char16_t *l, const char16_t *r) { + int x, y; + size_t i = 0; + while ((x = tolower(l[i])) == (y = tolower(r[i])) && r[i]) ++i; + return x - y; +} diff --git a/libc/str/strcmp.c b/libc/str/strcmp.c new file mode 100644 index 000000000..fbfe1149a --- /dev/null +++ b/libc/str/strcmp.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated strings. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strcmp(const char *a, const char *b) { + size_t i = 0; + if (a == b) return 0; + while (a[i] == b[i] && b[i]) ++i; + return (a[i] & 0xff) - (b[i] & 0xff); +} diff --git a/libc/str/strcmp16.c b/libc/str/strcmp16.c new file mode 100644 index 000000000..e09250ef3 --- /dev/null +++ b/libc/str/strcmp16.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/str/str.h" + +/** + * Compares NUL-terminated UCS-2 strings. + * + * @param a is first non-null NUL-terminated char16 string pointer + * @param b is second non-null NUL-terminated char16 string pointer + * @return is <0, 0, or >0 based on uint16_t comparison + * @asyncsignalsafe + */ +int strcmp16(const char16_t *l, const char16_t *r) { + size_t i = 0; + while (l[i] == r[i] && r[i]) ++i; + return l[i] - r[i]; +} diff --git a/libc/str/strncasecmp.c b/libc/str/strncasecmp.c new file mode 100644 index 000000000..0a953ff48 --- /dev/null +++ b/libc/str/strncasecmp.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/str/str.h" + +/** + * Compares NUL-terminated strings case-insensitively w/ limit. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strncasecmp(const char *a, const char *b, size_t n) { + int x, y; + size_t i = 0; + if (!n-- || a == b) return 0; + while ((x = kToLower[a[i] & 0xff]) == (y = kToLower[b[i] & 0xff]) && b[i] && + i < n) { + ++i; + } + return x - y; +} diff --git a/libc/str/strncasecmp16.c b/libc/str/strncasecmp16.c new file mode 100644 index 000000000..f3919a6cc --- /dev/null +++ b/libc/str/strncasecmp16.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/str/str.h" + +/** + * Compares NUL-terminated UCS-2 strings case-insensitively w/ limit. + * + * @param a is first non-null NUL-terminated char16 string pointer + * @param b is second non-null NUL-terminated char16 string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strncasecmp16(const char16_t *a, const char16_t *b, size_t n) { + int x, y; + size_t i = 0; + if (!n-- || a == b) return 0; + while ((x = tolower(a[i])) == (y = tolower(b[i])) && b[i] && i < n) ++i; + return x - y; +} diff --git a/libc/str/strncmp.c b/libc/str/strncmp.c new file mode 100644 index 000000000..8eef1b59d --- /dev/null +++ b/libc/str/strncmp.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated strings w/ limit. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strncmp(const char *a, const char *b, size_t n) { + size_t i = 0; + if (!n-- || a == b) return 0; + while (a[i] == b[i] && b[i] && i < n) ++i; + return (a[i] & 0xff) - (b[i] & 0xff); +} diff --git a/libc/str/strncmp16.c b/libc/str/strncmp16.c new file mode 100644 index 000000000..9d38dc2ef --- /dev/null +++ b/libc/str/strncmp16.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated UCS-2 strings w/ limit. + * + * @param a is first non-null NUL-terminated char16 string pointer + * @param b is second non-null NUL-terminated char16 string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int strncmp16(const char16_t *a, const char16_t *b, size_t n) { + size_t i = 0; + if (!n-- || a == b) return 0; + while (i < n && a[i] == b[i] && b[i]) ++i; + return a[i] - b[i]; +} diff --git a/libc/str/tprecode16to8.c b/libc/str/tprecode16to8.c index 4d4e63dbf..f9c1d519c 100644 --- a/libc/str/tprecode16to8.c +++ b/libc/str/tprecode16to8.c @@ -18,38 +18,62 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/conv/conv.h" +#include "libc/intrin/packsswb.h" +#include "libc/intrin/pandn.h" +#include "libc/intrin/pcmpgtw.h" +#include "libc/intrin/pmovmskb.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" +static const int16_t kDel16[8] = {127, 127, 127, 127, 127, 127, 127, 127}; + /** * Transcodes UTF-16 to UTF-8. * * @param dst is output buffer * @param dstsize is bytes in dst * @param src is NUL-terminated UTF-16 input string - * @return number of bytes written excluding NUL + * @return ax bytes written excluding nul + * @return dx index of character after nul word in src */ -size_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) { - size_t i; +axdx_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) { + axdx_t r; uint64_t w; wint_t x, y; - i = 0; - if (dstsize) { - for (;;) { - if (!(x = *src++)) break; - if (IsUtf16Cont(x)) continue; - if (!IsUcs2(x)) { - if (!(y = *src++)) break; - x = MergeUtf16(x, y); - } - w = tpenc(x); - while (w && i + 1 < dstsize) { - dst[i++] = w & 0xFF; - w >>= 8; + int16_t v1[8], v2[8], v3[8], vz[8]; + r.ax = 0; + r.dx = 0; + for (;;) { + if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { + /* 10x speedup for ascii */ + memset(vz, 0, 16); + while (r.ax + 8 < dstsize) { + memcpy(v1, src + r.dx, 16); + pcmpgtw(v2, v1, vz); + pcmpgtw(v3, v1, kDel16); + pandn((void *)v2, (void *)v3, (void *)v2); + if (pmovmskb((void *)v2) != 0xFFFF) break; + packsswb((void *)v1, v1, v1); + memcpy(dst + r.ax, v1, 8); + r.ax += 8; + r.dx += 8; } } - dst[i] = 0; + if (!(x = src[r.dx++])) break; + if (IsUtf16Cont(x)) continue; + if (!IsUcs2(x)) { + if (!(y = src[r.dx++])) break; + x = MergeUtf16(x, y); + } + w = tpenc(x); + while (w && r.ax + 1 < dstsize) { + dst[r.ax++] = w & 0xFF; + w >>= 8; + } } - return i; + if (r.ax < dstsize) { + dst[r.ax] = 0; + } + return r; } diff --git a/libc/str/tprecode8to16.c b/libc/str/tprecode8to16.c index 3ed2436f2..5a14ea71c 100644 --- a/libc/str/tprecode8to16.c +++ b/libc/str/tprecode8to16.c @@ -17,6 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pcmpgtb.h" +#include "libc/intrin/pmovmskb.h" +#include "libc/intrin/punpckhbw.h" +#include "libc/intrin/punpcklbw.h" #include "libc/str/str.h" #include "libc/str/thompike.h" #include "libc/str/utf16.h" @@ -27,34 +31,56 @@ * @param dst is output buffer * @param dstsize is shorts in dst * @param src is NUL-terminated UTF-8 input string - * @return number of shorts written excluding NUL + * @return ax shorts written excluding nul + * @return dx index of character after nul word in src */ -size_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { - size_t i; +axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { + axdx_t r; unsigned n; uint64_t w; wint_t x, y; - i = 0; - if (dstsize) { - for (;;) { - if (!(x = *src++ & 0xff)) break; - if (ThomPikeCont(x)) continue; - if (!isascii(x)) { - n = ThomPikeLen(x); - x = ThomPikeByte(x); - while (--n) { - if (!(y = *src++ & 0xff)) goto stop; - x = ThomPikeMerge(x, y); - } - } - w = EncodeUtf16(x); - while (w && i + 1 < dstsize) { - dst[i++] = w & 0xFFFF; - w >>= 16; + uint8_t v1[16], v2[16], vz[16]; + r.ax = 0; + r.dx = 0; + for (;;) { + if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { + /* 34x speedup for ascii */ + memset(vz, 0, 16); + while (r.ax + 16 < dstsize) { + memcpy(v1, src + r.dx, 16); + pcmpgtb((int8_t *)v2, (int8_t *)v1, (int8_t *)vz); + if (pmovmskb(v2) != 0xFFFF) break; + punpcklbw(v2, v1, vz); + punpckhbw(v1, v1, vz); + memcpy(dst + r.ax + 0, v2, 16); + memcpy(dst + r.ax + 8, v1, 16); + r.ax += 16; + r.dx += 16; } } - stop: - dst[i] = 0; + x = src[r.dx++] & 0xff; + if (ThomPikeCont(x)) continue; + if (!isascii(x)) { + n = ThomPikeLen(x); + x = ThomPikeByte(x); + while (--n) { + if ((y = src[r.dx++] & 0xff)) { + x = ThomPikeMerge(x, y); + } else { + x = 0; + break; + } + } + } + if (!x) break; + w = EncodeUtf16(x); + while (w && r.ax + 1 < dstsize) { + dst[r.ax++] = w & 0xFFFF; + w >>= 16; + } } - return i; + if (r.ax < dstsize) { + dst[r.ax] = 0; + } + return r; } diff --git a/libc/str/utf16.h b/libc/str/utf16.h index d6516a9cd..8042c0210 100644 --- a/libc/str/utf16.h +++ b/libc/str/utf16.h @@ -20,8 +20,6 @@ COSMOPOLITAN_C_START_ ((((wc)-0x10000) & 1023) + 0xDC00) << 16) \ : 0xFFFD) -wint_t DecodeNtsUtf16(const char16_t **); - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STR_UTF16_H_ */ diff --git a/libc/str/wcscasecmp.c b/libc/str/wcscasecmp.c new file mode 100644 index 000000000..91dace194 --- /dev/null +++ b/libc/str/wcscasecmp.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/str/str.h" + +/** + * Compares NUL-terminated wide strings case-insensitively. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int wcscasecmp(const wchar_t *a, const wchar_t *b) { + size_t i = 0; + unsigned x, y; + if (a == b) return 0; + while ((x = tolower(a[i])) == (y = tolower(b[i])) && b[i]) ++i; + return x - y; +} diff --git a/libc/str/wcscmp.c b/libc/str/wcscmp.c new file mode 100644 index 000000000..40ac75304 --- /dev/null +++ b/libc/str/wcscmp.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated wide strings. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int wcscmp(const wchar_t *a, const wchar_t *b) { + size_t i = 0; + if (a == b) return 0; + while (a[i] == b[i] && b[i]) ++i; + return (unsigned)a[i] - (unsigned)b[i]; +} diff --git a/libc/str/wcsncasecmp.c b/libc/str/wcsncasecmp.c new file mode 100644 index 000000000..e315e004a --- /dev/null +++ b/libc/str/wcsncasecmp.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/str/str.h" + +/** + * Compares NUL-terminated wide strings case-insensitively w/ limit. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { + size_t i = 0; + unsigned x, y; + if (!n-- || a == b) return 0; + while ((x = tolower(a[i])) == (y = tolower(b[i])) && b[i] && i < n) ++i; + return x - y; +} diff --git a/libc/str/wcsncmp.c b/libc/str/wcsncmp.c new file mode 100644 index 000000000..8cdb2b338 --- /dev/null +++ b/libc/str/wcsncmp.c @@ -0,0 +1,35 @@ +/*-*- 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/str/str.h" + +/** + * Compares NUL-terminated wide strings w/ limit. + * + * @param a is first non-null NUL-terminated string pointer + * @param b is second non-null NUL-terminated string pointer + * @return is <0, 0, or >0 based on uint8_t comparison + * @asyncsignalsafe + */ +int wcsncmp(const wchar_t *a, const wchar_t *b, size_t n) { + size_t i = 0; + if (!n-- || a == b) return 0; + while (i < n && a[i] == b[i] && b[i]) ++i; + return (unsigned)a[i] - (unsigned)b[i]; +} diff --git a/libc/str/wmemcpy.c b/libc/str/wmemcpy.c index c327ea7d0..fede0b621 100644 --- a/libc/str/wmemcpy.c +++ b/libc/str/wmemcpy.c @@ -19,6 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" -compatfn wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t count) { +wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t count) { return memcpy(dest, src, count * sizeof(wchar_t)); } diff --git a/libc/str/wmemmove.c b/libc/str/wmemmove.c index 975c5e21f..109e0616f 100644 --- a/libc/str/wmemmove.c +++ b/libc/str/wmemmove.c @@ -19,6 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" -compatfn wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t count) { +wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t count) { return memmove(dest, src, count * sizeof(wchar_t)); } diff --git a/libc/stubs/abort.S b/libc/stubs/abort.S index e2445e669..a94e6d847 100644 --- a/libc/stubs/abort.S +++ b/libc/stubs/abort.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" .real .source __FILE__ .code16 # ∩ .code32 ∩ .code64 diff --git a/libc/stubs/assertfail.S b/libc/stubs/assertfail.S index f5aef21d9..3cf60a906 100644 --- a/libc/stubs/assertfail.S +++ b/libc/stubs/assertfail.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" .real .source __FILE__ .code16 # ∩ .code32 ∩ .code64 diff --git a/libc/stubs/exit.S b/libc/stubs/exit.S index c11b56643..b54896cea 100644 --- a/libc/stubs/exit.S +++ b/libc/stubs/exit.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" .real .source __FILE__ .code16 # ∩ .code32 ∩ .code64 diff --git a/libc/stubs/stackguard.S b/libc/stubs/stackguard.S index 9545e957a..15a9a988d 100644 --- a/libc/stubs/stackguard.S +++ b/libc/stubs/stackguard.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" .real .source __FILE__ .code16 # ∩ .code32 ∩ .code64 diff --git a/libc/stubs/triplf.S b/libc/stubs/triplf.S index 12d25b7b4..0462dac75 100644 --- a/libc/stubs/triplf.S +++ b/libc/stubs/triplf.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" #include "ape/config.h" .real .source __FILE__ diff --git a/libc/stubs/ubsan.S b/libc/stubs/ubsan.S index 8f163f97f..f17396528 100644 --- a/libc/stubs/ubsan.S +++ b/libc/stubs/ubsan.S @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/macros.h" +#include "ape/macros.internal.h" .real .code16 # ∩ .code32 ∩ .code64 .source __FILE__ diff --git a/libc/sysv/consts/syscon.inc b/libc/sysv/consts/syscon.inc index f765b0c26..8200adb12 100644 --- a/libc/sysv/consts/syscon.inc +++ b/libc/sysv/consts/syscon.inc @@ -18,7 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ /* clang-format off */ -.include "libc/macros.inc" +.include "libc/macros.internal.inc" .macro .syscon group:req name:req linux:req xnu:req freebsd:req openbsd:req windows:req yoink _init_systemfive diff --git a/libc/sysv/macros.h b/libc/sysv/macros.internal.h similarity index 96% rename from libc/sysv/macros.h rename to libc/sysv/macros.internal.h index 789ff0f43..88d1057c0 100644 --- a/libc/sysv/macros.h +++ b/libc/sysv/macros.internal.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_MACROS_H_ #define COSMOPOLITAN_LIBC_SYSV_MACROS_H_ #ifdef __ASSEMBLER__ -#include "libc/macros.inc" +#include "libc/macros.internal.inc" /* clang-format off */ /** diff --git a/libc/sysv/sysv.mk b/libc/sysv/sysv.mk index 2e5ca3282..c23ccd3f4 100644 --- a/libc/sysv/sysv.mk +++ b/libc/sysv/sysv.mk @@ -28,7 +28,7 @@ LIBC_SYSV_A_DIRECTDEPS = \ LIBC_STUBS LIBC_SYSV_A_FILES := \ - libc/sysv/macros.h \ + libc/sysv/macros.internal.h \ libc/sysv/errfuns.h \ libc/sysv/g_syscount.S \ libc/sysv/restorert.S \ @@ -65,7 +65,7 @@ $(LIBC_SYSV_A).pkg: \ $(LIBC_SYSV_A_OBJS): \ libc/sysv/consts/syscon.inc -libc/sysv/consts/syscon.inc: libc/macros.inc +libc/sysv/consts/syscon.inc: libc/macros.internal.inc #─────────────────────────────────────────────────────────────────────────────── diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 7c0a6bc65..d51ee5d08 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -22,7 +22,6 @@ #include "libc/calls/internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/x86feature.h" -#include "libc/runtime/rbx.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" diff --git a/libc/tinymath/emod.h b/libc/tinymath/emod.h index a8dd6e8d9..114f60217 100644 --- a/libc/tinymath/emod.h +++ b/libc/tinymath/emod.h @@ -9,7 +9,7 @@ * @return (𝑥 mod 𝑦) ∈ [0.,𝑦) * @see fmod() */ -static double emod(double x, double y) { +static inline double emod(double x, double y) { return x - fabs(y) * floor(x / fabs(y)); } diff --git a/libc/tinymath/emodl.h b/libc/tinymath/emodl.h index 7a5656dfd..00bbac5d2 100644 --- a/libc/tinymath/emodl.h +++ b/libc/tinymath/emodl.h @@ -9,7 +9,7 @@ * @return (𝑥 mod 𝑦) ∈ [0.,𝑦) * @see fmodl() */ -static long double emodl(long double x, long double y) { +static inline long double emodl(long double x, long double y) { return x - fabsl(y) * floorl(x / fabsl(y)); } diff --git a/libc/unicode/blocks.txt b/libc/unicode/blocks.txt index 56877db10..0d21f6d72 100644 --- a/libc/unicode/blocks.txt +++ b/libc/unicode/blocks.txt @@ -38,7 +38,9 @@ 0180..024F; Latin Extended-B 0250..02AF; IPA Extensions 02B0..02FF; Spacing Modifier Letters + 0300..036F; Combining Diacritical Marks + 0370..03FF; Greek and Coptic 0400..04FF; Cyrillic 0500..052F; Cyrillic Supplement @@ -73,6 +75,7 @@ 1380..139F; Ethiopic Supplement 13A0..13FF; Cherokee 1400..167F; Unified Canadian Aboriginal Syllabics + 1680..169F; Ogham 16A0..16FF; Runic 1700..171F; Tagalog diff --git a/libc/unicode/keastasianwidth.s b/libc/unicode/keastasianwidth.s index e5068de46..0605585d5 100644 --- a/libc/unicode/keastasianwidth.s +++ b/libc/unicode/keastasianwidth.s @@ -1,5 +1,5 @@ / o/$(MODE)/tool/build/lz4toasm.com -o o/$(MODE)/libc/str/EastAsianWidth.s -s kEastAsianWidth o/$(MODE)/libc/str/EastAsianWidth.bin.lz4 -.include "libc/macros.inc" +.include "libc/macros.internal.inc" .rodata .align 4 diff --git a/libc/zipos/find.c b/libc/zipos/find.c index 1b3607912..f53fe88ac 100644 --- a/libc/zipos/find.c +++ b/libc/zipos/find.c @@ -19,7 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/relocations.h" #include "libc/assert.h" -#include "libc/runtime/rbx.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/zip.h" diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 3ccb0fd1e..a6cbbb8c5 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -28,7 +28,6 @@ #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" -#include "libc/runtime/rbx.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/str/undeflate.h" diff --git a/libc/zipos/stat-impl.c b/libc/zipos/stat-impl.c index 8eb0814ca..fab65612a 100644 --- a/libc/zipos/stat-impl.c +++ b/libc/zipos/stat-impl.c @@ -20,7 +20,6 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" -#include "libc/runtime/rbx.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" #include "libc/zip.h" diff --git a/test/ape/lib/getpagetableentry_test.c b/test/ape/lib/getpagetableentry_test.c index d61eb6643..ea38d5e33 100644 --- a/test/ape/lib/getpagetableentry_test.c +++ b/test/ape/lib/getpagetableentry_test.c @@ -31,7 +31,7 @@ TEST(getpagetableentry, testLowestAddress) { memset(&stack, 0, sizeof(stack)); uint64_t vaddr = 0; uint64_t paddr = 0x31337000; - *getpagetableentry(vaddr, 3, &pml4t, &ptsp) = paddr | PAGE_V; + *__getpagetableentry(vaddr, 3, &pml4t, &ptsp) = paddr | PAGE_V; EXPECT_EQ(&stack[2].p[0], pml4t.p[0] & PAGE_TA); /* pml4t → pdpt */ EXPECT_EQ(&stack[1].p[0], stack[2].p[0] & PAGE_TA); /* pdpt → pdt */ EXPECT_EQ(&stack[0].p[0], stack[1].p[0] & PAGE_TA); /* pdt → pd */ @@ -52,7 +52,7 @@ TEST(getpagetableentry, testHigherAddress) { memset(&stack, 0, sizeof(stack)); uint64_t vaddr = 0x133731337000; uint64_t paddr = 0x123000; - *getpagetableentry(vaddr, 3, &pml4t, &ptsp) = paddr | PAGE_V; + *__getpagetableentry(vaddr, 3, &pml4t, &ptsp) = paddr | PAGE_V; EXPECT_EQ(&stack[2].p[0], pml4t.p[38] & PAGE_TA); /* pml4t → pdpt */ EXPECT_EQ(&stack[1].p[0], stack[2].p[220] & PAGE_TA); /* pdpt → pdt */ EXPECT_EQ(&stack[0].p[0], stack[1].p[393] & PAGE_TA); /* pdt → pd */ diff --git a/test/libc/calls/mkntpath_test.c b/test/libc/calls/mkntpath_test.c index 32e4d074a..a605bf181 100644 --- a/test/libc/calls/mkntpath_test.c +++ b/test/libc/calls/mkntpath_test.c @@ -24,7 +24,7 @@ char16_t p[PATH_MAX]; TEST(mkntpath, testEmpty) { - EXPECT_EQ(0, mkntpath("", p)); + EXPECT_EQ(0, __mkntpath("", p)); EXPECT_STREQ(u"", p); } @@ -35,11 +35,11 @@ TEST(mkntpath, testSlashes) { * all it takes to make the feature entirely useless to us, similar to * the law of noncontradiction. We address the issue as follows: */ - EXPECT_EQ(9, mkntpath("o/foo.com", p)); + EXPECT_EQ(9, __mkntpath("o/foo.com", p)); EXPECT_STREQ(u"o\\foo.com", p); } TEST(mkntpath, testUnicode) { - EXPECT_EQ(20, mkntpath("C:\\𐌰𐌱𐌲𐌳\\𐌴𐌵𐌶𐌷", p)); + EXPECT_EQ(20, __mkntpath("C:\\𐌰𐌱𐌲𐌳\\𐌴𐌵𐌶𐌷", p)); EXPECT_STREQ(u"C:\\𐌰𐌱𐌲𐌳\\𐌴𐌵𐌶𐌷", p); } diff --git a/test/libc/conv/basename_test.c b/test/libc/conv/basename_test.c index 964b58f8a..9356e26ec 100644 --- a/test/libc/conv/basename_test.c +++ b/test/libc/conv/basename_test.c @@ -19,10 +19,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/conv/conv.h" +#include "libc/mem/mem.h" #include "libc/testlib/testlib.h" TEST(basename, test) { EXPECT_STREQ("", basename("")); + EXPECT_STREQ("/", basename("/")); EXPECT_STREQ("hello", basename("hello")); EXPECT_STREQ("there", basename("hello/there")); EXPECT_STREQ("yo", basename("hello/there/yo")); @@ -42,3 +44,13 @@ 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/conv/test.mk b/test/libc/conv/test.mk index 9d642c945..2c33c8629 100644 --- a/test/libc/conv/test.mk +++ b/test/libc/conv/test.mk @@ -27,6 +27,7 @@ TEST_LIBC_CONV_DIRECTDEPS = \ LIBC_CONV \ LIBC_FMT \ LIBC_LOG \ + LIBC_MEM \ LIBC_STDIO \ LIBC_NEXGEN32E \ LIBC_STUBS \ diff --git a/test/libc/intrin/intrin_test.c b/test/libc/intrin/intrin_test.c index d7add1c86..f09a20fe7 100644 --- a/test/libc/intrin/intrin_test.c +++ b/test/libc/intrin/intrin_test.c @@ -44,6 +44,8 @@ #include "libc/intrin/pcmpgtb.h" #include "libc/intrin/pcmpgtd.h" #include "libc/intrin/pcmpgtw.h" +#include "libc/intrin/pdep.h" +#include "libc/intrin/pext.h" #include "libc/intrin/phaddd.h" #include "libc/intrin/phaddsw.h" #include "libc/intrin/phaddw.h" @@ -101,7 +103,7 @@ #include "libc/limits.h" #include "libc/log/check.h" #include "libc/nexgen32e/kcpuids.h" -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.h" #include "libc/stdio/stdio.h" @@ -2062,6 +2064,26 @@ TEST(pcmpeqw, test2) { } } +TEST(pdep, fuzz) { + int i; + uint64_t x, y; + for (i = 0; i < 1000; ++i) { + x = rand64(); + y = rand64(); + ASSERT_EQ(pdep(x, y), (pdep)(x, y)); + } +} + +TEST(pext, fuzz) { + int i; + uint64_t x, y; + for (i = 0; i < 1000; ++i) { + x = rand64(); + y = rand64(); + ASSERT_EQ(pext(x, y), (pext)(x, y)); + } +} + BENCH(psrldq, bench) { volatile uint8_t A[16]; volatile uint8_t B[16]; diff --git a/test/libc/intrin/test.mk b/test/libc/intrin/test.mk index bf7010ea0..980caa666 100644 --- a/test/libc/intrin/test.mk +++ b/test/libc/intrin/test.mk @@ -30,6 +30,7 @@ TEST_LIBC_INTRIN_DIRECTDEPS = \ LIBC_RAND \ LIBC_LOG \ LIBC_STUBS \ + LIBC_STR \ LIBC_TESTLIB \ LIBC_TINYMATH \ LIBC_RUNTIME \ diff --git a/test/libc/nexgen32e/crc32_test.c b/test/libc/nexgen32e/crc32_test.c index 44374106a..2e7a57221 100644 --- a/test/libc/nexgen32e/crc32_test.c +++ b/test/libc/nexgen32e/crc32_test.c @@ -20,7 +20,7 @@ #include "libc/macros.h" #include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/x86feature.h" -#include "libc/str/knuthmultiplicativehash.h" +#include "libc/str/knuthmultiplicativehash.internal.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/hyperion.h" diff --git a/test/libc/runtime/getdosenviron_test.c b/test/libc/runtime/getdosenviron_test.c index 37d960a5f..c17d047ed 100644 --- a/test/libc/runtime/getdosenviron_test.c +++ b/test/libc/runtime/getdosenviron_test.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/runtime/getdosenviron.internal.h" +#include "libc/runtime/internal.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" @@ -98,6 +98,6 @@ TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_3) { size_t size = 2; char *block = tmalloc(size); EXPECT_EQ(0, GetDosEnviron(u"", block, size, NULL, 0)); - EXPECT_BINEQ(u"  ", block); + EXPECT_BINEQ(u" ", block); tfree(block); } diff --git a/test/libc/stdio/system_test.c b/test/libc/stdio/system_test.c index d2479b1d9..14b9f3b01 100644 --- a/test/libc/stdio/system_test.c +++ b/test/libc/stdio/system_test.c @@ -17,9 +17,17 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" #include "libc/calls/calls.h" +#include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -TEST(system, nullParam_testsIfSystemHasShell) { ASSERT_EQ(true, system(NULL)); } -TEST(system, test) { ASSERT_EQ(42, WEXITSTATUS(system("exit 42"))); } +TEST(system, nullParam_testsIfSystemHasShell) { + ASSERT_EQ(true, system(NULL)); +} + +TEST(system, test) { + int rc; + rc = system("exit 42"); + ASSERT_NE(-1, rc); + ASSERT_EQ(42, WEXITSTATUS(rc)); +} diff --git a/test/libc/str/strcmp_test.c b/test/libc/str/strcmp_test.c index 98fab4a6f..f15f0a650 100644 --- a/test/libc/str/strcmp_test.c +++ b/test/libc/str/strcmp_test.c @@ -21,7 +21,6 @@ #include "libc/dce.h" #include "libc/macros.h" #include "libc/nexgen32e/cachesize.h" -#include "libc/nexgen32e/tinystrcmp.internal.h" #include "libc/nexgen32e/x86feature.h" #include "libc/rand/rand.h" #include "libc/stdio/stdio.h" @@ -29,35 +28,15 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -int memcmp$sse2(const void *, const void *, size_t) hidden; -int memcmp$avx2(const void *, const void *, size_t) hidden; -int (*memcmpi)(const void *, const void *, size_t) = memcmp$sse2; - -int strcmp$k8(const char *, const char *) hidden; -int strcmp$avx(const char *, const char *) hidden; -int (*strcmpi)(const char *, const char *) = tinystrcmp; - -int strncmp$k8(const char *, const char *, size_t) hidden; -int strncmp$avx(const char *, const char *, size_t) hidden; -int (*strncmpi)(const char *, const char *, size_t) = tinystrncmp; - -FIXTURE(strcmp, avx) { - if (X86_HAVE(AVX)) { - strcmpi = strcmp$avx; - strncmpi = strncmp$avx; - } - if (X86_HAVE(AVX2)) { - memcmpi = memcmp$avx2; - } -} +int (*memcmpi)(const void *, const void *, size_t) = memcmp; /*───────────────────────────────────────────────────────────────────────────│─╗ │ test/libc/str/strcmp_test.c § emptiness ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ TEST(strcmp, emptyString) { - EXPECT_EQ(0, strcmpi("", "")); - EXPECT_NE(0, strcmpi("", "a")); + EXPECT_EQ(0, strcmp("", "")); + EXPECT_NE(0, strcmp("", "a")); } TEST(strcasecmp, emptyString) { @@ -88,11 +67,11 @@ TEST(wcscasecmp, emptyString) { TEST(strncmp, emptyString) { char *s1 = strcpy(tmalloc(1), ""); char *s2 = strcpy(tmalloc(1), ""); - ASSERT_EQ(0, strncmpi(s1, s2, 0)); - ASSERT_EQ(0, strncmpi(s1, s2, 1)); - ASSERT_EQ(0, strncmpi(s1, s2, -1)); - ASSERT_EQ(0, strncmpi(s1, s1, -1)); - ASSERT_EQ(0, strncmpi(s2, s2, -1)); + ASSERT_EQ(0, strncmp(s1, s2, 0)); + ASSERT_EQ(0, strncmp(s1, s2, 1)); + ASSERT_EQ(0, strncmp(s1, s2, -1)); + ASSERT_EQ(0, strncmp(s1, s1, -1)); + ASSERT_EQ(0, strncmp(s2, s2, -1)); tfree(s2); tfree(s1); } @@ -116,9 +95,9 @@ TEST(strncasecmp, emptyString) { TEST(strncmp, testInequality) { char *s1 = strcpy(tmalloc(2), "1"); char *s2 = strcpy(tmalloc(1), ""); - ASSERT_EQ(0, strncmpi(s1, s2, 0)); - ASSERT_EQ('1', strncmpi(s1, s2, 1)); - ASSERT_EQ(-'1', strncmpi(s2, s1, 1)); + ASSERT_EQ(0, strncmp(s1, s2, 0)); + ASSERT_EQ('1', strncmp(s1, s2, 1)); + ASSERT_EQ(-'1', strncmp(s2, s1, 1)); tfree(s2); tfree(s1); } @@ -166,37 +145,37 @@ TEST(memcmp, test) { } TEST(strcmp, testItWorks) { - EXPECT_EQ(strcmpi("", ""), 0); - EXPECT_EQ(strcmpi("a", "a"), 0); - EXPECT_GT(strcmpi("a", "A"), 0); - EXPECT_LT(strcmpi("A", "a"), 0); - EXPECT_LT(strcmpi("\001", "\377"), 0); - EXPECT_GT(strcmpi("\377", "\001"), 0); - EXPECT_LT(strcmpi("a", "aa"), 0); - EXPECT_GT(strcmpi("aa", "a"), 0); - EXPECT_LT(strcmpi("a\000", "aa\000"), 0); - EXPECT_GT(strcmpi("aa\000", "a\000"), 0); - EXPECT_LT(strcmpi("aaaaaaaaaaaaaaa\001", "aaaaaaaaaaaaaaa\377"), 0); - EXPECT_GT(strcmpi("aaaaaaaaaaaaaaa\377", "aaaaaaaaaaaaaaa\001"), 0); - EXPECT_LT(strcmpi("aaaaaaaaaaaaaaaa\001", "aaaaaaaaaaaaaaaa\377"), 0); - EXPECT_GT(strcmpi("aaaaaaaaaaaaaaaa\377", "aaaaaaaaaaaaaaaa\001"), 0); - EXPECT_LT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\001", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\377"), + EXPECT_EQ(strcmp("", ""), 0); + EXPECT_EQ(strcmp("a", "a"), 0); + EXPECT_GT(strcmp("a", "A"), 0); + EXPECT_LT(strcmp("A", "a"), 0); + EXPECT_LT(strcmp("\001", "\377"), 0); + EXPECT_GT(strcmp("\377", "\001"), 0); + EXPECT_LT(strcmp("a", "aa"), 0); + EXPECT_GT(strcmp("aa", "a"), 0); + EXPECT_LT(strcmp("a\000", "aa\000"), 0); + EXPECT_GT(strcmp("aa\000", "a\000"), 0); + EXPECT_LT(strcmp("aaaaaaaaaaaaaaa\001", "aaaaaaaaaaaaaaa\377"), 0); + EXPECT_GT(strcmp("aaaaaaaaaaaaaaa\377", "aaaaaaaaaaaaaaa\001"), 0); + EXPECT_LT(strcmp("aaaaaaaaaaaaaaaa\001", "aaaaaaaaaaaaaaaa\377"), 0); + EXPECT_GT(strcmp("aaaaaaaaaaaaaaaa\377", "aaaaaaaaaaaaaaaa\001"), 0); + EXPECT_LT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\001", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\377"), 0); - EXPECT_GT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\377", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\001"), + EXPECT_GT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\377", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaa\001"), 0); - EXPECT_LT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\001", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\377"), + EXPECT_LT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\001", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\377"), 0); - EXPECT_GT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\377", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\001"), + EXPECT_GT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\377", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaa\001"), 0); - EXPECT_LT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\001", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\377"), + EXPECT_LT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\001", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\377"), 0); - EXPECT_GT(strcmpi("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\377", - "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\001"), + EXPECT_GT(strcmp("aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\377", + "aaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaa\001"), 0); } @@ -347,8 +326,8 @@ TEST(strncmp, testEqualManyNs) { s1[PAGESIZE - 1] = '\0'; s2[PAGESIZE - 1] = '\0'; for (unsigned i = 1; i <= 128; ++i) { - ASSERT_EQ(0, strncmpi(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 0)); - ASSERT_EQ(0, strncmpi(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 1)); + ASSERT_EQ(0, strncmp(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 0)); + ASSERT_EQ(0, strncmp(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 1)); } tfree(s2); tfree(s1); @@ -362,8 +341,8 @@ TEST(strncmp, testNotEqualManyNs) { memset(s2, 7, PAGESIZE); s1[PAGESIZE - 1] = (unsigned char)0; s2[PAGESIZE - 1] = (unsigned char)255; - ASSERT_EQ(-255, strncmpi(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 0)); - ASSERT_EQ(-255, strncmpi(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 1)); + ASSERT_EQ(-255, strncmp(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 0)); + ASSERT_EQ(-255, strncmp(s1 + PAGESIZE - i, s2 + PAGESIZE - i, i + 1)); } tfree(s2); tfree(s1); @@ -379,9 +358,9 @@ TEST(strncmp, testStringNulTerminatesBeforeExplicitLength) { char *rdi = memcpy(tmalloc(sizeof(kRdi)), kRdi, sizeof(kRdi)); char *rsi = memcpy(tmalloc(sizeof(kRsi)), kRsi, sizeof(kRsi)); size_t rdx = 3; - EXPECT_EQ(strncmpi(rdi, rdi, rdx), 0); - EXPECT_LT(strncmpi(rdi, rsi, rdx), 0); - EXPECT_GT(strncmpi(rsi, rdi, rdx), 0); + EXPECT_EQ(strncmp(rdi, rdi, rdx), 0); + EXPECT_LT(strncmp(rdi, rsi, rdx), 0); + EXPECT_GT(strncmp(rsi, rdi, rdx), 0); tfree(rsi); tfree(rdi); } @@ -417,9 +396,9 @@ TEST(strncmp16, testStringNulTerminatesBeforeExplicitLength) { ╚────────────────────────────────────────────────────────────────────────────│*/ TEST(strcmp, testTwosComplementBane_hasUnsignedBehavior) { - EXPECT_EQ(strcmpi("\200", "\200"), 0); - EXPECT_LT(strcmpi("\x7f", "\x80"), 0); - EXPECT_GT(strcmpi("\x80", "\x7f"), 0); + EXPECT_EQ(strcmp("\200", "\200"), 0); + EXPECT_LT(strcmp("\x7f", "\x80"), 0); + EXPECT_GT(strcmp("\x80", "\x7f"), 0); } TEST(strcasecmp, testTwosComplementBane_hasUnsignedBehavior) { @@ -468,7 +447,7 @@ TEST(strncmp16, testTwosComplementBane_hasUnsignedBehavior) { tfree(B1); } -TEST(wcscmp, testTwosComplementBane_hasSignedBehavior) { +TEST(wcscmp, testTwosComplementBane) { wchar_t *B1 = tmalloc(8); wchar_t *B2 = tmalloc(8); B1[1] = L'\0'; @@ -476,28 +455,27 @@ TEST(wcscmp, testTwosComplementBane_hasSignedBehavior) { EXPECT_EQ(wcscmp(memcpy(B1, "\x00\x00\x00\x80", 4), memcpy(B2, "\x00\x00\x00\x80", 4)), 0); - EXPECT_GT(wcscmp(memcpy(B1, "\xff\xff\xff\x7f", 4), - memcpy(B2, "\x00\x00\x00\x80", 4)), - 0); - EXPECT_LT(wcscmp(memcpy(B1, "\x00\x00\x00\x80", 4), + EXPECT_EQ(-1, wcscmp(memcpy(B1, "\xff\xff\xff\x7f", 4), + memcpy(B2, "\x00\x00\x00\x80", 4))); + EXPECT_EQ(wcscmp(memcpy(B1, "\x00\x00\x00\x80", 4), memcpy(B2, "\xff\xff\xff\x7f", 4)), - 0); + 1); tfree(B2); tfree(B1); } -TEST(wcsncmp, testTwosComplementBane_hasSignedBehavior) { +TEST(wcsncmp, testTwosComplementBane) { wchar_t *B1 = tmalloc(4); wchar_t *B2 = tmalloc(4); EXPECT_EQ(wcsncmp(memcpy(B1, "\x00\x00\x00\x80", 4), memcpy(B2, "\x00\x00\x00\x80", 4), 1), 0); - EXPECT_GT(wcsncmp(memcpy(B1, "\xff\xff\xff\x7f", 4), + EXPECT_EQ(wcsncmp(memcpy(B1, "\xff\xff\xff\x7f", 4), memcpy(B2, "\x00\x00\x00\x80", 4), 1), - 0); - EXPECT_LT(wcsncmp(memcpy(B1, "\x00\x00\x00\x80", 4), + -1); + EXPECT_EQ(wcsncmp(memcpy(B1, "\x00\x00\x00\x80", 4), memcpy(B2, "\xff\xff\xff\x7f", 4), 1), - 0); + 1); tfree(B2); tfree(B1); } @@ -555,12 +533,42 @@ BENCH(bench_00_strcmp, bench) { PAGESIZE); data = tgc(tmalloc(size)); dupe = tgc(tmalloc(size)); + + fprintf(stderr, "\n"); EZBENCH2("strcmp [identity]", longstringislong(size, data), EXPROPRIATE(strcmp(VEIL("r", data), data))); + + fprintf(stderr, "\n"); + EZBENCH2("strcmp [2 diff]", donothing, + EXPROPRIATE(strcmp(VEIL("r", "hi"), VEIL("r", "there")))); + EZBENCH2("strcmp$pure [2 diff]", donothing, + EXPROPRIATE(strcmp$pure(VEIL("r", "hi"), VEIL("r", "there")))); + + fprintf(stderr, "\n"); + EZBENCH2("strcmp [2 dupe]", randomize_buf2str_dupe(2, data, dupe), + EXPROPRIATE(strcmp(VEIL("r", data), VEIL("r", dupe)))); + EZBENCH2("strcmp$pure [2 dupe]", randomize_buf2str_dupe(2, data, dupe), + EXPROPRIATE(strcmp$pure(VEIL("r", data), VEIL("r", dupe)))); + + fprintf(stderr, "\n"); + EZBENCH2("strcmp [4 dupe]", randomize_buf2str_dupe(4, data, dupe), + EXPROPRIATE(strcmp(VEIL("r", data), VEIL("r", dupe)))); + EZBENCH2("strcmp$pure [4 dupe]", randomize_buf2str_dupe(4, data, dupe), + EXPROPRIATE(strcmp$pure(VEIL("r", data), VEIL("r", dupe)))); + + fprintf(stderr, "\n"); + EZBENCH2("strcmp [8 dupe]", randomize_buf2str_dupe(8, data, dupe), + EXPROPRIATE(strcmp(VEIL("r", data), VEIL("r", dupe)))); + EZBENCH2("strcmp$pure [8 dupe]", randomize_buf2str_dupe(8, data, dupe), + EXPROPRIATE(strcmp$pure(VEIL("r", data), VEIL("r", dupe)))); + + fprintf(stderr, "\n"); EZBENCH2("strcmp [short dupe]", randomize_buf2str_dupe(size, data, dupe), EXPROPRIATE(strcmp(VEIL("r", data), VEIL("r", dupe)))); EZBENCH2("strcmp$pure [short dupe]", randomize_buf2str_dupe(size, data, dupe), EXPROPRIATE(strcmp$pure(VEIL("r", data), VEIL("r", dupe)))); + + fprintf(stderr, "\n"); EZBENCH2("strcmp [long dupe]", longstringislong_dupe(size, data, dupe), EXPROPRIATE(strcmp(VEIL("r", data), VEIL("r", dupe)))); EZBENCH2("strcmp$pure [long dupe]", longstringislong_dupe(size, data, dupe), @@ -574,13 +582,19 @@ BENCH(bench_01_strcasecmp, bench) { PAGESIZE); data = tgc(tmalloc(size)); dupe = tgc(tmalloc(size)); + + fprintf(stderr, "\n"); EZBENCH2("strcasecmp [identity]", longstringislong(size, data), EXPROPRIATE(strcasecmp(VEIL("r", data), data))); + + fprintf(stderr, "\n"); EZBENCH2("strcasecmp [short dupe]", randomize_buf2str_dupe(size, data, dupe), EXPROPRIATE(strcasecmp(VEIL("r", data), VEIL("r", dupe)))); EZBENCH2("strcasecmp$pure [short dupe]", randomize_buf2str_dupe(size, data, dupe), EXPROPRIATE(strcasecmp$pure(VEIL("r", data), VEIL("r", dupe)))); + + fprintf(stderr, "\n"); EZBENCH2("strcasecmp [long dupe]", longstringislong_dupe(size, data, dupe), EXPROPRIATE(strcasecmp(VEIL("r", data), VEIL("r", dupe)))); EZBENCH2("strcasecmp$pure [long dupe]", diff --git a/test/libc/str/tprecode16to8_test.c b/test/libc/str/tprecode16to8_test.c index 471b70227..53e89eddd 100644 --- a/test/libc/str/tprecode16to8_test.c +++ b/test/libc/str/tprecode16to8_test.c @@ -17,11 +17,50 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" #include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" TEST(tprecode16to8, test) { char b[128]; - EXPECT_EQ(69, tprecode16to8(b, 128, u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_EQ(69, tprecode16to8(b, 128, u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹").ax); EXPECT_STREQ("(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); } + +TEST(tprecode16to8, testEmptyOut_doesNothingButStillCountsSrcLength) { + axdx_t r; + r = tprecode16to8(NULL, 0, u"hi"); + EXPECT_EQ(0, r.ax); + EXPECT_EQ(3, r.dx); +} + +TEST(tprecode16to8, testTooLittle_stillNulTerminates) { + axdx_t r; + char b[2] = {1, 2}; + r = tprecode16to8(b, 2, u"hi"); + EXPECT_EQ(1, r.ax); + EXPECT_EQ(3, r.dx); + EXPECT_EQ('h', b[0]); + EXPECT_EQ(0, b[1]); +} + +TEST(tprecode16to8, testAscii_vectorSpeedupWorks) { + size_t size = 32; + char *buf = tmalloc(size); + EXPECT_EQ(31, + tprecode16to8(buf, size, u"babaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").ax); + EXPECT_STREQ("babaaaaaaaaaaaaaaaaaaaaaaaaaaaa", buf); + tfree(buf); +} + +BENCH(tprecode16to8, bench) { + char *buf8 = malloc(kHyperionSize + 1); + char16_t *buf16 = malloc((kHyperionSize + 1) * 2); + tprecode8to16(buf16, kHyperionSize + 1, kHyperion); + EZBENCH2("tprecode16to8", donothing, + tprecode16to8(buf8, kHyperionSize + 1, buf16)); + free(buf16); + free(buf8); +} diff --git a/test/libc/str/tprecode8to16_test.c b/test/libc/str/tprecode8to16_test.c index 2c449c871..6ba3cb694 100644 --- a/test/libc/str/tprecode8to16_test.c +++ b/test/libc/str/tprecode8to16_test.c @@ -18,19 +18,64 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/mem/mem.h" #include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" TEST(tprecode8to16, test) { size_t size = 8; char16_t *buf = tmalloc(size * sizeof(char16_t)); - EXPECT_EQ(7, tprecode8to16(buf, size, "hello☻♥")); + EXPECT_EQ(7, tprecode8to16(buf, size, "hello☻♥").ax); EXPECT_STREQ(u"hello☻♥", buf); tfree(buf); } +TEST(tprecode8to16, testEmptyOut_doesNothingButStillCountsSrcLength) { + axdx_t r; + r = tprecode8to16(NULL, 0, "hi"); + EXPECT_EQ(0, r.ax); + EXPECT_EQ(3, r.dx); +} + +TEST(tprecode8to16, testOnlyRoomForNul_writesIt) { + axdx_t r; + char16_t b[1] = {1}; + r = tprecode8to16(b, 1, "hi"); + EXPECT_EQ(0, r.ax); + EXPECT_EQ(3, r.dx); + EXPECT_EQ(0, b[0]); +} + +TEST(tprecode8to16, testTooLittle_stillNulTerminates) { + axdx_t r; + char16_t b[2] = {1, 2}; + r = tprecode8to16(b, 2, "hi"); + EXPECT_EQ(1, r.ax); + EXPECT_EQ(3, r.dx); + EXPECT_EQ('h', b[0]); + EXPECT_EQ(0, b[1]); +} + TEST(tprecode8to16, test2) { char16_t b[128]; - EXPECT_EQ(34, tprecode8to16(b, 128, "(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_EQ(34, tprecode8to16(b, 128, "(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹").ax); EXPECT_STREQ(u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); } + +TEST(tprecode8to16, testAscii_vectorSpeedupWorks) { + size_t size = 32; + char16_t *buf = tmalloc(size * sizeof(char16_t)); + EXPECT_EQ(31, + tprecode8to16(buf, size, "babaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").ax); + EXPECT_STREQ(u"babaaaaaaaaaaaaaaaaaaaaaaaaaaaa", buf); + tfree(buf); +} + +BENCH(tprecode8to16, bench) { + char16_t *buf = malloc((kHyperionSize + 1) * 2); + EZBENCH2("tprecode8to16", donothing, + tprecode8to16(buf, kHyperionSize, kHyperion)); + free(buf); +} diff --git a/test/libc/str/undeflate_test.c b/test/libc/str/undeflate_test.c index 89a64e938..7a48ea5b4 100644 --- a/test/libc/str/undeflate_test.c +++ b/test/libc/str/undeflate_test.c @@ -25,7 +25,6 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/gc.h" -#include "libc/runtime/rbx.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/stdio.h" diff --git a/test/tool/build/lib/disinst_test.c b/test/tool/build/lib/disinst_test.c index 55eac8e78..ee44d3ca1 100644 --- a/test/tool/build/lib/disinst_test.c +++ b/test/tool/build/lib/disinst_test.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" #include "libc/stdio/stdio.h" #include "libc/testlib/ezbench.h" diff --git a/third_party/chibicc/LICENSE b/third_party/chibicc/LICENSE new file mode 100644 index 000000000..2d1fd9405 --- /dev/null +++ b/third_party/chibicc/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Rui Ueyama + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c new file mode 100644 index 000000000..8a8e2bc29 --- /dev/null +++ b/third_party/chibicc/chibicc.c @@ -0,0 +1,720 @@ +#include "third_party/chibicc/chibicc.h" + +typedef enum { + FILE_NONE, + FILE_C, + FILE_ASM, + FILE_OBJ, + FILE_AR, + FILE_DSO, +} FileType; + +StringArray include_paths; +bool opt_fcommon = true; +bool opt_fpic; + +static FileType opt_x; +static StringArray opt_include; +static bool opt_E; +static bool opt_M; +static bool opt_MD; +static bool opt_MMD; +static bool opt_MP; +static bool opt_S; +static bool opt_c; +static bool opt_cc1; +static bool opt_hash_hash_hash; +static bool opt_static; +static bool opt_shared; +static char *opt_MF; +static char *opt_MT; +static char *opt_o; + +static StringArray ld_extra_args; +static StringArray std_include_paths; + +char *base_file; +static char *output_file; + +static StringArray input_paths; + +static char **tmpfiles; + +static void usage(int status) { + fprintf(stderr, "chibicc [ -o ] \n"); + exit(status); +} + +static bool take_arg(char *arg) { + char *x[] = { + "-o", "-I", "-idirafter", "-include", "-x", "-MF", "-MT", "-Xlinker", + }; + + for (int i = 0; i < sizeof(x) / sizeof(*x); i++) + if (!strcmp(arg, x[i])) return true; + return false; +} + +static void add_default_include_paths(char *argv0) { + // We expect that chibicc-specific include files are installed + // to ./include relative to argv[0]. + char *buf = calloc(1, strlen(argv0) + 10); + sprintf(buf, "%s/include", dirname(strdup(argv0))); + strarray_push(&include_paths, buf); + + // Add standard include paths. + strarray_push(&include_paths, "."); + + // Keep a copy of the standard include paths for -MMD option. + for (int i = 0; i < include_paths.len; i++) + strarray_push(&std_include_paths, include_paths.data[i]); +} + +static void define(char *str) { + char *eq = strchr(str, '='); + if (eq) + define_macro(strndup(str, eq - str), eq + 1); + else + define_macro(str, "1"); +} + +static FileType parse_opt_x(char *s) { + if (!strcmp(s, "c")) return FILE_C; + if (!strcmp(s, "assembler")) return FILE_ASM; + if (!strcmp(s, "none")) return FILE_NONE; + error(": unknown argument for -x: %s", s); +} + +static char *quote_makefile(char *s) { + char *buf = calloc(1, strlen(s) * 2 + 1); + + for (int i = 0, j = 0; s[i]; i++) { + switch (s[i]) { + case '$': + buf[j++] = '$'; + buf[j++] = '$'; + break; + case '#': + buf[j++] = '\\'; + buf[j++] = '#'; + break; + case ' ': + case '\t': + for (int k = i - 1; k >= 0 && s[k] == '\\'; k--) buf[j++] = '\\'; + buf[j++] = '\\'; + buf[j++] = s[i]; + break; + default: + buf[j++] = s[i]; + break; + } + } + return buf; +} + +static void parse_args(int argc, char **argv) { + // Make sure that all command line options that take an argument + // have an argument. + for (int i = 1; i < argc; i++) + if (take_arg(argv[i])) + if (!argv[++i]) usage(1); + + StringArray idirafter = {}; + + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-###")) { + opt_hash_hash_hash = true; + continue; + } + + if (!strcmp(argv[i], "-cc1")) { + opt_cc1 = true; + continue; + } + + if (!strcmp(argv[i], "--help")) usage(0); + + if (!strcmp(argv[i], "-o")) { + opt_o = argv[++i]; + continue; + } + + if (!strncmp(argv[i], "-o", 2)) { + opt_o = argv[i] + 2; + continue; + } + + if (!strcmp(argv[i], "-S")) { + opt_S = true; + continue; + } + + if (!strcmp(argv[i], "-fcommon")) { + opt_fcommon = true; + continue; + } + + if (!strcmp(argv[i], "-fno-common")) { + opt_fcommon = false; + continue; + } + + if (!strcmp(argv[i], "-c")) { + opt_c = true; + continue; + } + + if (!strcmp(argv[i], "-E")) { + opt_E = true; + continue; + } + + if (!strncmp(argv[i], "-I", 2)) { + strarray_push(&include_paths, argv[i] + 2); + continue; + } + + if (!strcmp(argv[i], "-D")) { + define(argv[++i]); + continue; + } + + if (!strncmp(argv[i], "-D", 2)) { + define(argv[i] + 2); + continue; + } + + if (!strcmp(argv[i], "-U")) { + undef_macro(argv[++i]); + continue; + } + + if (!strncmp(argv[i], "-U", 2)) { + undef_macro(argv[i] + 2); + continue; + } + + if (!strcmp(argv[i], "-include")) { + strarray_push(&opt_include, argv[++i]); + continue; + } + + if (!strcmp(argv[i], "-x")) { + opt_x = parse_opt_x(argv[++i]); + continue; + } + + if (!strncmp(argv[i], "-x", 2)) { + opt_x = parse_opt_x(argv[i] + 2); + continue; + } + + if (!strncmp(argv[i], "-l", 2) || !strncmp(argv[i], "-Wl,", 4)) { + strarray_push(&input_paths, argv[i]); + continue; + } + + if (!strcmp(argv[i], "-Xlinker")) { + strarray_push(&ld_extra_args, argv[++i]); + continue; + } + + if (!strcmp(argv[i], "-s")) { + strarray_push(&ld_extra_args, "-s"); + continue; + } + + if (!strcmp(argv[i], "-M")) { + opt_M = true; + continue; + } + + if (!strcmp(argv[i], "-MF")) { + opt_MF = argv[++i]; + continue; + } + + if (!strcmp(argv[i], "-MP")) { + opt_MP = true; + continue; + } + + if (!strcmp(argv[i], "-MT")) { + if (opt_MT == NULL) + opt_MT = argv[++i]; + else + opt_MT = format("%s %s", opt_MT, argv[++i]); + continue; + } + + if (!strcmp(argv[i], "-MD")) { + opt_MD = true; + continue; + } + + if (!strcmp(argv[i], "-MQ")) { + if (opt_MT == NULL) + opt_MT = quote_makefile(argv[++i]); + else + opt_MT = format("%s %s", opt_MT, quote_makefile(argv[++i])); + continue; + } + + if (!strcmp(argv[i], "-MMD")) { + opt_MD = opt_MMD = true; + continue; + } + + if (!strcmp(argv[i], "-fpic") || !strcmp(argv[i], "-fPIC")) { + opt_fpic = true; + continue; + } + + if (!strcmp(argv[i], "-cc1-input")) { + base_file = argv[++i]; + continue; + } + + if (!strcmp(argv[i], "-cc1-output")) { + output_file = argv[++i]; + continue; + } + + if (!strcmp(argv[i], "-idirafter")) { + strarray_push(&idirafter, argv[i++]); + continue; + } + + if (!strcmp(argv[i], "-static")) { + opt_static = true; + strarray_push(&ld_extra_args, "-static"); + continue; + } + + if (!strcmp(argv[i], "-shared")) { + opt_shared = true; + strarray_push(&ld_extra_args, "-shared"); + continue; + } + + if (!strcmp(argv[i], "-L")) { + strarray_push(&ld_extra_args, "-L"); + strarray_push(&ld_extra_args, argv[++i]); + continue; + } + + if (!strncmp(argv[i], "-L", 2)) { + strarray_push(&ld_extra_args, "-L"); + strarray_push(&ld_extra_args, argv[i] + 2); + continue; + } + + if (!strcmp(argv[i], "-hashmap-test")) { + hashmap_test(); + exit(0); + } + + // These options are ignored for now. + if (!strncmp(argv[i], "-O", 2) || !strncmp(argv[i], "-W", 2) || + !strncmp(argv[i], "-g", 2) || !strncmp(argv[i], "-std=", 5) || + !strcmp(argv[i], "-ffreestanding") || + !strcmp(argv[i], "-fno-builtin") || + !strcmp(argv[i], "-fno-omit-frame-pointer") || + !strcmp(argv[i], "-fno-stack-protector") || + !strcmp(argv[i], "-fno-strict-aliasing") || !strcmp(argv[i], "-m64") || + !strcmp(argv[i], "-mno-red-zone") || !strcmp(argv[i], "-w")) + continue; + + if (argv[i][0] == '-' && argv[i][1] != '\0') + error("unknown argument: %s", argv[i]); + + strarray_push(&input_paths, argv[i]); + } + + for (int i = 0; i < idirafter.len; i++) + strarray_push(&include_paths, idirafter.data[i]); + + if (input_paths.len == 0) error("no input files"); + + // -E implies that the input is the C macro language. + if (opt_E) opt_x = FILE_C; +} + +static FILE *open_file(char *path) { + if (!path || strcmp(path, "-") == 0) return stdout; + + FILE *out = fopen(path, "w"); + if (!out) error("cannot open output file: %s: %s", path, strerror(errno)); + return out; +} + +static bool ends_with(char *p, char *q) { + int len1 = strlen(p); + int len2 = strlen(q); + return (len1 >= len2) && !strcmp(p + len1 - len2, q); +} + +// Replace file extension +static char *replace_extn(char *tmpl, char *extn) { + char *filename = basename(strdup(tmpl)); + int len1 = strlen(filename); + int len2 = strlen(extn); + char *buf = calloc(1, len1 + len2 + 2); + + char *dot = strrchr(filename, '.'); + if (dot) *dot = '\0'; + sprintf(buf, "%s%s", filename, extn); + return buf; +} + +static void cleanup(void) { + if (tmpfiles) + for (int i = 0; tmpfiles[i]; i++) unlink(tmpfiles[i]); +} + +static char *create_tmpfile(void) { + char tmpl[] = "/tmp/chibicc-XXXXXX"; + char *path = calloc(1, sizeof(tmpl)); + memcpy(path, tmpl, sizeof(tmpl)); + + int fd = mkstemp(path); + if (fd == -1) error("mkstemp failed: %s", strerror(errno)); + close(fd); + + static int len = 2; + tmpfiles = realloc(tmpfiles, sizeof(char *) * len); + tmpfiles[len - 2] = path; + tmpfiles[len - 1] = NULL; + len++; + + return path; +} + +static void run_subprocess(char **argv) { + // If -### is given, dump the subprocess's command line. + if (opt_hash_hash_hash) { + fprintf(stderr, "%s", argv[0]); + for (int i = 1; argv[i]; i++) fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + + if (fork() == 0) { + // Child process. Run a new command. + execvp(argv[0], argv); + fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + + // Wait for the child process to finish. + int status; + while (wait(&status) > 0) + ; + if (status != 0) exit(1); +} + +static void run_cc1(int argc, char **argv, char *input, char *output) { + char **args = calloc(argc + 10, sizeof(char *)); + memcpy(args, argv, argc * sizeof(char *)); + args[argc++] = "-cc1"; + + if (input) { + args[argc++] = "-cc1-input"; + args[argc++] = input; + } + + if (output) { + args[argc++] = "-cc1-output"; + args[argc++] = output; + } + + run_subprocess(args); +} + +// Print tokens to stdout. Used for -E. +static void print_tokens(Token *tok) { + FILE *out = open_file(opt_o ? opt_o : "-"); + + int line = 1; + for (; tok->kind != TK_EOF; tok = tok->next) { + if (line > 1 && tok->at_bol) fprintf(out, "\n"); + if (tok->has_space && !tok->at_bol) fprintf(out, " "); + fprintf(out, "%.*s", tok->len, tok->loc); + line++; + } + fprintf(out, "\n"); +} + +static bool in_std_include_path(char *path) { + for (int i = 0; i < std_include_paths.len; i++) { + char *dir = std_include_paths.data[i]; + int len = strlen(dir); + if (strncmp(dir, path, len) == 0 && path[len] == '/') return true; + } + return false; +} + +// If -M options is given, the compiler write a list of input files to +// stdout in a format that "make" command can read. This feature is +// used to automate file dependency management. +static void print_dependencies(void) { + char *path; + if (opt_MF) + path = opt_MF; + else if (opt_MD) + path = replace_extn(opt_o ? opt_o : base_file, ".d"); + else if (opt_o) + path = opt_o; + else + path = "-"; + + FILE *out = open_file(path); + if (opt_MT) + fprintf(out, "%s:", opt_MT); + else + fprintf(out, "%s:", quote_makefile(replace_extn(base_file, ".o"))); + + File **files = get_input_files(); + + for (int i = 0; files[i]; i++) { + if (opt_MMD && in_std_include_path(files[i]->name)) continue; + fprintf(out, " \\\n %s", files[i]->name); + } + + fprintf(out, "\n\n"); + + if (opt_MP) { + for (int i = 1; files[i]; i++) { + if (opt_MMD && in_std_include_path(files[i]->name)) continue; + fprintf(out, "%s:\n\n", quote_makefile(files[i]->name)); + } + } +} + +static Token *must_tokenize_file(char *path) { + Token *tok = tokenize_file(path); + if (!tok) error("%s: %s", path, strerror(errno)); + return tok; +} + +static Token *append_tokens(Token *tok1, Token *tok2) { + if (!tok1 || tok1->kind == TK_EOF) return tok2; + + Token *t = tok1; + while (t->next->kind != TK_EOF) t = t->next; + t->next = tok2; + return tok1; +} + +static void cc1(void) { + Token *tok = NULL; + + // Process -include option + for (int i = 0; i < opt_include.len; i++) { + char *incl = opt_include.data[i]; + + char *path; + if (file_exists(incl)) { + path = incl; + } else { + path = search_include_paths(incl); + if (!path) error("-include: %s: %s", incl, strerror(errno)); + } + + Token *tok2 = must_tokenize_file(path); + tok = append_tokens(tok, tok2); + } + + // Tokenize and parse. + Token *tok2 = must_tokenize_file(base_file); + tok = append_tokens(tok, tok2); + tok = preprocess(tok); + + // If -M or -MD are given, print file dependencies. + if (opt_M || opt_MD) { + print_dependencies(); + if (opt_M) return; + } + + // If -E is given, print out preprocessed C code as a result. + if (opt_E) { + print_tokens(tok); + return; + } + + Obj *prog = parse(tok); + + // Traverse the AST to emit assembly. + FILE *out = open_file(output_file); + codegen(prog, out); + fclose(out); +} + +static void assemble(char *input, char *output) { + char *cmd[] = {"as", "-W", "-I.", "-c", input, "-o", output, NULL}; + run_subprocess(cmd); +} + +static void run_linker(StringArray *inputs, char *output) { + StringArray arr = {}; + + strarray_push(&arr, "ld"); + strarray_push(&arr, "-o"); + strarray_push(&arr, output); + strarray_push(&arr, "-m"); + strarray_push(&arr, "elf_x86_64"); + + if (opt_shared) { + strarray_push(&arr, "/usr/lib/x86_64-linux-gnu/crti.o"); + strarray_push(&arr, "/usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o"); + } else { + strarray_push(&arr, "/usr/lib/x86_64-linux-gnu/crt1.o"); + strarray_push(&arr, "/usr/lib/x86_64-linux-gnu/crti.o"); + strarray_push(&arr, "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o"); + } + + strarray_push(&arr, "-L/usr/lib/gcc/x86_64-linux-gnu/9"); + strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu"); + strarray_push(&arr, "-L/usr/lib64"); + strarray_push(&arr, "-L/lib/x86_64-linux-gnu"); + strarray_push(&arr, "-L/lib64"); + strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu"); + strarray_push(&arr, "-L/usr/lib"); + strarray_push(&arr, "-L/lib"); + + if (!opt_static) { + strarray_push(&arr, "-dynamic-linker"); + strarray_push(&arr, "/lib64/ld-linux-x86-64.so.2"); + } + + for (int i = 0; i < ld_extra_args.len; i++) + strarray_push(&arr, ld_extra_args.data[i]); + + for (int i = 0; i < inputs->len; i++) strarray_push(&arr, inputs->data[i]); + + if (opt_static) { + strarray_push(&arr, "--start-group"); + strarray_push(&arr, "-lgcc"); + strarray_push(&arr, "-lgcc_eh"); + strarray_push(&arr, "-lc"); + strarray_push(&arr, "--end-group"); + } else { + strarray_push(&arr, "-lc"); + strarray_push(&arr, "-lgcc"); + strarray_push(&arr, "--as-needed"); + strarray_push(&arr, "-lgcc_s"); + strarray_push(&arr, "--no-as-needed"); + } + + if (opt_shared) + strarray_push(&arr, "/usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o"); + else + strarray_push(&arr, "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o"); + + strarray_push(&arr, "/usr/lib/x86_64-linux-gnu/crtn.o"); + strarray_push(&arr, NULL); + + run_subprocess(arr.data); +} + +static FileType get_file_type(char *filename) { + if (opt_x != FILE_NONE) return opt_x; + + if (ends_with(filename, ".a")) return FILE_AR; + if (ends_with(filename, ".so")) return FILE_DSO; + if (ends_with(filename, ".o")) return FILE_OBJ; + if (ends_with(filename, ".c")) return FILE_C; + if (ends_with(filename, ".s")) return FILE_ASM; + + error(": unknown file extension: %s", filename); +} + +int main(int argc, char **argv) { + atexit(cleanup); + init_macros(); + parse_args(argc, argv); + + if (opt_cc1) { + add_default_include_paths(argv[0]); + cc1(); + return 0; + } + + if (input_paths.len > 1 && opt_o && (opt_c || opt_S | opt_E)) + error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files"); + + StringArray ld_args = {}; + + for (int i = 0; i < input_paths.len; i++) { + char *input = input_paths.data[i]; + + if (!strncmp(input, "-l", 2)) { + strarray_push(&ld_args, input); + continue; + } + + if (!strncmp(input, "-Wl,", 4)) { + char *s = strdup(input + 4); + char *arg = strtok(s, ","); + while (arg) { + strarray_push(&ld_args, arg); + arg = strtok(NULL, ","); + } + continue; + } + + char *output; + if (opt_o) + output = opt_o; + else if (opt_S) + output = replace_extn(input, ".s"); + else + output = replace_extn(input, ".o"); + + FileType type = get_file_type(input); + + // Handle .o or .a + if (type == FILE_OBJ || type == FILE_AR || type == FILE_DSO) { + strarray_push(&ld_args, input); + continue; + } + + // Handle .s + if (type == FILE_ASM) { + if (!opt_S) assemble(input, output); + continue; + } + + assert(type == FILE_C); + + // Just preprocess + if (opt_E || opt_M) { + run_cc1(argc, argv, input, NULL); + continue; + } + + // Compile + if (opt_S) { + run_cc1(argc, argv, input, output); + continue; + } + + // Compile and assemble + if (opt_c) { + char *tmp = create_tmpfile(); + run_cc1(argc, argv, input, tmp); + assemble(tmp, output); + continue; + } + + // Compile, assemble and link + char *tmp1 = create_tmpfile(); + char *tmp2 = create_tmpfile(); + run_cc1(argc, argv, input, tmp1); + assemble(tmp1, tmp2); + strarray_push(&ld_args, tmp2); + continue; + } + + if (ld_args.len > 0) run_linker(&ld_args, opt_o ? opt_o : "a.out"); + return 0; +} diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h new file mode 100644 index 000000000..91c059f69 --- /dev/null +++ b/third_party/chibicc/chibicc.h @@ -0,0 +1,474 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ +#define COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define _POSIX_C_SOURCE 200809L + +#include "libc/assert.h" +#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/weirdtypes.h" +#include "libc/conv/conv.h" +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/stdio/temp.h" +#include "libc/str/str.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" +#include "libc/unicode/unicode.h" +#include "libc/x/x.h" +#include "third_party/gdtoa/gdtoa.h" + +#pragma GCC diagnostic ignored "-Wswitch" + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +typedef struct Type Type; +typedef struct Node Node; +typedef struct Member Member; +typedef struct Relocation Relocation; +typedef struct Hideset Hideset; + +// +// strarray.c +// + +typedef struct { + char **data; + int capacity; + int len; +} StringArray; + +void strarray_push(StringArray *arr, char *s); + +// +// tokenize.c +// + +// Token +typedef enum { + TK_RESERVED, // Keywords or punctuators + TK_IDENT, // Identifiers + TK_STR, // String literals + TK_NUM, // Numeric literals + TK_PP_NUM, // Preprocessing numbers + TK_EOF, // End-of-file markers +} TokenKind; + +typedef struct { + char *name; + int file_no; + char *contents; + + // For #line directive + char *display_name; + int line_delta; +} File; + +// Token type +typedef struct Token Token; +struct Token { + TokenKind kind; // Token kind + Token *next; // Next token + int64_t val; // If kind is TK_NUM, its value + long double fval; // If kind is TK_NUM, its value + char *loc; // Token location + int len; // Token length + Type *ty; // Used if TK_NUM or TK_STR + char *str; // String literal contents including terminating '\0' + + File *file; // Source location + char *filename; // Filename + int line_no; // Line number + int line_delta; // Line number + bool at_bol; // True if this token is at beginning of line + bool has_space; // True if this token follows a space character + Hideset *hideset; // For macro expansion + Token *origin; // If this is expanded from a macro, the original token +}; + +noreturn void error(char *fmt, ...) __attribute__((format(printf, 1, 2))); +noreturn void error_at(char *loc, char *fmt, ...) + __attribute__((format(printf, 2, 3))); +noreturn void error_tok(Token *tok, char *fmt, ...) + __attribute__((format(printf, 2, 3))); +void warn_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3))); +bool equal(Token *tok, char *op); +Token *skip(Token *tok, char *op); +bool consume(Token **rest, Token *tok, char *str); +void convert_pp_tokens(Token *tok); +File **get_input_files(void); +File *new_file(char *name, int file_no, char *contents); +Token *tokenize_string_literal(Token *tok, Type *basety); +Token *tokenize(File *file); +Token *tokenize_file(char *filename); + +#define UNREACHABLE() error("internal error at %s:%d", __FILE__, __LINE__) + +// +// preprocess.c +// + +char *format(char *fmt, ...); +char *search_include_paths(char *filename); +bool file_exists(char *path); +void init_macros(void); +void define_macro(char *name, char *buf); +void undef_macro(char *name); +Token *preprocess(Token *tok); + +// +// parse.c +// + +// Variable or function +typedef struct Obj Obj; +struct Obj { + Obj *next; + char *name; // Variable name + Type *ty; // Type + Token *tok; // representative token + bool is_local; // local or global/function + int align; // alignment + + // Local variable + int offset; + + // Global variable or function + bool is_function; + bool is_definition; + bool is_static; + + // Global variable + bool is_tentative; + bool is_tls; + char *init_data; + Relocation *rel; + + // Function + bool is_inline; + Obj *params; + Node *body; + Obj *locals; + Obj *va_area; + Obj *alloca_bottom; + int stack_size; + + // Static inline function + bool is_live; + bool is_root; + StringArray refs; +}; + +// Global variable can be initialized either by a constant expression +// or a pointer to another global variable. This struct represents the +// latter. +typedef struct Relocation Relocation; +struct Relocation { + Relocation *next; + int offset; + char **label; + long addend; +}; + +// AST node +typedef enum { + ND_NULL_EXPR, // Do nothing + ND_ADD, // + + ND_SUB, // - + ND_MUL, // * + ND_DIV, // / + ND_NEG, // unary - + ND_MOD, // % + ND_BITAND, // & + ND_BITOR, // | + ND_BITXOR, // ^ + ND_SHL, // << + ND_SHR, // >> + ND_EQ, // == + ND_NE, // != + ND_LT, // < + ND_LE, // <= + ND_ASSIGN, // = + ND_COND, // ?: + ND_COMMA, // , + ND_MEMBER, // . (struct member access) + ND_ADDR, // unary & + ND_DEREF, // unary * + ND_NOT, // ! + ND_BITNOT, // ~ + ND_LOGAND, // && + ND_LOGOR, // || + ND_RETURN, // "return" + ND_IF, // "if" + ND_FOR, // "for" or "while" + ND_DO, // "do" + ND_SWITCH, // "switch" + ND_CASE, // "case" + ND_BLOCK, // { ... } + ND_GOTO, // "goto" + ND_GOTO_EXPR, // "goto" labels-as-values + ND_LABEL, // Labeled statement + ND_LABEL_VAL, // [GNU] Labels-as-values + ND_FUNCALL, // Function call + ND_EXPR_STMT, // Expression statement + ND_STMT_EXPR, // Statement expression + ND_VAR, // Variable + ND_VLA_PTR, // VLA designator + ND_NUM, // Integer + ND_CAST, // Type cast + ND_MEMZERO, // Zero-clear a stack variable + ND_ASM, // "asm" + ND_CAS, // Atomic compare-and-swap + ND_EXCH, // Atomic exchange +} NodeKind; + +// AST node type +struct Node { + NodeKind kind; // Node kind + Node *next; // Next node + Type *ty; // Type, e.g. int or pointer to int + Token *tok; // Representative token + + Node *lhs; // Left-hand side + Node *rhs; // Right-hand side + + // "if" or "for" statement + Node *cond; + Node *then; + Node *els; + Node *init; + Node *inc; + + // "break" and "continue" labels + char *brk_label; + char *cont_label; + + // Block or statement expression + Node *body; + + // Struct member access + Member *member; + + // Function call + Type *func_ty; + Node *args; + bool pass_by_stack; + Obj *ret_buffer; + + // Goto or labeled statement, or labels-as-values + char *label; + char *unique_label; + Node *goto_next; + + // Switch + Node *case_next; + Node *default_case; + + // Case + long begin; + long end; + + // "asm" string literal + char *asm_str; + + // Atomic compare-and-swap + Node *cas_addr; + Node *cas_old; + Node *cas_new; + + // Atomic op= operators + Obj *atomic_addr; + Node *atomic_expr; + + // Variable + Obj *var; + + // Numeric literal + int64_t val; + long double fval; +}; + +Node *new_cast(Node *expr, Type *ty); +int64_t const_expr(Token **rest, Token *tok); +Obj *parse(Token *tok); + +// +// type.c +// + +typedef enum { + TY_VOID, + TY_BOOL, + TY_CHAR, + TY_SHORT, + TY_INT, + TY_LONG, + TY_FLOAT, + TY_DOUBLE, + TY_LDOUBLE, + TY_ENUM, + TY_PTR, + TY_FUNC, + TY_ARRAY, + TY_VLA, // variable-length array + TY_STRUCT, + TY_UNION, +} TypeKind; + +struct Type { + TypeKind kind; + int size; // sizeof() value + int align; // alignment + bool is_unsigned; // unsigned or signed + bool is_atomic; // true if _Atomic + Type *origin; // for type compatibility check + + // Pointer-to or array-of type. We intentionally use the same member + // to represent pointer/array duality in C. + // + // In many contexts in which a pointer is expected, we examine this + // member instead of "kind" member to determine whether a type is a + // pointer or not. That means in many contexts "array of T" is + // naturally handled as if it were "pointer to T", as required by + // the C spec. + Type *base; + + // Declaration + Token *name; + Token *name_pos; + + // Array + int array_len; + + // Variable-length array + Node *vla_len; // # of elements + Obj *vla_size; // sizeof() value + + // Struct + Member *members; + bool is_flexible; + bool is_packed; + + // Function type + Type *return_ty; + Type *params; + bool is_variadic; + Type *next; +}; + +// Struct member +struct Member { + Member *next; + Type *ty; + Token *tok; // for error message + Token *name; + int idx; + int align; + int offset; + + // Bitfield + bool is_bitfield; + int bit_offset; + int bit_width; +}; + +extern Type *ty_void; +extern Type *ty_bool; + +extern Type *ty_char; +extern Type *ty_short; +extern Type *ty_int; +extern Type *ty_long; + +extern Type *ty_uchar; +extern Type *ty_ushort; +extern Type *ty_uint; +extern Type *ty_ulong; + +extern Type *ty_float; +extern Type *ty_double; +extern Type *ty_ldouble; + +bool is_integer(Type *ty); +bool is_flonum(Type *ty); +bool is_numeric(Type *ty); +bool is_compatible(Type *t1, Type *t2); +Type *copy_type(Type *ty); +Type *pointer_to(Type *base); +Type *func_type(Type *return_ty); +Type *array_of(Type *base, int size); +Type *vla_of(Type *base, Node *expr); +Type *enum_type(void); +Type *struct_type(void); +void add_type(Node *node); + +// +// codegen.c +// + +void codegen(Obj *prog, FILE *out); +int align_to(int n, int align); + +// +// unicode.c +// + +int encode_utf8(char *buf, uint32_t c); +uint32_t decode_utf8(char **new_pos, char *p); +bool is_ident1(uint32_t c); +bool is_ident2(uint32_t c); +int str_width(char *p, int len); + +// +// hashmap.c +// + +typedef struct { + char *key; + int keylen; + void *val; +} HashEntry; + +typedef struct { + HashEntry *buckets; + int capacity; + int used; +} HashMap; + +void *hashmap_get(HashMap *map, char *key); +void *hashmap_get2(HashMap *map, char *key, int keylen); +void hashmap_put(HashMap *map, char *key, void *val); +void hashmap_put2(HashMap *map, char *key, int keylen, void *val); +void hashmap_delete(HashMap *map, char *key); +void hashmap_delete2(HashMap *map, char *key, int keylen); +void hashmap_test(void); + +// +// main.c +// + +extern StringArray include_paths; +extern bool opt_fpic; +extern bool opt_fcommon; +extern char *base_file; + +typedef struct StaticAsm { + struct StaticAsm *next; + Node *body; +} StaticAsm; + +extern struct StaticAsm *staticasms; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ */ diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk new file mode 100644 index 000000000..000f5ccff --- /dev/null +++ b/third_party/chibicc/chibicc.mk @@ -0,0 +1,78 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += THIRD_PARTY_CHIBICC + +THIRD_PARTY_CHIBICC_ARTIFACTS += THIRD_PARTY_CHIBICC_A +THIRD_PARTY_CHIBICC = $(THIRD_PARTY_CHIBICC_A_DEPS) $(THIRD_PARTY_CHIBICC_A) +THIRD_PARTY_CHIBICC_A = o/$(MODE)/third_party/chibicc/chibicc.a +THIRD_PARTY_CHIBICC_A_FILES := $(wildcard third_party/chibicc/*) +THIRD_PARTY_CHIBICC_A_HDRS = $(filter %.h,$(THIRD_PARTY_CHIBICC_A_FILES)) +THIRD_PARTY_CHIBICC_A_SRCS_S = $(filter %.S,$(THIRD_PARTY_CHIBICC_A_FILES)) +THIRD_PARTY_CHIBICC_A_SRCS_C = $(filter %.c,$(THIRD_PARTY_CHIBICC_A_FILES)) + +THIRD_PARTY_CHIBICC_BINS = \ + o/$(MODE)/third_party/chibicc/chibicc.com + +THIRD_PARTY_CHIBICC_A_SRCS = \ + $(THIRD_PARTY_CHIBICC_A_SRCS_S) \ + $(THIRD_PARTY_CHIBICC_A_SRCS_C) + +THIRD_PARTY_CHIBICC_A_OBJS = \ + $(THIRD_PARTY_CHIBICC_A_SRCS:%=o/$(MODE)/%.zip.o) \ + $(THIRD_PARTY_CHIBICC_A_SRCS_S:%.S=o/$(MODE)/%.o) \ + $(THIRD_PARTY_CHIBICC_A_SRCS_C:%.c=o/$(MODE)/%.o) + +THIRD_PARTY_CHIBICC_A_CHECKS = \ + $(THIRD_PARTY_CHIBICC_A).pkg \ + $(THIRD_PARTY_CHIBICC_A_HDRS:%=o/$(MODE)/%.ok) + +THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_FMT \ + LIBC_NEXGEN32E \ + LIBC_UNICODE \ + LIBC_STDIO \ + LIBC_MEM \ + LIBC_LOG \ + LIBC_CALLS \ + LIBC_CALLS_HEFTY \ + LIBC_TIME \ + LIBC_X \ + LIBC_CONV \ + LIBC_RUNTIME \ + THIRD_PARTY_GDTOA + +THIRD_PARTY_CHIBICC_A_DEPS := \ + $(call uniq,$(foreach x,$(THIRD_PARTY_CHIBICC_A_DIRECTDEPS),$($(x)))) + +$(THIRD_PARTY_CHIBICC_A): \ + third_party/chibicc/ \ + $(THIRD_PARTY_CHIBICC_A).pkg \ + $(THIRD_PARTY_CHIBICC_A_OBJS) + +$(THIRD_PARTY_CHIBICC_A).pkg: \ + $(THIRD_PARTY_CHIBICC_A_OBJS) \ + $(foreach x,$(THIRD_PARTY_CHIBICC_A_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/third_party/chibicc/%.com.dbg: \ + $(THIRD_PARTY_CHIBICC_A_DEPS) \ + $(THIRD_PARTY_CHIBICC_A) \ + o/$(MODE)/third_party/chibicc/%.o \ + $(THIRD_PARTY_CHIBICC_A).pkg \ + $(CRT) \ + $(APE) + @$(APELINK) + +THIRD_PARTY_CHIBICC_LIBS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x))) +THIRD_PARTY_CHIBICC_SRCS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x)_SRCS)) +THIRD_PARTY_CHIBICC_HDRS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x)_HDRS)) +THIRD_PARTY_CHIBICC_CHECKS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x)_CHECKS)) +THIRD_PARTY_CHIBICC_OBJS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x)_OBJS)) +$(THIRD_PARTY_CHIBICC_OBJS): $(BUILD_FILES) third_party/chibicc/chibicc.mk + +.PHONY: o/$(MODE)/third_party/chibicc +o/$(MODE)/third_party/chibicc: \ + $(THIRD_PARTY_CHIBICC_BINS) \ + $(THIRD_PARTY_CHIBICC_CHECKS) diff --git a/third_party/chibicc/codegen.c b/third_party/chibicc/codegen.c new file mode 100644 index 000000000..bc9375b7f --- /dev/null +++ b/third_party/chibicc/codegen.c @@ -0,0 +1,1590 @@ +#include "third_party/chibicc/chibicc.h" + +#define GP_MAX 6 +#define FP_MAX 8 + +static FILE *output_file; +static int depth; +static char *argreg8[] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"}; +static char *argreg16[] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"}; +static char *argreg32[] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"}; +static char *argreg64[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"}; +static Obj *current_fn; + +static void gen_expr(Node *node); +static void gen_stmt(Node *node); + +__attribute__((format(printf, 1, 2))) static void println(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(output_file, fmt, ap); + fprintf(output_file, "\n"); +} + +static int count(void) { + static int i = 1; + return i++; +} + +static void push(void) { + println(" push %%rax"); + depth++; +} + +static void pop(char *arg) { + println(" pop %s", arg); + depth--; +} + +static void pushf(void) { + println(" sub $8, %%rsp"); + println(" movsd %%xmm0, (%%rsp)"); + depth++; +} + +static void popf(int reg) { + println(" movsd (%%rsp), %%xmm%d", reg); + println(" add $8, %%rsp"); + depth--; +} + +// Round up `n` to the nearest multiple of `align`. For instance, +// align_to(5, 8) returns 8 and align_to(11, 8) returns 16. +int align_to(int n, int align) { + return (n + align - 1) / align * align; +} + +static char *reg_dx(int sz) { + switch (sz) { + case 1: + return "%dl"; + case 2: + return "%dx"; + case 4: + return "%edx"; + case 8: + return "%rdx"; + } + UNREACHABLE(); +} + +static char *reg_ax(int sz) { + switch (sz) { + case 1: + return "%al"; + case 2: + return "%ax"; + case 4: + return "%eax"; + case 8: + return "%rax"; + } + UNREACHABLE(); +} + +// Compute the absolute address of a given node. +// It's an error if a given node does not reside in memory. +static void gen_addr(Node *node) { + switch (node->kind) { + case ND_VAR: + // Variable-length array, which is always local. + if (node->var->ty->kind == TY_VLA) { + println(" mov %d(%%rbp), %%rax", node->var->offset); + return; + } + + // Local variable + if (node->var->is_local) { + println(" lea %d(%%rbp), %%rax", node->var->offset); + return; + } + + if (opt_fpic) { + // Thread-local variable + if (node->var->is_tls) { + println(" data16 lea %s@tlsgd(%%rip), %%rdi", node->var->name); + println(" .value 0x6666"); + println(" rex64"); + println(" call __tls_get_addr@PLT"); + return; + } + + // Function or global variable + println(" mov %s@GOTPCREL(%%rip), %%rax", node->var->name); + return; + } + + // Thread-local variable + if (node->var->is_tls) { + println(" mov %%fs:0, %%rax"); + println(" add $%s@tpoff, %%rax", node->var->name); + return; + } + + // Here, we generate an absolute address of a function or a global + // variable. Even though they exist at a certain address at runtime, + // their addresses are not known at link-time for the following + // two reasons. + // + // - Address randomization: Executables are loaded to memory as a + // whole but it is not known what address they are loaded to. + // Therefore, at link-time, relative address in the same + // exectuable (i.e. the distance between two functions in the + // same executable) is known, but the absolute address is not + // known. + // + // - Dynamic linking: Dynamic shared objects (DSOs) or .so files + // are loaded to memory alongside an executable at runtime and + // linked by the runtime loader in memory. We know nothing + // about addresses of global stuff that may be defined by DSOs + // until the runtime relocation is complete. + // + // In order to deal with the former case, we use RIP-relative + // addressing, denoted by `(%rip)`. For the latter, we obtain an + // address of a stuff that may be in a shared object file from the + // Global Offset Table using `@GOTPCREL(%rip)` notation. + + // Function + if (node->ty->kind == TY_FUNC) { + if (node->var->is_definition) + println(" lea %s(%%rip), %%rax", node->var->name); + else + println(" mov %s@GOTPCREL(%%rip), %%rax", node->var->name); + return; + } + + // Global variable + println(" lea %s(%%rip), %%rax", node->var->name); + return; + case ND_DEREF: + gen_expr(node->lhs); + return; + case ND_COMMA: + gen_expr(node->lhs); + gen_addr(node->rhs); + return; + case ND_MEMBER: + gen_addr(node->lhs); + println(" add $%d, %%rax", node->member->offset); + return; + case ND_FUNCALL: + if (node->ret_buffer) { + gen_expr(node); + return; + } + break; + case ND_VLA_PTR: + println(" lea %d(%%rbp), %%rax", node->var->offset); + return; + } + + error_tok(node->tok, "not an lvalue"); +} + +// Load a value from where %rax is pointing to. +static void load(Type *ty) { + switch (ty->kind) { + case TY_ARRAY: + case TY_STRUCT: + case TY_UNION: + case TY_FUNC: + case TY_VLA: + // If it is an array, do not attempt to load a value to the + // register because in general we can't load an entire array to a + // register. As a result, the result of an evaluation of an array + // becomes not the array itself but the address of the array. + // This is where "array is automatically converted to a pointer to + // the first element of the array in C" occurs. + return; + case TY_FLOAT: + println(" movss (%%rax), %%xmm0"); + return; + case TY_DOUBLE: + println(" movsd (%%rax), %%xmm0"); + return; + case TY_LDOUBLE: + println(" fldt (%%rax)"); + return; + } + + char *insn = ty->is_unsigned ? "movz" : "movs"; + + // When we load a char or a short value to a register, we always + // extend them to the size of int, so we can assume the lower half of + // a register always contains a valid value. The upper half of a + // register for char, short and int may contain garbage. When we load + // a long value to a register, it simply occupies the entire register. + if (ty->size == 1) + println(" %sbl (%%rax), %%eax", insn); + else if (ty->size == 2) + println(" %swl (%%rax), %%eax", insn); + else if (ty->size == 4) + println(" movslq (%%rax), %%rax"); + else + println(" mov (%%rax), %%rax"); +} + +// Store %rax to an address that the stack top is pointing to. +static void store(Type *ty) { + pop("%rdi"); + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + for (int i = 0; i < ty->size; i++) { + println(" mov %d(%%rax), %%r8b", i); + println(" mov %%r8b, %d(%%rdi)", i); + } + return; + case TY_FLOAT: + println(" movss %%xmm0, (%%rdi)"); + return; + case TY_DOUBLE: + println(" movsd %%xmm0, (%%rdi)"); + return; + case TY_LDOUBLE: + println(" fstpt (%%rdi)"); + return; + } + + if (ty->size == 1) + println(" mov %%al, (%%rdi)"); + else if (ty->size == 2) + println(" mov %%ax, (%%rdi)"); + else if (ty->size == 4) + println(" mov %%eax, (%%rdi)"); + else + println(" mov %%rax, (%%rdi)"); +} + +static void cmp_zero(Type *ty) { + switch (ty->kind) { + case TY_FLOAT: + println(" xorps %%xmm1, %%xmm1"); + println(" ucomiss %%xmm1, %%xmm0"); + return; + case TY_DOUBLE: + println(" xorpd %%xmm1, %%xmm1"); + println(" ucomisd %%xmm1, %%xmm0"); + return; + case TY_LDOUBLE: + println(" fldz"); + println(" fucomip"); + println(" fstp %%st(0)"); + return; + } + + if (is_integer(ty) && ty->size <= 4) + println(" cmp $0, %%eax"); + else + println(" cmp $0, %%rax"); +} + +enum { I8, I16, I32, I64, U8, U16, U32, U64, F32, F64, F80 }; + +static int getTypeId(Type *ty) { + switch (ty->kind) { + case TY_CHAR: + return ty->is_unsigned ? U8 : I8; + case TY_SHORT: + return ty->is_unsigned ? U16 : I16; + case TY_INT: + return ty->is_unsigned ? U32 : I32; + case TY_LONG: + return ty->is_unsigned ? U64 : I64; + case TY_FLOAT: + return F32; + case TY_DOUBLE: + return F64; + case TY_LDOUBLE: + return F80; + } + return U64; +} + +// The table for type casts +static char i32i8[] = "movsbl %al, %eax"; +static char i32u8[] = "movzbl %al, %eax"; +static char i32i16[] = "movswl %ax, %eax"; +static char i32u16[] = "movzwl %ax, %eax"; +static char i32f32[] = "cvtsi2ssl %eax, %xmm0"; +static char i32i64[] = "movslq %eax, %rax"; +static char i32f64[] = "cvtsi2sdl %eax, %xmm0"; +static char i32f80[] = "mov %eax, -4(%rsp)\n fildl -4(%rsp)"; + +static char u32f32[] = "mov %eax, %eax\n cvtsi2ssq %rax, %xmm0"; +static char u32i64[] = "mov %eax, %eax"; +static char u32f64[] = "mov %eax, %eax\n cvtsi2sdq %rax, %xmm0"; +static char u32f80[] = + "mov %eax, %eax\n mov %rax, -8(%rsp)\n fildll -8(%rsp)"; + +static char i64f32[] = "cvtsi2ssq %rax, %xmm0"; +static char i64f64[] = "cvtsi2sdq %rax, %xmm0"; +static char i64f80[] = "movq %rax, -8(%rsp)\n fildll -8(%rsp)"; + +static char u64f32[] = "cvtsi2ssq %rax, %xmm0"; +static char u64f64[] = + "test %rax,%rax\n js 1f\n pxor %xmm0,%xmm0\n cvtsi2sd %rax,%xmm0\n jmp " + "2f\n1: mov %rax,%rdi\n and $1,%eax\n pxor %xmm0,%xmm0\n shr %rdi\n " + "or %rax,%rdi\n cvtsi2sd %rdi,%xmm0\n addsd %xmm0,%xmm0\n 2:"; +static char u64f80[] = + "mov %rax, -8(%rsp)\n fildq -8(%rsp)\n test %rax, %rax\n jns 1f;" + "mov $1602224128, %eax\n mov %eax, -4(%rsp)\n fadds -4(%rsp)\n 1:"; + +static char f32i8[] = "cvttss2sil %xmm0, %eax\n movsbl %al, %eax"; +static char f32u8[] = "cvttss2sil %xmm0, %eax\n movzbl %al, %eax"; +static char f32i16[] = "cvttss2sil %xmm0, %eax\n movswl %ax, %eax"; +static char f32u16[] = "cvttss2sil %xmm0, %eax\n movzwl %ax, %eax"; +static char f32i32[] = "cvttss2sil %xmm0, %eax"; +static char f32u32[] = "cvttss2siq %xmm0, %rax"; +static char f32i64[] = "cvttss2siq %xmm0, %rax"; +static char f32u64[] = "cvttss2siq %xmm0, %rax"; +static char f32f64[] = "cvtss2sd %xmm0, %xmm0"; +static char f32f80[] = "movss %xmm0, -4(%rsp)\n flds -4(%rsp)"; + +static char f64i8[] = "cvttsd2sil %xmm0, %eax\n movsbl %al, %eax"; +static char f64u8[] = "cvttsd2sil %xmm0, %eax\n movzbl %al, %eax"; +static char f64i16[] = "cvttsd2sil %xmm0, %eax\n movswl %ax, %eax"; +static char f64u16[] = "cvttsd2sil %xmm0, %eax\n movzwl %ax, %eax"; +static char f64i32[] = "cvttsd2sil %xmm0, %eax"; +static char f64u32[] = "cvttsd2siq %xmm0, %rax"; +static char f64i64[] = "cvttsd2siq %xmm0, %rax"; +static char f64u64[] = "cvttsd2siq %xmm0, %rax"; +static char f64f32[] = "cvtsd2ss %xmm0, %xmm0"; +static char f64f80[] = "movsd %xmm0, -8(%rsp)\n fldl -8(%rsp)"; + +#define FROM_F80_1 \ + "fnstcw -10(%rsp)\n movzwl -10(%rsp), %eax\n or $12, %ah\n " \ + "mov %ax, -12(%rsp)\n fldcw -12(%rsp)\n " + +#define FROM_F80_2 " -24(%rsp)\n fldcw -10(%rsp)\n " + +static char f80i8[] = FROM_F80_1 "fistps" FROM_F80_2 "movsbl -24(%rsp), %eax"; +static char f80u8[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +static char f80i16[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +static char f80u16[] = FROM_F80_1 "fistpl" FROM_F80_2 "movswl -24(%rsp), %eax"; +static char f80i32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +static char f80u32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +static char f80i64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +static char f80u64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +static char f80f32[] = "fstps -8(%rsp)\n movss -8(%rsp), %xmm0"; +static char f80f64[] = "fstpl -8(%rsp)\n movsd -8(%rsp), %xmm0"; + +static const char *const cast_table[11][11] = /* clang-format off */ { + // i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 f80 + {NULL, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, // i8 + {i32i8, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, // i16 + {i32i8, i32i16, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, // i32 + {i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, i64f32, i64f64, i64f80}, // i64 + {i32i8, NULL, NULL, i32i64, NULL, NULL, NULL, i32i64, i32f32, i32f64, i32f80}, // u8 + {i32i8, i32i16, NULL, i32i64, i32u8, NULL, NULL, i32i64, i32f32, i32f64, i32f80}, // u16 + {i32i8, i32i16, NULL, u32i64, i32u8, i32u16, NULL, u32i64, u32f32, u32f64, u32f80}, // u32 + {i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, u64f32, u64f64, u64f80}, // u64 + {f32i8, f32i16, f32i32, f32i64, f32u8, f32u16, f32u32, f32u64, NULL, f32f64, f32f80}, // f32 + {f64i8, f64i16, f64i32, f64i64, f64u8, f64u16, f64u32, f64u64, f64f32, NULL, f64f80}, // f64 + {f80i8, f80i16, f80i32, f80i64, f80u8, f80u16, f80u32, f80u64, f80f32, f80f64, NULL}, // f80 +} /* clang-format on */; + +static void cast(Type *from, Type *to) { + if (to->kind == TY_VOID) return; + + if (to->kind == TY_BOOL) { + cmp_zero(from); + println(" setne %%al"); + println(" movzbl %%al, %%eax"); + return; + } + + int t1 = getTypeId(from); + int t2 = getTypeId(to); + if (cast_table[t1][t2]) println(" %s", cast_table[t1][t2]); +} + +// Structs or unions equal or smaller than 16 bytes are passed +// using up to two registers. +// +// If the first 8 bytes contains only floating-point type members, +// they are passed in an XMM register. Otherwise, they are passed +// in a general-purpose register. +// +// If a struct/union is larger than 8 bytes, the same rule is +// applied to the the next 8 byte chunk. +// +// This function returns true if `ty` has only floating-point +// members in its byte range [lo, hi). +static bool has_flonum(Type *ty, int lo, int hi, int offset) { + if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) { + for (Member *mem = ty->members; mem; mem = mem->next) + if (!has_flonum(mem->ty, lo, hi, offset + mem->offset)) return false; + return true; + } + + if (ty->kind == TY_ARRAY) { + for (int i = 0; i < ty->array_len; i++) + if (!has_flonum(ty->base, lo, hi, offset + ty->base->size * i)) + return false; + return true; + } + + return offset < lo || hi <= offset || ty->kind == TY_FLOAT || + ty->kind == TY_DOUBLE; +} + +static bool has_flonum1(Type *ty) { + return has_flonum(ty, 0, 8, 0); +} + +static bool has_flonum2(Type *ty) { + return has_flonum(ty, 8, 16, 0); +} + +static void push_struct(Type *ty) { + int sz = align_to(ty->size, 8); + println(" sub $%d, %%rsp", sz); + depth += sz / 8; + + for (int i = 0; i < ty->size; i++) { + println(" mov %d(%%rax), %%r10b", i); + println(" mov %%r10b, %d(%%rsp)", i); + } +} + +static void push_args2(Node *args, bool first_pass) { + if (!args) return; + push_args2(args->next, first_pass); + + if ((first_pass && !args->pass_by_stack) || + (!first_pass && args->pass_by_stack)) + return; + + gen_expr(args); + + switch (args->ty->kind) { + case TY_STRUCT: + case TY_UNION: + push_struct(args->ty); + break; + case TY_FLOAT: + case TY_DOUBLE: + pushf(); + break; + case TY_LDOUBLE: + println(" sub $16, %%rsp"); + println(" fstpt (%%rsp)"); + depth += 2; + break; + default: + push(); + } +} + +// Load function call arguments. Arguments are already evaluated and +// stored to the stack as local variables. What we need to do in this +// function is to load them to registers or push them to the stack as +// specified by the x86-64 psABI. Here is what the spec says: +// +// - Up to 6 arguments of integral type are passed using RDI, RSI, +// RDX, RCX, R8 and R9. +// +// - Up to 8 arguments of floating-point type are passed using XMM0 to +// XMM7. +// +// - If all registers of an appropriate type are already used, push an +// argument to the stack in the right-to-left order. +// +// - Each argument passed on the stack takes 8 bytes, and the end of +// the argument area must be aligned to a 16 byte boundary. +// +// - If a function is variadic, set the number of floating-point type +// arguments to RAX. +static int push_args(Node *node) { + int stack = 0, gp = 0, fp = 0; + + // If the return type is a large struct/union, the caller passes + // a pointer to a buffer as if it were the first argument. + if (node->ret_buffer && node->ty->size > 16) gp++; + + // Load as many arguments to the registers as possible. + for (Node *arg = node->args; arg; arg = arg->next) { + Type *ty = arg->ty; + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + if (ty->size > 16) { + arg->pass_by_stack = true; + stack += align_to(ty->size, 8) / 8; + } else { + bool fp1 = has_flonum1(ty); + bool fp2 = has_flonum2(ty); + + if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { + fp = fp + fp1 + fp2; + gp = gp + !fp1 + !fp2; + } else { + arg->pass_by_stack = true; + stack += align_to(ty->size, 8) / 8; + } + } + break; + case TY_FLOAT: + case TY_DOUBLE: + if (fp++ >= FP_MAX) { + arg->pass_by_stack = true; + stack++; + } + break; + case TY_LDOUBLE: + arg->pass_by_stack = true; + stack += 2; + break; + default: + if (gp++ >= GP_MAX) { + arg->pass_by_stack = true; + stack++; + } + } + } + + if ((depth + stack) % 2 == 1) { + println(" sub $8, %%rsp"); + depth++; + stack++; + } + + push_args2(node->args, true); + push_args2(node->args, false); + + // If the return type is a large struct/union, the caller passes + // a pointer to a buffer as if it were the first argument. + if (node->ret_buffer && node->ty->size > 16) { + println(" lea %d(%%rbp), %%rax", node->ret_buffer->offset); + push(); + } + + return stack; +} + +static void copy_ret_buffer(Obj *var) { + Type *ty = var->ty; + int gp = 0, fp = 0; + + if (has_flonum1(ty)) { + assert(ty->size == 4 || 8 <= ty->size); + if (ty->size == 4) + println(" movss %%xmm0, %d(%%rbp)", var->offset); + else + println(" movsd %%xmm0, %d(%%rbp)", var->offset); + fp++; + } else { + for (int i = 0; i < MIN(8, ty->size); i++) { + println(" mov %%al, %d(%%rbp)", var->offset + i); + println(" shr $8, %%rax"); + } + gp++; + } + + if (ty->size > 8) { + if (has_flonum2(ty)) { + assert(ty->size == 12 || ty->size == 16); + if (ty->size == 12) + println(" movss %%xmm%d, %d(%%rbp)", fp, var->offset + 8); + else + println(" movsd %%xmm%d, %d(%%rbp)", fp, var->offset + 8); + } else { + char *reg1 = (gp == 0) ? "%al" : "%dl"; + char *reg2 = (gp == 0) ? "%rax" : "%rdx"; + for (int i = 8; i < MIN(16, ty->size); i++) { + println(" mov %s, %d(%%rbp)", reg1, var->offset + i); + println(" shr $8, %s", reg2); + } + } + } +} + +static void copy_struct_reg(void) { + Type *ty = current_fn->ty->return_ty; + int gp = 0, fp = 0; + + println(" mov %%rax, %%rdi"); + + if (has_flonum(ty, 0, 8, 0)) { + assert(ty->size == 4 || 8 <= ty->size); + if (ty->size == 4) + println(" movss (%%rdi), %%xmm0"); + else + println(" movsd (%%rdi), %%xmm0"); + fp++; + } else { + println(" mov $0, %%rax"); + for (int i = MIN(8, ty->size) - 1; i >= 0; i--) { + println(" shl $8, %%rax"); + println(" mov %d(%%rdi), %%al", i); + } + gp++; + } + + if (ty->size > 8) { + if (has_flonum(ty, 8, 16, 0)) { + assert(ty->size == 12 || ty->size == 16); + if (ty->size == 4) + println(" movss 8(%%rdi), %%xmm%d", fp); + else + println(" movsd 8(%%rdi), %%xmm%d", fp); + } else { + char *reg1 = (gp == 0) ? "%al" : "%dl"; + char *reg2 = (gp == 0) ? "%rax" : "%rdx"; + println(" mov $0, %s", reg2); + for (int i = MIN(16, ty->size) - 1; i >= 8; i--) { + println(" shl $8, %s", reg2); + println(" mov %d(%%rdi), %s", i, reg1); + } + } + } +} + +static void copy_struct_mem(void) { + Type *ty = current_fn->ty->return_ty; + Obj *var = current_fn->params; + + println(" mov %d(%%rbp), %%rdi", var->offset); + + for (int i = 0; i < ty->size; i++) { + println(" mov %d(%%rax), %%dl", i); + println(" mov %%dl, %d(%%rdi)", i); + } +} + +static void builtin_alloca(void) { + // Align size to 16 bytes. + println(" add $15, %%rdi"); + println(" and $0xfffffff0, %%edi"); + + // Shift the temporary area by %rdi. + println(" mov %d(%%rbp), %%rcx", current_fn->alloca_bottom->offset); + println(" sub %%rsp, %%rcx"); + println(" mov %%rsp, %%rax"); + println(" sub %%rdi, %%rsp"); + println(" mov %%rsp, %%rdx"); + println("1:"); + println(" cmp $0, %%rcx"); + println(" je 2f"); + println(" mov (%%rax), %%r8b"); + println(" mov %%r8b, (%%rdx)"); + println(" inc %%rdx"); + println(" inc %%rax"); + println(" dec %%rcx"); + println(" jmp 1b"); + println("2:"); + + // Move alloca_bottom pointer. + println(" mov %d(%%rbp), %%rax", current_fn->alloca_bottom->offset); + println(" sub %%rdi, %%rax"); + println(" mov %%rax, %d(%%rbp)", current_fn->alloca_bottom->offset); +} + +// Generate code for a given node. +static void gen_expr(Node *node) { + println(" .loc %d %d", node->tok->file->file_no, node->tok->line_no); + + switch (node->kind) { + case ND_NULL_EXPR: + return; + case ND_NUM: { + switch (node->ty->kind) { + case TY_FLOAT: { + union { + float f32; + uint32_t u32; + } u = {node->fval}; + println(" mov $%u, %%eax # float %Lf", u.u32, node->fval); + println(" movq %%rax, %%xmm0"); + return; + } + case TY_DOUBLE: { + union { + double f64; + uint64_t u64; + } u = {node->fval}; + println(" mov $%lu, %%rax # double %Lf", u.u64, node->fval); + println(" movq %%rax, %%xmm0"); + return; + } + case TY_LDOUBLE: { + union { + long double f80; + uint64_t u64[2]; + } u; + memset(&u, 0, sizeof(u)); + u.f80 = node->fval; + println(" mov $%lu, %%rax # long double %Lf", u.u64[0], node->fval); + println(" mov %%rax, -16(%%rsp)"); + println(" mov $%lu, %%rax", u.u64[1]); + println(" mov %%rax, -8(%%rsp)"); + println(" fldt -16(%%rsp)"); + return; + } + } + + println(" mov $%ld, %%rax", node->val); + return; + } + case ND_NEG: + gen_expr(node->lhs); + + switch (node->ty->kind) { + case TY_FLOAT: + println(" mov $1, %%rax"); + println(" shl $31, %%rax"); + println(" movq %%rax, %%xmm1"); + println(" xorps %%xmm1, %%xmm0"); + return; + case TY_DOUBLE: + println(" mov $1, %%rax"); + println(" shl $63, %%rax"); + println(" movq %%rax, %%xmm1"); + println(" xorpd %%xmm1, %%xmm0"); + return; + case TY_LDOUBLE: + println(" fchs"); + return; + } + + println(" neg %%rax"); + return; + case ND_VAR: + gen_addr(node); + load(node->ty); + return; + case ND_MEMBER: { + gen_addr(node); + load(node->ty); + + Member *mem = node->member; + if (mem->is_bitfield) { + println(" shl $%d, %%rax", 64 - mem->bit_width - mem->bit_offset); + if (mem->ty->is_unsigned) + println(" shr $%d, %%rax", 64 - mem->bit_width); + else + println(" sar $%d, %%rax", 64 - mem->bit_width); + } + return; + } + case ND_DEREF: + gen_expr(node->lhs); + load(node->ty); + return; + case ND_ADDR: + gen_addr(node->lhs); + return; + case ND_ASSIGN: + gen_addr(node->lhs); + push(); + gen_expr(node->rhs); + + if (node->lhs->kind == ND_MEMBER && node->lhs->member->is_bitfield) { + println(" mov %%rax, %%r8"); + + // If the lhs is a bitfield, we need to read the current value + // from memory and merge it with a new value. + Member *mem = node->lhs->member; + println(" mov %%rax, %%rdi"); + println(" and $%ld, %%rdi", (1L << mem->bit_width) - 1); + println(" shl $%d, %%rdi", mem->bit_offset); + + println(" mov (%%rsp), %%rax"); + load(mem->ty); + + long mask = ((1L << mem->bit_width) - 1) << mem->bit_offset; + println(" mov $%ld, %%r9", ~mask); + println(" and %%r9, %%rax"); + println(" or %%rdi, %%rax"); + store(node->ty); + println(" mov %%r8, %%rax"); + return; + } + + store(node->ty); + return; + case ND_STMT_EXPR: + for (Node *n = node->body; n; n = n->next) gen_stmt(n); + return; + case ND_COMMA: + gen_expr(node->lhs); + gen_expr(node->rhs); + return; + case ND_CAST: + gen_expr(node->lhs); + cast(node->lhs->ty, node->ty); + return; + case ND_MEMZERO: + // `rep stosb` is equivalent to `memset(%rdi, %al, %rcx)`. + println(" mov $%d, %%rcx", node->var->ty->size); + println(" lea %d(%%rbp), %%rdi", node->var->offset); + println(" mov $0, %%al"); + println(" rep stosb"); + return; + case ND_COND: { + int c = count(); + gen_expr(node->cond); + cmp_zero(node->cond->ty); + println(" je .L.else.%d", c); + gen_expr(node->then); + println(" jmp .L.end.%d", c); + println(".L.else.%d:", c); + gen_expr(node->els); + println(".L.end.%d:", c); + return; + } + case ND_NOT: + gen_expr(node->lhs); + cmp_zero(node->lhs->ty); + println(" sete %%al"); + println(" movzbl %%al, %%rax"); + return; + case ND_BITNOT: + gen_expr(node->lhs); + println(" not %%rax"); + return; + case ND_LOGAND: { + int c = count(); + gen_expr(node->lhs); + cmp_zero(node->lhs->ty); + println(" je .L.false.%d", c); + gen_expr(node->rhs); + cmp_zero(node->rhs->ty); + println(" je .L.false.%d", c); + println(" mov $1, %%rax"); + println(" jmp .L.end.%d", c); + println(".L.false.%d:", c); + println(" mov $0, %%rax"); + println(".L.end.%d:", c); + return; + } + case ND_LOGOR: { + int c = count(); + gen_expr(node->lhs); + cmp_zero(node->lhs->ty); + println(" jne .L.true.%d", c); + gen_expr(node->rhs); + cmp_zero(node->rhs->ty); + println(" jne .L.true.%d", c); + println(" mov $0, %%rax"); + println(" jmp .L.end.%d", c); + println(".L.true.%d:", c); + println(" mov $1, %%rax"); + println(".L.end.%d:", c); + return; + } + case ND_FUNCALL: { + if (node->lhs->kind == ND_VAR && + !strcmp(node->lhs->var->name, "alloca")) { + gen_expr(node->args); + println(" mov %%rax, %%rdi"); + builtin_alloca(); + return; + } + + int stack_args = push_args(node); + gen_expr(node->lhs); + + int gp = 0, fp = 0; + + // If the return type is a large struct/union, the caller passes + // a pointer to a buffer as if it were the first argument. + if (node->ret_buffer && node->ty->size > 16) pop(argreg64[gp++]); + + for (Node *arg = node->args; arg; arg = arg->next) { + Type *ty = arg->ty; + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + if (ty->size > 16) continue; + + bool fp1 = has_flonum1(ty); + bool fp2 = has_flonum2(ty); + + if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { + if (fp1) + popf(fp++); + else + pop(argreg64[gp++]); + + if (ty->size > 8) { + if (fp2) + popf(fp++); + else + pop(argreg64[gp++]); + } + } + break; + case TY_FLOAT: + case TY_DOUBLE: + if (fp < FP_MAX) popf(fp++); + break; + case TY_LDOUBLE: + break; + default: + if (gp < GP_MAX) pop(argreg64[gp++]); + } + } + + println(" mov %%rax, %%r10"); + println(" mov $%d, %%eax", fp); + println(" call *%%r10"); + println(" add $%d, %%rsp", stack_args * 8); + + depth -= stack_args; + + // It looks like the most significant 48 or 56 bits in RAX may + // contain garbage if a function return type is short or bool/char, + // respectively. We clear the upper bits here. + switch (node->ty->kind) { + case TY_BOOL: + println(" movzbl %%al, %%eax"); + return; + case TY_CHAR: + if (node->ty->is_unsigned) + println(" movzbl %%al, %%eax"); + else + println(" movsbl %%al, %%eax"); + return; + case TY_SHORT: + if (node->ty->is_unsigned) + println(" movzwl %%ax, %%eax"); + else + println(" movswl %%ax, %%eax"); + return; + } + + // If the return type is a small struct, a value is returned + // using up to two registers. + if (node->ret_buffer && node->ty->size <= 16) { + copy_ret_buffer(node->ret_buffer); + println(" lea %d(%%rbp), %%rax", node->ret_buffer->offset); + } + + return; + } + case ND_LABEL_VAL: + println(" lea %s(%%rip), %%rax", node->unique_label); + return; + case ND_CAS: { + gen_expr(node->cas_addr); + push(); + gen_expr(node->cas_new); + push(); + gen_expr(node->cas_old); + println(" mov %%rax, %%r8"); + load(node->cas_old->ty->base); + pop("%rdx"); // new + pop("%rdi"); // addr + + int sz = node->cas_addr->ty->base->size; + println(" lock cmpxchg %s, (%%rdi)", reg_dx(sz)); + println(" sete %%cl"); + println(" je 1f"); + println(" mov %s, (%%r8)", reg_ax(sz)); + println("1:"); + println(" movzbl %%cl, %%eax"); + return; + } + case ND_EXCH: { + gen_expr(node->lhs); + push(); + gen_expr(node->rhs); + pop("%rdi"); + + int sz = node->lhs->ty->base->size; + println(" xchg %s, (%%rdi)", reg_ax(sz)); + return; + } + } + + switch (node->lhs->ty->kind) { + case TY_FLOAT: + case TY_DOUBLE: { + gen_expr(node->rhs); + pushf(); + gen_expr(node->lhs); + popf(1); + + char *sz = (node->lhs->ty->kind == TY_FLOAT) ? "ss" : "sd"; + + switch (node->kind) { + case ND_ADD: + println(" add%s %%xmm1, %%xmm0", sz); + return; + case ND_SUB: + println(" sub%s %%xmm1, %%xmm0", sz); + return; + case ND_MUL: + println(" mul%s %%xmm1, %%xmm0", sz); + return; + case ND_DIV: + println(" div%s %%xmm1, %%xmm0", sz); + return; + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + println(" ucomi%s %%xmm0, %%xmm1", sz); + + if (node->kind == ND_EQ) { + println(" sete %%al"); + println(" setnp %%dl"); + println(" and %%dl, %%al"); + } else if (node->kind == ND_NE) { + println(" setne %%al"); + println(" setp %%dl"); + println(" or %%dl, %%al"); + } else if (node->kind == ND_LT) { + println(" seta %%al"); + } else { + println(" setae %%al"); + } + + println(" and $1, %%al"); + println(" movzb %%al, %%rax"); + return; + } + + error_tok(node->tok, "invalid expression"); + } + case TY_LDOUBLE: { + gen_expr(node->lhs); + gen_expr(node->rhs); + + switch (node->kind) { + case ND_ADD: + println(" faddp"); + return; + case ND_SUB: + println(" fsubrp"); + return; + case ND_MUL: + println(" fmulp"); + return; + case ND_DIV: + println(" fdivrp"); + return; + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + println(" fcomip"); + println(" fstp %%st(0)"); + + if (node->kind == ND_EQ) + println(" sete %%al"); + else if (node->kind == ND_NE) + println(" setne %%al"); + else if (node->kind == ND_LT) + println(" seta %%al"); + else + println(" setae %%al"); + + println(" movzb %%al, %%rax"); + return; + } + + error_tok(node->tok, "invalid expression"); + } + } + + gen_expr(node->rhs); + push(); + gen_expr(node->lhs); + pop("%rdi"); + + char *ax, *di, *dx; + + if (node->lhs->ty->kind == TY_LONG || node->lhs->ty->base) { + ax = "%rax"; + di = "%rdi"; + dx = "%rdx"; + } else { + ax = "%eax"; + di = "%edi"; + dx = "%edx"; + } + + switch (node->kind) { + case ND_ADD: + println(" add %s, %s", di, ax); + return; + case ND_SUB: + println(" sub %s, %s", di, ax); + return; + case ND_MUL: + println(" imul %s, %s", di, ax); + return; + case ND_DIV: + case ND_MOD: + if (node->ty->is_unsigned) { + println(" mov $0, %s", dx); + println(" div %s", di); + } else { + if (node->lhs->ty->size == 8) + println(" cqo"); + else + println(" cdq"); + println(" idiv %s", di); + } + + if (node->kind == ND_MOD) println(" mov %%rdx, %%rax"); + return; + case ND_BITAND: + println(" and %s, %s", di, ax); + return; + case ND_BITOR: + println(" or %s, %s", di, ax); + return; + case ND_BITXOR: + println(" xor %s, %s", di, ax); + return; + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + println(" cmp %s, %s", di, ax); + + if (node->kind == ND_EQ) { + println(" sete %%al"); + } else if (node->kind == ND_NE) { + println(" setne %%al"); + } else if (node->kind == ND_LT) { + if (node->lhs->ty->is_unsigned) + println(" setb %%al"); + else + println(" setl %%al"); + } else if (node->kind == ND_LE) { + if (node->lhs->ty->is_unsigned) + println(" setbe %%al"); + else + println(" setle %%al"); + } + + println(" movzb %%al, %%rax"); + return; + case ND_SHL: + println(" mov %%rdi, %%rcx"); + println(" shl %%cl, %s", ax); + return; + case ND_SHR: + println(" mov %%rdi, %%rcx"); + if (node->lhs->ty->is_unsigned) + println(" shr %%cl, %s", ax); + else + println(" sar %%cl, %s", ax); + return; + } + + error_tok(node->tok, "invalid expression"); +} + +static void gen_stmt(Node *node) { + println(" .loc %d %d", node->tok->file->file_no, node->tok->line_no); + + switch (node->kind) { + case ND_IF: { + int c = count(); + gen_expr(node->cond); + cmp_zero(node->cond->ty); + println(" je .L.else.%d", c); + gen_stmt(node->then); + println(" jmp .L.end.%d", c); + println(".L.else.%d:", c); + if (node->els) gen_stmt(node->els); + println(".L.end.%d:", c); + return; + } + case ND_FOR: { + int c = count(); + if (node->init) gen_stmt(node->init); + println(".L.begin.%d:", c); + if (node->cond) { + gen_expr(node->cond); + cmp_zero(node->cond->ty); + println(" je %s", node->brk_label); + } + gen_stmt(node->then); + println("%s:", node->cont_label); + if (node->inc) gen_expr(node->inc); + println(" jmp .L.begin.%d", c); + println("%s:", node->brk_label); + return; + } + case ND_DO: { + int c = count(); + println(".L.begin.%d:", c); + gen_stmt(node->then); + println("%s:", node->cont_label); + gen_expr(node->cond); + cmp_zero(node->cond->ty); + println(" jne .L.begin.%d", c); + println("%s:", node->brk_label); + return; + } + case ND_SWITCH: + gen_expr(node->cond); + + for (Node *n = node->case_next; n; n = n->case_next) { + char *ax = (node->cond->ty->size == 8) ? "%rax" : "%eax"; + char *di = (node->cond->ty->size == 8) ? "%rdi" : "%edi"; + + if (n->begin == n->end) { + println(" cmp $%ld, %s", n->begin, ax); + println(" je %s", n->label); + continue; + } + + // [GNU] Case ranges + println(" mov %s, %s", ax, di); + println(" sub $%ld, %s", n->begin, di); + println(" cmp $%ld, %s", n->end - n->begin, di); + println(" jbe %s", n->label); + } + + if (node->default_case) println(" jmp %s", node->default_case->label); + + println(" jmp %s", node->brk_label); + gen_stmt(node->then); + println("%s:", node->brk_label); + return; + case ND_CASE: + println("%s:", node->label); + gen_stmt(node->lhs); + return; + case ND_BLOCK: + for (Node *n = node->body; n; n = n->next) gen_stmt(n); + return; + case ND_GOTO: + println(" jmp %s", node->unique_label); + return; + case ND_GOTO_EXPR: + gen_expr(node->lhs); + println(" jmp *%%rax"); + return; + case ND_LABEL: + println("%s:", node->unique_label); + gen_stmt(node->lhs); + return; + case ND_RETURN: + if (node->lhs) { + gen_expr(node->lhs); + Type *ty = node->lhs->ty; + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + if (ty->size <= 16) + copy_struct_reg(); + else + copy_struct_mem(); + break; + } + } + + println(" jmp .L.return.%s", current_fn->name); + return; + case ND_EXPR_STMT: + gen_expr(node->lhs); + return; + case ND_ASM: + println(" %s", node->asm_str); + return; + } + + error_tok(node->tok, "invalid statement"); +} + +// Assign offsets to local variables. +static void assign_lvar_offsets(Obj *prog) { + for (Obj *fn = prog; fn; fn = fn->next) { + if (!fn->is_function) continue; + + // If a function has many parameters, some parameters are + // inevitably passed by stack rather than by register. + // The first passed-by-stack parameter resides at RBP+16. + int top = 16; + int bottom = 0; + + int gp = 0, fp = 0; + + // Assign offsets to pass-by-stack parameters. + for (Obj *var = fn->params; var; var = var->next) { + Type *ty = var->ty; + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + if (ty->size <= 16) { + bool fp1 = has_flonum(ty, 0, 8, 0); + bool fp2 = has_flonum(ty, 8, 16, 8); + if (fp + fp1 + fp2 < FP_MAX && gp + !fp1 + !fp2 < GP_MAX) { + fp = fp + fp1 + fp2; + gp = gp + !fp1 + !fp2; + continue; + } + } + break; + case TY_FLOAT: + case TY_DOUBLE: + if (fp++ < FP_MAX) continue; + break; + case TY_LDOUBLE: + break; + default: + if (gp++ < GP_MAX) continue; + } + + top = align_to(top, 8); + var->offset = top; + top += var->ty->size; + } + + // Assign offsets to pass-by-register parameters and local variables. + for (Obj *var = fn->locals; var; var = var->next) { + if (var->offset) continue; + + // AMD64 System V ABI has a special alignment rule for an array of + // length at least 16 bytes. We need to align such array to at least + // 16-byte boundaries. See p.14 of + // https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-draft.pdf. + int align = (var->ty->kind == TY_ARRAY && var->ty->size >= 16) + ? MAX(16, var->align) + : var->align; + + bottom += var->ty->size; + bottom = align_to(bottom, align); + var->offset = -bottom; + } + + fn->stack_size = align_to(bottom, 16); + } +} + +static void emit_data(Obj *prog) { + for (Obj *var = prog; var; var = var->next) { + if (var->is_function || !var->is_definition) continue; + + if (var->is_static) + println(" .local %s", var->name); + else + println(" .globl %s", var->name); + + int align = (var->ty->kind == TY_ARRAY && var->ty->size >= 16) + ? MAX(16, var->align) + : var->align; + + // Common symbol + if (opt_fcommon && var->is_tentative && !var->is_tls) { + println(" .comm %s, %d, %d", var->name, var->ty->size, align); + continue; + } + + // .data or .tdata + if (var->init_data) { + if (var->is_tls) + println(" .section .tdata,\"awT\",@progbits"); + else + println(" .data"); + + println(" .type %s, @object", var->name); + println(" .size %s, %d", var->name, var->ty->size); + println(" .align %d", align); + println("%s:", var->name); + + Relocation *rel = var->rel; + int pos = 0; + while (pos < var->ty->size) { + if (rel && rel->offset == pos) { + println(" .quad %s%+ld", *rel->label, rel->addend); + rel = rel->next; + pos += 8; + } else { + println(" .byte %d", var->init_data[pos++]); + } + } + continue; + } + + // .bss or .tbss + if (var->is_tls) + println(" .section .tbss,\"awT\",@nobits"); + else + println(" .bss"); + + println(" .align %d", align); + println("%s:", var->name); + println(" .zero %d", var->ty->size); + } +} + +static void store_fp(int r, int offset, int sz) { + switch (sz) { + case 4: + println(" movss %%xmm%d, %d(%%rbp)", r, offset); + return; + case 8: + println(" movsd %%xmm%d, %d(%%rbp)", r, offset); + return; + } + UNREACHABLE(); +} + +static void store_gp(int r, int offset, int sz) { + switch (sz) { + case 1: + println(" mov %s, %d(%%rbp)", argreg8[r], offset); + return; + case 2: + println(" mov %s, %d(%%rbp)", argreg16[r], offset); + return; + case 4: + println(" mov %s, %d(%%rbp)", argreg32[r], offset); + return; + case 8: + println(" mov %s, %d(%%rbp)", argreg64[r], offset); + return; + default: + for (int i = 0; i < sz; i++) { + println(" mov %s, %d(%%rbp)", argreg8[r], offset + i); + println(" shr $8, %s", argreg64[r]); + } + return; + } +} + +static void emit_text(Obj *prog) { + for (Obj *fn = prog; fn; fn = fn->next) { + if (!fn->is_function || !fn->is_definition) continue; + + // No code is emitted for "static inline" functions + // if no one is referencing them. + if (!fn->is_live) continue; + + if (fn->is_static) + println(" .local %s", fn->name); + else + println(" .globl %s", fn->name); + + println(" .text"); + println(" .type %s, @function", fn->name); + println("%s:", fn->name); + current_fn = fn; + + // Prologue + println(" push %%rbp"); + println(" mov %%rsp, %%rbp"); + println(" sub $%d, %%rsp", fn->stack_size); + println(" mov %%rsp, %d(%%rbp)", fn->alloca_bottom->offset); + + // Save arg registers if function is variadic + if (fn->va_area) { + int gp = 0, fp = 0; + for (Obj *var = fn->params; var; var = var->next) { + if (is_flonum(var->ty)) + fp++; + else + gp++; + } + + int off = fn->va_area->offset; + + // va_elem + println(" movl $%d, %d(%%rbp)", gp * 8, off); // gp_offset + println(" movl $%d, %d(%%rbp)", fp * 8 + 48, off + 4); // fp_offset + println(" movq %%rbp, %d(%%rbp)", off + 8); // overflow_arg_area + println(" addq $16, %d(%%rbp)", off + 8); + println(" movq %%rbp, %d(%%rbp)", off + 16); // reg_save_area + println(" addq $%d, %d(%%rbp)", off + 24, off + 16); + + // __reg_save_area__ + println(" movq %%rdi, %d(%%rbp)", off + 24); + println(" movq %%rsi, %d(%%rbp)", off + 32); + println(" movq %%rdx, %d(%%rbp)", off + 40); + println(" movq %%rcx, %d(%%rbp)", off + 48); + println(" movq %%r8, %d(%%rbp)", off + 56); + println(" movq %%r9, %d(%%rbp)", off + 64); + println(" movsd %%xmm0, %d(%%rbp)", off + 72); + println(" movsd %%xmm1, %d(%%rbp)", off + 80); + println(" movsd %%xmm2, %d(%%rbp)", off + 88); + println(" movsd %%xmm3, %d(%%rbp)", off + 96); + println(" movsd %%xmm4, %d(%%rbp)", off + 104); + println(" movsd %%xmm5, %d(%%rbp)", off + 112); + println(" movsd %%xmm6, %d(%%rbp)", off + 120); + println(" movsd %%xmm7, %d(%%rbp)", off + 128); + } + + // Save passed-by-register arguments to the stack + int gp = 0, fp = 0; + for (Obj *var = fn->params; var; var = var->next) { + if (var->offset > 0) continue; + + Type *ty = var->ty; + + switch (ty->kind) { + case TY_STRUCT: + case TY_UNION: + assert(ty->size <= 16); + if (has_flonum(ty, 0, 8, 0)) + store_fp(fp++, var->offset, MIN(8, ty->size)); + else + store_gp(gp++, var->offset, MIN(8, ty->size)); + + if (ty->size > 8) { + if (has_flonum(ty, 8, 16, 0)) + store_fp(fp++, var->offset + 8, ty->size - 8); + else + store_gp(gp++, var->offset + 8, ty->size - 8); + } + break; + case TY_FLOAT: + case TY_DOUBLE: + store_fp(fp++, var->offset, ty->size); + break; + default: + store_gp(gp++, var->offset, ty->size); + } + } + + // Emit code + gen_stmt(fn->body); + assert(depth == 0); + + // The C spec defines a special rule for the main function. + // Reaching the end of the main function is equivalent to + // returning 0, even though the behavior is undefined for the + // other functions. See C11 5.1.2.2.3. + if (strcmp(fn->name, "main") == 0) println(" mov $0, %%rax"); + + // Epilogue + println(".L.return.%s:", fn->name); + println(" mov %%rbp, %%rsp"); + println(" pop %%rbp"); + println(" ret"); + } +} + +static void emit_staticasms(StaticAsm *a) { + if (!a) return; + emit_staticasms(a->next); + println(" .loc %d %d", a->body->tok->file->file_no, a->body->tok->line_no); + println(" %s", a->body->asm_str); +} + +void codegen(Obj *prog, FILE *out) { + output_file = out; + + File **files = get_input_files(); + for (int i = 0; files[i]; i++) + println(" .file %d \"%s\"", files[i]->file_no, files[i]->name); + + assign_lvar_offsets(prog); + emit_staticasms(staticasms); + emit_data(prog); + emit_text(prog); +} diff --git a/third_party/chibicc/hashmap.c b/third_party/chibicc/hashmap.c new file mode 100644 index 000000000..5d05af9c5 --- /dev/null +++ b/third_party/chibicc/hashmap.c @@ -0,0 +1,130 @@ +// This is an implementation of the open-addressing hash table. + +#include "third_party/chibicc/chibicc.h" + +#define TOMBSTONE ((void *)-1) // Represents a deleted hash entry + +static uint64_t fnv_hash(char *s, int len) { + uint64_t hash = 0xcbf29ce484222325; + for (int i = 0; i < len; i++) { + hash *= 0x100000001b3; + hash ^= (unsigned char)s[i]; + } + return hash; +} + +// Make room for new entires in a given hashmap by removing +// tombstones and possibly extending the bucket size. +static void rehash(HashMap *map) { + // Compute the size of the new hashmap. + int nkeys = 0; + for (int i = 0; i < map->capacity; i++) + if (map->buckets[i].key && map->buckets[i].key != TOMBSTONE) nkeys++; + int cap = map->capacity; + while ((nkeys * 100) / cap >= 50) cap = cap * 2; + // Create a new hashmap and copy all key-values. + HashMap map2 = {}; + map2.buckets = calloc(cap, sizeof(HashEntry)); + map2.capacity = cap; + for (int i = 0; i < map->capacity; i++) { + HashEntry *ent = &map->buckets[i]; + if (ent->key && ent->key != TOMBSTONE) + hashmap_put2(&map2, ent->key, ent->keylen, ent->val); + } + assert(map2.used == nkeys); + *map = map2; +} + +static bool match(HashEntry *ent, char *key, int keylen) { + return ent->key && ent->key != TOMBSTONE && ent->keylen == keylen && + memcmp(ent->key, key, keylen) == 0; +} + +static HashEntry *get_entry(HashMap *map, char *key, int keylen) { + if (!map->buckets) return NULL; + uint64_t hash = fnv_hash(key, keylen); + for (int i = 0; i < map->capacity; i++) { + HashEntry *ent = &map->buckets[(hash + i) % map->capacity]; + if (match(ent, key, keylen)) return ent; + if (ent->key == NULL) return NULL; + } + UNREACHABLE(); +} + +static HashEntry *get_or_insert_entry(HashMap *map, char *key, int keylen) { + if (!map->buckets) { + map->buckets = calloc((map->capacity = 16), sizeof(HashEntry)); + } + if ((map->used * 100) / map->capacity >= 70) rehash(map); + uint64_t hash = fnv_hash(key, keylen); + for (int i = 0; i < map->capacity; i++) { + HashEntry *ent = &map->buckets[(hash + i) % map->capacity]; + if (match(ent, key, keylen)) return ent; + if (ent->key == TOMBSTONE) { + ent->key = key; + ent->keylen = keylen; + return ent; + } + if (ent->key == NULL) { + ent->key = key; + ent->keylen = keylen; + map->used++; + return ent; + } + } + UNREACHABLE(); +} + +void *hashmap_get(HashMap *map, char *key) { + return hashmap_get2(map, key, strlen(key)); +} + +void *hashmap_get2(HashMap *map, char *key, int keylen) { + HashEntry *ent = get_entry(map, key, keylen); + return ent ? ent->val : NULL; +} + +void hashmap_put(HashMap *map, char *key, void *val) { + hashmap_put2(map, key, strlen(key), val); +} + +void hashmap_put2(HashMap *map, char *key, int keylen, void *val) { + HashEntry *ent = get_or_insert_entry(map, key, keylen); + ent->val = val; +} + +void hashmap_delete(HashMap *map, char *key) { + hashmap_delete2(map, key, strlen(key)); +} + +void hashmap_delete2(HashMap *map, char *key, int keylen) { + HashEntry *ent = get_entry(map, key, keylen); + if (ent) ent->key = TOMBSTONE; +} + +void hashmap_test(void) { + HashMap *map = calloc(1, sizeof(HashMap)); + for (int i = 0; i < 5000; i++) + hashmap_put(map, format("key %d", i), (void *)(size_t)i); + for (int i = 1000; i < 2000; i++) hashmap_delete(map, format("key %d", i)); + for (int i = 1500; i < 1600; i++) + hashmap_put(map, format("key %d", i), (void *)(size_t)i); + for (int i = 6000; i < 7000; i++) + hashmap_put(map, format("key %d", i), (void *)(size_t)i); + for (int i = 0; i < 1000; i++) + assert((size_t)hashmap_get(map, format("key %d", i)) == i); + for (int i = 1000; i < 1500; i++) + assert(hashmap_get(map, "no such key") == NULL); + for (int i = 1500; i < 1600; i++) + assert((size_t)hashmap_get(map, format("key %d", i)) == i); + for (int i = 1600; i < 2000; i++) + assert(hashmap_get(map, "no such key") == NULL); + for (int i = 2000; i < 5000; i++) + assert((size_t)hashmap_get(map, format("key %d", i)) == i); + for (int i = 5000; i < 6000; i++) + assert(hashmap_get(map, "no such key") == NULL); + for (int i = 6000; i < 7000; i++) + hashmap_put(map, format("key %d", i), (void *)(size_t)i); + assert(hashmap_get(map, "no such key") == NULL); + printf("OK\n"); +} diff --git a/third_party/chibicc/parse.c b/third_party/chibicc/parse.c new file mode 100644 index 000000000..6d394908e --- /dev/null +++ b/third_party/chibicc/parse.c @@ -0,0 +1,3301 @@ +// This file contains a recursive descent parser for C. +// +// Most functions in this file are named after the symbols they are +// supposed to read from an input token list. For example, stmt() is +// responsible for reading a statement from a token list. The function +// then construct an AST node representing a statement. +// +// Each function conceptually returns two values, an AST node and +// remaining part of the input tokens. Since C doesn't support +// multiple return values, the remaining tokens are returned to the +// caller via a pointer argument. +// +// Input tokens are represented by a linked list. Unlike many recursive +// descent parsers, we don't have the notion of the "input token stream". +// Most parsing functions don't change the global state of the parser. +// So it is very easy to lookahead arbitrary number of tokens in this +// parser. + +#include "third_party/chibicc/chibicc.h" + +// Scope for local variables, global variables, typedefs +// or enum constants +typedef struct { + char *name; + int depth; + + Obj *var; + Type *type_def; + Type *enum_ty; + int enum_val; +} VarScope; + +// Scope for struct, union or enum tags +typedef struct { + char *name; + int depth; + Type *ty; +} TagScope; + +typedef struct Scope Scope; +struct Scope { + Scope *next; + + // C has two block scopes; one is for variables/typedefs and + // the other is for struct/union/enum tags. + HashMap vars; + HashMap tags; +}; + +// Variable attributes such as typedef or extern. +typedef struct { + bool is_typedef; + bool is_static; + bool is_extern; + bool is_inline; + bool is_tls; + int align; +} VarAttr; + +// This struct represents a variable initializer. Since initializers +// can be nested (e.g. `int x[2][2] = {{1, 2}, {3, 4}}`), this struct +// is a tree data structure. +typedef struct Initializer Initializer; +struct Initializer { + Initializer *next; + Type *ty; + Token *tok; + bool is_flexible; + + // If it's not an aggregate type and has an initializer, + // `expr` has an initialization expression. + Node *expr; + + // If it's an initializer for an aggregate type (e.g. array or struct), + // `children` has initializers for its children. + Initializer **children; + + // Only one member can be initialized for a union. + // `mem` is used to clarify which member is initialized. + Member *mem; +}; + +// For local variable initializer. +typedef struct InitDesg InitDesg; +struct InitDesg { + InitDesg *next; + int idx; + Member *member; + Obj *var; +}; + +// All local variable instances created during parsing are +// accumulated to this list. +static Obj *locals; + +// Likewise, global variables are accumulated to this list. +static Obj *globals; + +static Scope *scope = &(Scope){}; + +// scope_depth is incremented by one at the beginning of a block +// scope and decremented by one at the end of a block scope. +static int scope_depth; + +// Points to the function object the parser is currently parsing. +static Obj *current_fn; + +// Lists of all goto statements and labels in the curent function. +static Node *gotos; +static Node *labels; + +// Current "goto" and "continue" jump targets. +static char *brk_label; +static char *cont_label; + +// Points to a node representing a switch if we are parsing +// a switch statement. Otherwise, NULL. +static Node *current_switch; + +static Obj *builtin_alloca; + +struct StaticAsm *staticasms; + +static bool is_typename(Token *tok); +static Type *typespec(Token **rest, Token *tok, VarAttr *attr); +static Type *typename(Token **rest, Token *tok); +static Type *enum_specifier(Token **rest, Token *tok); +static Type *typeof_specifier(Token **rest, Token *tok); +static Type *type_suffix(Token **rest, Token *tok, Type *ty); +static Type *declarator(Token **rest, Token *tok, Type *ty); +static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr); +static void array_initializer2(Token **rest, Token *tok, Initializer *init, + int i); +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, + Member *mem); +static void initializer2(Token **rest, Token *tok, Initializer *init); +static Initializer *initializer(Token **rest, Token *tok, Type *ty, + Type **new_ty); +static Node *lvar_initializer(Token **rest, Token *tok, Obj *var); +static void gvar_initializer(Token **rest, Token *tok, Obj *var); +static Node *compound_stmt(Token **rest, Token *tok); +static Node *stmt(Token **rest, Token *tok); +static Node *expr_stmt(Token **rest, Token *tok); +static Node *expr(Token **rest, Token *tok); +static int64_t eval(Node *node); +static int64_t eval2(Node *node, char ***label); +static int64_t eval_rval(Node *node, char ***label); +static bool is_const_expr(Node *node); +static Node *assign(Token **rest, Token *tok); +static Node *logor(Token **rest, Token *tok); +static double eval_double(Node *node); +static Node *conditional(Token **rest, Token *tok); +static Node *logand(Token **rest, Token *tok); +static Node * bitor (Token * *rest, Token *tok); +static Node *bitxor(Token **rest, Token *tok); +static Node *bitand(Token **rest, Token *tok); +static Node *equality(Token **rest, Token *tok); +static Node *relational(Token **rest, Token *tok); +static Node *shift(Token **rest, Token *tok); +static Node *add(Token **rest, Token *tok); +static Node *new_add(Node *lhs, Node *rhs, Token *tok); +static Node *new_sub(Node *lhs, Node *rhs, Token *tok); +static Node *mul(Token **rest, Token *tok); +static Node *cast(Token **rest, Token *tok); +static Member *get_struct_member(Type *ty, Token *tok); +static Type *struct_decl(Token **rest, Token *tok); +static Type *union_decl(Token **rest, Token *tok); +static Node *postfix(Token **rest, Token *tok); +static Node *funcall(Token **rest, Token *tok, Node *node); +static Node *unary(Token **rest, Token *tok); +static Node *primary(Token **rest, Token *tok); +static Token *parse_typedef(Token *tok, Type *basety); +static bool is_function(Token *tok); +static Token *function(Token *tok, Type *basety, VarAttr *attr); +static Token *global_variable(Token *tok, Type *basety, VarAttr *attr); + +static int align_down(int n, int align) { + return align_to(n - align + 1, align); +} + +static void enter_scope(void) { + Scope *sc = calloc(1, sizeof(Scope)); + sc->next = scope; + scope = sc; + scope_depth++; +} + +static void leave_scope(void) { + scope = scope->next; + scope_depth--; +} + +// Find a variable by name. +static VarScope *find_var(Token *tok) { + for (Scope *sc = scope; sc; sc = sc->next) { + VarScope *sc2 = hashmap_get2(&sc->vars, tok->loc, tok->len); + if (sc2) return sc2; + } + return NULL; +} + +static TagScope *find_tag(Token *tok) { + for (Scope *sc = scope; sc; sc = sc->next) { + TagScope *sc2 = hashmap_get2(&sc->tags, tok->loc, tok->len); + if (sc2) return sc2; + } + return NULL; +} + +static Node *new_node(NodeKind kind, Token *tok) { + Node *node = calloc(1, sizeof(Node)); + node->kind = kind; + node->tok = tok; + return node; +} + +static Node *new_binary(NodeKind kind, Node *lhs, Node *rhs, Token *tok) { + Node *node = new_node(kind, tok); + node->lhs = lhs; + node->rhs = rhs; + return node; +} + +static Node *new_unary(NodeKind kind, Node *expr, Token *tok) { + Node *node = new_node(kind, tok); + node->lhs = expr; + return node; +} + +static Node *new_num(int64_t val, Token *tok) { + Node *node = new_node(ND_NUM, tok); + node->val = val; + return node; +} + +static Node *new_long(int64_t val, Token *tok) { + Node *node = new_node(ND_NUM, tok); + node->val = val; + node->ty = ty_long; + return node; +} + +static Node *new_ulong(long val, Token *tok) { + Node *node = new_node(ND_NUM, tok); + node->val = val; + node->ty = ty_ulong; + return node; +} + +static Node *new_var_node(Obj *var, Token *tok) { + Node *node = new_node(ND_VAR, tok); + node->var = var; + return node; +} + +static Node *new_vla_ptr(Obj *var, Token *tok) { + Node *node = new_node(ND_VLA_PTR, tok); + node->var = var; + return node; +} + +Node *new_cast(Node *expr, Type *ty) { + add_type(expr); + + Node *node = calloc(1, sizeof(Node)); + node->kind = ND_CAST; + node->tok = expr->tok; + node->lhs = expr; + node->ty = copy_type(ty); + return node; +} + +static VarScope *push_scope(char *name) { + VarScope *sc = calloc(1, sizeof(VarScope)); + sc->name = name; + sc->depth = scope_depth; + hashmap_put(&scope->vars, name, sc); + return sc; +} + +static Initializer *new_initializer(Type *ty, bool is_flexible) { + Initializer *init = calloc(1, sizeof(Initializer)); + init->ty = ty; + + if (ty->kind == TY_ARRAY) { + if (is_flexible && ty->size < 0) { + init->is_flexible = true; + return init; + } + + init->children = calloc(ty->array_len, sizeof(Initializer *)); + for (int i = 0; i < ty->array_len; i++) + init->children[i] = new_initializer(ty->base, false); + return init; + } + + if (ty->kind == TY_STRUCT || ty->kind == TY_UNION) { + // Count the number of struct members. + int len = 0; + for (Member *mem = ty->members; mem; mem = mem->next) len++; + + init->children = calloc(len, sizeof(Initializer *)); + + for (Member *mem = ty->members; mem; mem = mem->next) { + if (is_flexible && ty->is_flexible && !mem->next) { + Initializer *child = calloc(1, sizeof(Initializer)); + child->ty = mem->ty; + child->is_flexible = true; + init->children[mem->idx] = child; + } else { + init->children[mem->idx] = new_initializer(mem->ty, false); + } + } + return init; + } + + return init; +} + +static Obj *new_var(char *name, Type *ty) { + Obj *var = calloc(1, sizeof(Obj)); + var->name = name; + var->ty = ty; + var->align = ty->align; + push_scope(name)->var = var; + return var; +} + +static Obj *new_lvar(char *name, Type *ty) { + Obj *var = new_var(name, ty); + var->is_local = true; + var->next = locals; + locals = var; + return var; +} + +static Obj *new_gvar(char *name, Type *ty) { + Obj *var = new_var(name, ty); + var->next = globals; + var->is_static = true; + var->is_definition = true; + globals = var; + return var; +} + +static char *new_unique_name(void) { + static int id = 0; + char *buf = calloc(1, 20); + sprintf(buf, ".L..%d", id++); + return buf; +} + +static Obj *new_anon_gvar(Type *ty) { + return new_gvar(new_unique_name(), ty); +} + +static Obj *new_string_literal(char *p, Type *ty) { + Obj *var = new_anon_gvar(ty); + var->init_data = p; + return var; +} + +static char *get_ident(Token *tok) { + if (tok->kind != TK_IDENT) error_tok(tok, "expected an identifier"); + return strndup(tok->loc, tok->len); +} + +static Type *find_typedef(Token *tok) { + if (tok->kind == TK_IDENT) { + VarScope *sc = find_var(tok); + if (sc) return sc->type_def; + } + return NULL; +} + +static void push_tag_scope(Token *tok, Type *ty) { + TagScope *sc = calloc(1, sizeof(TagScope)); + sc->name = strndup(tok->loc, tok->len); + sc->depth = scope_depth; + sc->ty = ty; + hashmap_put2(&scope->tags, tok->loc, tok->len, sc); +} + +// typespec = typename typename* +// typename = "void" | "_Bool" | "char" | "short" | "int" | "long" +// | struct-decl | union-decl | typedef-name +// +// The order of typenames in a type-specifier doesn't matter. For +// example, `int long static` means the same as `static long int`. +// That can also be written as `static long` because you can omit +// `int` if `long` or `short` are specified. However, something like +// `char int` is not a valid type specifier. We have to accept only a +// limited combinations of the typenames. +// +// In this function, we count the number of occurrences of each typename +// while keeping the "current" type object that the typenames up +// until that point represent. When we reach a non-typename token, +// we returns the current type object. +static Type *typespec(Token **rest, Token *tok, VarAttr *attr) { + // We use a single integer as counters for all typenames. + // For example, bits 0 and 1 represents how many times we saw the + // keyword "void" so far. With this, we can use a switch statement + // as you can see below. + enum { + VOID = 1 << 0, + BOOL = 1 << 2, + CHAR = 1 << 4, + SHORT = 1 << 6, + INT = 1 << 8, + LONG = 1 << 10, + FLOAT = 1 << 12, + DOUBLE = 1 << 14, + OTHER = 1 << 16, + SIGNED = 1 << 17, + UNSIGNED = 1 << 18, + }; + + Type *ty = ty_int; + int counter = 0; + bool is_atomic = false; + + while (is_typename(tok)) { + // Handle storage class specifiers. + if (equal(tok, "typedef") || equal(tok, "static") || equal(tok, "extern") || + equal(tok, "inline") || equal(tok, "_Thread_local") || + equal(tok, "__thread")) { + if (!attr) + error_tok(tok, + "storage class specifier is not allowed in this context"); + + if (equal(tok, "typedef")) + attr->is_typedef = true; + else if (equal(tok, "static")) + attr->is_static = true; + else if (equal(tok, "extern")) + attr->is_extern = true; + else if (equal(tok, "inline")) + attr->is_inline = true; + else + attr->is_tls = true; + + if (attr->is_typedef && + attr->is_static + attr->is_extern + attr->is_inline + attr->is_tls > + 1) + error_tok(tok, "typedef may not be used together with static," + " extern, inline, __thread or _Thread_local"); + tok = tok->next; + continue; + } + + // These keywords are recognized but ignored. + if (consume(&tok, tok, "const") || consume(&tok, tok, "volatile") || + consume(&tok, tok, "auto") || consume(&tok, tok, "register") || + consume(&tok, tok, "restrict") || consume(&tok, tok, "__restrict") || + consume(&tok, tok, "__restrict__") || consume(&tok, tok, "_Noreturn")) + continue; + + if (equal(tok, "_Atomic")) { + tok = tok->next; + if (equal(tok, "(")) { + ty = typename(&tok, tok->next); + tok = skip(tok, ")"); + } + is_atomic = true; + continue; + } + + if (equal(tok, "_Alignas")) { + if (!attr) error_tok(tok, "_Alignas is not allowed in this context"); + tok = skip(tok->next, "("); + + if (is_typename(tok)) + attr->align = typename(&tok, tok)->align; + else + attr->align = const_expr(&tok, tok); + tok = skip(tok, ")"); + continue; + } + + // Handle user-defined types. + Type *ty2 = find_typedef(tok); + if (equal(tok, "struct") || equal(tok, "union") || equal(tok, "enum") || + equal(tok, "typeof") || ty2) { + if (counter) break; + + if (equal(tok, "struct")) { + ty = struct_decl(&tok, tok->next); + } else if (equal(tok, "union")) { + ty = union_decl(&tok, tok->next); + } else if (equal(tok, "enum")) { + ty = enum_specifier(&tok, tok->next); + } else if (equal(tok, "typeof")) { + ty = typeof_specifier(&tok, tok->next); + } else { + ty = ty2; + tok = tok->next; + } + + counter += OTHER; + continue; + } + + // Handle built-in types. + if (equal(tok, "void")) + counter += VOID; + else if (equal(tok, "_Bool")) + counter += BOOL; + else if (equal(tok, "char")) + counter += CHAR; + else if (equal(tok, "short")) + counter += SHORT; + else if (equal(tok, "int")) + counter += INT; + else if (equal(tok, "long")) + counter += LONG; + else if (equal(tok, "float")) + counter += FLOAT; + else if (equal(tok, "double")) + counter += DOUBLE; + else if (equal(tok, "signed")) + counter |= SIGNED; + else if (equal(tok, "unsigned")) + counter |= UNSIGNED; + else + UNREACHABLE(); + + switch (counter) { + case VOID: + ty = ty_void; + break; + case BOOL: + ty = ty_bool; + break; + case CHAR: + case SIGNED + CHAR: + ty = ty_char; + break; + case UNSIGNED + CHAR: + ty = ty_uchar; + break; + case SHORT: + case SHORT + INT: + case SIGNED + SHORT: + case SIGNED + SHORT + INT: + ty = ty_short; + break; + case UNSIGNED + SHORT: + case UNSIGNED + SHORT + INT: + ty = ty_ushort; + break; + case INT: + case SIGNED: + case SIGNED + INT: + ty = ty_int; + break; + case UNSIGNED: + case UNSIGNED + INT: + ty = ty_uint; + break; + case LONG: + case LONG + INT: + case LONG + LONG: + case LONG + LONG + INT: + case SIGNED + LONG: + case SIGNED + LONG + INT: + case SIGNED + LONG + LONG: + case SIGNED + LONG + LONG + INT: + ty = ty_long; + break; + case UNSIGNED + LONG: + case UNSIGNED + LONG + INT: + case UNSIGNED + LONG + LONG: + case UNSIGNED + LONG + LONG + INT: + ty = ty_ulong; + break; + case FLOAT: + ty = ty_float; + break; + case DOUBLE: + ty = ty_double; + break; + case LONG + DOUBLE: + ty = ty_ldouble; + break; + default: + error_tok(tok, "invalid type"); + } + + tok = tok->next; + } + + if (is_atomic) { + ty = copy_type(ty); + ty->is_atomic = true; + } + + *rest = tok; + return ty; +} + +// func-params = ("void" | param ("," param)* ("," "...")?)? ")" +// param = typespec declarator +static Type *func_params(Token **rest, Token *tok, Type *ty) { + if (equal(tok, "void") && equal(tok->next, ")")) { + *rest = tok->next->next; + return func_type(ty); + } + + Type head = {}; + Type *cur = &head; + bool is_variadic = false; + + while (!equal(tok, ")")) { + if (cur != &head) tok = skip(tok, ","); + + if (equal(tok, "...")) { + is_variadic = true; + tok = tok->next; + skip(tok, ")"); + break; + } + + Type *ty2 = typespec(&tok, tok, NULL); + ty2 = declarator(&tok, tok, ty2); + + Token *name = ty2->name; + + if (ty2->kind == TY_ARRAY) { + // "array of T" is converted to "pointer to T" only in the parameter + // context. For example, *argv[] is converted to **argv by this. + ty2 = pointer_to(ty2->base); + ty2->name = name; + } else if (ty2->kind == TY_FUNC) { + // Likewise, a function is converted to a pointer to a function + // only in the parameter context. + ty2 = pointer_to(ty2); + ty2->name = name; + } + + cur = cur->next = copy_type(ty2); + } + + if (cur == &head) is_variadic = true; + + ty = func_type(ty); + ty->params = head.next; + ty->is_variadic = is_variadic; + *rest = tok->next; + return ty; +} + +// array-dimensions = ("static" | "restrict")* const-expr? "]" type-suffix +static Type *array_dimensions(Token **rest, Token *tok, Type *ty) { + while (equal(tok, "static") || equal(tok, "restrict")) tok = tok->next; + + if (equal(tok, "]")) { + ty = type_suffix(rest, tok->next, ty); + return array_of(ty, -1); + } + + Node *expr = conditional(&tok, tok); + tok = skip(tok, "]"); + ty = type_suffix(rest, tok, ty); + + if (ty->kind == TY_VLA || !is_const_expr(expr)) return vla_of(ty, expr); + return array_of(ty, eval(expr)); +} + +// type-suffix = "(" func-params +// | "[" array-dimensions +// | ε +static Type *type_suffix(Token **rest, Token *tok, Type *ty) { + if (equal(tok, "(")) return func_params(rest, tok->next, ty); + + if (equal(tok, "[")) return array_dimensions(rest, tok->next, ty); + + *rest = tok; + return ty; +} + +// pointers = ("*" ("const" | "volatile" | "restrict")*)* +static Type *pointers(Token **rest, Token *tok, Type *ty) { + while (consume(&tok, tok, "*")) { + ty = pointer_to(ty); + while (equal(tok, "const") || equal(tok, "volatile") || + equal(tok, "restrict") || equal(tok, "__restrict") || + equal(tok, "__restrict__")) + tok = tok->next; + } + *rest = tok; + return ty; +} + +// declarator = pointers ("(" ident ")" | "(" declarator ")" | ident) +// type-suffix +static Type *declarator(Token **rest, Token *tok, Type *ty) { + ty = pointers(&tok, tok, ty); + + if (equal(tok, "(")) { + Token *start = tok; + Type ignore = {}; + declarator(&tok, tok->next, &ignore); + tok = skip(tok, ")"); + ty = type_suffix(rest, tok, ty); + return declarator(&tok, start->next, ty); + } + + Token *name = NULL; + Token *name_pos = tok; + + if (tok->kind == TK_IDENT) { + name = tok; + tok = tok->next; + } + + ty = type_suffix(rest, tok, ty); + ty->name = name; + ty->name_pos = name_pos; + return ty; +} + +// abstract-declarator = pointers ("(" abstract-declarator ")")? type-suffix +static Type *abstract_declarator(Token **rest, Token *tok, Type *ty) { + ty = pointers(&tok, tok, ty); + + if (equal(tok, "(")) { + Token *start = tok; + Type ignore = {}; + abstract_declarator(&tok, tok->next, &ignore); + tok = skip(tok, ")"); + ty = type_suffix(rest, tok, ty); + return abstract_declarator(&tok, start->next, ty); + } + + return type_suffix(rest, tok, ty); +} + +// type-name = typespec abstract-declarator +static Type *typename(Token **rest, Token *tok) { + Type *ty = typespec(&tok, tok, NULL); + return abstract_declarator(rest, tok, ty); +} + +static bool is_end(Token *tok) { + return equal(tok, "}") || (equal(tok, ",") && equal(tok->next, "}")); +} + +static bool consume_end(Token **rest, Token *tok) { + if (equal(tok, "}")) { + *rest = tok->next; + return true; + } + + if (equal(tok, ",") && equal(tok->next, "}")) { + *rest = tok->next->next; + return true; + } + + return false; +} + +// enum-specifier = ident? "{" enum-list? "}" +// | ident ("{" enum-list? "}")? +// +// enum-list = ident ("=" num)? ("," ident ("=" num)?)* ","? +static Type *enum_specifier(Token **rest, Token *tok) { + Type *ty = enum_type(); + + // Read a struct tag. + Token *tag = NULL; + if (tok->kind == TK_IDENT) { + tag = tok; + tok = tok->next; + } + + if (tag && !equal(tok, "{")) { + TagScope *sc = find_tag(tag); + if (!sc) error_tok(tag, "unknown enum type"); + if (sc->ty->kind != TY_ENUM) error_tok(tag, "not an enum tag"); + *rest = tok; + return sc->ty; + } + + tok = skip(tok, "{"); + + // Read an enum-list. + int i = 0; + int val = 0; + while (!consume_end(rest, tok)) { + if (i++ > 0) tok = skip(tok, ","); + + char *name = get_ident(tok); + tok = tok->next; + + if (equal(tok, "=")) val = const_expr(&tok, tok->next); + + VarScope *sc = push_scope(name); + sc->enum_ty = ty; + sc->enum_val = val++; + } + + if (tag) push_tag_scope(tag, ty); + return ty; +} + +// typeof-specifier = "(" (expr | typename) ")" +static Type *typeof_specifier(Token **rest, Token *tok) { + tok = skip(tok, "("); + + Type *ty; + if (is_typename(tok)) { + ty = typename(&tok, tok); + } else { + Node *node = expr(&tok, tok); + add_type(node); + ty = node->ty; + } + *rest = skip(tok, ")"); + return ty; +} + +// Generate code for computing a VLA size. +static Node *compute_vla_size(Type *ty, Token *tok) { + Node *node = new_node(ND_NULL_EXPR, tok); + if (ty->base) + node = new_binary(ND_COMMA, node, compute_vla_size(ty->base, tok), tok); + + if (ty->kind != TY_VLA) return node; + + Node *base_sz; + if (ty->base->kind == TY_VLA) + base_sz = new_var_node(ty->base->vla_size, tok); + else + base_sz = new_num(ty->base->size, tok); + + ty->vla_size = new_lvar("", ty_ulong); + Node *expr = new_binary(ND_ASSIGN, new_var_node(ty->vla_size, tok), + new_binary(ND_MUL, ty->vla_len, base_sz, tok), tok); + return new_binary(ND_COMMA, node, expr, tok); +} + +static Node *new_alloca(Node *sz) { + Node *node = + new_unary(ND_FUNCALL, new_var_node(builtin_alloca, sz->tok), sz->tok); + node->func_ty = builtin_alloca->ty; + node->ty = builtin_alloca->ty->return_ty; + node->args = sz; + add_type(sz); + return node; +} + +// declaration = typespec (declarator ("=" expr)? ("," declarator ("=" +// expr)?)*)? ";" +static Node *declaration(Token **rest, Token *tok, Type *basety, + VarAttr *attr) { + Node head = {}; + Node *cur = &head; + int i = 0; + + while (!equal(tok, ";")) { + if (i++ > 0) tok = skip(tok, ","); + + Type *ty = declarator(&tok, tok, basety); + if (ty->kind == TY_VOID) error_tok(tok, "variable declared void"); + if (!ty->name) error_tok(ty->name_pos, "variable name omitted"); + + if (attr && attr->is_static) { + // static local variable + Obj *var = new_anon_gvar(ty); + push_scope(get_ident(ty->name))->var = var; + if (equal(tok, "=")) gvar_initializer(&tok, tok->next, var); + continue; + } + + // Generate code for computing a VLA size. We need to do this + // even if ty is not VLA because ty may be a pointer to VLA + // (e.g. int (*foo)[n][m] where n and m are variables.) + cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok); + + if (ty->kind == TY_VLA) { + if (equal(tok, "=")) + error_tok(tok, "variable-sized object may not be initialized"); + + // Variable length arrays (VLAs) are translated to alloca() calls. + // For example, `int x[n+2]` is translated to `tmp = n + 2, + // x = alloca(tmp)`. + Obj *var = new_lvar(get_ident(ty->name), ty); + Token *tok = ty->name; + Node *expr = new_binary(ND_ASSIGN, new_vla_ptr(var, tok), + new_alloca(new_var_node(ty->vla_size, tok)), tok); + + cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok); + continue; + } + + Obj *var = new_lvar(get_ident(ty->name), ty); + if (attr && attr->align) var->align = attr->align; + + if (equal(tok, "=")) { + Node *expr = lvar_initializer(&tok, tok->next, var); + cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok); + } + + if (var->ty->size < 0) error_tok(ty->name, "variable has incomplete type"); + if (var->ty->kind == TY_VOID) error_tok(ty->name, "variable declared void"); + } + + Node *node = new_node(ND_BLOCK, tok); + node->body = head.next; + *rest = tok->next; + return node; +} + +static Token *skip_excess_element(Token *tok) { + if (equal(tok, "{")) { + tok = skip_excess_element(tok->next); + return skip(tok, "}"); + } + + assign(&tok, tok); + return tok; +} + +// string-initializer = string-literal +static void string_initializer(Token **rest, Token *tok, Initializer *init) { + if (init->is_flexible) + *init = + *new_initializer(array_of(init->ty->base, tok->ty->array_len), false); + + int len = MIN(init->ty->array_len, tok->ty->array_len); + + switch (init->ty->base->size) { + case 1: { + char *str = tok->str; + for (int i = 0; i < len; i++) + init->children[i]->expr = new_num(str[i], tok); + break; + } + case 2: { + uint16_t *str = (uint16_t *)tok->str; + for (int i = 0; i < len; i++) + init->children[i]->expr = new_num(str[i], tok); + break; + } + case 4: { + uint32_t *str = (uint32_t *)tok->str; + for (int i = 0; i < len; i++) + init->children[i]->expr = new_num(str[i], tok); + break; + } + default: + UNREACHABLE(); + } + + *rest = tok->next; +} + +// array-designator = "[" const-expr "]" +// +// C99 added the designated initializer to the language, which allows +// programmers to move the "cursor" of an initializer to any element. +// The syntax looks like this: +// +// int x[10] = { 1, 2, [5]=3, 4, 5, 6, 7 }; +// +// `[5]` moves the cursor to the 5th element, so the 5th element of x +// is set to 3. Initialization then continues forward in order, so +// 6th, 7th, 8th and 9th elements are initialized with 4, 5, 6 and 7, +// respectively. Unspecified elements (in this case, 3rd and 4th +// elements) are initialized with zero. +// +// Nesting is allowed, so the following initializer is valid: +// +// int x[5][10] = { [5][8]=1, 2, 3 }; +// +// It sets x[5][8], x[5][9] and x[6][0] to 1, 2 and 3, respectively. +// +// Use `.fieldname` to move the cursor for a struct initializer. E.g. +// +// struct { int a, b, c; } x = { .c=5 }; +// +// The above initializer sets x.c to 5. +static void array_designator(Token **rest, Token *tok, Type *ty, int *begin, + int *end) { + *begin = const_expr(&tok, tok->next); + if (*begin >= ty->array_len) + error_tok(tok, "array designator index exceeds array bounds"); + + if (equal(tok, "...")) { + *end = const_expr(&tok, tok->next); + if (*end >= ty->array_len) + error_tok(tok, "array designator index exceeds array bounds"); + if (*end < *begin) + error_tok(tok, "array designator range [%d, %d] is empty", *begin, *end); + } else { + *end = *begin; + } + + *rest = skip(tok, "]"); +} + +// struct-designator = "." ident +static Member *struct_designator(Token **rest, Token *tok, Type *ty) { + Token *start = tok; + tok = skip(tok, "."); + if (tok->kind != TK_IDENT) error_tok(tok, "expected a field designator"); + + for (Member *mem = ty->members; mem; mem = mem->next) { + // Anonymous struct member + if (mem->ty->kind == TY_STRUCT && !mem->name) { + if (get_struct_member(mem->ty, tok)) { + *rest = start; + return mem; + } + continue; + } + + // Regular struct member + if (mem->name->len == tok->len && + !strncmp(mem->name->loc, tok->loc, tok->len)) { + *rest = tok->next; + return mem; + } + } + + error_tok(tok, "struct has no such member"); +} + +// designation = ("[" const-expr "]" | "." ident)* "="? initializer +static void designation(Token **rest, Token *tok, Initializer *init) { + if (equal(tok, "[")) { + if (init->ty->kind != TY_ARRAY) + error_tok(tok, "array index in non-array initializer"); + + int begin, end; + array_designator(&tok, tok, init->ty, &begin, &end); + + Token *tok2; + for (int i = begin; i <= end; i++) + designation(&tok2, tok, init->children[i]); + array_initializer2(rest, tok2, init, begin + 1); + return; + } + + if (equal(tok, ".") && init->ty->kind == TY_STRUCT) { + Member *mem = struct_designator(&tok, tok, init->ty); + designation(&tok, tok, init->children[mem->idx]); + init->expr = NULL; + struct_initializer2(rest, tok, init, mem->next); + return; + } + + if (equal(tok, ".") && init->ty->kind == TY_UNION) { + Member *mem = struct_designator(&tok, tok, init->ty); + init->mem = mem; + designation(rest, tok, init->children[mem->idx]); + return; + } + + if (equal(tok, ".")) + error_tok(tok, "field name not in struct or union initializer"); + + if (equal(tok, "=")) tok = tok->next; + initializer2(rest, tok, init); +} + +// An array length can be omitted if an array has an initializer +// (e.g. `int x[] = {1,2,3}`). If it's omitted, count the number +// of initializer elements. +static int count_array_init_elements(Token *tok, Type *ty) { + bool first = true; + Initializer *dummy = new_initializer(ty->base, true); + + int i = 0, max = 0; + + while (!consume_end(&tok, tok)) { + if (!first) tok = skip(tok, ","); + first = false; + + if (equal(tok, "[")) { + i = const_expr(&tok, tok->next); + if (equal(tok, "...")) i = const_expr(&tok, tok->next); + tok = skip(tok, "]"); + designation(&tok, tok, dummy); + } else { + initializer2(&tok, tok, dummy); + } + + i++; + max = MAX(max, i); + } + return max; +} + +// array-initializer1 = "{" initializer ("," initializer)* ","? "}" +static void array_initializer1(Token **rest, Token *tok, Initializer *init) { + tok = skip(tok, "{"); + + if (init->is_flexible) { + int len = count_array_init_elements(tok, init->ty); + *init = *new_initializer(array_of(init->ty->base, len), false); + } + + bool first = true; + + if (init->is_flexible) { + int len = count_array_init_elements(tok, init->ty); + *init = *new_initializer(array_of(init->ty->base, len), false); + } + + for (int i = 0; !consume_end(rest, tok); i++) { + if (!first) tok = skip(tok, ","); + first = false; + + if (equal(tok, "[")) { + int begin, end; + array_designator(&tok, tok, init->ty, &begin, &end); + + Token *tok2; + for (int j = begin; j <= end; j++) + designation(&tok2, tok, init->children[j]); + tok = tok2; + i = end; + continue; + } + + if (i < init->ty->array_len) + initializer2(&tok, tok, init->children[i]); + else + tok = skip_excess_element(tok); + } +} + +// array-initializer2 = initializer ("," initializer)* +static void array_initializer2(Token **rest, Token *tok, Initializer *init, + int i) { + if (init->is_flexible) { + int len = count_array_init_elements(tok, init->ty); + *init = *new_initializer(array_of(init->ty->base, len), false); + } + + for (; i < init->ty->array_len && !is_end(tok); i++) { + Token *start = tok; + if (i > 0) tok = skip(tok, ","); + + if (equal(tok, "[") || equal(tok, ".")) { + *rest = start; + return; + } + + initializer2(&tok, tok, init->children[i]); + } + *rest = tok; +} + +// struct-initializer1 = "{" initializer ("," initializer)* ","? "}" +static void struct_initializer1(Token **rest, Token *tok, Initializer *init) { + tok = skip(tok, "{"); + + Member *mem = init->ty->members; + bool first = true; + + while (!consume_end(rest, tok)) { + if (!first) tok = skip(tok, ","); + first = false; + + if (equal(tok, ".")) { + mem = struct_designator(&tok, tok, init->ty); + designation(&tok, tok, init->children[mem->idx]); + mem = mem->next; + continue; + } + + if (mem) { + initializer2(&tok, tok, init->children[mem->idx]); + mem = mem->next; + } else { + tok = skip_excess_element(tok); + } + } +} + +// struct-initializer2 = initializer ("," initializer)* +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, + Member *mem) { + for (; mem && !is_end(tok); mem = mem->next) { + Token *start = tok; + if (mem != init->ty->members) tok = skip(tok, ","); + + if (equal(tok, "[") || equal(tok, ".")) { + *rest = start; + return; + } + + initializer2(&tok, tok, init->children[mem->idx]); + } + *rest = tok; +} + +static void union_initializer(Token **rest, Token *tok, Initializer *init) { + // Unlike structs, union initializers take only one initializer, + // and that initializes the first union member by default. + // You can initialize other member using a designated initializer. + if (equal(tok, "{") && equal(tok->next, ".")) { + Member *mem = struct_designator(&tok, tok->next, init->ty); + init->mem = mem; + designation(&tok, tok, init->children[mem->idx]); + *rest = skip(tok, "}"); + return; + } + + init->mem = init->ty->members; + + if (equal(tok, "{")) { + initializer2(&tok, tok->next, init->children[0]); + *rest = skip(tok, "}"); + } else { + initializer2(rest, tok, init->children[0]); + } +} + +// initializer = string-initializer | array-initializer +// | struct-initializer | union-initializer +// | assign +static void initializer2(Token **rest, Token *tok, Initializer *init) { + if (init->ty->kind == TY_ARRAY && tok->kind == TK_STR) { + string_initializer(rest, tok, init); + return; + } + + if (init->ty->kind == TY_ARRAY) { + if (equal(tok, "{")) + array_initializer1(rest, tok, init); + else + array_initializer2(rest, tok, init, 0); + return; + } + + if (init->ty->kind == TY_STRUCT) { + if (equal(tok, "{")) { + struct_initializer1(rest, tok, init); + return; + } + + // A struct can be initialized with another struct. E.g. + // `struct T x = y;` where y is a variable of type `struct T`. + // Handle that case first. + Node *expr = assign(rest, tok); + add_type(expr); + if (expr->ty->kind == TY_STRUCT) { + init->expr = expr; + return; + } + + struct_initializer2(rest, tok, init, init->ty->members); + return; + } + + if (init->ty->kind == TY_UNION) { + union_initializer(rest, tok, init); + return; + } + + if (equal(tok, "{")) { + // An initializer for a scalar variable can be surrounded by + // braces. E.g. `int x = {3};`. Handle that case. + initializer2(&tok, tok->next, init); + *rest = skip(tok, "}"); + return; + } + + init->expr = assign(rest, tok); +} + +static Type *copy_struct_type(Type *ty) { + ty = copy_type(ty); + + Member head = {}; + Member *cur = &head; + for (Member *mem = ty->members; mem; mem = mem->next) { + Member *m = calloc(1, sizeof(Member)); + *m = *mem; + cur = cur->next = m; + } + + ty->members = head.next; + return ty; +} + +static Initializer *initializer(Token **rest, Token *tok, Type *ty, + Type **new_ty) { + Initializer *init = new_initializer(ty, true); + initializer2(rest, tok, init); + + if ((ty->kind == TY_STRUCT || ty->kind == TY_UNION) && ty->is_flexible) { + ty = copy_struct_type(ty); + + Member *mem = ty->members; + while (mem->next) mem = mem->next; + mem->ty = init->children[mem->idx]->ty; + ty->size += mem->ty->size; + + *new_ty = ty; + return init; + } + + *new_ty = init->ty; + return init; +} + +static Node *init_desg_expr(InitDesg *desg, Token *tok) { + if (desg->var) return new_var_node(desg->var, tok); + + if (desg->member) { + Node *node = new_unary(ND_MEMBER, init_desg_expr(desg->next, tok), tok); + node->member = desg->member; + return node; + } + + Node *lhs = init_desg_expr(desg->next, tok); + Node *rhs = new_num(desg->idx, tok); + return new_unary(ND_DEREF, new_add(lhs, rhs, tok), tok); +} + +static Node *create_lvar_init(Initializer *init, Type *ty, InitDesg *desg, + Token *tok) { + if (ty->kind == TY_ARRAY) { + Node *node = new_node(ND_NULL_EXPR, tok); + for (int i = 0; i < ty->array_len; i++) { + InitDesg desg2 = {desg, i}; + Node *rhs = create_lvar_init(init->children[i], ty->base, &desg2, tok); + node = new_binary(ND_COMMA, node, rhs, tok); + } + return node; + } + + if (ty->kind == TY_STRUCT && !init->expr) { + Node *node = new_node(ND_NULL_EXPR, tok); + + for (Member *mem = ty->members; mem; mem = mem->next) { + InitDesg desg2 = {desg, 0, mem}; + Node *rhs = + create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok); + node = new_binary(ND_COMMA, node, rhs, tok); + } + return node; + } + + if (ty->kind == TY_UNION) { + Member *mem = init->mem ? init->mem : ty->members; + InitDesg desg2 = {desg, 0, mem}; + return create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok); + } + + if (!init->expr) return new_node(ND_NULL_EXPR, tok); + + Node *lhs = init_desg_expr(desg, tok); + return new_binary(ND_ASSIGN, lhs, init->expr, tok); +} + +// A variable definition with an initializer is a shorthand notation +// for a variable definition followed by assignments. This function +// generates assignment expressions for an initializer. For example, +// `int x[2][2] = {{6, 7}, {8, 9}}` is converted to the following +// expressions: +// +// x[0][0] = 6; +// x[0][1] = 7; +// x[1][0] = 8; +// x[1][1] = 9; +static Node *lvar_initializer(Token **rest, Token *tok, Obj *var) { + Initializer *init = initializer(rest, tok, var->ty, &var->ty); + InitDesg desg = {NULL, 0, NULL, var}; + + // If a partial initializer list is given, the standard requires + // that unspecified elements are set to 0. Here, we simply + // zero-initialize the entire memory region of a variable before + // initializing it with user-supplied values. + Node *lhs = new_node(ND_MEMZERO, tok); + lhs->var = var; + + Node *rhs = create_lvar_init(init, var->ty, &desg, tok); + return new_binary(ND_COMMA, lhs, rhs, tok); +} + +static uint64_t read_buf(char *buf, int sz) { + if (sz == 1) return *buf; + if (sz == 2) return *(uint16_t *)buf; + if (sz == 4) return *(uint32_t *)buf; + if (sz == 8) return *(uint64_t *)buf; + UNREACHABLE(); +} + +static void write_buf(char *buf, uint64_t val, int sz) { + if (sz == 1) + *buf = val; + else if (sz == 2) + *(uint16_t *)buf = val; + else if (sz == 4) + *(uint32_t *)buf = val; + else if (sz == 8) + *(uint64_t *)buf = val; + else + UNREACHABLE(); +} + +static Relocation *write_gvar_data(Relocation *cur, Initializer *init, Type *ty, + char *buf, int offset) { + if (ty->kind == TY_ARRAY) { + int sz = ty->base->size; + for (int i = 0; i < ty->array_len; i++) + cur = write_gvar_data(cur, init->children[i], ty->base, buf, + offset + sz * i); + return cur; + } + + if (ty->kind == TY_STRUCT) { + for (Member *mem = ty->members; mem; mem = mem->next) { + if (mem->is_bitfield) { + Node *expr = init->children[mem->idx]->expr; + if (!expr) break; + + char *loc = buf + offset + mem->offset; + uint64_t oldval = read_buf(loc, mem->ty->size); + uint64_t newval = eval(expr); + uint64_t mask = (1L << mem->bit_width) - 1; + uint64_t combined = oldval | ((newval & mask) << mem->bit_offset); + write_buf(loc, combined, mem->ty->size); + } else { + cur = write_gvar_data(cur, init->children[mem->idx], mem->ty, buf, + offset + mem->offset); + } + } + return cur; + } + + if (ty->kind == TY_UNION) { + if (!init->mem) return cur; + return write_gvar_data(cur, init->children[init->mem->idx], init->mem->ty, + buf, offset); + } + + if (!init->expr) return cur; + + if (ty->kind == TY_FLOAT) { + *(float *)(buf + offset) = eval_double(init->expr); + return cur; + } + + if (ty->kind == TY_DOUBLE) { + *(double *)(buf + offset) = eval_double(init->expr); + return cur; + } + + char **label = NULL; + uint64_t val = eval2(init->expr, &label); + + if (!label) { + write_buf(buf + offset, val, ty->size); + return cur; + } + + Relocation *rel = calloc(1, sizeof(Relocation)); + rel->offset = offset; + rel->label = label; + rel->addend = val; + cur->next = rel; + return cur->next; +} + +// Initializers for global variables are evaluated at compile-time and +// embedded to .data section. This function serializes Initializer +// objects to a flat byte array. It is a compile error if an +// initializer list contains a non-constant expression. +static void gvar_initializer(Token **rest, Token *tok, Obj *var) { + Initializer *init = initializer(rest, tok, var->ty, &var->ty); + + Relocation head = {}; + char *buf = calloc(1, var->ty->size); + write_gvar_data(&head, init, var->ty, buf, 0); + var->init_data = buf; + var->rel = head.next; +} + +// Returns true if a given token represents a type. +static bool is_typename(Token *tok) { + static HashMap map; + + if (map.capacity == 0) { + static char *kw[] = { + "void", "_Bool", "char", "short", "int", + "long", "struct", "union", "typedef", "enum", + "static", "extern", "_Alignas", "signed", "unsigned", + "const", "volatile", "auto", "register", "restrict", + "__restrict", "__restrict__", "_Noreturn", "float", "double", + "typeof", "inline", "_Thread_local", "__thread", "_Atomic", + }; + + for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) + hashmap_put(&map, kw[i], (void *)1); + } + + return hashmap_get2(&map, tok->loc, tok->len) || find_typedef(tok); +} + +// asm-stmt = "asm" ("volatile" | "inline")* "(" string-literal ")" +static Node *asm_stmt(Token **rest, Token *tok) { + Node *node = new_node(ND_ASM, tok); + tok = tok->next; + + while (equal(tok, "volatile") || equal(tok, "inline")) tok = tok->next; + + tok = skip(tok, "("); + if (tok->kind != TK_STR || tok->ty->base->kind != TY_CHAR) + error_tok(tok, "expected string literal"); + node->asm_str = tok->str; + *rest = skip(tok->next, ")"); + return node; +} + +// stmt = "return" expr? ";" +// | "if" "(" expr ")" stmt ("else" stmt)? +// | "switch" "(" expr ")" stmt +// | "case" const-expr ("..." const-expr)? ":" stmt +// | "default" ":" stmt +// | "for" "(" expr-stmt expr? ";" expr? ")" stmt +// | "while" "(" expr ")" stmt +// | "do" stmt "while" "(" expr ")" ";" +// | "asm" asm-stmt +// | "goto" (ident | "*" expr) ";" +// | "break" ";" +// | "continue" ";" +// | ident ":" stmt +// | "{" compound-stmt +// | expr-stmt +static Node *stmt(Token **rest, Token *tok) { + if (equal(tok, "return")) { + Node *node = new_node(ND_RETURN, tok); + if (consume(rest, tok->next, ";")) return node; + + Node *exp = expr(&tok, tok->next); + *rest = skip(tok, ";"); + + add_type(exp); + Type *ty = current_fn->ty->return_ty; + if (ty->kind != TY_STRUCT && ty->kind != TY_UNION) + exp = new_cast(exp, current_fn->ty->return_ty); + + node->lhs = exp; + return node; + } + + if (equal(tok, "if")) { + Node *node = new_node(ND_IF, tok); + tok = skip(tok->next, "("); + node->cond = expr(&tok, tok); + tok = skip(tok, ")"); + node->then = stmt(&tok, tok); + if (equal(tok, "else")) node->els = stmt(&tok, tok->next); + *rest = tok; + return node; + } + + if (equal(tok, "switch")) { + Node *node = new_node(ND_SWITCH, tok); + tok = skip(tok->next, "("); + node->cond = expr(&tok, tok); + tok = skip(tok, ")"); + + Node *sw = current_switch; + current_switch = node; + + char *brk = brk_label; + brk_label = node->brk_label = new_unique_name(); + + node->then = stmt(rest, tok); + + current_switch = sw; + brk_label = brk; + return node; + } + + if (equal(tok, "case")) { + if (!current_switch) error_tok(tok, "stray case"); + + Node *node = new_node(ND_CASE, tok); + int begin = const_expr(&tok, tok->next); + int end; + + if (equal(tok, "...")) { + // [GNU] Case ranges, e.g. "case 1 ... 5:" + end = const_expr(&tok, tok->next); + if (end < begin) error_tok(tok, "empty case range specified"); + } else { + end = begin; + } + + tok = skip(tok, ":"); + node->label = new_unique_name(); + node->lhs = stmt(rest, tok); + node->begin = begin; + node->end = end; + node->case_next = current_switch->case_next; + current_switch->case_next = node; + return node; + } + + if (equal(tok, "default")) { + if (!current_switch) error_tok(tok, "stray default"); + + Node *node = new_node(ND_CASE, tok); + tok = skip(tok->next, ":"); + node->label = new_unique_name(); + node->lhs = stmt(rest, tok); + current_switch->default_case = node; + return node; + } + + if (equal(tok, "for")) { + Node *node = new_node(ND_FOR, tok); + tok = skip(tok->next, "("); + + enter_scope(); + + char *brk = brk_label; + char *cont = cont_label; + brk_label = node->brk_label = new_unique_name(); + cont_label = node->cont_label = new_unique_name(); + + if (is_typename(tok)) { + Type *basety = typespec(&tok, tok, NULL); + node->init = declaration(&tok, tok, basety, NULL); + } else { + node->init = expr_stmt(&tok, tok); + } + + if (!equal(tok, ";")) node->cond = expr(&tok, tok); + tok = skip(tok, ";"); + + if (!equal(tok, ")")) node->inc = expr(&tok, tok); + tok = skip(tok, ")"); + + node->then = stmt(rest, tok); + + leave_scope(); + brk_label = brk; + cont_label = cont; + return node; + } + + if (equal(tok, "while")) { + Node *node = new_node(ND_FOR, tok); + tok = skip(tok->next, "("); + node->cond = expr(&tok, tok); + tok = skip(tok, ")"); + + char *brk = brk_label; + char *cont = cont_label; + brk_label = node->brk_label = new_unique_name(); + cont_label = node->cont_label = new_unique_name(); + + node->then = stmt(rest, tok); + + brk_label = brk; + cont_label = cont; + return node; + } + + if (equal(tok, "do")) { + Node *node = new_node(ND_DO, tok); + + char *brk = brk_label; + char *cont = cont_label; + brk_label = node->brk_label = new_unique_name(); + cont_label = node->cont_label = new_unique_name(); + + node->then = stmt(&tok, tok->next); + + brk_label = brk; + cont_label = cont; + + tok = skip(tok, "while"); + tok = skip(tok, "("); + node->cond = expr(&tok, tok); + tok = skip(tok, ")"); + *rest = skip(tok, ";"); + return node; + } + + if (equal(tok, "asm") || equal(tok, "__asm__")) { + return asm_stmt(rest, tok); + } + + if (equal(tok, "goto")) { + if (equal(tok->next, "*")) { + // [GNU] `goto *ptr` jumps to the address specified by `ptr`. + Node *node = new_node(ND_GOTO_EXPR, tok); + node->lhs = expr(&tok, tok->next->next); + *rest = skip(tok, ";"); + return node; + } + + Node *node = new_node(ND_GOTO, tok); + node->label = get_ident(tok->next); + node->goto_next = gotos; + gotos = node; + *rest = skip(tok->next->next, ";"); + return node; + } + + if (equal(tok, "break")) { + if (!brk_label) error_tok(tok, "stray break"); + Node *node = new_node(ND_GOTO, tok); + node->unique_label = brk_label; + *rest = skip(tok->next, ";"); + return node; + } + + if (equal(tok, "continue")) { + if (!cont_label) error_tok(tok, "stray continue"); + Node *node = new_node(ND_GOTO, tok); + node->unique_label = cont_label; + *rest = skip(tok->next, ";"); + return node; + } + + if (tok->kind == TK_IDENT && equal(tok->next, ":")) { + Node *node = new_node(ND_LABEL, tok); + node->label = strndup(tok->loc, tok->len); + node->unique_label = new_unique_name(); + node->lhs = stmt(rest, tok->next->next); + node->goto_next = labels; + labels = node; + return node; + } + + if (equal(tok, "{")) return compound_stmt(rest, tok->next); + + return expr_stmt(rest, tok); +} + +// compound-stmt = (typedef | declaration | stmt)* "}" +static Node *compound_stmt(Token **rest, Token *tok) { + Node *node = new_node(ND_BLOCK, tok); + Node head = {}; + Node *cur = &head; + + enter_scope(); + + while (!equal(tok, "}")) { + if (is_typename(tok) && !equal(tok->next, ":")) { + VarAttr attr = {}; + Type *basety = typespec(&tok, tok, &attr); + + if (attr.is_typedef) { + tok = parse_typedef(tok, basety); + continue; + } + + if (is_function(tok)) { + tok = function(tok, basety, &attr); + continue; + } + + if (attr.is_extern) { + tok = global_variable(tok, basety, &attr); + continue; + } + + cur = cur->next = declaration(&tok, tok, basety, &attr); + } else { + cur = cur->next = stmt(&tok, tok); + } + add_type(cur); + } + + leave_scope(); + + node->body = head.next; + *rest = tok->next; + return node; +} + +// expr-stmt = expr? ";" +static Node *expr_stmt(Token **rest, Token *tok) { + if (equal(tok, ";")) { + *rest = tok->next; + return new_node(ND_BLOCK, tok); + } + + Node *node = new_node(ND_EXPR_STMT, tok); + node->lhs = expr(&tok, tok); + *rest = skip(tok, ";"); + return node; +} + +// expr = assign ("," expr)? +static Node *expr(Token **rest, Token *tok) { + Node *node = assign(&tok, tok); + + if (equal(tok, ",")) + return new_binary(ND_COMMA, node, expr(rest, tok->next), tok); + + *rest = tok; + return node; +} + +static int64_t eval(Node *node) { + return eval2(node, NULL); +} + +// Evaluate a given node as a constant expression. +// +// A constant expression is either just a number or ptr+n where ptr +// is a pointer to a global variable and n is a postiive/negative +// number. The latter form is accepted only as an initialization +// expression for a global variable. +static int64_t eval2(Node *node, char ***label) { + add_type(node); + + if (is_flonum(node->ty)) return eval_double(node); + + switch (node->kind) { + case ND_ADD: + return eval2(node->lhs, label) + eval(node->rhs); + case ND_SUB: + return eval2(node->lhs, label) - eval(node->rhs); + case ND_MUL: + return eval(node->lhs) * eval(node->rhs); + case ND_DIV: + if (node->ty->is_unsigned) + return (uint64_t)eval(node->lhs) / eval(node->rhs); + return eval(node->lhs) / eval(node->rhs); + case ND_NEG: + return -eval(node->lhs); + case ND_MOD: + if (node->ty->is_unsigned) + return (uint64_t)eval(node->lhs) % eval(node->rhs); + return eval(node->lhs) % eval(node->rhs); + case ND_BITAND: + return eval(node->lhs) & eval(node->rhs); + case ND_BITOR: + return eval(node->lhs) | eval(node->rhs); + case ND_BITXOR: + return eval(node->lhs) ^ eval(node->rhs); + case ND_SHL: + return eval(node->lhs) << eval(node->rhs); + case ND_SHR: + if (node->ty->is_unsigned && node->ty->size == 8) + return (uint64_t)eval(node->lhs) >> eval(node->rhs); + return eval(node->lhs) >> eval(node->rhs); + case ND_EQ: + return eval(node->lhs) == eval(node->rhs); + case ND_NE: + return eval(node->lhs) != eval(node->rhs); + case ND_LT: + if (node->lhs->ty->is_unsigned) + return (uint64_t)eval(node->lhs) < eval(node->rhs); + return eval(node->lhs) < eval(node->rhs); + case ND_LE: + if (node->lhs->ty->is_unsigned) + return (uint64_t)eval(node->lhs) <= eval(node->rhs); + return eval(node->lhs) <= eval(node->rhs); + case ND_COND: + return eval(node->cond) ? eval2(node->then, label) + : eval2(node->els, label); + case ND_COMMA: + return eval2(node->rhs, label); + case ND_NOT: + return !eval(node->lhs); + case ND_BITNOT: + return ~eval(node->lhs); + case ND_LOGAND: + return eval(node->lhs) && eval(node->rhs); + case ND_LOGOR: + return eval(node->lhs) || eval(node->rhs); + case ND_CAST: { + int64_t val = eval2(node->lhs, label); + if (is_integer(node->ty)) { + switch (node->ty->size) { + case 1: + return node->ty->is_unsigned ? (uint8_t)val : (int8_t)val; + case 2: + return node->ty->is_unsigned ? (uint16_t)val : (int16_t)val; + case 4: + return node->ty->is_unsigned ? (uint32_t)val : (int32_t)val; + } + } + return val; + } + case ND_ADDR: + return eval_rval(node->lhs, label); + case ND_LABEL_VAL: + *label = &node->unique_label; + return 0; + case ND_MEMBER: + if (!label) error_tok(node->tok, "not a compile-time constant"); + if (node->ty->kind != TY_ARRAY) + error_tok(node->tok, "invalid initializer"); + return eval_rval(node->lhs, label) + node->member->offset; + case ND_VAR: + if (!label) error_tok(node->tok, "not a compile-time constant"); + if (node->var->ty->kind != TY_ARRAY && node->var->ty->kind != TY_FUNC) + error_tok(node->tok, "invalid initializer"); + *label = &node->var->name; + return 0; + case ND_NUM: + return node->val; + } + + error_tok(node->tok, "not a compile-time constant"); +} + +static int64_t eval_rval(Node *node, char ***label) { + switch (node->kind) { + case ND_VAR: + if (node->var->is_local) + error_tok(node->tok, "not a compile-time constant"); + *label = &node->var->name; + return 0; + case ND_DEREF: + return eval2(node->lhs, label); + case ND_MEMBER: + return eval_rval(node->lhs, label) + node->member->offset; + } + + error_tok(node->tok, "invalid initializer"); +} + +static bool is_const_expr(Node *node) { + add_type(node); + + switch (node->kind) { + case ND_ADD: + case ND_SUB: + case ND_MUL: + case ND_DIV: + case ND_BITAND: + case ND_BITOR: + case ND_BITXOR: + case ND_SHL: + case ND_SHR: + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + case ND_LOGAND: + case ND_LOGOR: + return is_const_expr(node->lhs) && is_const_expr(node->rhs); + case ND_COND: + if (!is_const_expr(node->cond)) return false; + return is_const_expr(eval(node->cond) ? node->then : node->els); + case ND_COMMA: + return is_const_expr(node->rhs); + case ND_NEG: + case ND_NOT: + case ND_BITNOT: + case ND_CAST: + return is_const_expr(node->lhs); + case ND_NUM: + return true; + } + + return false; +} + +int64_t const_expr(Token **rest, Token *tok) { + Node *node = conditional(rest, tok); + return eval(node); +} + +static double eval_double(Node *node) { + add_type(node); + + if (is_integer(node->ty)) { + if (node->ty->is_unsigned) return (unsigned long)eval(node); + return eval(node); + } + + switch (node->kind) { + case ND_ADD: + return eval_double(node->lhs) + eval_double(node->rhs); + case ND_SUB: + return eval_double(node->lhs) - eval_double(node->rhs); + case ND_MUL: + return eval_double(node->lhs) * eval_double(node->rhs); + case ND_DIV: + return eval_double(node->lhs) / eval_double(node->rhs); + case ND_NEG: + return -eval_double(node->lhs); + case ND_COND: + return eval_double(node->cond) ? eval_double(node->then) + : eval_double(node->els); + case ND_COMMA: + return eval_double(node->rhs); + case ND_CAST: + if (is_flonum(node->lhs->ty)) return eval_double(node->lhs); + return eval(node->lhs); + case ND_NUM: + return node->fval; + } + + error_tok(node->tok, "not a compile-time constant"); +} + +// Convert op= operators to expressions containing an assignment. +// +// In general, `A op= C` is converted to ``tmp = &A, *tmp = *tmp op B`. +// However, if a given expression is of form `A.x op= C`, the input is +// converted to `tmp = &A, (*tmp).x = (*tmp).x op C` to handle assignments +// to bitfields. +static Node *to_assign(Node *binary) { + add_type(binary->lhs); + add_type(binary->rhs); + Token *tok = binary->tok; + + // Convert `A.x op= C` to `tmp = &A, (*tmp).x = (*tmp).x op C`. + if (binary->lhs->kind == ND_MEMBER) { + Obj *var = new_lvar("", pointer_to(binary->lhs->lhs->ty)); + + Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok), + new_unary(ND_ADDR, binary->lhs->lhs, tok), tok); + + Node *expr2 = new_unary( + ND_MEMBER, new_unary(ND_DEREF, new_var_node(var, tok), tok), tok); + expr2->member = binary->lhs->member; + + Node *expr3 = new_unary( + ND_MEMBER, new_unary(ND_DEREF, new_var_node(var, tok), tok), tok); + expr3->member = binary->lhs->member; + + Node *expr4 = + new_binary(ND_ASSIGN, expr2, + new_binary(binary->kind, expr3, binary->rhs, tok), tok); + + return new_binary(ND_COMMA, expr1, expr4, tok); + } + + // If A is an atomic type, Convert `A op= B` to + // + // ({ + // T1 *addr = &A; T2 val = (B); T1 old = *addr; T1 new; + // do { + // new = old op val; + // } while (!atomic_compare_exchange_strong(addr, &old, new)); + // new; + // }) + if (binary->lhs->ty->is_atomic) { + Node head = {}; + Node *cur = &head; + + Obj *addr = new_lvar("", pointer_to(binary->lhs->ty)); + Obj *val = new_lvar("", binary->rhs->ty); + Obj *old = new_lvar("", binary->lhs->ty); + Obj *new = new_lvar("", binary->lhs->ty); + + cur = cur->next = + new_unary(ND_EXPR_STMT, + new_binary(ND_ASSIGN, new_var_node(addr, tok), + new_unary(ND_ADDR, binary->lhs, tok), tok), + tok); + + cur = cur->next = new_unary( + ND_EXPR_STMT, + new_binary(ND_ASSIGN, new_var_node(val, tok), binary->rhs, tok), tok); + + cur = cur->next = new_unary( + ND_EXPR_STMT, + new_binary(ND_ASSIGN, new_var_node(old, tok), + new_unary(ND_DEREF, new_var_node(addr, tok), tok), tok), + tok); + + Node *loop = new_node(ND_DO, tok); + loop->brk_label = new_unique_name(); + loop->cont_label = new_unique_name(); + + Node *body = new_binary(ND_ASSIGN, new_var_node(new, tok), + new_binary(binary->kind, new_var_node(old, tok), + new_var_node(val, tok), tok), + tok); + + loop->then = new_node(ND_BLOCK, tok); + loop->then->body = new_unary(ND_EXPR_STMT, body, tok); + + Node *cas = new_node(ND_CAS, tok); + cas->cas_addr = new_var_node(addr, tok); + cas->cas_old = new_unary(ND_ADDR, new_var_node(old, tok), tok); + cas->cas_new = new_var_node(new, tok); + loop->cond = new_unary(ND_NOT, cas, tok); + + cur = cur->next = loop; + cur = cur->next = new_unary(ND_EXPR_STMT, new_var_node(new, tok), tok); + + Node *node = new_node(ND_STMT_EXPR, tok); + node->body = head.next; + return node; + } + + // Convert `A op= B` to ``tmp = &A, *tmp = *tmp op B`. + Obj *var = new_lvar("", pointer_to(binary->lhs->ty)); + + Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok), + new_unary(ND_ADDR, binary->lhs, tok), tok); + + Node *expr2 = new_binary( + ND_ASSIGN, new_unary(ND_DEREF, new_var_node(var, tok), tok), + new_binary(binary->kind, new_unary(ND_DEREF, new_var_node(var, tok), tok), + binary->rhs, tok), + tok); + + return new_binary(ND_COMMA, expr1, expr2, tok); +} + +// assign = conditional (assign-op assign)? +// assign-op = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" +// | "<<=" | ">>=" +static Node *assign(Token **rest, Token *tok) { + Node *node = conditional(&tok, tok); + + if (equal(tok, "=")) + return new_binary(ND_ASSIGN, node, assign(rest, tok->next), tok); + + if (equal(tok, "+=")) + return to_assign(new_add(node, assign(rest, tok->next), tok)); + + if (equal(tok, "-=")) + return to_assign(new_sub(node, assign(rest, tok->next), tok)); + + if (equal(tok, "*=")) + return to_assign(new_binary(ND_MUL, node, assign(rest, tok->next), tok)); + + if (equal(tok, "/=")) + return to_assign(new_binary(ND_DIV, node, assign(rest, tok->next), tok)); + + if (equal(tok, "%=")) + return to_assign(new_binary(ND_MOD, node, assign(rest, tok->next), tok)); + + if (equal(tok, "&=")) + return to_assign(new_binary(ND_BITAND, node, assign(rest, tok->next), tok)); + + if (equal(tok, "|=")) + return to_assign(new_binary(ND_BITOR, node, assign(rest, tok->next), tok)); + + if (equal(tok, "^=")) + return to_assign(new_binary(ND_BITXOR, node, assign(rest, tok->next), tok)); + + if (equal(tok, "<<=")) + return to_assign(new_binary(ND_SHL, node, assign(rest, tok->next), tok)); + + if (equal(tok, ">>=")) + return to_assign(new_binary(ND_SHR, node, assign(rest, tok->next), tok)); + + *rest = tok; + return node; +} + +// conditional = logor ("?" expr? ":" conditional)? +static Node *conditional(Token **rest, Token *tok) { + Node *cond = logor(&tok, tok); + + if (!equal(tok, "?")) { + *rest = tok; + return cond; + } + + if (equal(tok->next, ":")) { + // [GNU] Compile `a ?: b` as `tmp = a, tmp ? tmp : b`. + add_type(cond); + Obj *var = new_lvar("", cond->ty); + Node *lhs = new_binary(ND_ASSIGN, new_var_node(var, tok), cond, tok); + Node *rhs = new_node(ND_COND, tok); + rhs->cond = new_var_node(var, tok); + rhs->then = new_var_node(var, tok); + rhs->els = conditional(rest, tok->next->next); + return new_binary(ND_COMMA, lhs, rhs, tok); + } + + Node *node = new_node(ND_COND, tok); + node->cond = cond; + node->then = expr(&tok, tok->next); + tok = skip(tok, ":"); + node->els = conditional(rest, tok); + return node; +} + +// logor = logand ("||" logand)* +static Node *logor(Token **rest, Token *tok) { + Node *node = logand(&tok, tok); + while (equal(tok, "||")) { + Token *start = tok; + node = new_binary(ND_LOGOR, node, logand(&tok, tok->next), start); + } + *rest = tok; + return node; +} + +// logand = bitor ("&&" bitor)* +static Node *logand(Token **rest, Token *tok) { + Node *node = bitor (&tok, tok); + while (equal(tok, "&&")) { + Token *start = tok; + node = new_binary(ND_LOGAND, node, bitor (&tok, tok->next), start); + } + *rest = tok; + return node; +} + +// bitor = bitxor ("|" bitxor)* +static Node * bitor (Token * *rest, Token *tok) { + Node *node = bitxor(&tok, tok); + while (equal(tok, "|")) { + Token *start = tok; + node = new_binary(ND_BITOR, node, bitxor(&tok, tok->next), start); + } + *rest = tok; + return node; +} + +// bitxor = bitand ("^" bitand)* +static Node *bitxor(Token **rest, Token *tok) { + Node *node = bitand(&tok, tok); + while (equal(tok, "^")) { + Token *start = tok; + node = new_binary(ND_BITXOR, node, bitand(&tok, tok->next), start); + } + *rest = tok; + return node; +} + +// bitand = equality ("&" equality)* +static Node *bitand(Token **rest, Token *tok) { + Node *node = equality(&tok, tok); + while (equal(tok, "&")) { + Token *start = tok; + node = new_binary(ND_BITAND, node, equality(&tok, tok->next), start); + } + *rest = tok; + return node; +} + +// equality = relational ("==" relational | "!=" relational)* +static Node *equality(Token **rest, Token *tok) { + Node *node = relational(&tok, tok); + + for (;;) { + Token *start = tok; + + if (equal(tok, "==")) { + node = new_binary(ND_EQ, node, relational(&tok, tok->next), start); + continue; + } + + if (equal(tok, "!=")) { + node = new_binary(ND_NE, node, relational(&tok, tok->next), start); + continue; + } + + *rest = tok; + return node; + } +} + +// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)* +static Node *relational(Token **rest, Token *tok) { + Node *node = shift(&tok, tok); + + for (;;) { + Token *start = tok; + + if (equal(tok, "<")) { + node = new_binary(ND_LT, node, shift(&tok, tok->next), start); + continue; + } + + if (equal(tok, "<=")) { + node = new_binary(ND_LE, node, shift(&tok, tok->next), start); + continue; + } + + if (equal(tok, ">")) { + node = new_binary(ND_LT, shift(&tok, tok->next), node, start); + continue; + } + + if (equal(tok, ">=")) { + node = new_binary(ND_LE, shift(&tok, tok->next), node, start); + continue; + } + + *rest = tok; + return node; + } +} + +// shift = add ("<<" add | ">>" add)* +static Node *shift(Token **rest, Token *tok) { + Node *node = add(&tok, tok); + + for (;;) { + Token *start = tok; + + if (equal(tok, "<<")) { + node = new_binary(ND_SHL, node, add(&tok, tok->next), start); + continue; + } + + if (equal(tok, ">>")) { + node = new_binary(ND_SHR, node, add(&tok, tok->next), start); + continue; + } + + *rest = tok; + return node; + } +} + +// In C, `+` operator is overloaded to perform the pointer arithmetic. +// If p is a pointer, p+n adds not n but sizeof(*p)*n to the value of p, +// so that p+n points to the location n elements (not bytes) ahead of p. +// In other words, we need to scale an integer value before adding to a +// pointer value. This function takes care of the scaling. +static Node *new_add(Node *lhs, Node *rhs, Token *tok) { + add_type(lhs); + add_type(rhs); + + // num + num + if (is_numeric(lhs->ty) && is_numeric(rhs->ty)) + return new_binary(ND_ADD, lhs, rhs, tok); + + if (lhs->ty->base && rhs->ty->base) error_tok(tok, "invalid operands"); + + // Canonicalize `num + ptr` to `ptr + num`. + if (!lhs->ty->base && rhs->ty->base) { + Node *tmp = lhs; + lhs = rhs; + rhs = tmp; + } + + // VLA + num + if (lhs->ty->base->kind == TY_VLA) { + rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), + tok); + return new_binary(ND_ADD, lhs, rhs, tok); + } + + // ptr + num + rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok); + return new_binary(ND_ADD, lhs, rhs, tok); +} + +// Like `+`, `-` is overloaded for the pointer type. +static Node *new_sub(Node *lhs, Node *rhs, Token *tok) { + add_type(lhs); + add_type(rhs); + + // num - num + if (is_numeric(lhs->ty) && is_numeric(rhs->ty)) + return new_binary(ND_SUB, lhs, rhs, tok); + + // VLA + num + if (lhs->ty->base->kind == TY_VLA) { + rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok), + tok); + add_type(rhs); + Node *node = new_binary(ND_SUB, lhs, rhs, tok); + node->ty = lhs->ty; + return node; + } + + // ptr - num + if (lhs->ty->base && is_integer(rhs->ty)) { + rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok); + add_type(rhs); + Node *node = new_binary(ND_SUB, lhs, rhs, tok); + node->ty = lhs->ty; + return node; + } + + // ptr - ptr, which returns how many elements are between the two. + if (lhs->ty->base && rhs->ty->base) { + Node *node = new_binary(ND_SUB, lhs, rhs, tok); + node->ty = ty_long; + return new_binary(ND_DIV, node, new_num(lhs->ty->base->size, tok), tok); + } + + error_tok(tok, "invalid operands"); +} + +// add = mul ("+" mul | "-" mul)* +static Node *add(Token **rest, Token *tok) { + Node *node = mul(&tok, tok); + + for (;;) { + Token *start = tok; + + if (equal(tok, "+")) { + node = new_add(node, mul(&tok, tok->next), start); + continue; + } + + if (equal(tok, "-")) { + node = new_sub(node, mul(&tok, tok->next), start); + continue; + } + + *rest = tok; + return node; + } +} + +// mul = cast ("*" cast | "/" cast | "%" cast)* +static Node *mul(Token **rest, Token *tok) { + Node *node = cast(&tok, tok); + + for (;;) { + Token *start = tok; + + if (equal(tok, "*")) { + node = new_binary(ND_MUL, node, cast(&tok, tok->next), start); + continue; + } + + if (equal(tok, "/")) { + node = new_binary(ND_DIV, node, cast(&tok, tok->next), start); + continue; + } + + if (equal(tok, "%")) { + node = new_binary(ND_MOD, node, cast(&tok, tok->next), start); + continue; + } + + *rest = tok; + return node; + } +} + +// compound-literal = initializer "}" +static Node *compound_literal(Token **rest, Token *tok, Type *ty, + Token *start) { + if (scope_depth == 0) { + Obj *var = new_anon_gvar(ty); + gvar_initializer(rest, tok, var); + return new_var_node(var, start); + } + Obj *var = new_lvar(new_unique_name(), ty); + Node *lhs = lvar_initializer(rest, tok, var); + Node *rhs = new_var_node(var, tok); + return new_binary(ND_COMMA, lhs, rhs, tok); +} + +// cast = "(" type-name ")" "{" compound-literal +// | "(" type-name ")" cast +// | unary +static Node *cast(Token **rest, Token *tok) { + if (equal(tok, "(") && is_typename(tok->next)) { + Token *start = tok; + Type *ty = typename(&tok, tok->next); + tok = skip(tok, ")"); + + // compound literal + if (equal(tok, "{")) return compound_literal(rest, tok, ty, start); + + // type cast + Node *node = new_cast(cast(rest, tok), ty); + node->tok = start; + return node; + } + + return unary(rest, tok); +} + +// unary = ("+" | "-" | "*" | "&" | "!" | "~") cast +// | ("++" | "--") unary +// | "&&" ident +// | postfix +static Node *unary(Token **rest, Token *tok) { + if (equal(tok, "+")) return cast(rest, tok->next); + + if (equal(tok, "-")) return new_unary(ND_NEG, cast(rest, tok->next), tok); + + if (equal(tok, "&")) { + Node *lhs = cast(rest, tok->next); + add_type(lhs); + if (lhs->kind == ND_MEMBER && lhs->member->is_bitfield) + error_tok(tok, "cannot take address of bitfield"); + return new_unary(ND_ADDR, lhs, tok); + } + + if (equal(tok, "*")) { + // [C18 6.5.3.2p4] This is an oddity in the C spec, but dereferencing + // a function shouldn't do anything. If foo is a function, `*foo`, + // `**foo` or `*****foo` are all equivalent to just `foo`. + Node *node = cast(rest, tok->next); + add_type(node); + if (node->ty->kind == TY_FUNC) return node; + return new_unary(ND_DEREF, node, tok); + } + + if (equal(tok, "!")) return new_unary(ND_NOT, cast(rest, tok->next), tok); + + if (equal(tok, "~")) return new_unary(ND_BITNOT, cast(rest, tok->next), tok); + + // Read ++i as i+=1 + if (equal(tok, "++")) + return to_assign(new_add(unary(rest, tok->next), new_num(1, tok), tok)); + + // Read --i as i-=1 + if (equal(tok, "--")) + return to_assign(new_sub(unary(rest, tok->next), new_num(1, tok), tok)); + + // [GNU] labels-as-values + if (equal(tok, "&&")) { + Node *node = new_node(ND_LABEL_VAL, tok); + node->label = get_ident(tok->next); + node->goto_next = gotos; + gotos = node; + *rest = tok->next->next; + return node; + } + + return postfix(rest, tok); +} + +// struct-members = (typespec declarator ("," declarator)* ";")* +static void struct_members(Token **rest, Token *tok, Type *ty) { + Member head = {}; + Member *cur = &head; + int idx = 0; + + while (!equal(tok, "}")) { + VarAttr attr = {}; + Type *basety = typespec(&tok, tok, &attr); + bool first = true; + + // Anonymous struct member + if ((basety->kind == TY_STRUCT || basety->kind == TY_UNION) && + consume(&tok, tok, ";")) { + Member *mem = calloc(1, sizeof(Member)); + mem->ty = basety; + mem->idx = idx++; + mem->align = attr.align ? attr.align : mem->ty->align; + cur = cur->next = mem; + continue; + } + + // Regular struct members + while (!consume(&tok, tok, ";")) { + if (!first) tok = skip(tok, ","); + first = false; + + Member *mem = calloc(1, sizeof(Member)); + mem->ty = declarator(&tok, tok, basety); + mem->name = mem->ty->name; + mem->idx = idx++; + mem->align = attr.align ? attr.align : mem->ty->align; + + if (consume(&tok, tok, ":")) { + mem->is_bitfield = true; + mem->bit_width = const_expr(&tok, tok); + } + + cur = cur->next = mem; + } + } + + // If the last element is an array of incomplete type, it's + // called a "flexible array member". It should behave as if + // if were a zero-sized array. + if (cur != &head && cur->ty->kind == TY_ARRAY && cur->ty->array_len < 0) { + cur->ty = array_of(cur->ty->base, 0); + ty->is_flexible = true; + } + + *rest = tok->next; + ty->members = head.next; +} + +// attribute = ("__attribute__" "(" "(" "packed" ")" ")")* +static Token *attribute_list(Token *tok, Type *ty) { + while (consume(&tok, tok, "__attribute__")) { + tok = skip(tok, "("); + tok = skip(tok, "("); + + bool first = true; + + while (!consume(&tok, tok, ")")) { + if (!first) tok = skip(tok, ","); + first = false; + + if (consume(&tok, tok, "packed")) { + ty->is_packed = true; + continue; + } + + if (consume(&tok, tok, "aligned")) { + tok = skip(tok, "("); + ty->align = const_expr(&tok, tok); + tok = skip(tok, ")"); + continue; + } + + error_tok(tok, "unknown attribute"); + } + + tok = skip(tok, ")"); + } + + return tok; +} + +// struct-union-decl = attribute? ident? ("{" struct-members)? +static Type *struct_union_decl(Token **rest, Token *tok) { + Type *ty = struct_type(); + tok = attribute_list(tok, ty); + + // Read a tag. + Token *tag = NULL; + if (tok->kind == TK_IDENT) { + tag = tok; + tok = tok->next; + } + + if (tag && !equal(tok, "{")) { + *rest = tok; + + TagScope *sc = find_tag(tag); + if (sc) return sc->ty; + + ty->size = -1; + push_tag_scope(tag, ty); + return ty; + } + + tok = skip(tok, "{"); + + // Construct a struct object. + struct_members(&tok, tok, ty); + *rest = attribute_list(tok, ty); + + if (tag) { + // If this is a redefinition, overwrite a previous type. + // Otherwise, register the struct type. + TagScope *sc = find_tag(tag); + if (sc && sc->depth == scope_depth) { + *sc->ty = *ty; + return sc->ty; + } + + push_tag_scope(tag, ty); + } + + return ty; +} + +// struct-decl = struct-union-decl +static Type *struct_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_STRUCT; + + if (ty->size < 0) return ty; + + // Assign offsets within the struct to members. + int bits = 0; + + for (Member *mem = ty->members; mem; mem = mem->next) { + if (mem->is_bitfield && mem->bit_width == 0) { + // Zero-width anonymous bitfield has a special meaning. + // It affects only alignment. + bits = align_to(bits, mem->ty->size * 8); + } else if (mem->is_bitfield) { + int sz = mem->ty->size; + if (bits / (sz * 8) != (bits + mem->bit_width - 1) / (sz * 8)) + bits = align_to(bits, sz * 8); + + mem->offset = align_down(bits / 8, sz); + mem->bit_offset = bits % (sz * 8); + bits += mem->bit_width; + } else { + if (!ty->is_packed) bits = align_to(bits, mem->align * 8); + mem->offset = bits / 8; + bits += mem->ty->size * 8; + } + + if (!ty->is_packed && ty->align < mem->align) ty->align = mem->align; + } + + ty->size = align_to(bits, ty->align * 8) / 8; + return ty; +} + +// union-decl = struct-union-decl +static Type *union_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_UNION; + + if (ty->size < 0) return ty; + + // If union, we don't have to assign offsets because they + // are already initialized to zero. We need to compute the + // alignment and the size though. + for (Member *mem = ty->members; mem; mem = mem->next) { + if (ty->align < mem->align) ty->align = mem->align; + if (ty->size < mem->ty->size) ty->size = mem->ty->size; + } + ty->size = align_to(ty->size, ty->align); + return ty; +} + +// Find a struct member by name. +static Member *get_struct_member(Type *ty, Token *tok) { + for (Member *mem = ty->members; mem; mem = mem->next) { + // Anonymous struct member + if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) && + !mem->name) { + if (get_struct_member(mem->ty, tok)) return mem; + continue; + } + + // Regular struct member + if (mem->name->len == tok->len && + !strncmp(mem->name->loc, tok->loc, tok->len)) + return mem; + } + return NULL; +} + +// Create a node representing a struct member access, such as foo.bar +// where foo is a struct and bar is a member name. +// +// C has a feature called "anonymous struct" which allows a struct to +// have another unnamed struct as a member like this: +// +// struct { struct { int a; }; int b; } x; +// +// The members of an anonymous struct belong to the outer struct's +// member namespace. Therefore, in the above example, you can access +// member "a" of the anonymous struct as "x.a". +// +// This function takes care of anonymous structs. +static Node *struct_ref(Node *node, Token *tok) { + add_type(node); + if (node->ty->kind != TY_STRUCT && node->ty->kind != TY_UNION) + error_tok(node->tok, "not a struct nor a union"); + + Type *ty = node->ty; + + for (;;) { + Member *mem = get_struct_member(ty, tok); + if (!mem) error_tok(tok, "no such member"); + node = new_unary(ND_MEMBER, node, tok); + node->member = mem; + if (mem->name) break; + ty = mem->ty; + } + return node; +} + +// Convert A++ to `(typeof A)((A += 1) - 1)` +static Node *new_inc_dec(Node *node, Token *tok, int addend) { + add_type(node); + return new_cast(new_add(to_assign(new_add(node, new_num(addend, tok), tok)), + new_num(-addend, tok), tok), + node->ty); +} + +// postfix = ident "(" func-args ")" postfix-tail* +// | primary postfix-tail* +// +// postfix-tail = "[" expr "]" +// | "(" func-args ")" +// | "." ident +// | "->" ident +// | "++" +// | "--" +static Node *postfix(Token **rest, Token *tok) { + Node *node = primary(&tok, tok); + + for (;;) { + if (equal(tok, "(")) { + node = funcall(&tok, tok->next, node); + continue; + } + + if (equal(tok, "[")) { + // x[y] is short for *(x+y) + Token *start = tok; + Node *idx = expr(&tok, tok->next); + tok = skip(tok, "]"); + node = new_unary(ND_DEREF, new_add(node, idx, start), start); + continue; + } + + if (equal(tok, ".")) { + node = struct_ref(node, tok->next); + tok = tok->next->next; + continue; + } + + if (equal(tok, "->")) { + // x->y is short for (*x).y + node = new_unary(ND_DEREF, node, tok); + node = struct_ref(node, tok->next); + tok = tok->next->next; + continue; + } + + if (equal(tok, "++")) { + node = new_inc_dec(node, tok, 1); + tok = tok->next; + continue; + } + + if (equal(tok, "--")) { + node = new_inc_dec(node, tok, -1); + tok = tok->next; + continue; + } + + *rest = tok; + return node; + } +} + +// funcall = (assign ("," assign)*)? ")" +static Node *funcall(Token **rest, Token *tok, Node *fn) { + add_type(fn); + + if (fn->ty->kind != TY_FUNC && + (fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) + error_tok(fn->tok, "not a function"); + + Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base; + Type *param_ty = ty->params; + + Node head = {}; + Node *cur = &head; + + while (!equal(tok, ")")) { + if (cur != &head) tok = skip(tok, ","); + + Node *arg = assign(&tok, tok); + add_type(arg); + + if (!param_ty && !ty->is_variadic) error_tok(tok, "too many arguments"); + + if (param_ty) { + if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) + arg = new_cast(arg, param_ty); + param_ty = param_ty->next; + } else if (arg->ty->kind == TY_FLOAT) { + // If parameter type is omitted (e.g. in "..."), float + // arguments are promoted to double. + arg = new_cast(arg, ty_double); + } + + cur = cur->next = arg; + } + + if (param_ty) error_tok(tok, "too few arguments"); + + *rest = skip(tok, ")"); + + Node *node = new_unary(ND_FUNCALL, fn, tok); + node->func_ty = ty; + node->ty = ty->return_ty; + node->args = head.next; + + // If a function returns a struct, it is caller's responsibility + // to allocate a space for the return value. + if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) + node->ret_buffer = new_lvar("", node->ty); + return node; +} + +// generic-selection = "(" assign "," generic-assoc ("," generic-assoc)* ")" +// +// generic-assoc = type-name ":" assign +// | "default" ":" assign +static Node *generic_selection(Token **rest, Token *tok) { + Token *start = tok; + tok = skip(tok, "("); + + Node *ctrl = assign(&tok, tok); + add_type(ctrl); + + Type *t1 = ctrl->ty; + if (t1->kind == TY_FUNC) + t1 = pointer_to(t1); + else if (t1->kind == TY_ARRAY) + t1 = pointer_to(t1->base); + + Node *ret = NULL; + + while (!consume(rest, tok, ")")) { + tok = skip(tok, ","); + + if (equal(tok, "default")) { + tok = skip(tok->next, ":"); + Node *node = assign(&tok, tok); + if (!ret) ret = node; + continue; + } + + Type *t2 = typename(&tok, tok); + tok = skip(tok, ":"); + Node *node = assign(&tok, tok); + if (is_compatible(t1, t2)) ret = node; + } + + if (!ret) + error_tok(start, "controlling expression type not compatible with" + " any generic association type"); + return ret; +} + +// primary = "(" "{" stmt stmt* "}" ")" +// | "(" expr ")" +// | "sizeof" "(" type-name ")" +// | "sizeof" unary +// | "_Alignof" "(" type-name ")" +// | "_Alignof" unary +// | "_Generic" generic-selection +// | "__builtin_types_compatible_p" "(" type-name, type-name, ")" +// | "__builtin_reg_class" "(" type-name ")" +// | ident +// | str +// | num +static Node *primary(Token **rest, Token *tok) { + Token *start = tok; + + if (equal(tok, "(") && equal(tok->next, "{")) { + // This is a GNU statement expresssion. + Node *node = new_node(ND_STMT_EXPR, tok); + node->body = compound_stmt(&tok, tok->next->next)->body; + *rest = skip(tok, ")"); + return node; + } + + if (equal(tok, "(")) { + Node *node = expr(&tok, tok->next); + *rest = skip(tok, ")"); + return node; + } + + if (equal(tok, "sizeof") && equal(tok->next, "(") && + is_typename(tok->next->next)) { + Type *ty = typename(&tok, tok->next->next); + *rest = skip(tok, ")"); + + if (ty->kind == TY_VLA) { + if (ty->vla_size) return new_var_node(ty->vla_size, tok); + + Node *lhs = compute_vla_size(ty, tok); + Node *rhs = new_var_node(ty->vla_size, tok); + return new_binary(ND_COMMA, lhs, rhs, tok); + } + + return new_ulong(ty->size, start); + } + + if (equal(tok, "sizeof")) { + Node *node = unary(rest, tok->next); + add_type(node); + if (node->ty->kind == TY_VLA) return new_var_node(node->ty->vla_size, tok); + return new_ulong(node->ty->size, tok); + } + + if (equal(tok, "_Alignof") && equal(tok->next, "(") && + is_typename(tok->next->next)) { + Type *ty = typename(&tok, tok->next->next); + *rest = skip(tok, ")"); + return new_ulong(ty->align, tok); + } + + if (equal(tok, "_Alignof")) { + Node *node = unary(rest, tok->next); + add_type(node); + return new_ulong(node->ty->align, tok); + } + + if (equal(tok, "_Generic")) return generic_selection(rest, tok->next); + + if (equal(tok, "__builtin_types_compatible_p")) { + tok = skip(tok->next, "("); + Type *t1 = typename(&tok, tok); + tok = skip(tok, ","); + Type *t2 = typename(&tok, tok); + *rest = skip(tok, ")"); + return new_num(is_compatible(t1, t2), start); + } + + if (equal(tok, "__builtin_reg_class")) { + tok = skip(tok->next, "("); + Type *ty = typename(&tok, tok); + *rest = skip(tok, ")"); + + if (is_integer(ty) || ty->kind == TY_PTR) return new_num(0, start); + if (is_flonum(ty)) return new_num(1, start); + return new_num(2, start); + } + + if (equal(tok, "__builtin_compare_and_swap")) { + Node *node = new_node(ND_CAS, tok); + tok = skip(tok->next, "("); + node->cas_addr = assign(&tok, tok); + tok = skip(tok, ","); + node->cas_old = assign(&tok, tok); + tok = skip(tok, ","); + node->cas_new = assign(&tok, tok); + *rest = skip(tok, ")"); + return node; + } + + if (equal(tok, "__builtin_atomic_exchange")) { + Node *node = new_node(ND_EXCH, tok); + tok = skip(tok->next, "("); + node->lhs = assign(&tok, tok); + tok = skip(tok, ","); + node->rhs = assign(&tok, tok); + *rest = skip(tok, ")"); + return node; + } + + if (tok->kind == TK_IDENT) { + // Variable or enum constant + VarScope *sc = find_var(tok); + *rest = tok->next; + + // For "static inline" function + if (sc && sc->var && sc->var->is_function) { + if (current_fn) + strarray_push(¤t_fn->refs, sc->var->name); + else + sc->var->is_root = true; + } + + if (sc) { + if (sc->var) return new_var_node(sc->var, tok); + if (sc->enum_ty) return new_num(sc->enum_val, tok); + } + + if (equal(tok->next, "(")) + error_tok(tok, "implicit declaration of a function"); + error_tok(tok, "undefined variable"); + } + + if (tok->kind == TK_STR) { + Obj *var = new_string_literal(tok->str, tok->ty); + *rest = tok->next; + return new_var_node(var, tok); + } + + if (tok->kind == TK_NUM) { + Node *node; + if (is_flonum(tok->ty)) { + node = new_node(ND_NUM, tok); + node->fval = tok->fval; + } else { + node = new_num(tok->val, tok); + } + + node->ty = tok->ty; + *rest = tok->next; + return node; + } + + error_tok(tok, "expected an expression"); +} + +static Token *parse_typedef(Token *tok, Type *basety) { + bool first = true; + + while (!consume(&tok, tok, ";")) { + if (!first) tok = skip(tok, ","); + first = false; + + Type *ty = declarator(&tok, tok, basety); + if (!ty->name) error_tok(ty->name_pos, "typedef name omitted"); + push_scope(get_ident(ty->name))->type_def = ty; + } + return tok; +} + +static void create_param_lvars(Type *param) { + if (param) { + create_param_lvars(param->next); + if (!param->name) error_tok(param->name_pos, "parameter name omitted"); + new_lvar(get_ident(param->name), param); + } +} + +// This function matches gotos or labels-as-values with labels. +// +// We cannot resolve gotos as we parse a function because gotos +// can refer a label that appears later in the function. +// So, we need to do this after we parse the entire function. +static void resolve_goto_labels(void) { + for (Node *x = gotos; x; x = x->goto_next) { + for (Node *y = labels; y; y = y->goto_next) { + if (!strcmp(x->label, y->label)) { + x->unique_label = y->unique_label; + break; + } + } + + if (x->unique_label == NULL) + error_tok(x->tok->next, "use of undeclared label"); + } + + gotos = labels = NULL; +} + +static Obj *find_func(char *name) { + Scope *sc = scope; + while (sc->next) sc = sc->next; + + VarScope *sc2 = hashmap_get(&sc->vars, name); + if (sc2 && sc2->var && sc2->var->is_function) return sc2->var; + return NULL; +} + +static void mark_live(Obj *var) { + if (!var->is_function || var->is_live) return; + var->is_live = true; + + for (int i = 0; i < var->refs.len; i++) { + Obj *fn = find_func(var->refs.data[i]); + if (fn) mark_live(fn); + } +} + +static Token *function(Token *tok, Type *basety, VarAttr *attr) { + Type *ty = declarator(&tok, tok, basety); + if (!ty->name) error_tok(ty->name_pos, "function name omitted"); + char *name_str = get_ident(ty->name); + + Obj *fn = find_func(name_str); + if (fn) { + // Redeclaration + if (!fn->is_function) + error_tok(tok, "redeclared as a different kind of symbol"); + if (fn->is_definition && equal(tok, "{")) + error_tok(tok, "redefinition of %s", name_str); + if (!fn->is_static && attr->is_static) + error_tok(tok, "static declaration follows a non-static declaration"); + fn->is_definition = fn->is_definition || equal(tok, "{"); + } else { + fn = new_gvar(name_str, ty); + fn->is_function = true; + fn->is_definition = equal(tok, "{"); + fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern); + fn->is_inline = attr->is_inline; + } + + fn->is_root = !(fn->is_static && fn->is_inline); + + if (consume(&tok, tok, ";")) return tok; + + current_fn = fn; + locals = NULL; + enter_scope(); + create_param_lvars(ty->params); + + // A buffer for a struct/union return value is passed + // as the hidden first parameter. + Type *rty = ty->return_ty; + if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) + new_lvar("", pointer_to(rty)); + + fn->params = locals; + + if (ty->is_variadic) + fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136)); + fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char)); + + tok = skip(tok, "{"); + + // [C18 6.4.2.2] "__func__" is automatically defined as a + // local variable containing the current function name. + push_scope("__func__")->var = + new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1)); + + // [GNU] __FUNCTION__ is yet another name of __func__. + push_scope("__FUNCTION__")->var = + new_string_literal(fn->name, array_of(ty_char, strlen(fn->name) + 1)); + + fn->body = compound_stmt(&tok, tok); + fn->locals = locals; + leave_scope(); + resolve_goto_labels(); + return tok; +} + +static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) { + bool first = true; + + while (!consume(&tok, tok, ";")) { + if (!first) tok = skip(tok, ","); + first = false; + + Type *ty = declarator(&tok, tok, basety); + if (!ty->name) error_tok(ty->name_pos, "variable name omitted"); + + Obj *var = new_gvar(get_ident(ty->name), ty); + var->is_definition = !attr->is_extern; + var->is_static = attr->is_static; + var->is_tls = attr->is_tls; + if (attr->align) var->align = attr->align; + + if (equal(tok, "=")) + gvar_initializer(&tok, tok->next, var); + else if (!attr->is_extern) + var->is_tentative = true; + } + return tok; +} + +// Lookahead tokens and returns true if a given token is a start +// of a function definition or declaration. +static bool is_function(Token *tok) { + if (equal(tok, ";")) return false; + + Type dummy = {}; + Type *ty = declarator(&tok, tok, &dummy); + return ty->kind == TY_FUNC; +} + +// Remove redundant tentative definitions. +static void scan_globals(void) { + Obj head; + Obj *cur = &head; + + for (Obj *var = globals; var; var = var->next) { + if (!var->is_tentative) { + cur = cur->next = var; + continue; + } + + // Find another definition of the same identifier. + Obj *var2 = globals; + for (; var2; var2 = var2->next) + if (var != var2 && var2->is_definition && !strcmp(var->name, var2->name)) + break; + + // If there's another definition, the tentative definition + // is redundant + if (!var2) cur = cur->next = var; + } + + cur->next = NULL; + globals = head.next; +} + +static void declare_builtin_functions(void) { + Type *ty = func_type(pointer_to(ty_void)); + ty->params = copy_type(ty_int); + builtin_alloca = new_gvar("alloca", ty); + builtin_alloca->is_definition = false; +} + +// program = (typedef | function-definition | global-variable)* +Obj *parse(Token *tok) { + declare_builtin_functions(); + globals = NULL; + + while (tok->kind != TK_EOF) { + if (equal(tok, "asm") || equal(tok, "__asm__")) { + StaticAsm *a = calloc(1, sizeof(StaticAsm)); + a->next = staticasms; + a->body = asm_stmt(&tok, tok); + staticasms = a; + continue; + } + + VarAttr attr = {}; + Type *basety = typespec(&tok, tok, &attr); + + // Typedef + if (attr.is_typedef) { + tok = parse_typedef(tok, basety); + continue; + } + + // Function + if (is_function(tok)) { + tok = function(tok, basety, &attr); + continue; + } + + // Global variable + tok = global_variable(tok, basety, &attr); + } + + for (Obj *var = globals; var; var = var->next) + if (var->is_root) mark_live(var); + + // Remove redundant tentative definitions. + scan_globals(); + return globals; +} diff --git a/third_party/chibicc/preprocess.c b/third_party/chibicc/preprocess.c new file mode 100644 index 000000000..fd3a9f60e --- /dev/null +++ b/third_party/chibicc/preprocess.c @@ -0,0 +1,1099 @@ +// This file implements the C preprocessor. +// +// The preprocessor takes a list of tokens as an input and returns a +// new list of tokens as an output. +// +// The preprocessing language is designed in such a way that that's +// guaranteed to stop even if there is a recursive macro. +// Informally speaking, a macro is applied only once for each token. +// That is, if a macro token T appears in a result of direct or +// indirect macro expansion of T, T won't be expanded any further. +// For example, if T is defined as U, and U is defined as T, then +// token T is expanded to U and then to T and the macro expansion +// stops at that point. +// +// To achieve the above behavior, we attach for each token a set of +// macro names from which the token is expanded. The set is called +// "hideset". Hideset is initially empty, and every time we expand a +// macro, the macro name is added to the resulting tokens' hidesets. +// +// The above macro expansion algorithm is explained in this document +// written by Dave Prossor, which is used as a basis for the +// standard's wording: +// https://github.com/rui314/chibicc/wiki/cpp.algo.pdf + +#include "third_party/chibicc/chibicc.h" + +typedef struct MacroParam MacroParam; +struct MacroParam { + MacroParam *next; + char *name; +}; + +typedef struct MacroArg MacroArg; +struct MacroArg { + MacroArg *next; + char *name; + bool is_va_args; + Token *tok; +}; + +typedef Token *macro_handler_fn(Token *); + +typedef struct Macro Macro; +struct Macro { + char *name; + bool is_objlike; // Object-like or function-like + MacroParam *params; + char *va_args_name; + Token *body; + macro_handler_fn *handler; +}; + +// `#if` can be nested, so we use a stack to manage nested `#if`s. +typedef struct CondIncl CondIncl; +struct CondIncl { + CondIncl *next; + enum { IN_THEN, IN_ELIF, IN_ELSE } ctx; + Token *tok; + bool included; +}; + +typedef struct Hideset Hideset; +struct Hideset { + Hideset *next; + char *name; +}; + +static HashMap macros; +static CondIncl *cond_incl; +static HashMap pragma_once; +static int include_next_idx; + +static Token *preprocess2(Token *tok); +static Macro *find_macro(Token *tok); + +char *format(char *fmt, ...) { + char *res; + va_list va; + va_start(va, fmt); + res = xvasprintf(fmt, va); + va_end(va); + return res; +} + +static bool is_hash(Token *tok) { + return tok->at_bol && equal(tok, "#"); +} + +// Some preprocessor directives such as #include allow extraneous +// tokens before newline. This function skips such tokens. +static Token *skip_line(Token *tok) { + if (tok->at_bol) return tok; + warn_tok(tok, "extra token"); + while (tok->at_bol) tok = tok->next; + return tok; +} + +static Token *copy_token(Token *tok) { + Token *t = calloc(1, sizeof(Token)); + *t = *tok; + t->next = NULL; + return t; +} + +static Token *new_eof(Token *tok) { + Token *t = copy_token(tok); + t->kind = TK_EOF; + t->len = 0; + return t; +} + +static Hideset *new_hideset(char *name) { + Hideset *hs = calloc(1, sizeof(Hideset)); + hs->name = name; + return hs; +} + +static Hideset *hideset_union(Hideset *hs1, Hideset *hs2) { + Hideset head = {}; + Hideset *cur = &head; + for (; hs1; hs1 = hs1->next) cur = cur->next = new_hideset(hs1->name); + cur->next = hs2; + return head.next; +} + +static bool hideset_contains(Hideset *hs, char *s, int len) { + for (; hs; hs = hs->next) + if (strlen(hs->name) == len && !strncmp(hs->name, s, len)) return true; + return false; +} + +static Hideset *hideset_intersection(Hideset *hs1, Hideset *hs2) { + Hideset head = {}; + Hideset *cur = &head; + for (; hs1; hs1 = hs1->next) + if (hideset_contains(hs2, hs1->name, strlen(hs1->name))) + cur = cur->next = new_hideset(hs1->name); + return head.next; +} + +static Token *add_hideset(Token *tok, Hideset *hs) { + Token head = {}; + Token *cur = &head; + for (; tok; tok = tok->next) { + Token *t = copy_token(tok); + t->hideset = hideset_union(t->hideset, hs); + cur = cur->next = t; + } + return head.next; +} + +// Append tok2 to the end of tok1. +static Token *append(Token *tok1, Token *tok2) { + if (tok1->kind == TK_EOF) return tok2; + Token head = {}; + Token *cur = &head; + for (; tok1->kind != TK_EOF; tok1 = tok1->next) + cur = cur->next = copy_token(tok1); + cur->next = tok2; + return head.next; +} + +static Token *skip_cond_incl2(Token *tok) { + while (tok->kind != TK_EOF) { + if (is_hash(tok) && (equal(tok->next, "if") || equal(tok->next, "ifdef") || + equal(tok->next, "ifndef"))) { + tok = skip_cond_incl2(tok->next->next); + continue; + } + if (is_hash(tok) && equal(tok->next, "endif")) return tok->next->next; + tok = tok->next; + } + return tok; +} + +// Skip until next `#else`, `#elif` or `#endif`. +// Nested `#if` and `#endif` are skipped. +static Token *skip_cond_incl(Token *tok) { + while (tok->kind != TK_EOF) { + if (is_hash(tok) && (equal(tok->next, "if") || equal(tok->next, "ifdef") || + equal(tok->next, "ifndef"))) { + tok = skip_cond_incl2(tok->next->next); + continue; + } + if (is_hash(tok) && (equal(tok->next, "elif") || equal(tok->next, "else") || + equal(tok->next, "endif"))) + break; + tok = tok->next; + } + return tok; +} + +// Double-quote a given string and returns it. +static char *quote_string(char *str) { + int bufsize = 3; + for (int i = 0; str[i]; i++) { + if (str[i] == '\\' || str[i] == '"') bufsize++; + bufsize++; + } + char *buf = calloc(1, bufsize); + char *p = buf; + *p++ = '"'; + for (int i = 0; str[i]; i++) { + if (str[i] == '\\' || str[i] == '"') *p++ = '\\'; + *p++ = str[i]; + } + *p++ = '"'; + *p++ = '\0'; + return buf; +} + +static Token *new_str_token(char *str, Token *tmpl) { + char *buf = quote_string(str); + return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); +} + +// Copy all tokens until the next newline, terminate them with +// an EOF token and then returns them. This function is used to +// create a new list of tokens for `#if` arguments. +static Token *copy_line(Token **rest, Token *tok) { + Token head = {}; + Token *cur = &head; + for (; !tok->at_bol; tok = tok->next) cur = cur->next = copy_token(tok); + cur->next = new_eof(tok); + *rest = tok; + return head.next; +} + +static Token *new_num_token(int val, Token *tmpl) { + char buf[30]; + sprintf(buf, "%d\n", val); + return tokenize(new_file(tmpl->file->name, tmpl->file->file_no, strdup(buf))); +} + +static Token *read_const_expr(Token **rest, Token *tok) { + tok = copy_line(rest, tok); + Token head = {}; + Token *cur = &head; + while (tok->kind != TK_EOF) { + // "defined(foo)" or "defined foo" becomes "1" if macro "foo" + // is defined. Otherwise "0". + if (equal(tok, "defined")) { + Token *start = tok; + bool has_paren = consume(&tok, tok->next, "("); + if (tok->kind != TK_IDENT) + error_tok(start, "macro name must be an identifier"); + Macro *m = find_macro(tok); + tok = tok->next; + if (has_paren) tok = skip(tok, ")"); + cur = cur->next = new_num_token(m ? 1 : 0, start); + continue; + } + cur = cur->next = tok; + tok = tok->next; + } + cur->next = tok; + return head.next; +} + +// Read and evaluate a constant expression. +static long eval_const_expr(Token **rest, Token *tok) { + Token *start = tok; + Token *expr = read_const_expr(rest, tok->next); + expr = preprocess2(expr); + if (expr->kind == TK_EOF) error_tok(start, "no expression"); + // [C18 6.10.1.4] The standard requires we replace remaining + // non-macro identifiers with "0" before evaluating a constant + // expression. For example, `#if foo` is equivalent to `#if 0` + // if foo is not defined. + for (Token *t = expr; t->kind != TK_EOF; t = t->next) { + if (t->kind == TK_IDENT) { + Token *next = t->next; + *t = *new_num_token(0, t); + t->next = next; + } + } + // Convert pp-numbers to regular numbers + convert_pp_tokens(expr); + Token *rest2; + long val = const_expr(&rest2, expr); + if (rest2->kind != TK_EOF) error_tok(rest2, "extra token"); + return val; +} + +static CondIncl *push_cond_incl(Token *tok, bool included) { + CondIncl *ci = calloc(1, sizeof(CondIncl)); + ci->next = cond_incl; + ci->ctx = IN_THEN; + ci->tok = tok; + ci->included = included; + cond_incl = ci; + return ci; +} + +static Macro *find_macro(Token *tok) { + if (tok->kind != TK_IDENT) return NULL; + return hashmap_get2(¯os, tok->loc, tok->len); +} + +static Macro *add_macro(char *name, bool is_objlike, Token *body) { + Macro *m = calloc(1, sizeof(Macro)); + m->name = name; + m->is_objlike = is_objlike; + m->body = body; + hashmap_put(¯os, name, m); + return m; +} + +static MacroParam *read_macro_params(Token **rest, Token *tok, + char **va_args_name) { + MacroParam head = {}; + MacroParam *cur = &head; + while (!equal(tok, ")")) { + if (cur != &head) tok = skip(tok, ","); + if (equal(tok, "...")) { + *va_args_name = "__VA_ARGS__"; + *rest = skip(tok->next, ")"); + return head.next; + } + if (tok->kind != TK_IDENT) error_tok(tok, "expected an identifier"); + if (equal(tok->next, "...")) { + *va_args_name = strndup(tok->loc, tok->len); + *rest = skip(tok->next->next, ")"); + return head.next; + } + MacroParam *m = calloc(1, sizeof(MacroParam)); + m->name = strndup(tok->loc, tok->len); + cur = cur->next = m; + tok = tok->next; + } + *rest = tok->next; + return head.next; +} + +static void read_macro_definition(Token **rest, Token *tok) { + if (tok->kind != TK_IDENT) error_tok(tok, "macro name must be an identifier"); + char *name = strndup(tok->loc, tok->len); + tok = tok->next; + if (!tok->has_space && equal(tok, "(")) { + // Function-like macro + char *va_args_name = NULL; + MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name); + Macro *m = add_macro(name, false, copy_line(rest, tok)); + m->params = params; + m->va_args_name = va_args_name; + } else { + // Object-like macro + add_macro(name, true, copy_line(rest, tok)); + } +} + +static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) { + Token head = {}; + Token *cur = &head; + int level = 0; + for (;;) { + if (level == 0 && equal(tok, ")")) break; + if (level == 0 && !read_rest && equal(tok, ",")) break; + if (tok->kind == TK_EOF) error_tok(tok, "premature end of input"); + if (equal(tok, "(")) + level++; + else if (equal(tok, ")")) + level--; + cur = cur->next = copy_token(tok); + tok = tok->next; + } + cur->next = new_eof(tok); + MacroArg *arg = calloc(1, sizeof(MacroArg)); + arg->tok = head.next; + *rest = tok; + return arg; +} + +static MacroArg *read_macro_args(Token **rest, Token *tok, MacroParam *params, + char *va_args_name) { + Token *start = tok; + tok = tok->next->next; + MacroArg head = {}; + MacroArg *cur = &head; + MacroParam *pp = params; + for (; pp; pp = pp->next) { + if (cur != &head) tok = skip(tok, ","); + cur = cur->next = read_macro_arg_one(&tok, tok, false); + cur->name = pp->name; + } + if (va_args_name) { + MacroArg *arg; + if (equal(tok, ")")) { + arg = calloc(1, sizeof(MacroArg)); + arg->tok = new_eof(tok); + } else { + if (pp != params) tok = skip(tok, ","); + arg = read_macro_arg_one(&tok, tok, true); + } + arg->name = va_args_name; + ; + arg->is_va_args = true; + cur = cur->next = arg; + } else if (pp) { + error_tok(start, "too many arguments"); + } + skip(tok, ")"); + *rest = tok; + return head.next; +} + +static MacroArg *find_arg(MacroArg *args, Token *tok) { + for (MacroArg *ap = args; ap; ap = ap->next) + if (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len)) + return ap; + return NULL; +} + +// Concatenates all tokens in `tok` and returns a new string. +static char *join_tokens(Token *tok, Token *end) { + // Compute the length of the resulting token. + int len = 1; + for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + if (t != tok && t->has_space) len++; + len += t->len; + } + char *buf = calloc(1, len); + // Copy token texts. + int pos = 0; + for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + if (t != tok && t->has_space) buf[pos++] = ' '; + strncpy(buf + pos, t->loc, t->len); + pos += t->len; + } + buf[pos] = '\0'; + return buf; +} + +// Concatenates all tokens in `arg` and returns a new string token. +// This function is used for the stringizing operator (#). +static Token *stringize(Token *hash, Token *arg) { + // Create a new string token. We need to set some value to its + // source location for error reporting function, so we use a macro + // name token as a template. + char *s = join_tokens(arg, NULL); + return new_str_token(s, hash); +} + +// Concatenate two tokens to create a new token. +static Token *paste(Token *lhs, Token *rhs) { + // Paste the two tokens. + char *buf = calloc(1, lhs->len + rhs->len + 1); + sprintf(buf, "%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc); + // Tokenize the resulting string. + Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf)); + if (tok->next->kind != TK_EOF) + error_tok(lhs, "pasting forms '%s', an invalid token", buf); + return tok; +} + +static bool has_varargs(MacroArg *args) { + for (MacroArg *ap = args; ap; ap = ap->next) + if (!strcmp(ap->name, "__VA_ARGS__")) return ap->tok->kind != TK_EOF; + return false; +} + +// Replace func-like macro parameters with given arguments. +static Token *subst(Token *tok, MacroArg *args) { + Token head = {}; + Token *cur = &head; + + while (tok->kind != TK_EOF) { + // "#" followed by a parameter is replaced with stringized actuals. + if (equal(tok, "#")) { + MacroArg *arg = find_arg(args, tok->next); + if (!arg) + error_tok(tok->next, "'#' is not followed by a macro parameter"); + cur = cur->next = stringize(tok, arg->tok); + tok = tok->next->next; + continue; + } + + // [GNU] If __VA_ARG__ is empty, `,##__VA_ARGS__` is expanded + // to the empty token list. Otherwise, its expaned to `,` and + // __VA_ARGS__. + if (equal(tok, ",") && equal(tok->next, "##")) { + MacroArg *arg = find_arg(args, tok->next->next); + if (arg && arg->is_va_args) { + if (arg->tok->kind == TK_EOF) { + tok = tok->next->next->next; + } else { + cur = cur->next = copy_token(tok); + tok = tok->next->next; + } + continue; + } + } + + if (equal(tok, "##")) { + if (cur == &head) + error_tok(tok, "'##' cannot appear at start of macro expansion"); + + if (tok->next->kind == TK_EOF) + error_tok(tok, "'##' cannot appear at end of macro expansion"); + + MacroArg *arg = find_arg(args, tok->next); + if (arg) { + if (arg->tok->kind != TK_EOF) { + *cur = *paste(cur, arg->tok); + for (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next) + cur = cur->next = copy_token(t); + } + tok = tok->next->next; + continue; + } + + *cur = *paste(cur, tok->next); + tok = tok->next->next; + continue; + } + + MacroArg *arg = find_arg(args, tok); + + if (arg && equal(tok->next, "##")) { + Token *rhs = tok->next->next; + + if (arg->tok->kind == TK_EOF) { + MacroArg *arg2 = find_arg(args, rhs); + if (arg2) { + for (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = copy_token(t); + } else { + cur = cur->next = copy_token(rhs); + } + tok = rhs->next; + continue; + } + + for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = copy_token(t); + tok = tok->next; + continue; + } + + // If __VA_ARG__ is empty, __VA_OPT__(x) is expanded to the + // empty token list. Otherwise, __VA_OPT__(x) is expanded to x. + if (equal(tok, "__VA_OPT__") && equal(tok->next, "(")) { + MacroArg *arg = read_macro_arg_one(&tok, tok->next->next, true); + if (has_varargs(args)) + for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = t; + tok = skip(tok, ")"); + continue; + } + + // Handle a macro token. Macro arguments are completely macro-expanded + // before they are substituted into a macro body. + if (arg) { + Token *t = preprocess2(arg->tok); + t->at_bol = tok->at_bol; + t->has_space = tok->has_space; + for (; t->kind != TK_EOF; t = t->next) cur = cur->next = copy_token(t); + tok = tok->next; + continue; + } + + // Handle a non-macro token. + cur = cur->next = copy_token(tok); + tok = tok->next; + continue; + } + + cur->next = tok; + return head.next; +} + +// If tok is a macro, expand it and return true. +// Otherwise, do nothing and return false. +static bool expand_macro(Token **rest, Token *tok) { + if (hideset_contains(tok->hideset, tok->loc, tok->len)) return false; + Macro *m = find_macro(tok); + if (!m) return false; + // Built-in dynamic macro application such as __LINE__ + if (m->handler) { + *rest = m->handler(tok); + (*rest)->next = tok->next; + return true; + } + // Object-like macro application + if (m->is_objlike) { + Hideset *hs = hideset_union(tok->hideset, new_hideset(m->name)); + Token *body = add_hideset(m->body, hs); + for (Token *t = body; t->kind != TK_EOF; t = t->next) t->origin = tok; + *rest = append(body, tok->next); + (*rest)->at_bol = tok->at_bol; + (*rest)->has_space = tok->has_space; + return true; + } + // If a funclike macro token is not followed by an argument list, + // treat it as a normal identifier. + if (!equal(tok->next, "(")) return false; + // Function-like macro application + Token *macro_token = tok; + MacroArg *args = read_macro_args(&tok, tok, m->params, m->va_args_name); + Token *rparen = tok; + // Tokens that consist a func-like macro invocation may have different + // hidesets, and if that's the case, it's not clear what the hideset + // for the new tokens should be. We take the interesection of the + // macro token and the closing parenthesis and use it as a new hideset + // as explained in the Dave Prossor's algorithm. + Hideset *hs = hideset_intersection(macro_token->hideset, rparen->hideset); + hs = hideset_union(hs, new_hideset(m->name)); + Token *body = subst(m->body, args); + body = add_hideset(body, hs); + for (Token *t = body; t->kind != TK_EOF; t = t->next) t->origin = macro_token; + *rest = append(body, tok->next); + (*rest)->at_bol = macro_token->at_bol; + (*rest)->has_space = macro_token->has_space; + return true; +} + +// Returns true if a given file exists. +bool file_exists(char *path) { + struct stat st; + return !stat(path, &st); +} + +char *search_include_paths(char *filename) { + if (filename[0] == '/') return filename; + static HashMap cache; + char *cached = hashmap_get(&cache, filename); + if (cached) return cached; + // Search a file from the include paths. + for (int i = 0; i < include_paths.len; i++) { + char *path = format("%s/%s", include_paths.data[i], filename); + if (!file_exists(path)) continue; + hashmap_put(&cache, filename, path); + include_next_idx = i + 1; + return path; + } + return NULL; +} + +static char *search_include_next(char *filename) { + for (; include_next_idx < include_paths.len; include_next_idx++) { + char *path = + format("%s/%s", include_paths.data[include_next_idx], filename); + if (file_exists(path)) return path; + } + return NULL; +} + +// Read an #include argument. +static char *read_include_filename(Token **rest, Token *tok, bool *is_dquote) { + // Pattern 1: #include "foo.h" + if (tok->kind == TK_STR) { + // A double-quoted filename for #include is a special kind of + // token, and we don't want to interpret any escape sequences in it. + // For example, "\f" in "C:\foo" is not a formfeed character but + // just two non-control characters, backslash and f. + // So we don't want to use token->str. + *is_dquote = true; + *rest = skip_line(tok->next); + return strndup(tok->loc + 1, tok->len - 2); + } + // Pattern 2: #include + if (equal(tok, "<")) { + // Reconstruct a filename from a sequence of tokens between + // "<" and ">". + Token *start = tok; + // Find closing ">". + for (; !equal(tok, ">"); tok = tok->next) + if (tok->at_bol || tok->kind == TK_EOF) error_tok(tok, "expected '>'"); + *is_dquote = false; + *rest = skip_line(tok->next); + return join_tokens(start->next, tok); + } + // Pattern 3: #include FOO + // In this case FOO must be macro-expanded to either + // a single string token or a sequence of "<" ... ">". + if (tok->kind == TK_IDENT) { + Token *tok2 = preprocess2(copy_line(rest, tok)); + return read_include_filename(&tok2, tok2, is_dquote); + } + error_tok(tok, "expected a filename"); +} + +// Detect the following "include guard" pattern. +// +// #ifndef FOO_H +// #define FOO_H +// ... +// #endif +static char *detect_include_guard(Token *tok) { + // Detect the first two lines. + if (!is_hash(tok) || !equal(tok->next, "ifndef")) return NULL; + tok = tok->next->next; + if (tok->kind != TK_IDENT) return NULL; + char *macro = strndup(tok->loc, tok->len); + tok = tok->next; + if (!is_hash(tok) || !equal(tok->next, "define") || + !equal(tok->next->next, macro)) + return NULL; + // Read until the end of the file. + while (tok->kind != TK_EOF) { + if (!is_hash(tok)) { + tok = tok->next; + continue; + } + if (equal(tok->next, "endif") && tok->next->next->kind == TK_EOF) + return macro; + if (equal(tok, "if") || equal(tok, "ifdef") || equal(tok, "ifndef")) + tok = skip_cond_incl(tok->next); + else + tok = tok->next; + } + return NULL; +} + +static Token *include_file(Token *tok, char *path) { + // Check for "#pragma once" + if (hashmap_get(&pragma_once, path)) return tok; + // If we read the same file before, and if the file was guarded + // by the usual #ifndef ... #endif pattern, we may be able to + // skip the file without opening it. + static HashMap include_guards; + char *guard_name = hashmap_get(&include_guards, path); + if (guard_name && hashmap_get(¯os, guard_name)) return tok; + Token *tok2 = tokenize_file(path); + if (!tok2) error_tok(tok, "%s: cannot open file: %s", path, strerror(errno)); + guard_name = detect_include_guard(tok2); + if (guard_name) hashmap_put(&include_guards, path, guard_name); + return append(tok2, tok); +} + +// Read #line arguments +static void read_line_marker(Token **rest, Token *tok) { + Token *start = tok; + tok = preprocess(copy_line(rest, tok)); + + if (tok->kind != TK_NUM || tok->ty->kind != TY_INT) + error_tok(tok, "invalid line marker"); + start->file->line_delta = tok->val - start->line_no; + + tok = tok->next; + if (tok->kind == TK_EOF) return; + + if (tok->kind != TK_STR) error_tok(tok, "filename expected"); + start->file->display_name = tok->str; +} + +// Visit all tokens in `tok` while evaluating preprocessing +// macros and directives. +static Token *preprocess2(Token *tok) { + Token head = {}; + Token *cur = &head; + + while (tok->kind != TK_EOF) { + // If it is a macro, expand it. + if (expand_macro(&tok, tok)) continue; + + // Pass through if it is not a "#". + if (!is_hash(tok)) { + tok->line_delta = tok->file->line_delta; + tok->filename = tok->file->display_name; + cur = cur->next = tok; + tok = tok->next; + continue; + } + + Token *start = tok; + tok = tok->next; + + if (equal(tok, "include")) { + bool is_dquote; + char *filename = read_include_filename(&tok, tok->next, &is_dquote); + + if (filename[0] != '/' && is_dquote) { + char *path = + format("%s/%s", dirname(strdup(start->file->name)), filename); + if (file_exists(path)) { + tok = include_file(tok, path); + continue; + } + } + + char *path = search_include_paths(filename); + tok = include_file(tok, path ? path : filename); + continue; + } + + if (equal(tok, "include_next")) { + bool ignore; + char *filename = read_include_filename(&tok, tok->next, &ignore); + char *path = search_include_next(filename); + tok = include_file(tok, path ? path : filename); + continue; + } + + if (equal(tok, "define")) { + read_macro_definition(&tok, tok->next); + continue; + } + + if (equal(tok, "undef")) { + tok = tok->next; + if (tok->kind != TK_IDENT) + error_tok(tok, "macro name must be an identifier"); + undef_macro(strndup(tok->loc, tok->len)); + tok = skip_line(tok->next); + continue; + } + + if (equal(tok, "if")) { + long val = eval_const_expr(&tok, tok); + push_cond_incl(start, val); + if (!val) tok = skip_cond_incl(tok); + continue; + } + + if (equal(tok, "ifdef")) { + bool defined = find_macro(tok->next); + push_cond_incl(tok, defined); + tok = skip_line(tok->next->next); + if (!defined) tok = skip_cond_incl(tok); + continue; + } + + if (equal(tok, "ifndef")) { + bool defined = find_macro(tok->next); + push_cond_incl(tok, !defined); + tok = skip_line(tok->next->next); + if (defined) tok = skip_cond_incl(tok); + continue; + } + + if (equal(tok, "elif")) { + if (!cond_incl || cond_incl->ctx == IN_ELSE) + error_tok(start, "stray #elif"); + cond_incl->ctx = IN_ELIF; + + if (!cond_incl->included && eval_const_expr(&tok, tok)) + cond_incl->included = true; + else + tok = skip_cond_incl(tok); + continue; + } + + if (equal(tok, "else")) { + if (!cond_incl || cond_incl->ctx == IN_ELSE) + error_tok(start, "stray #else"); + cond_incl->ctx = IN_ELSE; + tok = skip_line(tok->next); + + if (cond_incl->included) tok = skip_cond_incl(tok); + continue; + } + + if (equal(tok, "endif")) { + if (!cond_incl) error_tok(start, "stray #endif"); + cond_incl = cond_incl->next; + tok = skip_line(tok->next); + continue; + } + + if (equal(tok, "line")) { + read_line_marker(&tok, tok->next); + continue; + } + + if (tok->kind == TK_PP_NUM) { + read_line_marker(&tok, tok); + continue; + } + + if (equal(tok, "pragma") && equal(tok->next, "once")) { + hashmap_put(&pragma_once, tok->file->name, (void *)1); + tok = skip_line(tok->next->next); + continue; + } + + if (equal(tok, "pragma")) { + do { + tok = tok->next; + } while (!tok->at_bol); + continue; + } + + if (equal(tok, "error")) error_tok(tok, "error"); + + // `#`-only line is legal. It's called a null directive. + if (tok->at_bol) continue; + + error_tok(tok, "invalid preprocessor directive"); + } + + cur->next = tok; + return head.next; +} + +void define_macro(char *name, char *buf) { + Token *tok = tokenize(new_file("", 1, buf)); + add_macro(name, true, tok); +} + +void undef_macro(char *name) { + hashmap_delete(¯os, name); +} + +static Macro *add_builtin(char *name, macro_handler_fn *fn) { + Macro *m = add_macro(name, true, NULL); + m->handler = fn; + return m; +} + +static Token *file_macro(Token *tmpl) { + while (tmpl->origin) tmpl = tmpl->origin; + return new_str_token(tmpl->file->display_name, tmpl); +} + +static Token *line_macro(Token *tmpl) { + while (tmpl->origin) tmpl = tmpl->origin; + int i = tmpl->line_no + tmpl->file->line_delta; + return new_num_token(i, tmpl); +} + +// __COUNTER__ is expanded to serial values starting from 0. +static Token *counter_macro(Token *tmpl) { + static int i = 0; + return new_num_token(i++, tmpl); +} + +// __TIMESTAMP__ is expanded to a string describing the last +// modification time of the current file. E.g. +// "Fri Jul 24 01:32:50 2020" +static Token *timestamp_macro(Token *tmpl) { + struct stat st; + if (stat(tmpl->file->name, &st) != 0) + return new_str_token("??? ??? ?? ??:??:?? ????", tmpl); + char buf[30]; + ctime_r(&st.st_mtime, buf); + buf[24] = '\0'; + return new_str_token(buf, tmpl); +} + +static Token *base_file_macro(Token *tmpl) { + return new_str_token(base_file, tmpl); +} + +// __DATE__ is expanded to the current date, e.g. "May 17 2020". +static char *format_date(struct tm *tm) { + static char mon[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + char buf[30]; + sprintf(buf, "\"%s %2d %d\"", mon[tm->tm_mon], tm->tm_mday, + tm->tm_year + 1900); + return strdup(buf); +} + +// __TIME__ is expanded to the current time, e.g. "13:34:03". +static char *format_time(struct tm *tm) { + char buf[30]; + sprintf(buf, "\"%02d:%02d:%02d\"", tm->tm_hour, tm->tm_min, tm->tm_sec); + return strdup(buf); +} + +void init_macros(void) { + // Define predefined macros + define_macro("_LP64", "1"); + define_macro("__STRICT_ANSI__", "1"); + define_macro("__C99_MACRO_WITH_VA_ARGS", "1"); + define_macro("__ELF__", "1"); + define_macro("__LP64__", "1"); + define_macro("__SIZEOF_DOUBLE__", "8"); + define_macro("__SIZEOF_FLOAT__", "4"); + define_macro("__SIZEOF_INT__", "4"); + define_macro("__SIZEOF_LONG_DOUBLE__", "8"); + define_macro("__SIZEOF_LONG_LONG__", "8"); + define_macro("__SIZEOF_LONG__", "8"); + define_macro("__SIZEOF_POINTER__", "8"); + define_macro("__SIZEOF_PTRDIFF_T__", "8"); + define_macro("__SIZEOF_SHORT__", "2"); + define_macro("__SIZEOF_SIZE_T__", "8"); + define_macro("__SIZE_TYPE__", "unsigned long"); + define_macro("__STDC_HOSTED__", "1"); + define_macro("__STDC_NO_COMPLEX__", "1"); + define_macro("__STDC_UTF_16__", "1"); + define_macro("__STDC_UTF_32__", "1"); + define_macro("__STDC_VERSION__", "201112L"); + define_macro("__STDC__", "1"); + define_macro("__USER_LABEL_PREFIX__", ""); + define_macro("__alignof__", "_Alignof"); + define_macro("__amd64", "1"); + define_macro("__amd64__", "1"); + define_macro("__chibicc__", "1"); + define_macro("__const__", "const"); + define_macro("__gnu_linux__", "1"); + define_macro("__inline__", "inline"); + define_macro("__linux", "1"); + define_macro("__linux__", "1"); + define_macro("__signed__", "signed"); + define_macro("__typeof__", "typeof"); + define_macro("__unix", "1"); + define_macro("__unix__", "1"); + define_macro("__volatile__", "volatile"); + define_macro("__x86_64", "1"); + define_macro("__x86_64__", "1"); + define_macro("linux", "1"); + define_macro("unix", "1"); + add_builtin("__FILE__", file_macro); + add_builtin("__LINE__", line_macro); + add_builtin("__COUNTER__", counter_macro); + add_builtin("__TIMESTAMP__", timestamp_macro); + add_builtin("__BASE_FILE__", base_file_macro); + time_t now = time(NULL); + struct tm *tm = localtime(&now); + define_macro("__DATE__", format_date(tm)); + define_macro("__TIME__", format_time(tm)); +} + +typedef enum { + STR_NONE, + STR_UTF8, + STR_UTF16, + STR_UTF32, + STR_WIDE, +} StringKind; + +static StringKind getStringKind(Token *tok) { + if (!strcmp(tok->loc, "u8")) return STR_UTF8; + switch (tok->loc[0]) { + case '"': + return STR_NONE; + case 'u': + return STR_UTF16; + case 'U': + return STR_UTF32; + case 'L': + return STR_WIDE; + } + UNREACHABLE(); +} + +// Concatenate adjacent string literals into a single string literal +// as per the C spec. +static void join_adjacent_string_literals(Token *tok) { + // First pass: If regular string literals are adjacent to wide + // string literals, regular string literals are converted to a wide + // type before concatenation. In this pass, we do the conversion. + for (Token *tok1 = tok; tok1->kind != TK_EOF;) { + if (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { + tok1 = tok1->next; + continue; + } + StringKind kind = getStringKind(tok1); + Type *basety = tok1->ty->base; + for (Token *t = tok1->next; t->kind == TK_STR; t = t->next) { + StringKind k = getStringKind(t); + if (kind == STR_NONE) { + kind = k; + basety = t->ty->base; + } else if (k != STR_NONE && kind != k) { + error_tok(t, + "unsupported non-standard concatenation of string literals"); + } + } + if (basety->size > 1) + for (Token *t = tok1; t->kind == TK_STR; t = t->next) + if (t->ty->base->size == 1) *t = *tokenize_string_literal(t, basety); + while (tok1->kind == TK_STR) tok1 = tok1->next; + } + // Second pass: concatenate adjacent string literals. + for (Token *tok1 = tok; tok1->kind != TK_EOF;) { + Token *tok2 = tok1->next; + if (tok1->kind != TK_STR || tok2->kind != TK_STR) { + tok1 = tok1->next; + continue; + } + assert(tok1->ty->base->size == tok2->ty->base->size); + Token *t = copy_token(tok1); + t->ty = + array_of(tok1->ty->base, tok1->ty->array_len + tok2->ty->array_len - 1); + t->str = calloc(1, t->ty->size); + t->next = tok2->next; + int i = 0; + for (int j = 0; j < tok1->ty->size - tok1->ty->base->size; i++, j++) + t->str[i] = tok1->str[j]; + for (int j = 0; j < tok2->ty->size; i++, j++) t->str[i] = tok2->str[j]; + *tok1 = *t; + } +} + +// Entry point function of the preprocessor. +Token *preprocess(Token *tok) { + tok = preprocess2(tok); + if (cond_incl) + error_tok(cond_incl->tok, "unterminated conditional directive"); + convert_pp_tokens(tok); + join_adjacent_string_literals(tok); + for (Token *t = tok; t; t = t->next) t->line_no += t->line_delta; + return tok; +} diff --git a/third_party/chibicc/strarray.c b/third_party/chibicc/strarray.c new file mode 100644 index 000000000..1feb6d9e6 --- /dev/null +++ b/third_party/chibicc/strarray.c @@ -0,0 +1,16 @@ +#include "third_party/chibicc/chibicc.h" + +void strarray_push(StringArray *arr, char *s) { + if (!arr->data) { + arr->data = calloc(8, sizeof(char *)); + arr->capacity = 8; + } + + if (arr->capacity == arr->len) { + arr->data = realloc(arr->data, sizeof(char *) * arr->capacity * 2); + arr->capacity *= 2; + for (int i = arr->len; i < arr->capacity; i++) arr->data[i] = NULL; + } + + arr->data[arr->len++] = s; +} diff --git a/third_party/chibicc/tokenize.c b/third_party/chibicc/tokenize.c new file mode 100644 index 000000000..bfb3f4ea5 --- /dev/null +++ b/third_party/chibicc/tokenize.c @@ -0,0 +1,785 @@ +#include "third_party/chibicc/chibicc.h" + +// Input file +static File *current_file; + +// A list of all input files. +static File **input_files; + +// True if the current position is at the beginning of a line +static bool at_bol; + +// True if the current position follows a space character +static bool has_space; + +// Reports an error and exit. +void error(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + exit(1); +} + +// Reports an error message in the following format. +// +// foo.c:10: x = y + 1; +// ^ +static void verror_at(char *filename, char *input, int line_no, char *loc, + char *fmt, va_list ap) { + // Find a line containing `loc`. + char *line = loc; + while (input < line && line[-1] != '\n') line--; + + char *end = loc; + while (*end && *end != '\n') end++; + + // Print out the line. + int indent = fprintf(stderr, "%s:%d: ", filename, line_no); + fprintf(stderr, "%.*s\n", (int)(end - line), line); + + // Show the error message. + int pos = str_width(line, loc - line) + indent; + + fprintf(stderr, "%*s", pos, ""); // print pos spaces. + fprintf(stderr, "^ "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + +void error_at(char *loc, char *fmt, ...) { + int line_no = 1; + for (char *p = current_file->contents; p < loc; p++) + if (*p == '\n') line_no++; + + va_list ap; + va_start(ap, fmt); + verror_at(current_file->name, current_file->contents, line_no, loc, fmt, ap); + exit(1); +} + +void error_tok(Token *tok, char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, + ap); + exit(1); +} + +void warn_tok(Token *tok, char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, + ap); +} + +// Consumes the current token if it matches `op`. +bool equal(Token *tok, char *op) { + return strlen(op) == tok->len && !strncmp(tok->loc, op, tok->len); +} + +// Ensure that the current token is `op`. +Token *skip(Token *tok, char *op) { + if (!equal(tok, op)) error_tok(tok, "expected '%s'", op); + return tok->next; +} + +bool consume(Token **rest, Token *tok, char *str) { + if (equal(tok, str)) { + *rest = tok->next; + return true; + } + *rest = tok; + return false; +} + +// Create a new token and add it as the next token of `cur`. +static Token *new_token(TokenKind kind, char *start, char *end) { + Token *tok = calloc(1, sizeof(Token)); + tok->kind = kind; + tok->loc = start; + tok->len = end - start; + tok->file = current_file; + tok->filename = current_file->display_name; + tok->at_bol = at_bol; + tok->has_space = has_space; + + at_bol = has_space = false; + return tok; +} + +static bool starts_with(char *p, char *q) { + return strncmp(p, q, strlen(q)) == 0; +} + +// Read an identifier and returns a pointer pointing to the end +// of an identifier. +// +// Returns null if p does not point to a valid identifier. +static char *read_ident(char *p) { + uint32_t c = decode_utf8(&p, p); + if (!is_ident1(c)) return NULL; + + for (;;) { + char *q; + c = decode_utf8(&q, p); + if (!is_ident2(c)) return p; + p = q; + } +} + +static int from_hex(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return c - 'a' + 10; + return c - 'A' + 10; +} + +static bool is_keyword(Token *tok) { + static HashMap map; + + if (map.capacity == 0) { + static char *kw[] = { + "return", "if", "else", + "for", "while", "int", + "sizeof", "char", "struct", + "union", "short", "long", + "void", "typedef", "_Bool", + "enum", "static", "goto", + "break", "continue", "switch", + "case", "default", "extern", + "_Alignof", "_Alignas", "do", + "signed", "unsigned", "const", + "volatile", "auto", "register", + "restrict", "__restrict", "__restrict__", + "_Noreturn", "float", "double", + "typeof", "asm", "_Thread_local", + "__thread", "_Atomic", "__attribute__", + }; + + for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) + hashmap_put(&map, kw[i], (void *)1); + } + + return hashmap_get2(&map, tok->loc, tok->len); +} + +static int read_escaped_char(char **new_pos, char *p) { + if ('0' <= *p && *p <= '7') { + // Read an octal number. + int c = *p++ - '0'; + if ('0' <= *p && *p <= '7') { + c = (c << 3) + (*p++ - '0'); + if ('0' <= *p && *p <= '7') c = (c << 3) + (*p++ - '0'); + } + *new_pos = p; + return c; + } + + if (*p == 'x') { + // Read a hexadecimal number. + p++; + if (!isxdigit(*p)) error_at(p, "invalid hex escape sequence"); + + int c = 0; + for (; isxdigit(*p); p++) c = (c << 4) + from_hex(*p); + *new_pos = p; + return c; + } + + *new_pos = p + 1; + + switch (*p) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 't': + return '\t'; + case 'n': + return '\n'; + case 'v': + return '\v'; + case 'f': + return '\f'; + case 'r': + return '\r'; + // [GNU] \e for the ASCII escape character is a GNU C extension. + case 'e': + return 27; + default: + return *p; + } +} + +// Find a closing double-quote. +static char *string_literal_end(char *p) { + char *start = p; + for (; *p != '"'; p++) { + if (*p == '\0') error_at(start, "unclosed string literal"); + if (*p == '\\') p++; + } + return p; +} + +static Token *read_string_literal(char *start, char *quote) { + char *end = string_literal_end(quote + 1); + char *buf = calloc(1, end - quote); + int len = 0; + + for (char *p = quote + 1; p < end;) { + if (*p == '\\') + buf[len++] = read_escaped_char(&p, p + 1); + else + buf[len++] = *p++; + } + + Token *tok = new_token(TK_STR, start, end + 1); + tok->ty = array_of(ty_char, len + 1); + tok->str = buf; + return tok; +} + +// Read a UTF-8-encoded string literal and transcode it in UTF-16. +// +// UTF-16 is yet another variable-width encoding for Unicode. Code +// points smaller than U+10000 are encoded in 2 bytes. Code points +// equal to or larger than that are encoded in 4 bytes. Each 2 bytes +// in the 4 byte sequence is called "surrogate", and a 4 byte sequence +// is called a "surrogate pair". +static Token *read_utf16_string_literal(char *start, char *quote) { + char *end = string_literal_end(quote + 1); + uint16_t *buf = calloc(2, end - start - 1); + int len = 0; + + for (char *p = quote + 1; p < end;) { + if (*p == '\\') { + buf[len++] = read_escaped_char(&p, p + 1); + continue; + } + + uint32_t c = decode_utf8(&p, p); + if (c < 0x10000) { + // Encode a code point in 2 bytes. + buf[len++] = c; + } else { + // Encode a code point in 4 bytes. + c -= 0x10000; + buf[len++] = 0xd800 + ((c >> 10) & 0x3ff); + buf[len++] = 0xdc00 + (c & 0x3ff); + } + } + + Token *tok = new_token(TK_STR, start, end + 1); + tok->ty = array_of(ty_ushort, len + 1); + tok->str = (char *)buf; + return tok; +} + +// Read a UTF-8-encoded string literal and transcode it in UTF-32. +// +// UTF-32 is a fixed-width encoding for Unicode. Each code point is +// encoded in 4 bytes. +static Token *read_utf32_string_literal(char *start, char *quote, Type *ty) { + char *end = string_literal_end(quote + 1); + uint32_t *buf = calloc(4, end - quote); + int len = 0; + + for (char *p = quote + 1; p < end;) { + if (*p == '\\') + buf[len++] = read_escaped_char(&p, p + 1); + else + buf[len++] = decode_utf8(&p, p); + } + + Token *tok = new_token(TK_STR, start, end + 1); + tok->ty = array_of(ty, len + 1); + tok->str = (char *)buf; + return tok; +} + +static Token *read_char_literal(char *start, char *quote, Type *ty) { + char *p = quote + 1; + if (*p == '\0') error_at(start, "unclosed char literal"); + + int c; + if (*p == '\\') + c = read_escaped_char(&p, p + 1); + else + c = decode_utf8(&p, p); + + char *end = strchr(p, '\''); + if (!end) error_at(p, "unclosed char literal"); + + Token *tok = new_token(TK_NUM, start, end + 1); + tok->val = c; + tok->ty = ty; + return tok; +} + +static bool convert_pp_int(Token *tok) { + char *p = tok->loc; + + // Read a binary, octal, decimal or hexadecimal number. + int base = 10; + if (!strncasecmp(p, "0x", 2) && isxdigit(p[2])) { + p += 2; + base = 16; + } else if (!strncasecmp(p, "0b", 2) && (p[2] == '0' || p[2] == '1')) { + p += 2; + base = 2; + } else if (*p == '0') { + base = 8; + } + + int64_t val = strtoul(p, &p, base); + + // Read U, L or LL suffixes. + bool l = false; + bool u = false; + + if (starts_with(p, "LLU") || starts_with(p, "LLu") || starts_with(p, "llU") || + starts_with(p, "llu") || starts_with(p, "ULL") || starts_with(p, "Ull") || + starts_with(p, "uLL") || starts_with(p, "ull")) { + p += 3; + l = u = true; + } else if (!strncasecmp(p, "lu", 2) || !strncasecmp(p, "ul", 2)) { + p += 2; + l = u = true; + } else if (starts_with(p, "LL") || starts_with(p, "ll")) { + p += 2; + l = true; + } else if (*p == 'L' || *p == 'l') { + p++; + l = true; + } else if (*p == 'U' || *p == 'u') { + p++; + u = true; + } + + if (p != tok->loc + tok->len) return false; + + // Infer a type. + Type *ty; + if (base == 10) { + if (l && u) + ty = ty_ulong; + else if (l) + ty = ty_long; + else if (u) + ty = (val >> 32) ? ty_ulong : ty_uint; + else + ty = (val >> 31) ? ty_long : ty_int; + } else { + if (l && u) + ty = ty_ulong; + else if (l) + ty = (val >> 63) ? ty_ulong : ty_long; + else if (u) + ty = (val >> 32) ? ty_ulong : ty_uint; + else if (val >> 63) + ty = ty_ulong; + else if (val >> 32) + ty = ty_long; + else if (val >> 31) + ty = ty_uint; + else + ty = ty_int; + } + + tok->kind = TK_NUM; + tok->val = val; + tok->ty = ty; + return true; +} + +// The definition of the numeric literal at the preprocessing stage +// is more relaxed than the definition of that at the later stages. +// In order to handle that, a numeric literal is tokenized as a +// "pp-number" token first and then converted to a regular number +// token after preprocessing. +// +// This function converts a pp-number token to a regular number token. +static void convert_pp_number(Token *tok) { + // Try to parse as an integer constant. + if (convert_pp_int(tok)) return; + + // If it's not an integer, it must be a floating point constant. + char *end; + long double val = strtold(tok->loc, &end); + + Type *ty; + if (*end == 'f' || *end == 'F') { + ty = ty_float; + end++; + } else if (*end == 'l' || *end == 'L') { + ty = ty_ldouble; + end++; + } else { + ty = ty_double; + } + + if (tok->loc + tok->len != end) error_tok(tok, "invalid numeric constant"); + + tok->kind = TK_NUM; + tok->fval = val; + tok->ty = ty; +} + +void convert_pp_tokens(Token *tok) { + for (Token *t = tok; t->kind != TK_EOF; t = t->next) { + if (is_keyword(t)) + t->kind = TK_RESERVED; + else if (t->kind == TK_PP_NUM) + convert_pp_number(t); + } +} + +// Initialize line info for all tokens. +static void add_line_numbers(Token *tok) { + char *p = current_file->contents; + int n = 1; + + do { + if (p == tok->loc) { + tok->line_no = n; + tok = tok->next; + } + if (*p == '\n') n++; + } while (*p++); +} + +Token *tokenize_string_literal(Token *tok, Type *basety) { + Token *t; + if (basety->size == 2) + t = read_utf16_string_literal(tok->loc, tok->loc); + else + t = read_utf32_string_literal(tok->loc, tok->loc, basety); + t->next = tok->next; + return t; +} + +// Tokenize a given string and returns new tokens. +Token *tokenize(File *file) { + current_file = file; + + char *p = file->contents; + Token head = {}; + Token *cur = &head; + + at_bol = true; + has_space = false; + + while (*p) { + // Skip line comments. + if (starts_with(p, "//")) { + p += 2; + while (*p != '\n') p++; + has_space = true; + continue; + } + + // Skip block comments. + if (starts_with(p, "/*")) { + char *q = strstr(p + 2, "*/"); + if (!q) error_at(p, "unclosed block comment"); + p = q + 2; + has_space = true; + continue; + } + + // Skip newline. + if (*p == '\n') { + p++; + at_bol = true; + has_space = false; + continue; + } + + // Skip whitespace characters. + if (isspace(*p)) { + p++; + has_space = true; + continue; + } + + // Numeric literal + if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) { + char *q = p++; + for (;;) { + if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1])) + p += 2; + else if (isalnum(*p) || *p == '.') + p++; + else + break; + } + cur = cur->next = new_token(TK_PP_NUM, q, p); + continue; + } + + // String literal + if (*p == '"') { + cur = cur->next = read_string_literal(p, p); + p += cur->len; + continue; + } + + // UTF-8 string literal + if (starts_with(p, "u8\"")) { + cur = cur->next = read_string_literal(p, p + 2); + p += cur->len; + continue; + } + + // UTF-16 string literal + if (starts_with(p, "u\"")) { + cur = cur->next = read_utf16_string_literal(p, p + 1); + p += cur->len; + continue; + } + + // Wide string literal + if (starts_with(p, "L\"")) { + cur = cur->next = read_utf32_string_literal(p, p + 1, ty_int); + p += cur->len; + continue; + } + + // UTF-32 string literal + if (starts_with(p, "U\"")) { + cur = cur->next = read_utf32_string_literal(p, p + 1, ty_uint); + p += cur->len; + continue; + } + + // Character literal + if (*p == '\'') { + cur = cur->next = read_char_literal(p, p, ty_int); + cur->val = (char)cur->val; + p += cur->len; + continue; + } + + // UTF-16 character literal + if (starts_with(p, "u'")) { + cur = cur->next = read_char_literal(p, p + 1, ty_ushort); + cur->val &= 0xffff; + p += cur->len; + continue; + } + + // Wide character literal + if (starts_with(p, "L'")) { + cur = cur->next = read_char_literal(p, p + 1, ty_int); + p += cur->len; + continue; + } + + // UTF-32 character literal + if (starts_with(p, "U'")) { + cur = cur->next = read_char_literal(p, p + 1, ty_uint); + p += cur->len; + continue; + } + + // Identifier or keyword + char *q; + if ((q = read_ident(p)) != NULL) { + cur = cur->next = new_token(TK_IDENT, p, q); + p = q; + continue; + } + + // Three-letter punctuators + if (starts_with(p, "<<=") || starts_with(p, ">>=") || + starts_with(p, "...")) { + cur = cur->next = new_token(TK_RESERVED, p, p + 3); + p += 3; + continue; + } + + // Two-letter punctuators + if (starts_with(p, "==") || starts_with(p, "!=") || starts_with(p, "<=") || + starts_with(p, ">=") || starts_with(p, "->") || starts_with(p, "+=") || + starts_with(p, "-=") || starts_with(p, "*=") || starts_with(p, "/=") || + starts_with(p, "++") || starts_with(p, "--") || starts_with(p, "%=") || + starts_with(p, "&=") || starts_with(p, "|=") || starts_with(p, "^=") || + starts_with(p, "&&") || starts_with(p, "||") || starts_with(p, "<<") || + starts_with(p, ">>") || starts_with(p, "##")) { + cur = cur->next = new_token(TK_RESERVED, p, p + 2); + p += 2; + continue; + } + + // Single-letter punctuators + if (ispunct(*p)) { + cur = cur->next = new_token(TK_RESERVED, p, p + 1); + p++; + continue; + } + + error_at(p, "invalid token"); + } + + cur = cur->next = new_token(TK_EOF, p, p); + add_line_numbers(head.next); + return head.next; +} + +// Returns the contents of a given file. +static char *read_file(char *path) { + FILE *fp; + + if (strcmp(path, "-") == 0) { + // By convention, read from stdin if a given filename is "-". + fp = stdin; + } else { + fp = fopen(path, "r"); + if (!fp) return NULL; + } + + int buflen = 4096; + int nread = 0; + char *buf = calloc(1, buflen); + + // Read the entire file. + for (;;) { + int end = buflen - 2; // extra 2 bytes for the trailing "\n\0" + int n = fread(buf + nread, 1, end - nread, fp); + if (n == 0) break; + nread += n; + if (nread == end) { + buflen *= 2; + buf = realloc(buf, buflen); + } + } + + if (fp != stdin) fclose(fp); + + // Make sure that the last logical line is properly terminated with '\n'. + if (nread > 0 && buf[nread - 1] == '\\') + buf[nread - 1] = '\n'; + else if (nread == 0 || buf[nread - 1] != '\n') + buf[nread++] = '\n'; + + buf[nread] = '\0'; + return buf; +} + +File **get_input_files(void) { + return input_files; +} + +File *new_file(char *name, int file_no, char *contents) { + File *file = calloc(1, sizeof(File)); + file->name = name; + file->display_name = name; + file->file_no = file_no; + file->contents = contents; + return file; +} + +// Replaces \r or \r\n with \n. +static void canonicalize_newline(char *p) { + int i = 0, j = 0; + + while (p[i]) { + if (p[i] == '\r' && p[i + 1] == '\n') { + i += 2; + p[j++] = '\n'; + } else if (p[i] == '\r') { + i++; + p[j++] = '\n'; + } else { + p[j++] = p[i++]; + } + } + + p[j] = '\0'; +} + +// Removes backslashes followed by a newline. +static void remove_backslash_newline(char *p) { + int i = 0, j = 0; + + // We want to keep the number of newline characters so that + // the logical line number matches the physical one. + // This counter maintain the number of newlines we have removed. + int n = 0; + + while (p[i]) { + if (p[i] == '\\' && p[i + 1] == '\n') { + i += 2; + n++; + } else if (p[i] == '\n') { + p[j++] = p[i++]; + for (; n > 0; n--) p[j++] = '\n'; + } else { + p[j++] = p[i++]; + } + } + + p[j] = '\0'; +} + +static uint32_t read_universal_char(char *p, int len) { + uint32_t c = 0; + for (int i = 0; i < len; i++) { + if (!isxdigit(p[i])) return 0; + c = (c << 4) | from_hex(p[i]); + } + return c; +} + +// Replace \u or \U escape sequences with corresponding UTF-8 bytes. +static void convert_universal_chars(char *p) { + char *q = p; + + while (*p) { + if (starts_with(p, "\\u")) { + uint32_t c = read_universal_char(p + 2, 4); + if (c) { + p += 6; + q += encode_utf8(q, c); + } else { + *q++ = *p++; + } + } else if (starts_with(p, "\\U")) { + uint32_t c = read_universal_char(p + 2, 8); + if (c) { + p += 10; + q += encode_utf8(q, c); + } else { + *q++ = *p++; + } + } else if (p[0] == '\\') { + *q++ = *p++; + *q++ = *p++; + } else { + *q++ = *p++; + } + } + + *q = '\0'; +} + +Token *tokenize_file(char *path) { + char *p = read_file(path); + if (!p) return NULL; + + canonicalize_newline(p); + remove_backslash_newline(p); + convert_universal_chars(p); + + // Save the filename for assembler .file directive. + static int file_no; + File *file = new_file(path, file_no + 1, p); + + // Save the filename for assembler .file directive. + input_files = realloc(input_files, sizeof(char *) * (file_no + 2)); + input_files[file_no] = file; + input_files[file_no + 1] = NULL; + file_no++; + + return tokenize(file); +} diff --git a/third_party/chibicc/type.c b/third_party/chibicc/type.c new file mode 100644 index 000000000..df8e34972 --- /dev/null +++ b/third_party/chibicc/type.c @@ -0,0 +1,286 @@ +#include "third_party/chibicc/chibicc.h" + +Type *ty_void = &(Type){TY_VOID, 1, 1}; +Type *ty_bool = &(Type){TY_BOOL, 1, 1}; + +Type *ty_char = &(Type){TY_CHAR, 1, 1}; +Type *ty_short = &(Type){TY_SHORT, 2, 2}; +Type *ty_int = &(Type){TY_INT, 4, 4}; +Type *ty_long = &(Type){TY_LONG, 8, 8}; + +Type *ty_uchar = &(Type){TY_CHAR, 1, 1, true}; +Type *ty_ushort = &(Type){TY_SHORT, 2, 2, true}; +Type *ty_uint = &(Type){TY_INT, 4, 4, true}; +Type *ty_ulong = &(Type){TY_LONG, 8, 8, true}; + +Type *ty_float = &(Type){TY_FLOAT, 4, 4}; +Type *ty_double = &(Type){TY_DOUBLE, 8, 8}; +Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16}; + +static Type *new_type(TypeKind kind, int size, int align) { + Type *ty = calloc(1, sizeof(Type)); + ty->kind = kind; + ty->size = size; + ty->align = align; + return ty; +} + +bool is_integer(Type *ty) { + TypeKind k = ty->kind; + return k == TY_BOOL || k == TY_CHAR || k == TY_SHORT || k == TY_INT || + k == TY_LONG || k == TY_ENUM; +} + +bool is_flonum(Type *ty) { + return ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE || + ty->kind == TY_LDOUBLE; +} + +bool is_numeric(Type *ty) { + return is_integer(ty) || is_flonum(ty); +} + +bool is_compatible(Type *t1, Type *t2) { + if (t1 == t2) return true; + + if (t1->origin) return is_compatible(t1->origin, t2); + + if (t2->origin) return is_compatible(t1, t2->origin); + + if (t1->kind != t2->kind) return false; + + switch (t1->kind) { + case TY_CHAR: + case TY_SHORT: + case TY_INT: + case TY_LONG: + return t1->is_unsigned == t2->is_unsigned; + case TY_FLOAT: + case TY_DOUBLE: + case TY_LDOUBLE: + return true; + case TY_PTR: + return is_compatible(t1->base, t2->base); + case TY_FUNC: { + if (!is_compatible(t1->return_ty, t2->return_ty)) return false; + if (t1->is_variadic != t2->is_variadic) return false; + + Type *p1 = t1->params; + Type *p2 = t2->params; + for (; p1 && p2; p1 = p1->next, p2 = p2->next) + if (!is_compatible(p1, p2)) return false; + return p1 == NULL && p2 == NULL; + } + case TY_ARRAY: + if (!is_compatible(t1->base, t2->base)) return false; + return t1->array_len < 0 && t2->array_len < 0 && + t1->array_len == t2->array_len; + } + return false; +} + +Type *copy_type(Type *ty) { + Type *ret = calloc(1, sizeof(Type)); + *ret = *ty; + ret->origin = ty; + return ret; +} + +Type *pointer_to(Type *base) { + Type *ty = new_type(TY_PTR, 8, 8); + ty->base = base; + ty->is_unsigned = true; + return ty; +} + +Type *func_type(Type *return_ty) { + // The C spec disallows sizeof(), but + // GCC allows that and the expression is evaluated to 1. + Type *ty = new_type(TY_FUNC, 1, 1); + ty->return_ty = return_ty; + return ty; +} + +Type *array_of(Type *base, int len) { + Type *ty = new_type(TY_ARRAY, base->size * len, base->align); + ty->base = base; + ty->array_len = len; + return ty; +} + +Type *vla_of(Type *base, Node *len) { + Type *ty = new_type(TY_VLA, 8, 8); + ty->base = base; + ty->vla_len = len; + return ty; +} + +Type *enum_type(void) { + return new_type(TY_ENUM, 4, 4); +} + +Type *struct_type(void) { + return new_type(TY_STRUCT, 0, 1); +} + +static Type *get_common_type(Type *ty1, Type *ty2) { + if (ty1->base) return pointer_to(ty1->base); + + if (ty1->kind == TY_FUNC) return pointer_to(ty1); + if (ty2->kind == TY_FUNC) return pointer_to(ty2); + + if (ty1->kind == TY_LDOUBLE || ty2->kind == TY_LDOUBLE) return ty_ldouble; + if (ty1->kind == TY_DOUBLE || ty2->kind == TY_DOUBLE) return ty_double; + if (ty1->kind == TY_FLOAT || ty2->kind == TY_FLOAT) return ty_float; + + if (ty1->size < 4) ty1 = ty_int; + if (ty2->size < 4) ty2 = ty_int; + + if (ty1->size != ty2->size) return (ty1->size < ty2->size) ? ty2 : ty1; + + if (ty2->is_unsigned) return ty2; + return ty1; +} + +// For many binary operators, we implicitly promote operands so that +// both operands have the same type. Any integral type smaller than +// int is always promoted to int. If the type of one operand is larger +// than the other's (e.g. "long" vs. "int"), the smaller operand will +// be promoted to match with the other. +// +// This operation is called the "usual arithmetic conversion". +static void usual_arith_conv(Node **lhs, Node **rhs) { + Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty); + *lhs = new_cast(*lhs, ty); + *rhs = new_cast(*rhs, ty); +} + +void add_type(Node *node) { + if (!node || node->ty) return; + + add_type(node->lhs); + add_type(node->rhs); + add_type(node->cond); + add_type(node->then); + add_type(node->els); + add_type(node->init); + add_type(node->inc); + + for (Node *n = node->body; n; n = n->next) add_type(n); + for (Node *n = node->args; n; n = n->next) add_type(n); + + switch (node->kind) { + case ND_NUM: + node->ty = ty_int; + return; + case ND_ADD: + case ND_SUB: + case ND_MUL: + case ND_DIV: + case ND_MOD: + case ND_BITAND: + case ND_BITOR: + case ND_BITXOR: + usual_arith_conv(&node->lhs, &node->rhs); + node->ty = node->lhs->ty; + return; + case ND_NEG: { + Type *ty = get_common_type(ty_int, node->lhs->ty); + node->lhs = new_cast(node->lhs, ty); + node->ty = ty; + return; + } + case ND_ASSIGN: + if (node->lhs->ty->kind == TY_ARRAY) + error_tok(node->lhs->tok, "not an lvalue"); + if (node->lhs->ty->kind != TY_STRUCT) + node->rhs = new_cast(node->rhs, node->lhs->ty); + node->ty = node->lhs->ty; + return; + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + usual_arith_conv(&node->lhs, &node->rhs); + node->ty = ty_int; + return; + case ND_FUNCALL: + node->ty = node->func_ty->return_ty; + return; + case ND_NOT: + case ND_LOGOR: + case ND_LOGAND: + node->ty = ty_int; + return; + case ND_BITNOT: + case ND_SHL: + case ND_SHR: + node->ty = node->lhs->ty; + return; + case ND_VAR: + case ND_VLA_PTR: + node->ty = node->var->ty; + return; + case ND_COND: + if (node->then->ty->kind == TY_VOID || node->els->ty->kind == TY_VOID) { + node->ty = ty_void; + } else { + usual_arith_conv(&node->then, &node->els); + node->ty = node->then->ty; + } + return; + case ND_COMMA: + node->ty = node->rhs->ty; + return; + case ND_MEMBER: + node->ty = node->member->ty; + return; + case ND_ADDR: { + Type *ty = node->lhs->ty; + if (ty->kind == TY_ARRAY) + node->ty = pointer_to(ty->base); + else + node->ty = pointer_to(ty); + return; + } + case ND_DEREF: + if (!node->lhs->ty->base) + error_tok(node->tok, "invalid pointer dereference"); + if (node->lhs->ty->base->kind == TY_VOID) + error_tok(node->tok, "dereferencing a void pointer"); + + node->ty = node->lhs->ty->base; + return; + case ND_STMT_EXPR: + if (node->body) { + Node *stmt = node->body; + while (stmt->next) stmt = stmt->next; + if (stmt->kind == ND_EXPR_STMT) { + node->ty = stmt->lhs->ty; + return; + } + } + error_tok(node->tok, + "statement expression returning void is not supported"); + return; + case ND_LABEL_VAL: + node->ty = pointer_to(ty_void); + return; + case ND_CAS: + add_type(node->cas_addr); + add_type(node->cas_old); + add_type(node->cas_new); + node->ty = ty_bool; + + if (node->cas_addr->ty->kind != TY_PTR) + error_tok(node->cas_addr->tok, "pointer expected"); + if (node->cas_old->ty->kind != TY_PTR) + error_tok(node->cas_old->tok, "pointer expected"); + return; + case ND_EXCH: + if (node->lhs->ty->kind != TY_PTR) + error_tok(node->cas_addr->tok, "pointer expected"); + node->ty = node->lhs->ty->base; + return; + } +} diff --git a/third_party/chibicc/unicode.c b/third_party/chibicc/unicode.c new file mode 100644 index 000000000..da87caf74 --- /dev/null +++ b/third_party/chibicc/unicode.c @@ -0,0 +1,186 @@ +#include "third_party/chibicc/chibicc.h" + +// Encode a given character in UTF-8. +int encode_utf8(char *buf, uint32_t c) { + if (c <= 0x7F) { + buf[0] = c; + return 1; + } + + if (c <= 0x7FF) { + buf[0] = 0b11000000 | (c >> 6); + buf[1] = 0b10000000 | (c & 0b00111111); + return 2; + } + + if (c <= 0xFFFF) { + buf[0] = 0b11100000 | (c >> 12); + buf[1] = 0b10000000 | ((c >> 6) & 0b00111111); + buf[2] = 0b10000000 | (c & 0b00111111); + return 3; + } + + buf[0] = 0b11110000 | (c >> 18); + buf[1] = 0b10000000 | ((c >> 12) & 0b00111111); + buf[2] = 0b10000000 | ((c >> 6) & 0b00111111); + buf[3] = 0b10000000 | (c & 0b00111111); + return 4; +} + +// Read a UTF-8-encoded Unicode code point from a source file. +// We assume that source files are always in UTF-8. +// +// UTF-8 is a variable-width encoding in which one code point is +// encoded in one to four bytes. One byte UTF-8 code points are +// identical to ASCII. Non-ASCII characters are encoded using more +// than one byte. +uint32_t decode_utf8(char **new_pos, char *p) { + if ((unsigned char)*p < 128) { + *new_pos = p + 1; + return *p; + } + + char *start = p; + int len; + uint32_t c; + + if ((unsigned char)*p >= 0b11110000) { + len = 4; + c = *p & 0b111; + } else if ((unsigned char)*p >= 0b11100000) { + len = 3; + c = *p & 0b1111; + } else if ((unsigned char)*p >= 0b11000000) { + len = 2; + c = *p & 0b11111; + } else { + error_at(start, "invalid UTF-8 sequence"); + } + + for (int i = 1; i < len; i++) { + if ((unsigned char)p[i] >> 6 != 0b10) + error_at(start, "invalid UTF-8 sequence"); + c = (c << 6) | (p[i] & 0b111111); + } + + *new_pos = p + len; + return c; +} + +static bool in_range(uint32_t *range, uint32_t c) { + for (int i = 0; range[i] != -1; i += 2) + if (range[i] <= c && c <= range[i + 1]) return true; + return false; +} + +// C11 allows not only ASCII but some multibyte characters in certan +// Unicode ranges to be used in an identifier. See C11 Annex D for the +// details. +// +// This function returns true if a given character is acceptable as +// the first character of an identifier. +// +// For example, ¾ (U+00BE) is a valid identifier because characters in +// 0x00BE-0x00C0 are allowed, while neither ⟘ (U+27D8) nor ' ' +// (U+3000, full-width space) are allowed because they are out of range. +bool is_ident1(uint32_t c) { + static uint32_t range[] = { + '_', '_', 'a', 'z', 'A', 'Z', '$', '$', + 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF, + 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6, + 0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F, + 0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D, + 0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F, + 0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793, + 0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F, + 0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF, + 0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD, 0x10000, 0x1FFFD, + 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD, 0x50000, 0x5FFFD, + 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD, 0x90000, 0x9FFFD, + 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD, 0xD0000, 0xDFFFD, + 0xE0000, 0xEFFFD, -1, + }; + + return in_range(range, c); +} + +// Returns true if a given character is acceptable as a non-first +// character of an identifier. +bool is_ident2(uint32_t c) { + static uint32_t range[] = { + '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, + 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F, -1, + }; + + return is_ident1(c) || in_range(range, c); +} + +// Returns the number of columns needed to display a given +// character in a fixed-width font. +// +// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c +static int char_width(uint32_t c) { + static uint32_t range1[] = { + 0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486, + 0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2, + 0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615, + 0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8, + 0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A, + 0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C, + 0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963, + 0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD, + 0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42, + 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82, + 0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD, + 0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F, + 0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82, + 0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48, + 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF, + 0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43, + 0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6, + 0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1, + 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19, + 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E, + 0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC, + 0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037, + 0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F, + 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773, + 0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3, + 0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922, + 0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18, + 0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C, + 0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF, + 0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F, + 0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806, + 0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F, + 0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03, + 0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F, + 0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD, + 0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF, + -1, + }; + + if (in_range(range1, c)) return 0; + + static uint32_t range2[] = { + 0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E, + 0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19, + 0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644, + 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, -1, + }; + + if (in_range(range2, c)) return 2; + return 1; +} + +// Returns the number of columns needed to display a given +// string in a fixed-width font. +int str_width(char *p, int len) { + char *start = p; + int w = 0; + while (p - start < len) { + uint32_t c = decode_utf8(&p, p); + w += char_width(c); + } + return w; +} diff --git a/third_party/gdtoa/README b/third_party/gdtoa/README new file mode 100644 index 000000000..220ecd9aa --- /dev/null +++ b/third_party/gdtoa/README @@ -0,0 +1,419 @@ +This directory contains source for a library of binary -> decimal +and decimal -> binary conversion routines, for single-, double-, +and extended-precision IEEE binary floating-point arithmetic, and +other IEEE-like binary floating-point, including "double double", +as in + + T. J. Dekker, "A Floating-Point Technique for Extending the + Available Precision", Numer. Math. 18 (1971), pp. 224-242 + +and + + "Inside Macintosh: PowerPC Numerics", Addison-Wesley, 1994 + +The conversion routines use double-precision floating-point arithmetic +and, where necessary, high precision integer arithmetic. The routines +are generalizations of the strtod and dtoa routines described in + + David M. Gay, "Correctly Rounded Binary-Decimal and + Decimal-Binary Conversions", Numerical Analysis Manuscript + No. 90-10, Bell Labs, Murray Hill, 1990; + http://cm.bell-labs.com/cm/cs/what/ampl/REFS/rounding.ps.gz + +(based in part on papers by Clinger and Steele & White: see the +references in the above paper). + +The present conversion routines should be able to use any of IEEE binary, +VAX, or IBM-mainframe double-precision arithmetic internally, but I (dmg) +have so far only had a chance to test them with IEEE double precision +arithmetic. + +The core conversion routines are strtodg for decimal -> binary conversions +and gdtoa for binary -> decimal conversions. These routines operate +on arrays of unsigned 32-bit integers of type ULong, a signed 32-bit +exponent of type Long, and arithmetic characteristics described in +struct FPI; FPI, Long, and ULong are defined in gdtoa.h. File arith.h +is supposed to provide #defines that cause gdtoa.h to define its +types correctly. File arithchk.c is source for a program that +generates a suitable arith.h on all systems where I've been able to +test it. + +The core conversion routines are meant to be called by helper routines +that know details of the particular binary arithmetic of interest and +convert. The present directory provides helper routines for 5 variants +of IEEE binary floating-point arithmetic, each indicated by one or +two letters: + + f IEEE single precision + d IEEE double precision + x IEEE extended precision, as on Intel 80x87 + and software emulations of Motorola 68xxx chips + that do not pad the way the 68xxx does, but + only store 80 bits + xL IEEE extended precision, as on Motorola 68xxx chips + Q quad precision, as on Sun Sparc chips + dd double double, pairs of IEEE double numbers + whose sum is the desired value + +For decimal -> binary conversions, there are three families of +helper routines: one for round-nearest (or the current rounding +mode on IEEE-arithmetic systems that provide the C99 fegetround() +function, if compiled with -DHonor_FLT_ROUNDS): + + strtof + strtod + strtodd + strtopd + strtopf + strtopx + strtopxL + strtopQ + +one with rounding direction specified: + + strtorf + strtord + strtordd + strtorx + strtorxL + strtorQ + +and one for computing an interval (at most one bit wide) that contains +the decimal number: + + strtoIf + strtoId + strtoIdd + strtoIx + strtoIxL + strtoIQ + +The latter call strtoIg, which makes one call on strtodg and adjusts +the result to provide the desired interval. On systems where native +arithmetic can easily make one-ulp adjustments on values in the +desired floating-point format, it might be more efficient to use the +native arithmetic. Routine strtodI is a variant of strtoId that +illustrates one way to do this for IEEE binary double-precision +arithmetic -- but whether this is more efficient remains to be seen. + +Functions strtod and strtof have "natural" return types, float and +double -- strtod is specified by the C standard, and strtof appears +in the stdlib.h of some systems, such as (at least some) Linux systems. +The other functions write their results to their final argument(s): +to the final two argument for the strtoI... (interval) functions, +and to the final argument for the others (strtop... and strtor...). +Where possible, these arguments have "natural" return types (double* +or float*), to permit at least some type checking. In reality, they +are viewed as arrays of ULong (or, for the "x" functions, UShort) +values. On systems where long double is the appropriate type, one can +pass long double* final argument(s) to these routines. The int value +that these routines return is the return value from the call they make +on strtodg; see the enum of possible return values in gdtoa.h. + +Source files g_ddfmt.c, misc.c, smisc.c, strtod.c, strtodg.c, and ulp.c +should use true IEEE double arithmetic (not, e.g., double extended), +at least for storing (and viewing the bits of) the variables declared +"double" within them. + +One detail indicated in struct FPI is whether the target binary +arithmetic departs from the IEEE standard by flushing denormalized +numbers to 0. On systems that do this, the helper routines for +conversion to double-double format (when compiled with +Sudden_Underflow #defined) penalize the bottom of the exponent +range so that they return a nonzero result only when the least +significant bit of the less significant member of the pair of +double values returned can be expressed as a normalized double +value. An alternative would be to drop to 53-bit precision near +the bottom of the exponent range. To get correct rounding, this +would (in general) require two calls on strtodg (one specifying +126-bit arithmetic, then, if necessary, one specifying 53-bit +arithmetic). + +By default, the core routine strtodg and strtod set errno to ERANGE +if the result overflows to +Infinity or underflows to 0. Compile +these routines with NO_ERRNO #defined to inhibit errno assignments. + +Routine strtod is based on netlib's "dtoa.c from fp", and +(f = strtod(s,se)) is more efficient for some conversions than, say, +strtord(s,se,1,&f). Parts of strtod require true IEEE double +arithmetic with the default rounding mode (round-to-nearest) and, on +systems with IEEE extended-precision registers, double-precision +(53-bit) rounding precision. If the machine uses (the equivalent of) +Intel 80x87 arithmetic, the call + _control87(PC_53, MCW_PC); +does this with many compilers. Whether this or another call is +appropriate depends on the compiler; for this to work, it may be +necessary third_party/gdtoa/to #include "float.h" or another system-dependent header +file. + +Source file strtodnrp.c gives a strtod that does not require 53-bit +rounding precision on systems (such as Intel IA32 systems) that may +suffer double rounding due to use of extended-precision registers. +For some conversions this variant of strtod is less efficient than the +one in strtod.c when the latter is run with 53-bit rounding precision. + +When float or double are involved, the values that the strto* routines +return for NaNs are determined by gd_qnan.h, which the makefile +generates by running the program whose source is qnan.c. For other +types, default NaN values are specified in g__fmt.c and may need +adjusting. Note that the rules for distinguishing signaling from +quiet NaNs are system-dependent. For cross-compilation, you need to +determine arith.h and gd_qnan.h suitably, e.g., using the arithmetic +of the target machine. + +C99's hexadecimal floating-point constants are recognized by the +strto* routines (but this feature has not yet been heavily tested). +Compiling with NO_HEX_FP #defined disables this feature. + +When compiled with -DINFNAN_CHECK, the strto* routines recognize C99's +NaN and Infinity syntax. Moreover, unless No_Hex_NaN is #defined, the +strto* routines also recognize C99's NaN(...) syntax: they accept +(case insensitively) strings of the form NaN(x), where x is a string +of hexadecimal digits and spaces; if there is only one string of +hexadecimal digits, it is taken for the fraction bits of the resulting +NaN; if there are two or more strings of hexadecimal digits, each +string is assigned to the next available sequence of 32-bit words of +fractions bits (starting with the most significant), right-aligned in +each sequence. Strings of hexadecimal digits may be preceded by "0x" +or "0X". + +For binary -> decimal conversions, I've provided a family of helper +routines: + + g_ffmt + g_dfmt + g_ddfmt + g_xfmt + g_xLfmt + g_Qfmt + g_ffmt_p + g_dfmt_p + g_ddfmt_p + g_xfmt_p + g_xLfmt_p + g_Qfmt_p + +which do a "%g" style conversion either to a specified number of decimal +places (if their ndig argument is positive), or to the shortest +decimal string that rounds to the given binary floating-point value +(if ndig <= 0). They write into a buffer supplied as an argument +and return either a pointer to the end of the string (a null character) +in the buffer, if the buffer was long enough, or 0. Other forms of +conversion are easily done with the help of gdtoa(), such as %e or %f +style and conversions with direction of rounding specified (so that, if +desired, the decimal value is either >= or <= the binary value). +On IEEE-arithmetic systems that provide the C99 fegetround() function, +if compiled with -DHonor_FLT_ROUNDS, these routines honor the current +rounding mode. For pedants, the ...fmt_p() routines are similar to the +...fmt() routines, but have an additional final int argument, nik, +that for conversions of Infinity or NaN, determines whether upper, +lower, or mixed case is used, whether (...) is added to NaN values, +and whether the sign of a NaN is reported or suppressed: + + nik = ic + 6*(nb + 3*ns), + +where ic with 0 <= ic < 6 controls the rendering of Infinity and NaN: + + 0 ==> Infinity or NaN + 1 ==> infinity or nan + 2 ==> INFINITY or NAN + 3 ==> Inf or NaN + 4 ==> inf or nan + 5 ==> INF or NAN + +nb with 0 <= nb < 3 determines whether NaN values are rendered +as NaN(...): + + 0 ==> no + 1 ==> yes + 2 ==> no for default NaN values; yes otherwise + +ns = 0 or 1 determines whether the sign of NaN values reported: + + 0 ==> distinguish NaN and -NaN + 1 ==> report both as NaN + +For an example of more general conversions based on dtoa(), see +netlib's "printf.c from ampl/solvers". + +For double-double -> decimal, g_ddfmt() assumes IEEE-like arithmetic +of precision max(126, #bits(input)) bits, where #bits(input) is the +number of mantissa bits needed to represent the sum of the two double +values in the input. + +The makefile creates a library, gdtoa.a. To use the helper +routines, a program only needs to include gdtoa.h. All the +source files for gdtoa.a include a more extensive gdtoaimp.h; +among other things, gdtoaimp.h has #defines that make "internal" +names end in _D2A. To make a "system" library, one could modify +these #defines to make the names start with __. + +Various comments about possible #defines appear in gdtoaimp.h, +but for most purposes, arith.h should set suitable #defines. + +Systems with preemptive scheduling of multiple threads require some +manual intervention. On such systems, it's necessary to compile +dmisc.c, dtoa.c gdota.c, and misc.c with MULTIPLE_THREADS #defined, +and to provide (or suitably #define) two locks, acquired by +ACQUIRE_DTOA_LOCK(n) and freed by FREE_DTOA_LOCK(n) for n = 0 or 1. +(The second lock, accessed in pow5mult, ensures lazy evaluation of +only one copy of high powers of 5; omitting this lock would introduce +a small probability of wasting memory, but would otherwise be harmless.) +Routines that call dtoa or gdtoa directly must also invoke freedtoa(s) +to free the value s returned by dtoa or gdtoa. It's OK to do so whether +or not MULTIPLE_THREADS is #defined, and the helper g_*fmt routines +listed above all do this indirectly (in gfmt_D2A(), which they all call). + +When MULTIPLE_THREADS is #defined, source file misc.c provides + void set_max_gdtoa_threads(unsigned int n); +and expects + unsigned int dtoa_get_threadno(void); +to be available (possibly provided by + #define dtoa_get_threadno omp_get_thread_num +if OpenMP is in use or by + #define dtoa_get_threadno pthread_self +if Pthreads is in use), to return the current thread number. If +set_max_gdtoa_threads(n) was called and the current thread number is +k with k < n, then calls on ACQUIRE_DTOA_LOCK(...) and +FREE_DTOA_LOCK(...) are avoided; instead each thread with thread +number < n has a separate copy of relevant data structures. After +set_max_gdtoa_threads(n), a call set_max_gdtoa_threads(m) with m <= n +has has no effect, but a call with m > n is honored. Such a call +invokes REALLOC (assumed to be "realloc" if REALLOC is not #defined) +to extend the size of the relevant array. + +By default, there is a private pool of memory of length 2304 bytes +for intermediate quantities, and MALLOC (see gdtoaimp.h) is called only +if the private pool does not suffice. 2304 is large enough that MALLOC +is called only under very unusual circumstances (decimal -> binary +conversion of very long strings) for conversions to and from double +precision. For systems with preemptively scheduled multiple threads +or for conversions to extended or quad, it may be appropriate to +#define PRIVATE_MEM nnnn, where nnnn is a suitable value > 2304. +For extended and quad precisions, -DPRIVATE_MEM=20000 is probably +plenty even for many digits at the ends of the exponent range. +Use of the private pool avoids some overhead. When MULTIPLE_THREADS +is #defined, only thread 0 uses PRIVATE_MEM. + +Directory test provides some test routines. See its README. +I've also tested this stuff (except double double conversions) +with Vern Paxson's testbase program: see + + V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal + Conversion", manuscript, May 1991, + ftp://ftp.ee.lbl.gov/testbase-report.ps.Z . + +(The same ftp directory has source for testbase.) + +Some system-dependent additions to CFLAGS in the makefile: + + HP-UX: -Aa -Ae + OSF (DEC Unix): -ieee_with_no_inexact + SunOS 4.1x: -DKR_headers -DBad_float_h + +If you want to put this stuff into a shared library and your +operating system requires export lists for shared libraries, +the following would be an appropriate export list: + + dtoa + freedtoa + g_Qfmt + g_ddfmt + g_dfmt + g_ffmt + g_xLfmt + g_xfmt + gdtoa + strtoIQ + strtoId + strtoIdd + strtoIf + strtoIx + strtoIxL + strtod + strtodI + strtodg + strtof + strtopQ + strtopd + strtopdd + strtopf + strtopx + strtopxL + strtorQ + strtord + strtordd + strtorf + strtorx + strtorxL + +When time permits, I (dmg) hope to write in more detail about the +present conversion routines; for now, this README file must suffice. +Meanwhile, if you wish to write helper functions for other kinds of +IEEE-like arithmetic, some explanation of struct FPI and the bits +array may be helpful. Both gdtoa and strtodg operate on a bits array +described by FPI *fpi. The bits array is of type ULong, a 32-bit +unsigned integer type. Floating-point numbers have fpi->nbits bits, +with the least significant 32 bits in bits[0], the next 32 bits in +bits[1], etc. These numbers are regarded as integers multiplied by +2^e (i.e., 2 to the power of the exponent e), where e is the second +argument (be) to gdtoa and is stored in *exp by strtodg. The minimum +and maximum exponent values fpi->emin and fpi->emax for normalized +floating-point numbers reflect this arrangement. For example, the +P754 standard for binary IEEE arithmetic specifies doubles as having +53 bits, with normalized values of the form 1.xxxxx... times 2^(b-1023), +with 52 bits (the x's) and the biased exponent b represented explicitly; +b is an unsigned integer in the range 1 <= b <= 2046 for normalized +finite doubles, b = 0 for denormals, and b = 2047 for Infinities and NaNs. +To turn an IEEE double into the representation used by strtodg and gdtoa, +we multiply 1.xxxx... by 2^52 (to make it an integer) and reduce the +exponent e = (b-1023) by 52: + + fpi->emin = 1 - 1023 - 52 + fpi->emax = 1046 - 1023 - 52 + +In various wrappers for IEEE double, we actually write -53 + 1 rather +than -52, to emphasize that there are 53 bits including one implicit bit. +Field fpi->rounding indicates the desired rounding direction, with +possible values + FPI_Round_zero = toward 0, + FPI_Round_near = unbiased rounding -- the IEEE default, + FPI_Round_up = toward +Infinity, and + FPI_Round_down = toward -Infinity +given in gdtoa.h. + +Field fpi->sudden_underflow indicates whether strtodg should return +denormals or flush them to zero. Normal floating-point numbers have +bit fpi->nbits in the bits array on. Denormals have it off, with +exponent = fpi->emin. Strtodg provides distinct return values for normals +and denormals; see gdtoa.h. + +Compiling g__fmt.c, strtod.c, and strtodg.c with -DUSE_LOCALE causes +the decimal-point character to be taken from the current locale; otherwise +it is '.'. + +Source files dtoa.c and strtod.c in this directory are derived from +netlib's "dtoa.c from fp" and are meant to function equivalently. +When compiled with Honor_FLT_ROUNDS #defined (on systems that provide +FLT_ROUNDS and fegetround() as specified in the C99 standard), they +honor the current rounding mode. Because FLT_ROUNDS is buggy on some +(Linux) systems -- not reflecting calls on fesetround(), as the C99 +standard says it should -- when Honor_FLT_ROUNDS is #defined, the +current rounding mode is obtained from fegetround() rather than from +FLT_ROUNDS, unless Trust_FLT_ROUNDS is also #defined. + +Compile with -DUSE_LOCALE to use the current locale; otherwise +decimal points are assumed to be '.'. With -DUSE_LOCALE, unless +you also compile with -DNO_LOCALE_CACHE, the details about the +current "decimal point" character string are cached and assumed not +to change during the program's execution. + +On machines with a 64-bit long double and perhaps a 113-bit "quad" +type, you can invoke "make Printf" to add Printf (and variants, such +as Fprintf) to gdtoa.a. These are analogs, declared in stdio1.h, of +printf and fprintf, etc. in which %La, %Le, %Lf, and %Lg are for long +double and (if appropriate) %Lqa, %Lqe, %Lqf, and %Lqg are for quad +precision printing. + +Please send comments to David M. Gay (dmg at acm dot org, with " at " +changed at "@" and " dot " changed to "."). diff --git a/third_party/gdtoa/dmisc.c b/third_party/gdtoa/dmisc.c new file mode 100644 index 000000000..0850f6e49 --- /dev/null +++ b/third_party/gdtoa/dmisc.c @@ -0,0 +1,220 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#ifndef MULTIPLE_THREADS + char *dtoa_result; +#endif + + char * +#ifdef KR_headers +rv_alloc(i MTa) int i; MTk +#else +rv_alloc(int i MTd) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + (int)(sizeof(Bigint) - sizeof(ULong) - sizeof(int)) + j <= i; + j <<= 1) + k++; + r = (int*)Balloc(k MTa); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + char * +#ifdef KR_headers +nrv_alloc(s, rve, n MTa) char *s, **rve; int n; MTk +#else +nrv_alloc(char *s, char **rve, int n MTd) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n MTa); + while((*t = *s++) !=0) + t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b MTb); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + + int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } diff --git a/third_party/gdtoa/dtoa.c b/third_party/gdtoa/dtoa.c new file mode 100644 index 000000000..4c662e87a --- /dev/null +++ b/third_party/gdtoa/dtoa.c @@ -0,0 +1,801 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + + char * +dtoa +#ifdef KR_headers + (d0, mode, ndigits, decpt, sign, rve) + double d0; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d, d2, eps, eps1; + double ds; + char *s, *s0; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + d.d = d0; + if (word0(&d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&d) & Exp_mask) == Exp_mask) +#else + if (word0(&d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&d) && !(word0(&d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8 MTb); +#endif + return nrv_alloc("NaN", rve, 3 MTb); + } +#endif +#ifdef IBM + dval(&d) += 0; /* normalize */ +#endif + if (!dval(&d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1 MTb); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif + + b = d2b(dval(&d), &be, &bbits MTb); +#ifdef Sudden_Underflow + i = (int)(word0(&d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if (( i = (int)(word0(&d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)) )!=0) { +#endif + dval(&d2) = dval(&d); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (( j = 11 - hi0bits(word0(&d2) & Frac_mask) )!=0) + dval(&d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(&d) = (i-Bias)*log(2)/log(10) + log10(&d2) + * + * This suggests computing an approximation k to log10(&d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&d) << (64 - i) | word1(&d) >> (i - 32) + : word1(&d) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i MTb); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + j1 = 0; + dval(&d2) = dval(&d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&d) /= ds; + } + else if (( j1 = -k )!=0) { + dval(&d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&d) *= bigtens[i]; + } + } + if (k_check && dval(&d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&d) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&d) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&d) -= 5.; + if (dval(&d) > dval(&eps)) + goto one_digit; + if (dval(&d) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); + if (k0 < 0 && j1 >= 307) { + eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ + word0(&eps1) -= Exp_msk1 * (Bias+P-1); + dval(&eps1) *= tens[j1 & 0xf]; + for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) + if (j & 1) + dval(&eps1) *= bigtens[i]; + if (eps.d < eps1.d) + eps.d = eps1.d; + if (10. - d.d < 10.*eps.d && eps.d < 1.) { + /* eps.d < 1. excludes trouble with the tiniest denormal */ + *s++ = '1'; + ++k; + goto ret1; + } + } + for(i = 0;;) { + L = dval(&d); + dval(&d) -= L; + *s++ = '0' + (int)L; + if (dval(&d) < dval(&eps)) + goto retc; + if (1. - dval(&d) < dval(&eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&d) *= 10.) { + L = (Long)(dval(&d)); + if (!(dval(&d) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&d) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&d) < 0.5 - dval(&eps)) + goto retc; + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&d) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&d) *= 10.) { + L = (Long)(dval(&d) / ds); + dval(&d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&d) < 0) { + L--; + dval(&d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto retc; + case 2: goto bump_up; + } +#endif + dval(&d) += dval(&d); +#ifdef ROUND_BIASED + if (dval(&d) >= ds) +#else + if (dval(&d) > ds || (dval(&d) == ds && L & 1)) +#endif + { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto retc; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1 MTb); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5 MTb); + b1 = mult(mhi, b MTb); + Bfree(b MTb); + b = b1; + } + if (( j = b5 - m5 )!=0) + b = pow5mult(b, j MTb); + } + else + b = pow5mult(b, b5 MTb); + } + S = i2b(1 MTb); + if (s5 > 0) + S = pow5mult(S, s5 MTb); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&d) && !(word0(&d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if (( i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f )!=0) + i = 32 - i; +#else + if (( i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf )!=0) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2 MTb); + if (s2 > 0) + S = lshift(S, s2 MTb); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0 MTb); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0 MTb); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0 MTb)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2 MTb); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k MTb); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P MTb); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi MTb); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta MTb); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&d) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&d) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1 MTb); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding && mode > 1) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0 MTb); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0 MTb); + else { + mlo = multadd(mlo, 10, 0 MTb); + mhi = multadd(mhi, 10, 0 MTb); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0 MTb); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1 MTb); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S MTb); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo MTb); + Bfree(mhi MTb); + } + retc: + while(s > s0 && s[-1] == '0') + --s; + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&d) = Exp_1 + (70 << Exp_shift); + word1(&d) = 0; + dval(&d) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b MTb); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } diff --git a/third_party/gdtoa/g_Qfmt.c b/third_party/gdtoa/g_Qfmt.c new file mode 100644 index 000000000..69d0507a7 --- /dev/null +++ b/third_party/gdtoa/g_Qfmt.c @@ -0,0 +1,120 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#endif +#ifdef IEEE_8087 +#define _0 3 +#define _1 2 +#define _2 1 +#define _3 0 +#endif + + char* +#ifdef KR_headers +g_Qfmt(buf, V, ndig, bufsize) char *buf; char *V; int ndig; size_t bufsize; +#else +g_Qfmt(char *buf, void *V, int ndig, size_t bufsize) +#endif +{ + static const FPI fpi0 = { 113, 1-16383-113+1, 32766 - 16383 - 113 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[4], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)V; + sign = L[_0] & 0x80000000L; + bits[3] = L[_0] & 0xffff; + bits[2] = L[_1]; + bits[1] = L[_2]; + bits[0] = L[_3]; + b = buf; + if ( (ex = (L[_0] & 0x7fff0000L) >> 16) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (bits[0] | bits[1] | bits[2] | bits[3]) + b = strcp(b, "NaN"); + else { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, "Infinity"); + } + return b; + } + i = STRTOG_Normal; + bits[3] |= 0x10000; + } + else if (bits[0] | bits[1] | bits[2] | bits[3]) { + i = STRTOG_Denormal; + ex = 1; + } + else { +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 112; + mode = 2; + if (ndig <= 0) { + if (bufsize < 48) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_Qfmt_p.c b/third_party/gdtoa/g_Qfmt_p.c new file mode 100644 index 000000000..80482fdf5 --- /dev/null +++ b/third_party/gdtoa/g_Qfmt_p.c @@ -0,0 +1,133 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_Q_D2A[4]; + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#endif +#ifdef IEEE_8087 +#define _0 3 +#define _1 2 +#define _2 1 +#define _3 0 +#endif + + char* +#ifdef KR_headers +g_Qfmt_p(buf, V, ndig, bufsize, nik) char *buf; char *V; int ndig; size_t bufsize; int nik; +#else +g_Qfmt_p(char *buf, void *V, int ndig, size_t bufsize, int nik) +#endif +{ + static const FPI fpi0 = { 113, 1-16383-113+1, 32766 - 16383 - 113 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[4], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)V; + sign = L[_0] & 0x80000000L; + bits[3] = L[_0] & 0xffff; + bits[2] = L[_1]; + bits[1] = L[_2]; + bits[0] = L[_3]; + b = buf; + if ( (ex = (L[_0] & 0x7fff0000L) >> 16) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (nik < 0 || nik > 35) + nik = 0; + if (bits[0] | bits[1] | bits[2] | bits[3]) { + if (sign && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || bits[0] != NanDflt_Q_D2A[0] + || bits[1] != NanDflt_Q_D2A[1] + || bits[2] != NanDflt_Q_D2A[2] + || (bits[2] ^ NanDflt_Q_D2A[2]) & 0xffff)) + b = add_nanbits(b, bufsize - (b-buf), bits, 4); + } + else { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, InfName[nik%6]); + } + return b; + } + i = STRTOG_Normal; + bits[3] |= 0x10000; + } + else if (bits[0] | bits[1] | bits[2] | bits[3]) { + i = STRTOG_Denormal; + ex = 1; + } + else { +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 112; + mode = 2; + if (ndig <= 0) { + if (bufsize < 48) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g__fmt.c b/third_party/gdtoa/g__fmt.c new file mode 100644 index 000000000..55afbafb0 --- /dev/null +++ b/third_party/gdtoa/g__fmt.c @@ -0,0 +1,200 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#ifndef ldus_QNAN0 +#define ldus_QNAN0 0x7fff +#endif +#ifndef ldus_QNAN1 +#define ldus_QNAN1 0xc000 +#endif +#ifndef ldus_QNAN2 +#define ldus_QNAN2 0 +#endif +#ifndef ldus_QNAN3 +#define ldus_QNAN3 0 +#endif +#ifndef ldus_QNAN4 +#define ldus_QNAN4 0 +#endif + + const char *const InfName[6] = { "Infinity", "infinity", "INFINITY", "Inf", "inf", "INF" }; + const char *const NanName[3] = { "NaN", "nan", "NAN" }; + const ULong NanDflt_Q_D2A[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff }; + const ULong NanDflt_d_D2A[2] = { d_QNAN1, d_QNAN0 }; + const ULong NanDflt_f_D2A[1] = { f_QNAN }; + const ULong NanDflt_xL_D2A[3] = { 1, 0x80000000, 0x7fff0000 }; + const UShort NanDflt_ldus_D2A[5] = { ldus_QNAN4, ldus_QNAN3, ldus_QNAN2, ldus_QNAN1, ldus_QNAN0 }; + + char * +#ifdef KR_headers +g__fmt(b, s, se, decpt, sign, blen) char *b; char *s; char *se; int decpt; ULong sign; size_t blen; +#else +g__fmt(char *b, char *s, char *se, int decpt, ULong sign, size_t blen) +#endif +{ + int i, j, k; + char *be, *s0; + size_t len; +#ifdef USE_LOCALE +#ifdef NO_LOCALE_CACHE + char *decimalpoint = localeconv()->decimal_point; + size_t dlen = strlen(decimalpoint); +#else + char *decimalpoint; + static char *decimalpoint_cache; + static size_t dlen; + if (!(s0 = decimalpoint_cache)) { + s0 = localeconv()->decimal_point; + dlen = strlen(s0); + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#else +#define dlen 0 +#endif + s0 = s; + len = (se-s) + dlen + 6; /* 6 = sign + e+dd + trailing null */ + if (blen < len) + goto ret0; + be = b + blen - 1; + if (sign) + *b++ = '-'; + if (decpt <= -4 || decpt > se - s + 5) { + *b++ = *s++; + if (*s) { +#ifdef USE_LOCALE + while((*b = *decimalpoint++)) + ++b; +#else + *b++ = '.'; +#endif + while((*b = *s++) !=0) + b++; + } + *b++ = 'e'; + /* sprintf(b, "%+.2d", decpt - 1); */ + if (--decpt < 0) { + *b++ = '-'; + decpt = -decpt; + } + else + *b++ = '+'; + for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10){} + for(;;) { + i = decpt / k; + if (b >= be) + goto ret0; + *b++ = i + '0'; + if (--j <= 0) + break; + decpt -= i*k; + decpt *= 10; + } + *b = 0; + } + else if (decpt <= 0) { +#ifdef USE_LOCALE + while((*b = *decimalpoint++)) + ++b; +#else + *b++ = '.'; +#endif + if (be < b - decpt + (se - s)) + goto ret0; + for(; decpt < 0; decpt++) + *b++ = '0'; + while((*b = *s++) != 0) + b++; + } + else { + while((*b = *s++) != 0) { + b++; + if (--decpt == 0 && *s) { +#ifdef USE_LOCALE + while(*b = *decimalpoint++) + ++b; +#else + *b++ = '.'; +#endif + } + } + if (b + decpt > be) { + ret0: + b = 0; + goto ret; + } + for(; decpt > 0; decpt--) + *b++ = '0'; + *b = 0; + } + ret: + freedtoa(s0); + return b; + } + + char * +add_nanbits_D2A(char *b, size_t blen, ULong *bits, int nb) +{ + ULong t; + char *rv; + int i, j; + size_t L; + static char Hexdig[16] = "0123456789abcdef"; + + while(!bits[--nb]) + if (!nb) + return b; + L = 8*nb + 3; + t = bits[nb]; + do ++L; while((t >>= 4)); + if (L > blen) + return b; + b += L; + *--b = 0; + rv = b; + *--b = /*(*/ ')'; + for(i = 0; i < nb; ++i) { + t = bits[i]; + for(j = 0; j < 8; ++j, t >>= 4) + *--b = Hexdig[t & 0xf]; + } + t = bits[nb]; + do *--b = Hexdig[t & 0xf]; while(t >>= 4); + *--b = '('; /*)*/ + return rv; + } diff --git a/third_party/gdtoa/g_ddfmt.c b/third_party/gdtoa/g_ddfmt.c new file mode 100644 index 000000000..08263d661 --- /dev/null +++ b/third_party/gdtoa/g_ddfmt.c @@ -0,0 +1,174 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg@acm.org). */ + + char * +#ifdef KR_headers +g_ddfmt(buf, dd0, ndig, bufsize) char *buf; double *dd0; int ndig; size_t bufsize; +#else +g_ddfmt(char *buf, double *dd0, int ndig, size_t bufsize) +#endif +{ + FPI fpi; + char *b, *s, *se; + ULong *L, bits0[4], *bits, *zx; + int bx, by, decpt, ex, ey, i, j, mode; + Bigint *x, *y, *z; + U *dd, ddx[2]; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#else /*}{*/ +#define Rounding FPI_Round_near +#endif /*}}*/ + + if (bufsize < 10 || bufsize < (size_t)(ndig + 8)) + return 0; + + dd = (U*)dd0; + L = dd->L; + if ((L[_0] & 0x7ff00000L) == 0x7ff00000L) { + /* Infinity or NaN */ + if (L[_0] & 0xfffff || L[_1]) { + nanret: + return strcp(buf, "NaN"); + } + if ((L[2+_0] & 0x7ff00000) == 0x7ff00000) { + if (L[2+_0] & 0xfffff || L[2+_1]) + goto nanret; + if ((L[_0] ^ L[2+_0]) & 0x80000000L) + goto nanret; /* Infinity - Infinity */ + } + infret: + b = buf; + if (L[_0] & 0x80000000L) + *b++ = '-'; + return strcp(b, "Infinity"); + } + if ((L[2+_0] & 0x7ff00000) == 0x7ff00000) { + L += 2; + if (L[_0] & 0xfffff || L[_1]) + goto nanret; + goto infret; + } + if (dval(&dd[0]) + dval(&dd[1]) == 0.) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[_0] & L[2+_0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + if ((L[_0] & 0x7ff00000L) < (L[2+_0] & 0x7ff00000L)) { + dval(&ddx[1]) = dval(&dd[0]); + dval(&ddx[0]) = dval(&dd[1]); + dd = ddx; + L = dd->L; + } + z = d2b(dval(&dd[0]), &ex, &bx MTb); + if (dval(&dd[1]) == 0.) + goto no_y; + x = z; + y = d2b(dval(&dd[1]), &ey, &by MTb); + if ( (i = ex - ey) !=0) { + if (i > 0) { + x = lshift(x, i MTb); + ex = ey; + } + else + y = lshift(y, -i MTb); + } + if ((L[_0] ^ L[2+_0]) & 0x80000000L) { + z = diff(x, y MTb); + if (L[_0] & 0x80000000L) + z->sign = 1 - z->sign; + } + else { + z = sum(x, y MTb); + if (L[_0] & 0x80000000L) + z->sign = 1; + } + Bfree(x MTb); + Bfree(y MTb); + no_y: + bits = zx = z->x; + for(i = 0; !*zx; zx++) + i += 32; + i += lo0bits(zx); + if (i) { + rshift(z, i); + ex += i; + } + fpi.nbits = z->wds * 32 - hi0bits(z->x[j = z->wds-1]); + if (fpi.nbits < 106) { + fpi.nbits = 106; + if (j < 3) { + for(i = 0; i <= j; i++) + bits0[i] = bits[i]; + while(i < 4) + bits0[i++] = 0; + bits = bits0; + } + } + mode = 2; + if (ndig <= 0) { + if (bufsize < (size_t)(fpi.nbits * .301029995664) + 10) { + Bfree(z MTb); + return 0; + } + mode = 0; + } + fpi.emin = 1-1023-53+1; + fpi.emax = 2046-1023-106+1; + fpi.rounding = Rounding; + fpi.sudden_underflow = 0; + fpi.int_max = Int_max; + i = STRTOG_Normal; + s = gdtoa(&fpi, ex, bits, &i, mode, ndig, &decpt, &se); + b = g__fmt(buf, s, se, decpt, z->sign, bufsize); + Bfree(z MTb); + return b; + } diff --git a/third_party/gdtoa/g_ddfmt_p.c b/third_party/gdtoa/g_ddfmt_p.c new file mode 100644 index 000000000..3e514e668 --- /dev/null +++ b/third_party/gdtoa/g_ddfmt_p.c @@ -0,0 +1,194 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg@acm.org). */ + + extern ULong NanDflt_d_D2A[2]; + + char * +#ifdef KR_headers +g_ddfmt_p(buf, dd0, ndig, bufsize, nik) char *buf; double *dd0; int ndig; size_t bufsize; int nik; +#else +g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) +#endif +{ + FPI fpi; + char *b, *s, *se; + ULong *L, bits0[4], *bits, sign, *zx; + int bx, by, decpt, ex, ey, i, j, mode; + Bigint *x, *y, *z; + U *dd, ddx[2]; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#else /*}{*/ +#define Rounding FPI_Round_near +#endif /*}}*/ + + if (bufsize < 10 || bufsize < (size_t)(ndig + 8)) + return 0; + + dd = (U*)dd0; + L = dd->L; + sign = L[_0] & L[2+_0] & 0x80000000L; + if (nik < 0 || nik > 35) + nik = 0; + if ((L[_0] & 0x7ff00000L) == 0x7ff00000L) { + /* Infinity or NaN */ + if (L[_0] & 0xfffff || L[_1]) { + nanret: + b = buf; + if (sign && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || L[_1] != NanDflt_d_D2A[0] + || (L[_0] ^ NanDflt_d_D2A[1]) & 0xfffff + || L[2+_1] != NanDflt_d_D2A[0] + || (L[2+_0] ^ NanDflt_d_D2A[1]) & 0xfffff)) { + bits0[0] = L[2+_1]; + bits0[1] = (L[2+_0] & 0xfffff) | (L[_1] << 20); + bits0[2] = (L[_1] >> 12) | (L[_0] << 20); + bits0[3] = (L[_0] >> 12) & 0xff; + b = add_nanbits(b, bufsize - (b-buf), bits0, 4); + } + return b; + } + if ((L[2+_0] & 0x7ff00000) == 0x7ff00000) { + if (L[2+_0] & 0xfffff || L[2+_1]) + goto nanret; + if ((L[_0] ^ L[2+_0]) & 0x80000000L) + goto nanret; /* Infinity - Infinity */ + } + infret: + b = buf; + if (L[_0] & 0x80000000L) + *b++ = '-'; + return strcp(b, InfName[nik%6]); + } + if ((L[2+_0] & 0x7ff00000) == 0x7ff00000) { + L += 2; + if (L[_0] & 0xfffff || L[_1]) + goto nanret; + goto infret; + } + if (dval(&dd[0]) + dval(&dd[1]) == 0.) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + if ((L[_0] & 0x7ff00000L) < (L[2+_0] & 0x7ff00000L)) { + dval(&ddx[1]) = dval(&dd[0]); + dval(&ddx[0]) = dval(&dd[1]); + dd = ddx; + L = dd->L; + } + z = d2b(dval(&dd[0]), &ex, &bx MTb); + if (dval(&dd[1]) == 0.) + goto no_y; + x = z; + y = d2b(dval(&dd[1]), &ey, &by MTb); + if ( (i = ex - ey) !=0) { + if (i > 0) { + x = lshift(x, i MTb); + ex = ey; + } + else + y = lshift(y, -i MTb); + } + if ((L[_0] ^ L[2+_0]) & 0x80000000L) { + z = diff(x, y MTb); + if (L[_0] & 0x80000000L) + z->sign = 1 - z->sign; + } + else { + z = sum(x, y MTb); + if (L[_0] & 0x80000000L) + z->sign = 1; + } + Bfree(x MTb); + Bfree(y MTb); + no_y: + bits = zx = z->x; + for(i = 0; !*zx; zx++) + i += 32; + i += lo0bits(zx); + if (i) { + rshift(z, i); + ex += i; + } + fpi.nbits = z->wds * 32 - hi0bits(z->x[j = z->wds-1]); + if (fpi.nbits < 106) { + fpi.nbits = 106; + if (j < 3) { + for(i = 0; i <= j; i++) + bits0[i] = bits[i]; + while(i < 4) + bits0[i++] = 0; + bits = bits0; + } + } + mode = 2; + if (ndig <= 0) { + if (bufsize < (size_t)(fpi.nbits * .301029995664) + 10) { + Bfree(z MTb); + return 0; + } + mode = 0; + } + fpi.emin = 1-1023-53+1; + fpi.emax = 2046-1023-106+1; + fpi.rounding = Rounding; + fpi.sudden_underflow = 0; + fpi.int_max = Int_max; + i = STRTOG_Normal; + s = gdtoa(&fpi, ex, bits, &i, mode, ndig, &decpt, &se); + b = g__fmt(buf, s, se, decpt, z->sign, bufsize); + Bfree(z MTb); + return b; + } diff --git a/third_party/gdtoa/g_dfmt.c b/third_party/gdtoa/g_dfmt.c new file mode 100644 index 000000000..c258ca2af --- /dev/null +++ b/third_party/gdtoa/g_dfmt.c @@ -0,0 +1,96 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + char* +#ifdef KR_headers +g_dfmt(buf, d, ndig, bufsize) char *buf; double *d; int ndig; size_t bufsize; +#else +g_dfmt(char *buf, double *d, int ndig, size_t bufsize) +#endif +{ + static const FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)d; + sign = L[_0] & 0x80000000L; + if ((L[_0] & 0x7ff00000) == 0x7ff00000) { + /* Infinity or NaN */ + if (bufsize < 10) + return 0; + if (L[_0] & 0xfffff || L[_1]) { + return strcp(buf, "NaN"); + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, "Infinity"); + } + if (L[_1] == 0 && (L[_0] ^ sign) == 0 /*d == 0.*/) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[_0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[_1]; + bits[1] = L[_0] & 0xfffff; + if ( (ex = (L[_0] >> 20) & 0x7ff) !=0) + bits[1] |= 0x100000; + else + ex = 1; + ex -= 0x3ff + 52; + mode = 2; + if (ndig <= 0) + mode = 0; + i = STRTOG_Normal; + if (sign) + i = STRTOG_Normal | STRTOG_Neg; + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_dfmt_p.c b/third_party/gdtoa/g_dfmt_p.c new file mode 100644 index 000000000..6b34cc60d --- /dev/null +++ b/third_party/gdtoa/g_dfmt_p.c @@ -0,0 +1,111 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_d_D2A[2]; + + char* +#ifdef KR_headers +g_dfmt_p(buf, d, ndig, bufsize, nik) char *buf; double *d; int ndig; size_t bufsize; int nik; +#else +g_dfmt_p(char *buf, double *d, int ndig, size_t bufsize, int nik) +#endif +{ + static const FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)d; + sign = L[_0] & 0x80000000L; + if ((L[_0] & 0x7ff00000) == 0x7ff00000) { + /* Infinity or NaN */ + if (nik < 0 || nik > 35) + nik = 0; + if (bufsize < 10) + return 0; + if (L[_0] & 0xfffff || L[_1]) { + b = buf; + if (L[_0] & 0x80000000L && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || bits[0] != NanDflt_d_D2A[0] + || (bits[1] ^ NanDflt_d_D2A[1]) & 0xfffff)) { + bits[0] = L[_1]; + bits[1] = L[_0] & 0xfffff; + b = add_nanbits(b, bufsize - (b-buf), bits, 2); + } + return b; + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, InfName[nik%6]); + } + if (L[_1] == 0 && (L[_0] ^ sign) == 0 /*d == 0.*/) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[_0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[_1]; + bits[1] = L[_0] & 0xfffff; + if ( (ex = (L[_0] >> 20) & 0x7ff) !=0) + bits[1] |= 0x100000; + else + ex = 1; + ex -= 0x3ff + 52; + mode = 2; + if (ndig <= 0) + mode = 0; + i = STRTOG_Normal; + if (sign) + i = STRTOG_Normal | STRTOG_Neg; + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_ffmt.c b/third_party/gdtoa/g_ffmt.c new file mode 100644 index 000000000..db4813b97 --- /dev/null +++ b/third_party/gdtoa/g_ffmt.c @@ -0,0 +1,94 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + char* +#ifdef KR_headers +g_ffmt(buf, f, ndig, bufsize) char *buf; float *f; int ndig; size_t bufsize; +#else +g_ffmt(char *buf, float *f, int ndig, size_t bufsize) +#endif +{ + static const FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, 0, 6 }; + char *b, *s, *se; + ULong bits[1], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)f; + sign = L[0] & 0x80000000L; + if ((L[0] & 0x7f800000) == 0x7f800000) { + /* Infinity or NaN */ + if (L[0] & 0x7fffff) { + return strcp(buf, "NaN"); + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, "Infinity"); + } + if (*f == 0.) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[0] & 0x7fffff; + if ( (ex = (L[0] >> 23) & 0xff) !=0) + bits[0] |= 0x800000; + else + ex = 1; + ex -= 0x7f + 23; + mode = 2; + if (ndig <= 0) { + if (bufsize < 16) + return 0; + mode = 0; + } + i = STRTOG_Normal; + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_ffmt_p.c b/third_party/gdtoa/g_ffmt_p.c new file mode 100644 index 000000000..d7a0e764d --- /dev/null +++ b/third_party/gdtoa/g_ffmt_p.c @@ -0,0 +1,105 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_f_D2A[1]; + + char* +#ifdef KR_headers +g_ffmt_p(buf, f, ndig, bufsize, nik) char *buf; float *f; int ndig; size_t bufsize; int nik; +#else +g_ffmt_p(char *buf, float *f, int ndig, size_t bufsize, int nik) +#endif +{ + static const FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, 0, 6 }; + char *b, *s, *se; + ULong bits[1], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)f; + sign = L[0] & 0x80000000L; + if ((L[0] & 0x7f800000) == 0x7f800000) { + /* Infinity or NaN */ + if (nik < 0 || nik > 35) + nik = 0; + if ((bits[0] = L[0] & 0x7fffff)) { + b = buf; + if (sign && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || (bits[0] ^ NanDflt_f_D2A[0]) & 0x7fffff)) + b = add_nanbits(b, bufsize - (b-buf), bits, 1); + return b; + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, InfName[nik%6]); + } + if (*f == 0.) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[0] & 0x7fffff; + if ( (ex = (L[0] >> 23) & 0xff) !=0) + bits[0] |= 0x800000; + else + ex = 1; + ex -= 0x7f + 23; + mode = 2; + if (ndig <= 0) { + if (bufsize < 16) + return 0; + mode = 0; + } + i = STRTOG_Normal; + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_xLfmt.c b/third_party/gdtoa/g_xLfmt.c new file mode 100644 index 000000000..f8e54816d --- /dev/null +++ b/third_party/gdtoa/g_xLfmt.c @@ -0,0 +1,114 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#endif +#ifdef IEEE_8087 +#define _0 2 +#define _1 1 +#define _2 0 +#endif + + char* +#ifdef KR_headers +g_xLfmt(buf, V, ndig, bufsize) char *buf; char *V; int ndig; size_t bufsize; +#else +g_xLfmt(char *buf, void *V, int ndig, size_t bufsize) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)V; + sign = L[_0] & 0x80000000L; + bits[1] = L[_1]; + bits[0] = L[_2]; + if ( (ex = (L[_0] >> 16) & 0x7fff) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (bits[0] | bits[1]) + b = strcp(buf, "NaN"); + else { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, "Infinity"); + } + return b; + } + i = STRTOG_Normal; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + } + else { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 63; + mode = 2; + if (ndig <= 0) { + if (bufsize < 32) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_xLfmt_p.c b/third_party/gdtoa/g_xLfmt_p.c new file mode 100644 index 000000000..b30f3fa70 --- /dev/null +++ b/third_party/gdtoa/g_xLfmt_p.c @@ -0,0 +1,126 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_xL_D2A[3]; + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#endif +#ifdef IEEE_8087 +#define _0 2 +#define _1 1 +#define _2 0 +#endif + + char* +#ifdef KR_headers +g_xLfmt_p(buf, V, ndig, bufsize, nik) char *buf; char *V; int ndig; size_t bufsize; int nik; +#else +g_xLfmt_p(char *buf, void *V, int ndig, size_t bufsize, int nik) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (ULong*)V; + sign = L[_0] & 0x80000000L; + bits[1] = L[_1]; + bits[0] = L[_2]; + if ( (ex = (L[_0] >> 16) & 0x7fff) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (nik < 0 || nik > 35) + nik = 0; + if (!bits[0] && bits[1] == 0x80000000) { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, InfName[nik%6]); + } + else { + b = buf; + if (sign && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || bits[0] != NanDflt_xL_D2A[0] + || bits[1] != NanDflt_xL_D2A[1])) + b = add_nanbits(b, bufsize - (b-buf), bits, 2); + } + return b; + } + i = STRTOG_Normal; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + } + else { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 63; + mode = 2; + if (ndig <= 0) { + if (bufsize < 32) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_xfmt.c b/third_party/gdtoa/g_xfmt.c new file mode 100644 index 000000000..f1fa913de --- /dev/null +++ b/third_party/gdtoa/g_xfmt.c @@ -0,0 +1,120 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + + char* +#ifdef KR_headers +g_xfmt(buf, V, ndig, bufsize) char *buf; char *V; int ndig; size_t bufsize; +#else +g_xfmt(char *buf, void *V, int ndig, size_t bufsize) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], sign; + UShort *L; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (UShort *)V; + sign = L[_0] & 0x8000; + bits[1] = (L[_1] << 16) | L[_2]; + bits[0] = (L[_3] << 16) | L[_4]; + if ( (ex = L[_0] & 0x7fff) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (!bits[0] && bits[1]== 0x80000000) { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, "Infinity"); + } + else + b = strcp(buf, "NaN"); + return b; + } + i = STRTOG_Normal; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + ex = 1; + } + else { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 63; + mode = 2; + if (ndig <= 0) { + if (bufsize < 32) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/g_xfmt_p.c b/third_party/gdtoa/g_xfmt_p.c new file mode 100644 index 000000000..70d663436 --- /dev/null +++ b/third_party/gdtoa/g_xfmt_p.c @@ -0,0 +1,136 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern UShort NanDflt_ldus_D2A[5]; + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + + char* +#ifdef KR_headers +g_xfmt_p(buf, V, ndig, bufsize, nik) char *buf; char *V; int ndig; size_t bufsize; int nik; +#else +g_xfmt_p(char *buf, void *V, int ndig, size_t bufsize, int nik) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], sign; + UShort *L; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if (bufsize < (size_t)(ndig + 10)) + return 0; + + L = (UShort *)V; + sign = L[_0] & 0x8000; + bits[1] = (L[_1] << 16) | L[_2]; + bits[0] = (L[_3] << 16) | L[_4]; + if ( (ex = L[_0] & 0x7fff) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (nik < 0 || nik > 35) + nik = 0; + if (!bits[0] && bits[1]== 0x80000000) { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, InfName[nik%6]); + } + else { + b = buf; + if (sign && nik < 18) + *b++ = '-'; + b = strcp(b, NanName[nik%3]); + if (nik > 5 && (nik < 12 + || L[_1] != NanDflt_ldus_D2A[3] + || L[_2] != NanDflt_ldus_D2A[2] + || L[_3] != NanDflt_ldus_D2A[1] + || L[_4] != NanDflt_ldus_D2A[0])) { + bits[1] &= 0x7fffffff; + b = add_nanbits(b, bufsize - (b-buf), bits, 2); + } + } + return b; + } + i = STRTOG_Normal; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + ex = 1; + } + else { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 63; + mode = 2; + if (ndig <= 0) { + if (bufsize < 32) + return 0; + mode = 0; + } + s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return g__fmt(buf, s, se, decpt, sign, bufsize); + } diff --git a/third_party/gdtoa/gdtoa.c b/third_party/gdtoa/gdtoa.c new file mode 100644 index 000000000..18d2d42f5 --- /dev/null +++ b/third_party/gdtoa/gdtoa.c @@ -0,0 +1,759 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + static Bigint * +#ifdef KR_headers +bitstob(bits, nbits, bbits MTa) ULong *bits; int nbits; int *bbits; MTk +#else +bitstob(ULong *bits, int nbits, int *bbits MTd) +#endif +{ + int i, k; + Bigint *b; + ULong *be, *x, *x0; + + i = ULbits; + k = 0; + while(i < nbits) { + i <<= 1; + k++; + } +#ifndef Pack_32 + if (!k) + k = 1; +#endif + b = Balloc(k MTa); + be = bits + ((nbits - 1) >> kshift); + x = x0 = b->x; + do { + *x++ = *bits & ALL_ON; +#ifdef Pack_16 + *x++ = (*bits >> 16) & ALL_ON; +#endif + } while(++bits <= be); + i = x - x0; + while(!x0[--i]) + if (!i) { + b->wds = 0; + *bbits = 0; + goto ret; + } + b->wds = i + 1; + *bbits = i*ULbits + 32 - hi0bits(b->x[i]); + ret: + return b; + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +gdtoa +#ifdef KR_headers + (fpi, be, bits, kindp, mode, ndigits, decpt, rve) + CONST FPI *fpi; int be; ULong *bits; + int *kindp, mode, ndigits, *decpt; char **rve; +#else + (CONST FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, int *decpt, char **rve) +#endif +{ + /* Arguments ndigits and decpt are similar to the second and third + arguments of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + be = exponent: value = (integer represented by bits) * (2 to the power of be). + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + int bbits, b2, b5, be0, dig, i, ieps, ilim, ilim0, ilim1, inex; + int j, j1, k, k0, k_check, kind, leftright, m2, m5, nbits; + int rdir, s2, s5, spec_case, try_quick; + Long L; + Bigint *b, *b1, *delta, *mlo, *mhi, *mhi1, *S; + double d2, ds; + char *s, *s0; + U d, eps; + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + inex = 0; + kind = *kindp &= ~STRTOG_Inexact; + switch(kind & STRTOG_Retmask) { + case STRTOG_Zero: + goto ret_zero; + case STRTOG_Normal: + case STRTOG_Denormal: + break; + case STRTOG_Infinite: + *decpt = -32768; + return nrv_alloc("Infinity", rve, 8 MTb); + case STRTOG_NaN: + *decpt = -32768; + return nrv_alloc("NaN", rve, 3 MTb); + default: + return 0; + } + b = bitstob(bits, nbits = fpi->nbits, &bbits MTb); + be0 = be; + if ( (i = trailz(b)) !=0) { + rshift(b, i); + be += i; + bbits -= i; + } + if (!b->wds) { + Bfree(b MTb); + ret_zero: + *decpt = 1; + return nrv_alloc("0", rve, 1 MTb); + } + + dval(&d) = b2d(b, &i); + i = be + bbits - 1; + word0(&d) &= Frac_mask1; + word0(&d) |= Exp_11; +#ifdef IBM + if ( (j = 11 - hi0bits(word0(&d) & Frac_mask)) !=0) + dval(&d) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(&d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(&d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + ds = (dval(&d)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + + /* correct assumption about exponent range */ + if ((j = i) < 0) + j = -j; + if ((j -= 1077) > 0) + ds += j * 7e-17; + + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; +#ifdef IBM + j = be + bbits - 1; + if ( (j1 = j & 3) !=0) + dval(&d) *= 1 << j1; + word0(&d) += j << Exp_shift - 2 & Exp_mask; +#else + word0(&d) += (be + bbits - 1) << Exp_shift; +#endif + if (k >= 0 && k <= Ten_pmax) { + if (dval(&d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + else if (i >= -4 - Emin || i < Emin) + try_quick = 0; + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = (int)(nbits * .30103) + 3; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i MTb); + + if (mode <= 1) + rdir = 0; + else if ( (rdir = fpi->rounding - 1) !=0) { + if (rdir < 0) + rdir = 2; + if (kind & STRTOG_Neg) + rdir = 3 - rdir; + } + + /* Now rdir = 0 ==> round near, 1 ==> round up, 2 ==> round down. */ + + if (ilim >= 0 && ilim <= Quick_max && try_quick && !rdir +#ifndef IMPRECISE_INEXACT + && k == 0 +#endif + ) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = dval(&d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + } + else { + ds = 1.; + if ( (j1 = -k) !=0) { + dval(&d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&d) *= bigtens[i]; + } + } + } + if (k_check && dval(&d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&d) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&d) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&d) -= 5.; + if (dval(&d) > dval(&eps)) + goto one_digit; + if (dval(&d) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = ds*0.5/tens[ilim-1] - dval(&eps); + for(i = 0;;) { + L = (Long)(dval(&d)/ds); + dval(&d) -= L*ds; + *s++ = '0' + (int)L; + if (dval(&d) < dval(&eps)) { + if (dval(&d)) + inex = STRTOG_Inexlo; + goto ret1; + } + if (ds - dval(&d) < dval(&eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&d) *= 10.) { + if ( (L = (Long)(dval(&d)/ds)) !=0) + dval(&d) -= L*ds; + *s++ = '0' + (int)L; + if (i == ilim) { + ds *= 0.5; + if (dval(&d) > ds + dval(&eps)) + goto bump_up; + else if (dval(&d) < ds - dval(&eps)) { + if (dval(&d)) + inex = STRTOG_Inexlo; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&d) = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= fpi->int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&d) *= 10.) { + L = dval(&d) / ds; + dval(&d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&d) < 0) { + L--; + dval(&d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (dval(&d) == 0.) + break; + if (i == ilim) { + if (rdir) { + if (rdir == 1) + goto bump_up; + inex = STRTOG_Inexlo; + goto ret1; + } + dval(&d) += dval(&d); +#ifdef ROUND_BIASED + if (dval(&d) >= ds) +#else + if (dval(&d) > ds || (dval(&d) == ds && L & 1)) +#endif + { + bump_up: + inex = STRTOG_Inexhi; + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + else + inex = STRTOG_Inexlo; + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = nbits - bbits; + if (be - i++ < fpi->emin && mode != 3 && mode != 5) { + /* denormal */ + i = be - fpi->emin + 1; + if (mode >= 2 && ilim > 0 && ilim < i) + goto small_ilim; + } + else if (mode >= 2) { + small_ilim: + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1 MTb); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5 MTb); + b1 = mult(mhi, b MTb); + Bfree(b MTb); + b = b1; + } + if ( (j = b5 - m5) !=0) + b = pow5mult(b, j MTb); + } + else + b = pow5mult(b, b5 MTb); + } + S = i2b(1 MTb); + if (s5 > 0) + S = pow5mult(S, s5 MTb); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if (mode < 2) { + if (bbits == 1 && be0 > fpi->emin + 1) { + /* The special case */ + b2++; + s2++; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + i = ((s5 ? hi0bits(S->x[S->wds-1]) : ULbits - 1) - s2 - 4) & kmask; + m2 += i; + if ((b2 += i) > 0) + b = lshift(b, b2 MTb); + if ((s2 += i) > 0) + S = lshift(S, s2 MTb); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0 MTb); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0 MTb); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0 MTb)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + inex = STRTOG_Inexlo; + goto ret; + } + one_digit: + inex = STRTOG_Inexhi; + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2 MTb); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k MTb); + Bcopy(mhi, mlo); + mhi = lshift(mhi, 1 MTb); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi MTb); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta MTb); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(bits[0] & 1) && !rdir) { + if (dig == '9') + goto round_9_up; + if (j <= 0) { + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + } + else { + dig++; + inex = STRTOG_Inexhi; + } + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && !mode +#ifndef ROUND_BIASED + && !(bits[0] & 1) +#endif + )) { + if (rdir && (b->wds > 1 || b->x[0])) { + if (rdir == 2) { + inex = STRTOG_Inexlo; + goto accept; + } + while (cmp(S,mhi) > 0) { + *s++ = dig; + mhi1 = multadd(mhi, 10, 0 MTb); + if (mlo == mhi) + mlo = mhi1; + mhi = mhi1; + b = multadd(b, 10, 0 MTb); + dig = quorem(b,S) + '0'; + } + if (dig++ == '9') + goto round_9_up; + inex = STRTOG_Inexhi; + goto accept; + } + if (j1 > 0) { + b = lshift(b, 1 MTb); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + inex = STRTOG_Inexhi; + } + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + accept: + *s++ = dig; + goto ret; + } + if (j1 > 0 && rdir != 2) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + inex = STRTOG_Inexhi; + goto roundoff; + } + inex = STRTOG_Inexhi; + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0 MTb); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0 MTb); + else { + mlo = multadd(mlo, 10, 0 MTb); + mhi = multadd(mhi, 10, 0 MTb); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0 MTb); + } + + /* Round off last digit */ + + if (rdir) { + if (rdir == 2 || (b->wds <= 1 && !b->x[0])) + goto chopzeros; + goto roundoff; + } + b = lshift(b, 1 MTb); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + inex = STRTOG_Inexhi; + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + chopzeros: + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + } + ret: + Bfree(S MTb); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo MTb); + Bfree(mhi MTb); + } + ret1: + while(s > s0 && s[-1] == '0') + --s; + Bfree(b MTb); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + *kindp |= inex; + return s0; + } diff --git a/third_party/gdtoa/gdtoa.h b/third_party/gdtoa/gdtoa.h new file mode 100644 index 000000000..d5a3f8515 --- /dev/null +++ b/third_party/gdtoa/gdtoa.h @@ -0,0 +1,98 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_GDTOA_GDTOA_H_ +#define COSMOPOLITAN_THIRD_PARTY_GDTOA_GDTOA_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +enum { + /* return values from strtodg */ + STRTOG_Zero = 0, + STRTOG_Normal = 1, + STRTOG_Denormal = 2, + STRTOG_Infinite = 3, + STRTOG_NaN = 4, + STRTOG_NaNbits = 5, + STRTOG_NoNumber = 6, + STRTOG_Retmask = 7, + + /* The following may be or-ed into one of the above values. */ + STRTOG_Neg = 0x08, /* does not affect STRTOG_Inexlo or STRTOG_Inexhi */ + STRTOG_Inexlo = 0x10, /* returned result rounded toward zero */ + STRTOG_Inexhi = 0x20, /* returned result rounded away from zero */ + STRTOG_Inexact = 0x30, + STRTOG_Underflow = 0x40, + STRTOG_Overflow = 0x80 +}; + +typedef struct FPI { + int nbits; + int emin; + int emax; + int rounding; + int sudden_underflow; + int int_max; +} FPI; + +enum { + /* FPI.rounding values: same as FLT_ROUNDS */ + FPI_Round_zero = 0, + FPI_Round_near = 1, + FPI_Round_up = 2, + FPI_Round_down = 3 +}; + +char *dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve); +char *gdtoa(const FPI *fpi, int be, unsigned *bits, int *kindp, int mode, + int ndigits, int *decpt, char **rve); +void freedtoa(char *); + +float strtof(const char *, char **); +double strtod(const char *, char **); +int strtodg(const char *, char **, const FPI *, int *, unsigned *); +long double strtold(const char *, char **); + +char *g_ddfmt(char *, double *, int, size_t); +char *g_ddfmt_p(char *, double *, int, size_t, int); +char *g_dfmt(char *, double *, int, size_t); +char *g_dfmt_p(char *, double *, int, size_t, int); +char *g_ffmt(char *, float *, int, size_t); +char *g_ffmt_p(char *, float *, int, size_t, int); +char *g_Qfmt(char *, void *, int, size_t); +char *g_Qfmt_p(char *, void *, int, size_t, int); +char *g_xfmt(char *, void *, int, size_t); +char *g_xfmt_p(char *, void *, int, size_t, int); +char *g_xLfmt(char *, void *, int, size_t); +char *g_xLfmt_p(char *, void *, int, size_t, int); + +int strtoId(const char *, char **, double *, double *); +int strtoIdd(const char *, char **, double *, double *); +int strtoIf(const char *, char **, float *, float *); +int strtoIQ(const char *, char **, void *, void *); +int strtoIx(const char *, char **, void *, void *); +int strtoIxL(const char *, char **, void *, void *); +int strtord(const char *, char **, int, double *); +int strtordd(const char *, char **, int, double *); +int strtorf(const char *, char **, int, float *); +int strtorQ(const char *, char **, int, void *); +int strtorx(const char *, char **, int, void *); +int strtorxL(const char *, char **, int, void *); + +#if 1 +int strtodI(const char *, char **, double *); +int strtopd(const char *, char **, double *); +int strtopdd(const char *, char **, double *); +int strtopf(const char *, char **, float *); +int strtopQ(const char *, char **, void *); +int strtopx(const char *, char **, void *); +int strtopxL(const char *, char **, void *); +#else +#define strtopd(s, se, x) strtord(s, se, 1, x) +#define strtopdd(s, se, x) strtordd(s, se, 1, x) +#define strtopf(s, se, x) strtorf(s, se, 1, x) +#define strtopQ(s, se, x) strtorQ(s, se, 1, x) +#define strtopx(s, se, x) strtorx(s, se, 1, x) +#define strtopxL(s, se, x) strtorxL(s, se, 1, x) +#endif + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_GDTOA_GDTOA_H_ */ diff --git a/third_party/gdtoa/gdtoa.mk b/third_party/gdtoa/gdtoa.mk new file mode 100644 index 000000000..ebc56bfbe --- /dev/null +++ b/third_party/gdtoa/gdtoa.mk @@ -0,0 +1,62 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += THIRD_PARTY_GDTOA + +THIRD_PARTY_GDTOA_ARTIFACTS += THIRD_PARTY_GDTOA_A +THIRD_PARTY_GDTOA = $(THIRD_PARTY_GDTOA_A_DEPS) $(THIRD_PARTY_GDTOA_A) +THIRD_PARTY_GDTOA_A = o/$(MODE)/third_party/gdtoa/gdtoa.a +THIRD_PARTY_GDTOA_A_FILES := $(wildcard third_party/gdtoa/*) +THIRD_PARTY_GDTOA_A_HDRS = $(filter %.h,$(THIRD_PARTY_GDTOA_A_FILES)) +THIRD_PARTY_GDTOA_A_SRCS_S = $(filter %.S,$(THIRD_PARTY_GDTOA_A_FILES)) +THIRD_PARTY_GDTOA_A_SRCS_C = $(filter %.c,$(THIRD_PARTY_GDTOA_A_FILES)) + +THIRD_PARTY_GDTOA_A_SRCS = \ + $(THIRD_PARTY_GDTOA_A_SRCS_S) \ + $(THIRD_PARTY_GDTOA_A_SRCS_C) + +THIRD_PARTY_GDTOA_A_OBJS = \ + $(THIRD_PARTY_GDTOA_A_SRCS:%=o/$(MODE)/%.zip.o) \ + $(THIRD_PARTY_GDTOA_A_SRCS_S:%.S=o/$(MODE)/%.o) \ + $(THIRD_PARTY_GDTOA_A_SRCS_C:%.c=o/$(MODE)/%.o) + +THIRD_PARTY_GDTOA_A_CHECKS = \ + $(THIRD_PARTY_GDTOA_A).pkg \ + $(THIRD_PARTY_GDTOA_A_HDRS:%=o/$(MODE)/%.ok) + +THIRD_PARTY_GDTOA_A_DIRECTDEPS = \ + LIBC_TINYMATH \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_SYSV + +THIRD_PARTY_GDTOA_A_DEPS := \ + $(call uniq,$(foreach x,$(THIRD_PARTY_GDTOA_A_DIRECTDEPS),$($(x)))) + +$(THIRD_PARTY_GDTOA_A): \ + third_party/gdtoa/ \ + $(THIRD_PARTY_GDTOA_A).pkg \ + $(THIRD_PARTY_GDTOA_A_OBJS) + +$(THIRD_PARTY_GDTOA_A).pkg: \ + $(THIRD_PARTY_GDTOA_A_OBJS) \ + $(foreach x,$(THIRD_PARTY_GDTOA_A_DIRECTDEPS),$($(x)_A).pkg) + +$(THIRD_PARTY_GDTOA_A_OBJS): \ + OVERRIDE_CFLAGS += \ + $(OLD_CODE) \ + $(IEEE_MATH) \ + -ffunction-sections \ + -fdata-sections + +THIRD_PARTY_GDTOA_LIBS = $(foreach x,$(THIRD_PARTY_GDTOA_ARTIFACTS),$($(x))) +THIRD_PARTY_GDTOA_SRCS = $(foreach x,$(THIRD_PARTY_GDTOA_ARTIFACTS),$($(x)_SRCS)) +THIRD_PARTY_GDTOA_HDRS = $(foreach x,$(THIRD_PARTY_GDTOA_ARTIFACTS),$($(x)_HDRS)) +THIRD_PARTY_GDTOA_CHECKS = $(foreach x,$(THIRD_PARTY_GDTOA_ARTIFACTS),$($(x)_CHECKS)) +THIRD_PARTY_GDTOA_OBJS = $(foreach x,$(THIRD_PARTY_GDTOA_ARTIFACTS),$($(x)_OBJS)) +$(THIRD_PARTY_GDTOA_OBJS): $(BUILD_FILES) third_party/gdtoa/gdtoa.mk + +.PHONY: o/$(MODE)/third_party/gdtoa +o/$(MODE)/third_party/gdtoa: $(THIRD_PARTY_GDTOA_CHECKS) diff --git a/third_party/gdtoa/gdtoa_fltrnds.inc b/third_party/gdtoa/gdtoa_fltrnds.inc new file mode 100644 index 000000000..33e5f9e53 --- /dev/null +++ b/third_party/gdtoa/gdtoa_fltrnds.inc @@ -0,0 +1,18 @@ + FPI *fpi, fpi1; + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ + fpi = &fpi0; + if (Rounding != 1) { + fpi1 = fpi0; + fpi = &fpi1; + fpi1.rounding = Rounding; + } diff --git a/third_party/gdtoa/gdtoaimp.h b/third_party/gdtoa/gdtoaimp.h new file mode 100644 index 000000000..420e759e0 --- /dev/null +++ b/third_party/gdtoa/gdtoaimp.h @@ -0,0 +1,715 @@ +#include "libc/math.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "third_party/gdtoa/gdtoa.h" + +asm(".ident\t\"\\n\\n\ +gdtoa (MIT License)\\n\ +The author of this software is David M. Gay.\\n\ +Copyright (c) 1991, 2000, 2001 by Lucent Technologies.\""); +asm(".include \"libc/disclaimer.inc\""); + +#define IEEE_8087 1 +#define f_QNAN 0x7fc00000 +#define d_QNAN0 0x7ff80000 +#define d_QNAN1 0x0 + +#if __NO_MATH_ERRNO__ + 0 +#define NO_ERRNO 1 +#endif + +#if __FINITE_MATH_ONLY__ + 0 +#define NO_INFNAN_CHECK 1 +#endif + +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998-2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* This is a variation on dtoa.c that converts arbitary binary + floating-point formats to and from decimal notation. It uses + double-precision arithmetic internally, so there are still + various #ifdefs that adapt the calculations to the native + double-precision arithmetic (any of IEEE, VAX D_floating, + or IBM mainframe arithmetic). + + Please send bug reports to David M. Gay (dmg at acm dot org, + with " at " changed at "@" and " dot " changed to "."). + */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary third_party/gdtoa/to #include "float.h" or another system-dependent + *header file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa and gdtoa. This will cause modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a gdtoa call after a gdtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. When converting IEEE double precision values, the + * longest string gdtoa can return is about 751 bytes long. For + * conversions by strtod of strings of 800 digits and all gdtoa + * conversions of IEEE doubles in single-threaded executions with + * 8-byte pointers, PRIVATE_MEM >= 7400 appears to suffice; with + * 4-byte pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtodg also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits (optionally + * preceded by 0x or 0X) and spaces; if there is only one string + * of hexadecimal digits, it is taken for the fraction bits of the + * resulting NaN; if there are two or more strings of hexadecimal + * digits, each string is assigned to the next available sequence + * of 32-bit words of fractions bits (starting with the most + * significant), right-aligned in each sequence. + * Unless GDTOA_NON_PEDANTIC_NANCHECK is #defined, input "NaN(...)" + * is consumed even when ... has the wrong form (in which case the + * "(...)" is consumed but ignored). + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + + * When MULTIPLE_THREADS is #defined, source file misc.c provides + * void set_max_gdtoa_threads(unsigned int n); + * and expects + * unsigned int dtoa_get_threadno(void); + * to be available (possibly provided by + * #define dtoa_get_threadno omp_get_thread_num + * if OpenMP is in use or by + * #define dtoa_get_threadno pthread_self + * if Pthreads is in use), to return the current thread number. + * If set_max_dtoa_threads(n) was called and the current thread + * number is k with k < n, then calls on ACQUIRE_DTOA_LOCK(...) and + * FREE_DTOA_LOCK(...) are avoided; instead each thread with thread + * number < n has a separate copy of relevant data structures. + * After set_max_dtoa_threads(n), a call set_max_dtoa_threads(m) + * with m <= n has has no effect, but a call with m > n is honored. + * Such a call invokes REALLOC (assumed to be "realloc" if REALLOC + * is not #defined) to extend the size of the relevant array. + + * #define IMPRECISE_INEXACT if you do not care about the setting of + * the STRTOG_Inexact bits in the special case of doing IEEE double + * precision conversions (which could also be done by the strtod in + * dtoa.c). + * #define NO_HEX_FP to disable recognition of C9x's hexadecimal + * floating-point constants. + * #define -DNO_ERRNO to suppress setting errno (in strtod.c and + * strtodg.c). + * #define NO_STRING_H to use private versions of memcpy. + * On some K&R systems, it may also be necessary to + * #define DECLARE_SIZE_T in this case. + * #define USE_LOCALE to use the current locale's decimal_point value. + */ + +#ifndef ANSI +#ifdef KR_headers +#define ANSI(x) () +#define Void /*nothing*/ +#else +#define ANSI(x) x +#define Void void +#endif +#endif /* ANSI */ + +#ifndef Long +#define Long int +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif +#ifndef UShort +typedef unsigned short UShort; +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif /* CONST */ + +#ifdef DEBUG +#define Bug(x) \ + { \ + fprintf(stderr, "%s\n", x); \ + exit(1); \ + } +#endif + +#ifdef KR_headers +#define Char char +#else +#define Char void +#endif + +#ifdef MALLOC +extern Char *MALLOC ANSI((size_t)); +#else +#define MALLOC malloc +#endif + +#ifdef REALLOC +extern Char *REALLOC ANSI((Char *, size_t)); +#else +#define REALLOC realloc +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define DBL_MAX 1.7976931348623157e+308 +#endif + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#define n_bigtens 2 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#endif /* Bad_float_h */ + +#ifdef IEEE_Arith +#define Scale_Bit 0x10 +#define n_bigtens 5 +#endif + +#ifdef IBM +#define n_bigtens 3 +#endif + +#ifdef VAX +#define n_bigtens 2 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + + typedef union { + double d; + ULong L[2]; +} U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a, b, c) \ + (((unsigned short *)a)[1] = (unsigned short)b, \ + ((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a, b, c) \ + (((unsigned short *)a)[0] = (unsigned short)b, \ + ((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#else /* ifndef IEEE_Arith */ +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a, b) a = rnd_prod(a, b) +#define rounded_quotient(a, b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a, b) a *= b +#define rounded_quotient(a, b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1 * (DBL_MAX_EXP + Bias - 1)) +#define Big1 0xffffffff + +#undef Pack_16 +#ifndef Pack_32 +#define Pack_32 +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +#define Pack_16 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#define ALL_ON 0xffffffff +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#define ALL_ON 0xffff +#endif + +#ifdef MULTIPLE_THREADS /*{{*/ +#define MTa , PTI +#define MTb , &TI +#define MTd , ThInfo **PTI +#define MTk ThInfo **PTI; +extern void ACQUIRE_DTOA_LOCK ANSI((unsigned int)); +extern void FREE_DTOA_LOCK ANSI((unsigned int)); +extern unsigned int dtoa_get_threadno ANSI((void)); +#else /*}{*/ +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#define MTa /*nothing*/ +#define MTb /*nothing*/ +#define MTd /*nothing*/ +#define MTk /*nothing*/ +#endif /*}}*/ + +#define Kmax 9 + +struct Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; +}; + +typedef struct Bigint Bigint; + +typedef struct ThInfo { + Bigint *Freelist[Kmax + 1]; + Bigint *P5s; +} ThInfo; + +#ifdef NO_STRING_H +#ifdef DECLARE_SIZE_T +typedef unsigned int size_t; +#endif +extern void memcpy_D2A ANSI((void *, const void *, size_t)); +#define Bcopy(x, y) \ + memcpy_D2A(&x->sign, &y->sign, y->wds * sizeof(ULong) + 2 * sizeof(int)) +#else /* !NO_STRING_H */ +#define Bcopy(x, y) \ + memcpy(&x->sign, &y->sign, y->wds * sizeof(ULong) + 2 * sizeof(int)) +#endif /* NO_STRING_H */ + +#define Balloc Balloc_D2A +#define Bfree Bfree_D2A +#define InfName InfName_D2A +#define NanName NanName_D2A +#define ULtoQ ULtoQ_D2A +#define ULtof ULtof_D2A +#define ULtod ULtod_D2A +#define ULtodd ULtodd_D2A +#define ULtox ULtox_D2A +#define ULtoxL ULtoxL_D2A +#define add_nanbits add_nanbits_D2A +#define any_on any_on_D2A +#define b2d b2d_D2A +#define bigtens bigtens_D2A +#define cmp cmp_D2A +#define copybits copybits_D2A +#define d2b d2b_D2A +#define decrement decrement_D2A +#define diff diff_D2A +#define dtoa_result dtoa_result_D2A +#define g__fmt g__fmt_D2A +#define gethex gethex_D2A +#define hexdig hexdig_D2A +#define hexnan hexnan_D2A +#define hi0bits(x) hi0bits_D2A((ULong)(x)) +#define i2b i2b_D2A +#define increment increment_D2A +#define lo0bits lo0bits_D2A +#define lshift lshift_D2A +#define match match_D2A +#define mult mult_D2A +#define multadd multadd_D2A +#define nrv_alloc nrv_alloc_D2A +#define pow5mult pow5mult_D2A +#define quorem quorem_D2A +#define ratio ratio_D2A +#define rshift rshift_D2A +#define rv_alloc rv_alloc_D2A +#define s2b s2b_D2A +#define set_ones set_ones_D2A +#define strcp strcp_D2A +#define strtoIg strtoIg_D2A +#define sum sum_D2A +#define tens tens_D2A +#define tinytens tinytens_D2A +#define tinytens tinytens_D2A +#define trailz trailz_D2A +#define ulp ulp_D2A + +extern char *add_nanbits ANSI((char *, size_t, ULong *, int)); +extern char *dtoa_result; +extern CONST double bigtens[], tens[], tinytens[]; +extern const unsigned char hexdig[]; +extern const char *const InfName[6], *const NanName[3]; + +extern Bigint *Balloc ANSI((int MTd)); +extern void Bfree ANSI((Bigint * MTd)); +extern void ULtof ANSI((ULong *, ULong *, Long, int)); +extern void ULtod ANSI((ULong *, ULong *, Long, int)); +extern void ULtodd ANSI((ULong *, ULong *, Long, int)); +extern void ULtoQ ANSI((ULong *, ULong *, Long, int)); +extern void ULtox ANSI((UShort *, ULong *, Long, int)); +extern void ULtoxL ANSI((ULong *, ULong *, Long, int)); +extern ULong any_on ANSI((Bigint *, int)); +extern double b2d ANSI((Bigint *, int *)); +extern int cmp ANSI((Bigint *, Bigint *)); +extern void copybits ANSI((ULong *, int, Bigint *)); +extern Bigint *d2b ANSI((double, int *, int *MTd)); +extern void decrement ANSI((Bigint *)); +extern Bigint *diff ANSI((Bigint *, Bigint *MTd)); +extern char *g__fmt ANSI((char *, char *, char *, int, ULong, size_t)); +extern int gethex ANSI((CONST char **, CONST FPI *, Long *, Bigint **, + int MTd)); +extern void hexdig_init_D2A(Void); +extern int hexnan ANSI((CONST char **, CONST FPI *, ULong *)); +extern int hi0bits_D2A ANSI((ULong)); +extern Bigint *i2b ANSI((int MTd)); +extern Bigint *increment ANSI((Bigint * MTd)); +extern int lo0bits ANSI((ULong *)); +extern Bigint *lshift ANSI((Bigint *, int MTd)); +extern int match ANSI((CONST char **, char *)); +extern Bigint *mult ANSI((Bigint *, Bigint *MTd)); +extern Bigint *multadd ANSI((Bigint *, int, int MTd)); +extern char *nrv_alloc ANSI((char *, char **, int MTd)); +extern Bigint *pow5mult ANSI((Bigint *, int MTd)); +extern int quorem ANSI((Bigint *, Bigint *)); +extern double ratio ANSI((Bigint *, Bigint *)); +extern void rshift ANSI((Bigint *, int)); +extern char *rv_alloc ANSI((int MTd)); +extern Bigint *s2b ANSI((CONST char *, int, int, ULong, int MTd)); +extern Bigint *set_ones ANSI((Bigint *, int MTd)); +extern char *strcp ANSI((char *, const char *)); +extern int strtoIg ANSI((CONST char *, char **, CONST FPI *, Long *, Bigint **, + int *)); +extern Bigint *sum ANSI((Bigint *, Bigint *MTd)); +extern int trailz ANSI((Bigint *)); +extern double ulp ANSI((U *)); + +#ifdef __cplusplus +} +#endif +/* + * NAN_WORD0 and NAN_WORD1 are only referenced in strtod.c. Prior to + * 20050115, they used to be hard-wired here (to 0x7ff80000 and 0, + * respectively), but now are determined by compiling and running + * qnan.c to generate gd_qnan.h, which specifies d_QNAN0 and d_QNAN1. + * Formerly gdtoaimp.h recommended supplying suitable -DNAN_WORD0=... + * and -DNAN_WORD1=... values if necessary. This should still work. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + */ +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#ifndef NAN_WORD0 +#define NAN_WORD0 d_QNAN0 +#endif +#ifndef NAN_WORD1 +#define NAN_WORD1 d_QNAN1 +#endif +#else +#define _0 1 +#define _1 0 +#ifndef NAN_WORD0 +#define NAN_WORD0 d_QNAN1 +#endif +#ifndef NAN_WORD1 +#define NAN_WORD1 d_QNAN0 +#endif +#endif +#else +#undef INFNAN_CHECK +#endif + +#undef SI +#ifdef Sudden_Underflow +#define SI 1 +#else +#define SI 0 +#endif diff --git a/third_party/gdtoa/gethex.c b/third_party/gdtoa/gethex.c new file mode 100644 index 000000000..098754b8e --- /dev/null +++ b/third_party/gdtoa/gethex.c @@ -0,0 +1,357 @@ +#include "libc/errno.h" +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +gethex(sp, fpi, exp, bp, sign MTa) + CONST char **sp; CONST FPI *fpi; Long *exp; Bigint **bp; int sign; MTk +#else +gethex( CONST char **sp, CONST FPI *fpi, Long *exp, Bigint **bp, int sign MTd) +#endif +{ + Bigint *b; + CONST unsigned char *decpt, *s0, *s, *s1; + int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret; + ULong L, lostbits, *x; + Long e, e1; +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + /**** if (!hexdig['0']) hexdig_init_D2A(); ****/ + *bp = 0; + havedig = 0; + s0 = *(CONST unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + return STRTOG_Zero; + if (big) { + if (esign) { + switch(fpi->rounding) { + case FPI_Round_up: + if (sign) + break; + goto ret_tiny; + case FPI_Round_down: + if (!sign) + break; + goto ret_tiny; + } + goto retz; + ret_tiny: + b = Balloc(0 MTa); + b->wds = 1; + b->x[0] = 1; + goto dret; + } + switch(fpi->rounding) { + case FPI_Round_near: + goto ovfl1; + case FPI_Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case FPI_Round_down: + if (sign) + goto ovfl1; + } + ret_big: + nbits = fpi->nbits; + n0 = n = nbits >> kshift; + if (nbits & kmask) + ++n; + for(j = n, k = 0; j >>= 1; ++k); + *bp = b = Balloc(k MTa); + b->wds = n; + for(j = 0; j < n0; ++j) + b->x[j] = ALL_ON; + if (n > n0) + b->x[j] = ALL_ON >> (ULbits - (nbits & kmask)); + *exp = fpi->emax; + return STRTOG_Normal | STRTOG_Inexlo; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k MTa); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = fpi->nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n MTa); + e -= n; + x = b->x; + } + if (e > fpi->emax) { + ovfl: + Bfree(b MTa); + ovfl1: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + switch (fpi->rounding) { + case FPI_Round_zero: + goto ret_big; + case FPI_Round_down: + if (!sign) + goto ret_big; + break; + case FPI_Round_up: + if (sign) + goto ret_big; + } + return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; + } + irv = STRTOG_Normal; + if (e < fpi->emin) { + irv = STRTOG_Denormal; + n = fpi->emin - e; + if (n >= nbits) { + switch (fpi->rounding) { + case FPI_Round_near: + if (n == nbits && (n < 2 || lostbits || any_on(b,n-1))) + goto one_bit; + break; + case FPI_Round_up: + if (!sign) + goto one_bit; + break; + case FPI_Round_down: + if (sign) { + one_bit: + x[0] = b->wds = 1; + dret: + *bp = b; + *exp = fpi->emin; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + return STRTOG_Denormal | STRTOG_Inexhi + | STRTOG_Underflow; + } + } + Bfree(b MTa); + retz: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow; + } + k = n - 1; + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; + nbits -= n; + rshift(b,n); + e = fpi->emin; + } + if (lostbits) { + up = 0; + switch(fpi->rounding) { + case FPI_Round_zero: + break; + case FPI_Round_near: + if (lostbits & 2 + && (lostbits | x[0]) & 1) + up = 1; + break; + case FPI_Round_up: + up = 1 - sign; + break; + case FPI_Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b MTa); + x = b->x; + if (irv == STRTOG_Denormal) { + if (nbits == fpi->nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) + irv = STRTOG_Normal; + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > fpi->emax) + goto ovfl; + } + irv |= STRTOG_Inexhi; + } + else + irv |= STRTOG_Inexlo; + } + *bp = b; + *exp = e; + return irv; + } diff --git a/third_party/gdtoa/gmisc.c b/third_party/gdtoa/gmisc.c new file mode 100644 index 000000000..71c6f0e80 --- /dev/null +++ b/third_party/gdtoa/gmisc.c @@ -0,0 +1,87 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + void +#ifdef KR_headers +rshift(b, k) Bigint *b; int k; +#else +rshift(Bigint *b, int k) +#endif +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = ULbits - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & ALL_ON; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; + } + + int +#ifdef KR_headers +trailz(b) Bigint *b; +#else +trailz(Bigint *b) +#endif +{ + ULong L, *x, *xe; + int n = 0; + + x = b->x; + xe = x + b->wds; + for(n = 0; x < xe && !*x; x++) + n += ULbits; + if (x < xe) { + L = *x; + n += lo0bits(&L); + } + return n; + } diff --git a/third_party/gdtoa/hd_init.c b/third_party/gdtoa/hd_init.c new file mode 100644 index 000000000..2ffde4851 --- /dev/null +++ b/third_party/gdtoa/hd_init.c @@ -0,0 +1,78 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#if 0 + unsigned char hexdig[256]; + + static void +#ifdef KR_headers +htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; +#else +htinit(unsigned char *h, unsigned char *s, int inc) +#endif +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; + } + + void +hexdig_init_D2A(Void) /* Use of hexdig_init omitted 20121220 to avoid a */ + /* race condition when multiple threads are used. */ +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); + } +#else + const unsigned char hexdig[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0, + 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; +#endif diff --git a/third_party/gdtoa/hexnan.c b/third_party/gdtoa/hexnan.c new file mode 100644 index 000000000..7d5115f1c --- /dev/null +++ b/third_party/gdtoa/hexnan.c @@ -0,0 +1,160 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + static void +#ifdef KR_headers +L_shift(x, x1, i) ULong *x; ULong *x1; int i; +#else +L_shift(ULong *x, ULong *x1, int i) +#endif +{ + int j; + + i = 8 - i; + i <<= 2; + j = ULbits - i; + do { + *x |= x[1] << j; + x[1] >>= i; + } while(++x < x1); + } + + int +#ifdef KR_headers +hexnan(sp, fpi, x0) + CONST char **sp; CONST FPI *fpi; ULong *x0; +#else +hexnan( CONST char **sp, CONST FPI *fpi, ULong *x0) +#endif +{ + ULong c, h, *x, *x1, *xe; + CONST char *s; + int havedig, hd0, i, nbits; + + /**** if (!hexdig['0']) hexdig_init_D2A(); ****/ + nbits = fpi->nbits; + x = x0 + (nbits >> kshift); + if (nbits & kmask) + x++; + *--x = 0; + x1 = xe = x; + havedig = hd0 = i = 0; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') { + if (!c) + goto retnan; + ++s; + } + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X') + && *(CONST unsigned char*)(s+3) > ' ') + s += 2; + while((c = *(CONST unsigned char*)++s)) { + if (!(h = hexdig[c])) { + if (c <= ' ') { + if (hd0 < havedig) { + if (x < x1 && i < 8) + L_shift(x, x1, i); + if (x <= x0) { + i = 8; + continue; + } + hd0 = havedig; + *--x = 0; + x1 = x; + i = 0; + } + while((c = *(CONST unsigned char*)(s+1)) <= ' ') { + if (!c) + goto retnan; + ++s; + } + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X') + && *(CONST unsigned char*)(s+3) > ' ') + s += 2; + continue; + } + if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } +#ifndef GDTOA_NON_PEDANTIC_NANCHECK + do { + if (/*(*/ c == ')') { + *sp = s + 1; + goto break2; + } + } while((c = *++s)); +#endif + retnan: + return STRTOG_NaN; + } + havedig++; + if (++i > 8) { + if (x <= x0) + continue; + i = 1; + *--x = 0; + } + *x = (*x << 4) | (h & 0xf); + } +#ifndef GDTOA_NON_PEDANTIC_NANCHECK + break2: +#endif + if (!havedig) + return STRTOG_NaN; + if (x < x1 && i < 8) + L_shift(x, x1, i); + if (x > x0) { + x1 = x0; + do *x1++ = *x++; + while(x <= xe); + do *x1++ = 0; + while(x1 <= xe); + } + else { + /* truncate high-order word if necessary */ + if ( (i = nbits & (ULbits-1)) !=0) + *xe &= ((ULong)0xffffffff) >> (ULbits - i); + } + for(x1 = xe;; --x1) { + if (*x1 != 0) + break; + if (x1 == x0) { + *x1 = 1; + break; + } + } + return STRTOG_NaNbits; + } diff --git a/third_party/gdtoa/misc.c b/third_party/gdtoa/misc.c new file mode 100644 index 000000000..567c12453 --- /dev/null +++ b/third_party/gdtoa/misc.c @@ -0,0 +1,969 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next; +#endif + + static ThInfo TI0; + +#ifdef MULTIPLE_THREADS /*{{*/ + static unsigned int maxthreads = 0; + static ThInfo *TI1; + static int TI0_used; + + void +set_max_gdtoa_threads(unsigned int n) +{ + size_t L; + + if (n > maxthreads) { + L = n*sizeof(ThInfo); + if (TI1) { + TI1 = (ThInfo*)REALLOC(TI1, L); + memset(TI1 + maxthreads, 0, (n-maxthreads)*sizeof(ThInfo)); + } + else { + TI1 = (ThInfo*)MALLOC(L); + if (TI0_used) { + memcpy(TI1, &TI0, sizeof(ThInfo)); + if (n > 1) + memset(TI1 + 1, 0, L - sizeof(ThInfo)); + memset(&TI0, 0, sizeof(ThInfo)); + } + else + memset(TI1, 0, L); + } + maxthreads = n; + } + } + + static ThInfo* +get_TI(void) +{ + unsigned int thno = dtoa_get_threadno(); + if (thno < maxthreads) + return TI1 + thno; + if (thno == 0) + TI0_used = 1; + return &TI0; + } +#define freelist TI->Freelist +#define p5s TI->P5s +#else /*}{*/ +#define freelist TI0.Freelist +#define p5s TI0.P5s +#endif /*}}*/ + + Bigint * +Balloc +#ifdef KR_headers + (k MTa) int k; MTk +#else + (int k MTd) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + static bool once; + if (!once) { + pmem_next = private_mem; + once = true; + } + +#ifdef MULTIPLE_THREADS + ThInfo *TI; + + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(0); +#endif + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k]) !=0) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len - PRIVATE_mem <= 0 +#ifdef MULTIPLE_THREADS + && TI == TI1 +#endif + ) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } +#ifdef MULTIPLE_THREADS + if (TI == &TI0) + FREE_DTOA_LOCK(0); +#endif + rv->sign = rv->wds = 0; + return rv; + } + + void +Bfree +#ifdef KR_headers + (v MTa) Bigint *v; MTk +#else + (Bigint *v MTd) +#endif +{ +#ifdef MULTIPLE_THREADS + ThInfo *TI; +#endif + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(0); +#endif + v->next = freelist[v->k]; + freelist[v->k] = v; +#ifdef MULTIPLE_THREADS + if (TI == &TI0) + FREE_DTOA_LOCK(0); +#endif + } + } + } + + int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + Bigint * +multadd +#ifdef KR_headers + (b, m, a MTa) Bigint *b; int m, a; MTk +#else + (Bigint *b, int m, int a MTd) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1 MTa); + Bcopy(b1, b); + Bfree(b MTa); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; + } + + int +hi0bits_D2A +#ifdef KR_headers + (x) ULong x; +#else + (ULong x) +#endif +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + Bigint * +i2b +#ifdef KR_headers + (i MTa) int i; MTk +#else + (int i MTd) +#endif +{ + Bigint *b; + + b = Balloc(1 MTa); + b->x[0] = i; + b->wds = 1; + return b; + } + + Bigint * +mult +#ifdef KR_headers + (a, b MTa) Bigint *a, *b; MTk +#else + (Bigint *a, Bigint *b MTd) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k MTa); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ( (y = *xb++) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & 0xffffffffUL; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if ( (y = *xb & 0xffff) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if ( (y = *xb >> 16) !=0) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if ( (y = *xb++) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + Bigint * +pow5mult +#ifdef KR_headers + (b, k MTa) Bigint *b; int k; MTk +#else + (Bigint *b, int k MTd) +#endif +{ + Bigint *b1, *p5, *p51; +#ifdef MULTIPLE_THREADS + ThInfo *TI; +#endif + int i; + static const int p05[3] = { 5, 25, 125 }; + + if ( (i = k & 3) !=0) + b = multadd(b, p05[i-1], 0 MTa); + + if (!(k >>= 2)) + return b; +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); +#endif + if ((p5 = p5s) == 0) { + /* first time */ +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625 MTa); + p5->next = 0; + } + if (TI == &TI0) + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5 MTa); + Bfree(b MTa); + b = b1; + } + if (!(k >>= 1)) + break; + if ((p51 = p5->next) == 0) { +#ifdef MULTIPLE_THREADS + if (!TI && !(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5 MTa); + p51->next = 0; + } + if (TI == &TI0) + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5 MTa); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + Bigint * +lshift +#ifdef KR_headers + (b, k MTa) Bigint *b; int k; MTk +#else + (Bigint *b, int k MTd) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + + n = k >> kshift; + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1 MTa); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; + if (k &= kmask) { +#ifdef Pack_32 + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z) !=0) + ++n1; +#else + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; +#endif + } + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b MTa); + return b1; + } + + int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + Bigint * +diff +#ifdef KR_headers + (a, b MTa) Bigint *a, *b; MTk +#else + (Bigint *a, Bigint *b MTd) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0 MTa); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k MTa); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = y & 0xffffffffUL; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = y & 0xffffffffUL; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#endif + return dval(&d); + } +#undef d0 +#undef d1 + + Bigint * +d2b +#ifdef KR_headers + (dd, e, bits MTa) double dd; int *e, *bits; MTk +#else + (double dd, int *e, int *bits MTd) +#endif +{ + Bigint *b; + U d; +#ifndef Sudden_Underflow + int i; +#endif + int de, k; + ULong *x, y, z; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + d.d = dd; +#ifdef VAX + d0 = word0(&d) >> 16 | word0(&d) << 16; + d1 = word1(&d) >> 16 | word1(&d) << 16; +#endif + +#ifdef Pack_32 + b = Balloc(1 MTa); +#else + b = Balloc(2 MTa); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ( (de = (int)(d0 >> Exp_shift)) !=0) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ( (y = d1) !=0) { + if ( (k = lo0bits(&y)) !=0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) !=0 ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if ( (y = d1) !=0) { + if ( (k = lo0bits(&y)) !=0) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(&d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 + }; +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#else +bigtens[] = { 1e16, 1e32 }; +CONST double tinytens[] = { 1e-16, 1e-32 }; +#endif +#endif + + CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + char * +#ifdef KR_headers +strcp_D2A(a, b) char *a; char *b; +#else +strcp_D2A(char *a, CONST char *b) +#endif +{ + while((*a = *b++)) + a++; + return a; + } + +#ifdef NO_STRING_H + + Char * +#ifdef KR_headers +memcpy_D2A(a, b, len) Char *a; Char *b; size_t len; +#else +memcpy_D2A(void *a1, void *b1, size_t len) +#endif +{ + char *a = (char*)a1, *ae = a + len; + char *b = (char*)b1, *a0 = a; + while(a < ae) + *a++ = *b++; + return a0; + } + +#endif /* NO_STRING_H */ diff --git a/third_party/gdtoa/printf.c.txt b/third_party/gdtoa/printf.c.txt new file mode 100644 index 000000000..80a39ff60 --- /dev/null +++ b/third_party/gdtoa/printf.c.txt @@ -0,0 +1,10 @@ + +/* clang-format off */ +#ifdef __sun +#define Use_GDTOA_Qtype +#else +#if defined(__i386) || defined(__x86_64) +#define Use_GDTOA_for_i386_long_double +#endif +#endif +#include "third_party/gdtoa/printf.c0" diff --git a/third_party/gdtoa/printf.c0 b/third_party/gdtoa/printf.c0 new file mode 100644 index 000000000..45c2a5a32 --- /dev/null +++ b/third_party/gdtoa/printf.c0 @@ -0,0 +1,1635 @@ +#include "third_party/gdtoa/stdio1.h" + +/**************************************************************** +Copyright (C) 1997, 1999, 2001 Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +/* This implements most of ANSI C's printf, fprintf, and sprintf, + * omitting L, with %.0g and %.0G giving the shortest decimal string + * that rounds to the number being converted, and with negative + * precisions allowed for %f. + */ + +#ifdef Use_GDTOA_for_i386_long_double /*{{*/ +#include "third_party/gdtoa/gdtoa.h" +#else /*}{*/ +#ifndef NO_PRINTF_A_FMT /*{*/ +#include "third_party/gdtoa/gdtoa.h" +#endif /*}*/ +#endif /*}}*/ + +#ifdef __i386 +#define NO_GDTOA_i386_Quad +#endif + +#ifdef Use_GDTOA_for_i386_long_double /*{*/ +#ifndef NO_GDTOA_i386_Quad /*{*/ +#define GDTOA_both +#define Use_GDTOA_Qtype +#ifdef __ICC__ /* or __INTEL_COMPILER__ or __INTEL_COMPILER ?? */ +#define GDTOA_Qtype _Quad +#else /*{*/ +#ifdef __linux +#define GDTOA_Qtype __float128 +#else +#undef GDTOA_both +#undef Use_GDTOA_Qtype +#endif +#endif /*}*/ +#endif /*} NO_GDTOA_i386_Quad */ +#endif /*} Use_GDTOA_for_i386_long_double */ + +#ifdef Use_GDTOA_Qtype /*{*/ +#ifndef GDTOA_H_INCLUDED +#include "third_party/gdtoa/gdtoa.h" +#endif +#ifndef GDTOA_Qtype +#define GDTOA_Qtype long double +#endif +#endif /*}*/ + +#ifdef NO_PRINTF_A_FMT /*{{*/ +#define WANT_A_FMT(x) /*nothing*/ +#else /*}{*/ +#define WANT_A_FMT(x) x +#endif /*}}*/ + + typedef struct +Finfo { + union { FILE *cf; char *sf; } u; + char *ob0, *obe1; + size_t lastlen; + } Finfo; + + typedef char *(*pgdtoa) ANSI((CONST FPI*, int be, ULong *bits, int *kind, int mode, int ndigits, int *decpt, char **rve)); + + typedef struct +FPBits { + ULong bits[4]; /* sufficient for quad; modify if considering wider types */ + FPI *fpi; + pgdtoa gdtoa; + int sign; + int ex; /* exponent */ + int kind; + } FPBits; + + typedef union U +{ + double d; + long double ld; +#ifdef GDTOA_Qtype + GDTOA_Qtype Qd; +#endif + unsigned int ui[4]; + unsigned short us[5]; + } U; + + typedef char *(*Putfunc) ANSI((Finfo*, int*)); + typedef void (*Fpbits) ANSI((U*, FPBits*)); + +/* Would have preferred typedef void (*Fpbits)(va_list*, FPBits*) + * but gcc is buggy in this regard. + */ + +#ifdef Use_GDTOA_for_i386_long_double /*{*/ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + + static void +xfpbits(U *u, FPBits *b) +{ + ULong *bits; + int ex, i; + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, 0 /*not used*/ }; + + b->fpi = &fpi0; + b->gdtoa = gdtoa; + b->sign = u->us[_0] & 0x8000; + bits = b->bits; + bits[1] = (u->us[_1] << 16) | u->us[_2]; + bits[0] = (u->us[_3] << 16) | u->us[_4]; + if ( (ex = u->us[_0] & 0x7fff) !=0) { + i = STRTOG_Normal; + if (ex == 0x7fff) + /* Infinity or NaN */ + i = bits[0] | bits[1] ? STRTOG_NaN : STRTOG_Infinite; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + ex = 1; + } + else + i = STRTOG_Zero; + b->kind = i; + b->ex = ex - (0x3fff + 63); + } + +#undef _0 +#undef _1 +#undef _2 +#undef _3 +#undef _4 +#define GDTOA_LD_fpbits xfpbits +#endif /*} Use_GDTOA_for_i386_long_double */ + +#ifdef Use_GDTOA_Qtype /*{*/ +#include "third_party/gdtoa/gdtoa.h" +#ifndef GDTOA_Qtype +#define GDTOA_Qtype long double +#endif +#ifdef GDTOA_LD_fpbits +#define GDTOA_Q_fpbits Qfpbits +#else +#define GDTOA_LD_fpbits Qfpbits +#endif + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#endif +#ifdef IEEE_8087 +#define _0 3 +#define _1 2 +#define _2 1 +#define _3 0 +#endif + + static void +Qfpbits(U *u, FPBits *b) +{ + ULong *bits; + int ex, i; + static const FPI fpi0 = { 113, 1-16383-113+1, 32766 - 16383 - 113 + 1, 1, 0, 0 /*not used*/ }; + + b->fpi = &fpi0; + b->gdtoa = gdtoa; + b->sign = u->ui[_0] & 0x80000000L; + bits = b->bits; + bits[3] = u->ui[_0] & 0xffff; + bits[2] = u->ui[_1]; + bits[1] = u->ui[_2]; + bits[0] = u->ui[_3]; + if ( (ex = (u->ui[_0] & 0x7fff0000L) >> 16) !=0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + i = bits[0] | bits[1] | bits[2] | bits[3] + ? STRTOG_NaN : STRTOG_Infinite; + } + else { + i = STRTOG_Normal; + bits[3] |= 0x10000; + } + } + else if (bits[0] | bits[1] | bits[2] | bits[3]) { + i = STRTOG_Denormal; + ex = 1; + } + else + i = STRTOG_Zero; + b->kind = i; + b->ex = ex - (0x3fff + 112); + } + +#undef _0 +#undef _1 +#undef _2 +#undef _3 +#endif /*} GDTOA_Qtype */ + +#ifdef KR_headers +#define Const /* const */ +#define Voidptr char* +#ifndef size_t__ +#define size_t int +#define size_t__ +#endif + +#else + +#define Const const +#define Voidptr void* + +#endif + +#undef MESS +#ifndef Stderr +#define Stderr stderr +#endif + +#ifdef _windows_ +#undef PF_BUF +#define MESS +#include "third_party/gdtoa/mux0.h" +#define stdout_or_err(f) (f == stdout) +#else +#define stdout_or_err(f) (f == Stderr || f == stdout) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + extern char *dtoa ANSI((double, int, int, int*, int*, char **)); + extern void freedtoa ANSI((char*)); + + + +#ifdef USE_ULDIV +/* This is for avoiding 64-bit divisions on the DEC Alpha, since */ +/* they are not portable among variants of OSF1 (DEC's Unix). */ + +#define ULDIV(a,b) uldiv_ASL(a,(unsigned long)(b)) + +#ifndef LLBITS +#define LLBITS 6 +#endif +#ifndef ULONG +#define ULONG unsigned long +#endif + + static int +klog(ULONG x) +{ + int k, rv = 0; + + if (x > 1L) + for(k = 1 << LLBITS-1;;) { + if (x >= (1L << k)) { + rv |= k; + x >>= k; + } + if (!(k >>= 1)) + break; + } + return rv; + } + + ULONG +uldiv_ASL(ULONG a, ULONG b) +{ + int ka; + ULONG c, k; + static ULONG b0; + static int kb; + + if (a < b) + return 0; + if (b != b0) { + b0 = b; + kb = klog(b); + } + k = 1; + if ((ka = klog(a) - kb) > 0) { + k <<= ka; + b <<= ka; + } + c = 0; + for(;;) { + if (a >= b) { + a -= b; + c |= k; + } + if (!(k >>= 1)) + break; + a <<= 1; + } + return c; + } + +#else +#define ULDIV(a,b) a / b +#endif /* USE_ULDIV */ + +#ifdef PF_BUF +FILE *stderr_ASL = (FILE*)&stderr_ASL; +void (*pfbuf_print_ASL) ANSI((char*)); +char *pfbuf_ASL; +static char *pfbuf_next; +static size_t pfbuf_len; +extern Char *mymalloc_ASL ANSI((size_t)); +extern Char *myralloc_ASL ANSI((void *, size_t)); + +#undef fflush +#ifdef old_fflush_ASL +#define fflush old_fflush_ASL +#endif + + void +fflush_ASL(FILE *f) +{ + if (f == stderr_ASL) { + if (pfbuf_ASL && pfbuf_print_ASL) { + (*pfbuf_print_ASL)(pfbuf_ASL); + free(pfbuf_ASL); + pfbuf_ASL = 0; + } + } + else + fflush(f); + } + + static void +pf_put(char *buf, int len) +{ + size_t x, y; + if (!pfbuf_ASL) { + x = len + 256; + if (x < 512) + x = 512; + pfbuf_ASL = pfbuf_next = (char*)mymalloc_ASL(pfbuf_len = x); + } + else if ((y = (pfbuf_next - pfbuf_ASL) + len) >= pfbuf_len) { + x = pfbuf_len; + while((x <<= 1) <= y); + y = pfbuf_next - pfbuf_ASL; + pfbuf_ASL = (char*)myralloc_ASL(pfbuf_ASL, x); + pfbuf_next = pfbuf_ASL + y; + pfbuf_len = x; + } + memcpy(pfbuf_next, buf, len); + pfbuf_next += len; + *pfbuf_next = 0; + } + + static char * +pfput(Finfo *f, int *rvp) +{ + int n; + char *ob0 = f->ob0; + *rvp += n = (int)(f->obe1 - ob0); + pf_put(ob0, n); + return ob0; + } +#endif /* PF_BUF */ + + static char * +Fput +#ifdef KR_headers + (f, rvp) Finfo *f; int *rvp; +#else + (Finfo *f, int *rvp) +#endif +{ + char *ob0 = f->ob0; + + *rvp += f->obe1 - ob0; + *f->obe1 = 0; + fputs(ob0, f->u.cf); + return ob0; + } + + +#ifdef _windows_ +int stdout_fileno_ASL = 1; + + static char * +Wput +#ifdef KR_headers + (f, rvp) Finfo *f; int *rvp; +#else + (Finfo *f, int *rvp) +#endif +{ + char *ob0 = f->ob0; + + *rvp += f->obe1 - ob0; + *f->obe1 = 0; + mwrite(ob0, f->obe1 - ob0); + return ob0; + } +#endif /*_windows_*/ + + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#endif +#ifdef IEEE_8087 +#define _0 1 +#define _1 0 +#endif + + static void +dfpbits(U *u, FPBits *b) +{ + ULong *bits; + int ex, i; + static const FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, 0, 0 /*not used*/ }; + + b->fpi = &fpi0; + b->gdtoa = gdtoa; + b->sign = u->ui[_0] & 0x80000000L; + bits = b->bits; + bits[1] = u->ui[_0] & 0xfffff; + bits[0] = u->ui[_1]; + if ( (ex = (u->ui[_0] & 0x7ff00000L) >> 20) !=0) { + if (ex == 0x7ff) { + /* Infinity or NaN */ + i = bits[0] | bits[1] ? STRTOG_NaN : STRTOG_Infinite; + } + else { + i = STRTOG_Normal; + bits[1] |= 0x100000; + } + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + ex = 1; + } + else + i = STRTOG_Zero; + b->kind = i; + b->ex = ex - (0x3ff + 52); + } + +#undef _0 +#undef _1 + +#ifdef Honor_FLT_ROUNDS /*{{*/ +#ifdef Trust_FLT_ROUNDS /*{{*/ +#define RoundCheck if (Rounding == -1) Rounding = Flt_Rounds; if (Rounding != 1){\ + fpi1 = *fpb.fpi; fpi1.rounding = Rounding; fpb.fpi = &fpi1;} +#else /*}{*/ +#define RoundCheck if (Rounding == -1) { Rounding = 1; switch((fegetround()) {\ + case FE_TOWARDZERO: Rounding = 0; break;\ + case FE_UPWARD: Rounding = 2; break;\ + case FE_DOWNWARD: Rounding = 3; }}\ + if (Rounding != 1){\ + fpi1 = *fpb.fpi; fpi1.rounding = Rounding; fpb.fpi = &fpi1;} +#endif /*}}*/ +#else /*}{*/ +#define RoundCheck /*nothing*/ +#endif /*}}*/ + +#ifndef NO_PRINTF_A_FMT /*{*/ + static int +fpiprec(FPBits *b) /* return number of hex digits minus 1, or 0 for zero */ +{ + FPI *fpi; + ULong *bits; + int i, j, k, m; + + if (b->kind == STRTOG_Zero) + return b->ex = 0; + fpi = b->fpi; + bits = b->bits; + for(k = (fpi->nbits - 1) >> 2; k > 0; --k) + if ((bits[k >> 3] >> 4*(k & 7)) & 0xf) { + m = k >> 3; + for(i = 0; i <= m; ++i) + if (bits[i]) { + if (i > 0) { + k -= 8*i; + b->ex += 32*i; + for(j = i; j <= m; ++j) + bits[j-i] = bits[j]; + } + break; + } + for(i = 0; i < 28 && !((bits[0] >> i) & 0xf); i += 4); + if (i) { + b->ex += i; + m = k >> 3; + k -= (i >> 2); + for(j = 0;;++j) { + bits[j] >>= i; + if (j == m) + break; + bits[j] |= bits[j+1] << (32 - i); + } + } + break; + } + return k; + } + + static int +bround(FPBits *b, int prec, int prec1) /* round to prec hex digits after the "." */ +{ /* prec1 = incoming precision (after ".") */ + ULong *bits, t; + int i, inc, j, k, m, n; +#ifdef Honor_FLT_ROUNDS + int rounding = fpi->rounding; + + if (rounding > FPI_Round_near && b->sign) + rounding = FPI_Round_up + FPI_Round_down - rounding; + if (rounding == FPI_Round_down) + rounding = FPI_Round_zero; +#endif + m = prec1 - prec; + bits = b->bits; + inc = 0; +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case FPI_Round_up: + for(i = 0; i < m; i += 8) + if (bits[i>>3]) + goto inc1; + if ((j = i - m) > 0 && bits[(i-8)>>3] << j*4) + goto inc1; + break; + case FPI_Round_near: +#endif + k = m - 1; + if ((t = bits[k >> 3] >> (j = (k&7)*4)) & 8) { + if (t & 7) + goto inc1; + if (j && bits[k >> 3] << (32 - j)) + goto inc1; + while(k >= 8) { + k -= 8; + if (bits[k>>3]) { + inc1: + inc = 1; + goto haveinc; + } + } + } +#ifdef Honor_FLT_ROUNDS + } +#endif + haveinc: + b->ex += m*4; + i = m >> 3; + k = prec1 >> 3; + j = i; + if ((n = 4*(m & 7))) + for(;; ++j) { + bits[j-i] = bits[j] >> n; + if (j == k) + break; + bits[j-i] |= bits[j+1] << (32-n); + } + else + for(;; ++j) { + bits[j-i] = bits[j]; + if (j == k) + break; + } + k = prec >> 3; + if (inc) { + for(j = 0; !(++bits[j] & 0xffffffff); ++j); + if (j > k) { + onebit: + bits[0] = 1; + b->ex += 4*prec; + return 1; + } + if ((j = prec & 7) < 7 && bits[k] >> (j+1)*4) + goto onebit; + } + for(i = 0; !(bits[i >> 3] & (0xf << 4*(i&7))); ++i); + if (i) { + b->ex += 4*i; + prec -= i; + j = i >> 3; + i &= 7; + i *= 4; + for(m = j; ; ++m) { + bits[m-j] = bits[m] >> i; + if (m == k) + break; + bits[m-j] |= bits[m+1] << (32 - i); + } + } + return prec; + } +#endif /*}NO_PRINTF_A_FMT*/ + +#define put(x) { *outbuf++ = x; if (outbuf == obe) outbuf = (*fput)(f,&rv); } + + static int +x_sprintf +#ifdef KR_headers + (obe, fput, f, fmt, ap) + char *obe, *fmt; Finfo *f; Putfunc fput; va_list ap; +#else + (char *obe, Putfunc fput, Finfo *f, const char *fmt, va_list ap) +#endif +{ + FPBits fpb; + Fpbits fpbits; + U u; + char *digits, *ob0, *outbuf, *s, *s0, *se; + Const char *fmt0; + char buf[32]; + long i; + unsigned long j, ul; + double x; + int alt, base, c, decpt, dot, conv, i1, k, lead0, left, + len, prec, prec1, psign, rv, sign, width; + long Ltmp, *ip; + short sh; + unsigned short us; + unsigned int ui; +#ifdef Honor_FLT_ROUNDS + FPI fpi1; + int Rounding = -1; +#endif +#ifndef NO_PRINTF_A_FMT /*{*/ + int bex, bw; +#endif /*} NO_PRINTF_A_FMT */ + static char hex[] = "0123456789abcdefpx"; + static char Hex[] = "0123456789ABCDEFPX"; + + ob0 = outbuf = f->ob0; + rv = 0; + for(;;) { + for(;;) { + switch(c = *fmt++) { + case 0: + goto done; + case '%': + break; + default: + put(c) + continue; + } + break; + } + alt=dot=lead0=left=len=prec=psign=sign=width=0; + fpbits = dfpbits; + fmt0 = fmt; + fmtloop: + switch(conv = *fmt++) { + case ' ': + case '+': + sign = conv; + goto fmtloop; + case '-': + if (dot) + psign = 1; + else + left = 1; + goto fmtloop; + case '#': + alt = 1; + goto fmtloop; + case '0': + if (!lead0 && !dot) { + lead0 = 1; + goto fmtloop; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + k = conv - '0'; + while((c = *fmt) >= '0' && c <= '9') { + k = 10*k + c - '0'; + fmt++; + } + if (dot) + prec = psign ? -k : k; + else + width = k; + goto fmtloop; + case 'h': + len = 2; + goto fmtloop; + case 'L': +#ifdef GDTOA_LD_fpbits /*{*/ + fpbits = GDTOA_LD_fpbits; +#ifdef GDTOA_Q_fpbits + if (*fmt == 'q') { + ++fmt; + fpbits = Qfpbits; + } +#endif +#endif /*}*/ + goto fmtloop; + case 'l': + len = 1; + goto fmtloop; + case '.': + dot = 1; + goto fmtloop; + case '*': + k = va_arg(ap, int); + if (dot) + prec = k; + else { + if (k < 0) { + sign = '-'; + k = -k; + } + width = k; + } + goto fmtloop; + case 'c': + c = va_arg(ap, int); + put(c) + continue; + case '%': + put(conv) + continue; + case 'u': + switch(len) { + case 0: + ui = va_arg(ap, int); + i = ui; + break; + case 1: + i = va_arg(ap, long); + break; + case 2: + us = va_arg(ap, int); + i = us; + } + sign = 0; + goto have_i; + case 'i': + case 'd': + switch(len) { + case 0: + k = va_arg(ap, int); + i = k; + break; + case 1: + i = va_arg(ap, long); + break; + case 2: + sh = va_arg(ap, int); + i = sh; + } + if (i < 0) { + sign = '-'; + i = -i; + } + have_i: + base = 10; + ul = i; + digits = hex; + baseloop: + if (dot) + lead0 = 0; + s = buf; + if (!ul) + alt = 0; + do { + j = ULDIV(ul, base); + *s++ = digits[ul - base*j]; + } + while((ul = j)); + prec -= c = s - buf; + if (alt && conv == 'o' && prec <= 0) + prec = 1; + if ((width -= c) > 0) { + if (prec > 0) + width -= prec; + if (sign) + width--; + if (alt == 2) + width--; + } + if (left) { + if (alt == 2) + put('0') /* for 0x */ + if (sign) + put(sign) + while(--prec >= 0) + put('0') + do put(*--s) + while(s > buf); + while(--width >= 0) + put(' ') + continue; + } + if (width > 0) { + if (lead0) { + if (alt == 2) + put('0') + if (sign) + put(sign) + while(--width >= 0) + put('0') + goto s_loop; + } + else + while(--width >= 0) + put(' ') + } + if (alt == 2) + put('0') + if (sign) + put(sign) + s_loop: + while(--prec >= 0) + put('0') + do put(*--s) + while(s > buf); + continue; + case 'n': + ip = va_arg(ap, long*); + if (!ip) + ip = &Ltmp; + c = outbuf - ob0 + rv; + switch(len) { + case 0: + *(int*)ip = c; + break; + case 1: + *ip = c; + break; + case 2: + *(short*)ip = c; + } + break; + case 'p': + len = alt = 1; + /* no break */ + case 'x': + digits = hex; + goto more_x; + case 'X': + digits = Hex; + more_x: + if (alt) { + alt = 2; + sign = conv; + } + else + sign = 0; + base = 16; + get_u: + switch(len) { + case 0: + ui = va_arg(ap, int); + ul = ui; + break; + case 1: + ul = va_arg(ap, long); + break; + case 2: + us = va_arg(ap, int); + ul = us; + } + if (!ul) + sign = alt = 0; + goto baseloop; + case 'o': + base = 8; + digits = hex; + goto get_u; + case 's': + s0 = 0; + s = va_arg(ap, char*); + if (!s) + s = ""; + if (prec < 0) + prec = 0; + have_s: + if (dot) { + for(c = 0; c < prec; c++) + if (!s[c]) + break; + prec = c; + } + else + prec = strlen(s); + width -= prec; + if (!left) + while(--width >= 0) + put(' ') + while(--prec >= 0) + put(*s++) + while(--width >= 0) + put(' ') + if (s0) + freedtoa(s0); + continue; + case 'f': + if (!dot) + prec = 6; +#ifdef GDTOA_H_INCLUDED + if (fpbits == dfpbits) { +#endif + x = va_arg(ap, double); + s = s0 = dtoa(x, 3, prec, &decpt, &fpb.sign, &se); +#ifdef GDTOA_H_INCLUDED + } + else { +#ifdef GDTOA_both + if (fpbits == GDTOA_LD_fpbits) + u.ld = va_arg(ap, long double); + else + u.Qd = va_arg(ap, GDTOA_Qtype); +#else + u.ld = va_arg(ap, long double); +#endif + fpbits(&u, &fpb); + RoundCheck + s = s0 = fpb.gdtoa(fpb.fpi, fpb.ex, fpb.bits, + &fpb.kind, 3, prec, &decpt, &se); + } +#endif + if (decpt == 9999) { + fmt9999: + dot = prec = alt = 0; + if (*s == 'N') + goto have_s; + decpt = strlen(s); + } + f_fmt: + if (fpb.sign && (x||sign)) + sign = '-'; + if (prec > 0) + width -= prec; + if (width > 0) { + if (sign) + --width; + if (decpt <= 0) { + --width; + if (prec > 0) + --width; + } + else { + if (s == se) + decpt = 1; + width -= decpt; + if (prec > 0 || alt) + --width; + } + } + if (width > 0 && !left) { + if (lead0) { + if (sign) + put(sign) + sign = 0; + do put('0') + while(--width > 0); + } + else do put(' ') + while(--width > 0); + } + if (sign) + put(sign) + if (decpt <= 0) { + put('0') + if (prec > 0 || alt) + put('.') + while(decpt < 0) { + put('0') + prec--; + decpt++; + } + } + else { + do { + if ((c = *s)) + s++; + else + c = '0'; + put(c) + } + while(--decpt > 0); + if (prec > 0 || alt) + put('.') + } + while(--prec >= 0) { + if ((c = *s)) + s++; + else + c = '0'; + put(c) + } + while(--width >= 0) + put(' ') + if (s0) + freedtoa(s0); + continue; + case 'G': + case 'g': + if (!dot) + prec = 6; + if (prec < 0) + prec = 0; +#ifdef GDTOA_H_INCLUDED + if (fpbits == dfpbits) { +#endif + x = va_arg(ap, double); + s = s0 = dtoa(x, prec ? 2 : 0, prec, &decpt, + &fpb.sign, &se); +#ifdef GDTOA_H_INCLUDED + } + else { +#ifdef GDTOA_both + if (fpbits == GDTOA_LD_fpbits) + u.ld = va_arg(ap, long double); + else + u.Qd = va_arg(ap, GDTOA_Qtype); +#else + u.ld = va_arg(ap, long double); +#endif + fpbits(&u, &fpb); + RoundCheck + s = s0 = fpb.gdtoa(fpb.fpi, fpb.ex, fpb.bits, + &fpb.kind, prec ? 2 : 0, prec, &decpt, &se); + } +#endif + if (decpt == 9999) + goto fmt9999; + c = se - s; + prec1 = prec; + if (!prec) { + prec = c; + prec1 = c + (s[1] || alt ? 5 : 4); + /* %.0g gives 10 rather than 1e1 */ + } + if (decpt > -4 && decpt <= prec1) { + if (alt) + prec -= decpt; + else + prec = c - decpt; + if (prec < 0) + prec = 0; + goto f_fmt; + } + conv -= 2; + if (!alt && prec > c) + prec = c; + --prec; + goto e_fmt; + case 'e': + case 'E': + if (!dot) + prec = 6; + if (prec < 0) + prec = 0; +#ifdef GDTOA_H_INCLUDED + if (fpbits == dfpbits) { +#endif + x = va_arg(ap, double); + s = s0 = dtoa(x, prec ? 2 : 0, prec+1, &decpt, + &fpb.sign, &se); +#ifdef GDTOA_H_INCLUDED + } + else { +#ifdef GDTOA_both + if (fpbits == GDTOA_LD_fpbits) + u.ld = va_arg(ap, long double); + else + u.Qd = va_arg(ap, GDTOA_Qtype); +#else + u.ld = va_arg(ap, long double); +#endif + fpbits(&u, &fpb); + RoundCheck + s = s0 = fpb.gdtoa(fpb.fpi, fpb.ex, fpb.bits, + &fpb.kind, prec ? 2 : 0, prec, &decpt, &se); + } +#endif + if (decpt == 9999) + goto fmt9999; + e_fmt: + if (fpb.sign && (x||sign)) + sign = '-'; + if ((width -= prec + 5) > 0) { + if (sign) + --width; + if (prec || alt) + --width; + } + if ((c = --decpt) < 0) + c = -c; + while(c >= 100) { + --width; + c /= 10; + } + if (width > 0 && !left) { + if (lead0) { + if (sign) + put(sign) + sign = 0; + do put('0') + while(--width > 0); + } + else do put(' ') + while(--width > 0); + } + if (sign) + put(sign) + put(*s++) + if (prec || alt) + put('.') + while(--prec >= 0) { + if ((c = *s)) + s++; + else + c = '0'; + put(c) + } + put(conv) + if (decpt < 0) { + put('-') + decpt = -decpt; + } + else + put('+') + for(c = 2, k = 10; 10*k <= decpt; c++, k *= 10); + for(;;) { + i1 = decpt / k; + put(i1 + '0') + if (--c <= 0) + break; + decpt -= i1*k; + decpt *= 10; + } + while(--width >= 0) + put(' ') + freedtoa(s0); + continue; +#ifndef NO_PRINTF_A_FMT + case 'a': + digits = hex; + goto more_a; + case 'A': + digits = Hex; + more_a: +#ifdef GDTOA_H_INCLUDED /*{{*/ + if (fpbits == dfpbits) + u.d = va_arg(ap, double); +#ifdef GDTOA_both /*{*/ + else if (fpbits == GDTOA_LD_fpbits) + u.ld = va_arg(ap, long double); + else + u.Qd = va_arg(ap, GDTOA_Qtype); +#else + else + u.ld = va_arg(ap, long double); +#endif /*}*/ +#else /*}{*/ + u.d = va_arg(ap, double); +#endif /*}}*/ + fpbits(&u, &fpb); + if (fpb.kind == STRTOG_Infinite) { + s = "Infinity"; + s0 = 0; + goto fmt9999; + } + if (fpb.kind == STRTOG_NaN) { + s = "NaN"; + s0 = 0; + goto fmt9999; + } + prec1 = fpiprec(&fpb); + if (dot && prec < prec1) + prec1 = bround(&fpb, prec, prec1); + bw = 1; + bex = fpb.ex + 4*prec1; + if (bex) { + if ((i1 = bex) < 0) + i1 = -i1; + while(i1 >= 10) { + ++bw; + i1 /= 10; + } + } + if (fpb.sign && (sign || fpb.kind != STRTOG_Zero)) + sign = '-'; + if ((width -= bw + 5) > 0) { + if (sign) + --width; + if (prec1 || alt) + --width; + } + if ((width -= prec1) > 0 && !left && !lead0) { + do put(' ') + while(--width > 0); + } + if (sign) + put(sign) + put('0') + put(digits[17]) + if (lead0 && width > 0 && !left) { + do put('0') + while(--width > 0); + } + i1 = prec1 & 7; + k = prec1 >> 3; + put(digits[(fpb.bits[k] >> 4*i1) & 0xf]) + if (prec1 > 0 || alt) + put('.') + if (prec1 > 0) { + prec -= prec1; + while(prec1 > 0) { + if (--i1 < 0) { + if (--k < 0) + break; + i1 = 7; + } + put(digits[(fpb.bits[k] >> 4*i1) & 0xf]) + --prec1; + } + if (alt && prec > 0) + do put(0) + while(--prec > 0); + } + put(digits[16]) + if (bex < 0) { + put('-') + bex = -bex; + } + else + put('+') + for(c = 1; 10*c <= bex; c *= 10); + for(;;) { + i1 = bex / c; + put('0' + i1) + if (!--bw) + break; + bex -= i1 * c; + bex *= 10; + } + while(--width >= 0) + put(' ') + continue; +#endif /* NO_PRINTF_A_FMT */ + default: + put('%') + while(fmt0 < fmt) + put(*fmt0++) + continue; + } + } + done: + *outbuf = 0; + return (f->lastlen = outbuf - ob0) + rv; + } + +#define Bsize 256 + + int +Printf +#ifdef KR_headers + (va_alist) + va_dcl +{ + char *fmt; + + va_list ap; + int rv; + Finfo f; + char buf[Bsize]; + + va_start(ap); + fmt = va_arg(ap, char*); + /*}*/ +#else + (const char *fmt, ...) +{ + va_list ap; + int rv; + Finfo f; + char buf[Bsize]; + + va_start(ap, fmt); +#endif + f.u.cf = stdout; + f.ob0 = buf; + f.obe1 = buf + Bsize - 1; +#ifdef _windows_ + if (fileno(stdout) == stdout_fileno_ASL) { + rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); + mwrite(buf, f.lastlen); + } + else +#endif +#ifdef PF_BUF + if (stdout == stderr_ASL) { + rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); + pf_put(buf, f.lastlen); + } + else +#endif + { + rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); + fputs(buf, stdout); + } + va_end(ap); + return rv; + } + + static char * +Sput +#ifdef KR_headers + (f, rvp) Finfo *f; int *rvp; +#else + (Finfo *f, int *rvp) +#endif +{ + if (Printf("\nBUG! Sput called!\n", f, rvp)) + /* pass vp, rvp and return 0 to shut diagnostics off */ + exit(250); + return 0; + } + + int +Sprintf +#ifdef KR_headers + (va_alist) + va_dcl +{ + char *s, *fmt; + va_list ap; + int rv; + Finfo f; + + va_start(ap); + s = va_arg(ap, char*); + fmt = va_arg(ap, char*); + /*}*/ +#else + (char *s, const char *fmt, ...) +{ + va_list ap; + int rv; + Finfo f; + + va_start(ap, fmt); +#endif + f.ob0 = s; + rv = x_sprintf(s, Sput, &f, fmt, ap); + va_end(ap); + return rv; + } + + int +Fprintf +#ifdef KR_headers + (va_alist) + va_dcl +{ + FILE *F; + char *s, *fmt; + va_list ap; + int rv; + Finfo f; + char buf[Bsize]; + + va_start(ap); + F = va_arg(ap, FILE*); + fmt = va_arg(ap, char*); + /*}*/ +#else + (FILE *F, const char *fmt, ...) +{ + va_list ap; + int rv; + Finfo f; + char buf[Bsize]; + + va_start(ap, fmt); +#endif + f.u.cf = F; + f.ob0 = buf; + f.obe1 = buf + Bsize - 1; +#ifdef MESS + if (stdout_or_err(F)) { +#ifdef _windows_ + if (fileno(stdout) == stdout_fileno_ASL) { + rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); + mwrite(buf, f.lastlen); + } + else +#endif +#ifdef PF_BUF + if (F == stderr_ASL) { + rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); + pf_put(buf, f.lastlen); + } + else +#endif + { + rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); + fputs(buf, F); + } + } + else +#endif /*MESS*/ + { +#ifdef PF_BUF + if (F == stderr_ASL) { + rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); + pf_put(buf, f.lastlen); + } + else +#endif + { + rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); + fputs(buf, F); + } + } + va_end(ap); + return rv; + } + + int +Vsprintf +#ifdef KR_headers + (s, fmt, ap) char *s, *fmt; va_list ap; +#else + (char *s, const char *fmt, va_list ap) +#endif +{ + Finfo f; + return x_sprintf(f.ob0 = s, Sput, &f, fmt, ap); + } + + int +Vfprintf +#ifdef KR_headers + (F, fmt, ap) FILE *F; char *fmt; va_list ap; +#else + (FILE *F, const char *fmt, va_list ap) +#endif +{ + char buf[Bsize]; + int rv; + Finfo f; + + f.u.cf = F; + f.ob0 = buf; + f.obe1 = buf + Bsize - 1; +#ifdef MESS + if (stdout_or_err(F)) { +#ifdef _windows_ + if (fileno(stdout) == stdout_fileno_ASL) { + rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); + mwrite(buf, f.lastlen); + } + else +#endif +#ifdef PF_BUF + if (F == stderr_ASL) { + rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); + pf_put(buf, f.lastlen); + } + else +#endif + { + rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); + fputs(buf, F); + } + } + else +#endif /*MESS*/ + { +#ifdef PF_BUF + if (F == stderr_ASL) { + rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); + pf_put(buf, f.lastlen); + } + else +#endif + { + rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); + fputs(buf, F); + } + } + va_end(ap); + return rv; + } + + void +Perror +#ifdef KR_headers + (s) char *s; +#else + (const char *s) +#endif +{ + if (s && *s) + Fprintf(Stderr, "%s: ", s); + Fprintf(Stderr, "%s\n", strerror(errno)); + } + + static char * +Snput +#ifdef KR_headers + (f, rvp) Finfo *f; int *rvp; +#else + (Finfo *f, int *rvp) +#endif +{ + char *s, *s0; + size_t L; + + *rvp += Bsize; + s0 = f->ob0; + s = f->u.sf; + if ((L = f->obe1 - s) > Bsize) { + L = Bsize; + goto copy; + } + if (L > 0) { + copy: + memcpy(s, s0, L); + f->u.sf = s + L; + } + return s0; + } + + int +Vsnprintf +#ifdef KR_headers + (s, n, fmt, ap) char *s; size_t n; char *fmt; va_list ap; +#else + (char *s, size_t n, const char *fmt, va_list ap) +#endif +{ + Finfo f; + char buf[Bsize]; + int rv; + size_t L; + + if (n <= 0 || !s) { + n = 1; + s = buf; + } + f.u.sf = s; + f.ob0 = buf; + f.obe1 = s + n - 1; + rv = x_sprintf(buf + Bsize, Snput, &f, fmt, ap); + if (f.lastlen > (L = f.obe1 - f.u.sf)) + f.lastlen = L; + if (f.lastlen > 0) { + memcpy(f.u.sf, buf, f.lastlen); + f.u.sf += f.lastlen; + } + *f.u.sf = 0; + return rv; + } + int +Snprintf +#ifdef KR_headers + (va_alist) + va_dcl +{ + char *s, *fmt; + int rv; + size_t n; + va_list ap; + + va_start(ap); + s = va_arg(ap, char*); + n = va_arg(ap, size_t); + fmt = va_arg(ap, char*); + /*}*/ +#else + (char *s, size_t n, const char *fmt, ...) +{ + int rv; + va_list ap; + + va_start(ap, fmt); +#endif + rv = Vsnprintf(s, n, fmt, ap); + va_end(ap); + return rv; + } + + +#ifdef __cplusplus +} +#endif diff --git a/third_party/gdtoa/smisc.c b/third_party/gdtoa/smisc.c new file mode 100644 index 000000000..ebcc341f3 --- /dev/null +++ b/third_party/gdtoa/smisc.c @@ -0,0 +1,192 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9, dplen MTa) CONST char *s; int dplen, nd0, nd; ULong y9; MTk +#else + (CONST char *s, int nd0, int nd, ULong y9, int dplen MTd) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k MTa); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1 MTa); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0' MTa); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0' MTa); + return b; + } + + double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); + k = ka - kb + ULbits*(a->wds - b->wds); +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&da) *= 1 << k; + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&db) *= 1 << k; + } +#else + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); + } + +#ifdef INFNAN_CHECK + + int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (CONST char **sp, char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while( (d = *t++) !=0) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } +#endif /* INFNAN_CHECK */ + + void +#ifdef KR_headers +copybits(c, n, b) ULong *c; int n; Bigint *b; +#else +copybits(ULong *c, int n, Bigint *b) +#endif +{ + ULong *ce, *x, *xe; +#ifdef Pack_16 + int nw, nw1; +#endif + + ce = c + ((n-1) >> kshift) + 1; + x = b->x; +#ifdef Pack_32 + xe = x + b->wds; + while(x < xe) + *c++ = *x++; +#else + nw = b->wds; + nw1 = nw & 1; + for(xe = x + (nw - nw1); x < xe; x += 2) + Storeinc(c, x[1], x[0]); + if (nw1) + *c++ = *x; +#endif + while(c < ce) + *c++ = 0; + } + + ULong +#ifdef KR_headers +any_on(b, k) Bigint *b; int k; +#else +any_on(Bigint *b, int k) +#endif +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; + } diff --git a/third_party/gdtoa/stdio1.h.txt b/third_party/gdtoa/stdio1.h.txt new file mode 100644 index 000000000..b11031513 --- /dev/null +++ b/third_party/gdtoa/stdio1.h.txt @@ -0,0 +1,106 @@ +#include "libc/stdio/stdio.h" + +/**************************************************************** +Copyright (C) 1997-1999 Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +/* stdio1.h -- for using Printf, Fprintf, Sprintf while + * retaining the system-supplied printf, fprintf, sprintf. + */ + +#ifndef STDIO1_H_included +#define STDIO1_H_included +#ifndef STDIO_H_included /* allow suppressing stdio.h */ +#endif /* e.g., by cplex.h */ + +#ifdef KR_headers +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif +#define ANSI(x) () +#ifndef Char +#define Char char +#endif +#else +#define ANSI(x) x +#ifndef Char +#define Char void +#endif +#endif + +#ifndef NO_STDIO1 +#ifdef _WIN32 +/* Avoid Microsoft bug that perrror may appear in stdlib.h. */ +/* It should only be declared in stdio.h. */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern int Fprintf ANSI((FILE *, const char *, ...)); +extern int Printf ANSI((const char *, ...)); +extern int Sprintf ANSI((char *, const char *, ...)); +extern int Snprintf ANSI((char *, size_t, const char *, ...)); +extern void Perror ANSI((const char *)); +extern int Vfprintf ANSI((FILE *, const char *, va_list)); +extern int Vsprintf ANSI((char *, const char *, va_list)); +extern int Vsnprintf ANSI((char *, size_t, const char *, va_list)); + +#ifdef PF_BUF +extern FILE *stderr_ASL; +extern void(*pfbuf_print_ASL) ANSI((char *)); +extern char *pfbuf_ASL; +extern void fflush_ASL ANSI((FILE *)); +#ifdef fflush +#define old_fflush_ASL fflush +#undef fflush +#endif +#define fflush fflush_ASL +#endif + +#ifdef __cplusplus +} +#endif + +#undef printf +#undef fprintf +#undef sprintf +#undef perror +#undef vfprintf +#undef vsprintf +#define printf Printf +#define fprintf Fprintf +#undef snprintf /* for MacOSX */ +#undef vsnprintf /* for MacOSX */ +#define snprintf Snprintf +#define sprintf Sprintf +#define perror Perror +#define vfprintf Vfprintf +#define vsnprintf Vsnprintf +#define vsprintf Vsprintf + +#endif /* NO_STDIO1 */ + +#endif /* STDIO1_H_included */ diff --git a/third_party/gdtoa/strtoIQ.c b/third_party/gdtoa/strtoIQ.c new file mode 100644 index 000000000..82ca38cfd --- /dev/null +++ b/third_party/gdtoa/strtoIQ.c @@ -0,0 +1,67 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIQ(s, sp, a, b) CONST char *s; char **sp; void *a; void *b; +#else +strtoIQ(CONST char *s, char **sp, void *a, void *b) +#endif +{ + static const FPI fpi = { 113, 1-16383-113+1, 32766-16383-113+1, 1, SI, 0 /*unused*/ }; + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; + ULong *L = (ULong *)a, *M = (ULong *)b; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(2 MTb); + B[0]->wds = 4; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtoQ(L, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtoQ(M, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else { + M[0] = L[0]; + M[1] = L[1]; + M[2] = L[2]; + M[3] = L[3]; + } + return k; + } diff --git a/third_party/gdtoa/strtoId.c b/third_party/gdtoa/strtoId.c new file mode 100644 index 000000000..fe284ad24 --- /dev/null +++ b/third_party/gdtoa/strtoId.c @@ -0,0 +1,64 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoId(s, sp, f0, f1) CONST char *s; char **sp; double *f0, *f1; +#else +strtoId(CONST char *s, char **sp, double *f0, double *f1) +#endif +{ + static const FPI fpi = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(1 MTb); + B[0]->wds = 2; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtod((ULong*)f0, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtod((ULong*)f1, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else { + ((ULong*)f1)[0] = ((ULong*)f0)[0]; + ((ULong*)f1)[1] = ((ULong*)f0)[1]; + } + return k; + } diff --git a/third_party/gdtoa/strtoIdd.c b/third_party/gdtoa/strtoIdd.c new file mode 100644 index 000000000..6d4c6ea94 --- /dev/null +++ b/third_party/gdtoa/strtoIdd.c @@ -0,0 +1,70 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIdd(s, sp, f0, f1) CONST char *s; char **sp; double *f0, *f1; +#else +strtoIdd(CONST char *s, char **sp, double *f0, double *f1) +#endif +{ +#ifdef Sudden_Underflow + static const FPI fpi = { 106, 1-1023, 2046-1023-106+1, 1, 1, 0 /*unused*/ }; +#else + static const FPI fpi = { 106, 1-1023-53+1, 2046-1023-106+1, 1, 0, 0 /*unused*/ }; +#endif + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(2 MTb); + B[0]->wds = 4; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtodd((ULong*)f0, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtodd((ULong*)f1, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else { + ((ULong*)f1)[0] = ((ULong*)f0)[0]; + ((ULong*)f1)[1] = ((ULong*)f0)[1]; + ((ULong*)f1)[2] = ((ULong*)f0)[2]; + ((ULong*)f1)[3] = ((ULong*)f0)[3]; + } + return k; + } diff --git a/third_party/gdtoa/strtoIf.c b/third_party/gdtoa/strtoIf.c new file mode 100644 index 000000000..ad2682f84 --- /dev/null +++ b/third_party/gdtoa/strtoIf.c @@ -0,0 +1,62 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIf(s, sp, f0, f1) CONST char *s; char **sp; float *f0, *f1; +#else +strtoIf(CONST char *s, char **sp, float *f0, float *f1) +#endif +{ + static const FPI fpi = { 24, 1-127-24+1, 254-127-24+1, 1, SI, 0 /*unused*/ }; + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(0 MTb); + B[0]->wds = 1; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtof((ULong*)f0, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtof((ULong*)f1, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else + *(ULong*)f1 = *(ULong*)f0; + return k; + } diff --git a/third_party/gdtoa/strtoIg.c b/third_party/gdtoa/strtoIg.c new file mode 100644 index 000000000..d16a5d2c1 --- /dev/null +++ b/third_party/gdtoa/strtoIg.c @@ -0,0 +1,141 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIg(s00, se, fpi, exp, B, rvp) CONST char *s00; char **se; CONST FPI *fpi; Long *exp; Bigint **B; int *rvp; +#else +strtoIg(CONST char *s00, char **se, CONST FPI *fpi, Long *exp, Bigint **B, int *rvp) +#endif +{ + Bigint *b, *b1; + int i, nb, nw, nw1, rv, rv1, swap; + unsigned int nb1, nb11; + Long e1; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + b = *B; + rv = strtodg(s00, se, fpi, exp, b->x); + if (!(rv & STRTOG_Inexact)) { + B[1] = 0; + return *rvp = rv; + } + e1 = exp[0]; + rv1 = rv ^ STRTOG_Inexact; + b1 = Balloc(b->k MTb); + Bcopy(b1, b); + nb = fpi->nbits; + nb1 = nb & 31; + nb11 = (nb1 - 1) & 31; + nw = b->wds; + nw1 = nw - 1; + if (rv & STRTOG_Inexlo) { + swap = 0; + b1 = increment(b1 MTb); + if ((rv & STRTOG_Retmask) == STRTOG_Zero) { + if (fpi->sudden_underflow) { + b1->x[0] = 0; + b1->x[nw1] = 1L << nb11; + rv1 += STRTOG_Normal - STRTOG_Zero; + rv1 &= ~STRTOG_Underflow; + goto swapcheck; + } + rv1 &= STRTOG_Inexlo | STRTOG_Underflow | STRTOG_Zero | STRTOG_Neg; + rv1 |= STRTOG_Inexhi | STRTOG_Denormal; + goto swapcheck; + } + if (b1->wds > nw + || (nb1 && b1->x[nw1] & 1L << nb1)) { + if (++e1 > fpi->emax) + rv1 = STRTOG_Infinite | STRTOG_Inexhi; + rshift(b1, 1); + } + else if ((rv & STRTOG_Retmask) == STRTOG_Denormal) { + if (b1->x[nw1] & 1L << nb11) { + rv1 += STRTOG_Normal - STRTOG_Denormal; + rv1 &= ~STRTOG_Underflow; + } + } + } + else { + swap = STRTOG_Neg; + if ((rv & STRTOG_Retmask) == STRTOG_Infinite) { + b1 = set_ones(b1, nb MTb); + e1 = fpi->emax; + rv1 = STRTOG_Normal | STRTOG_Inexlo | (rv & STRTOG_Neg); + goto swapcheck; + } + decrement(b1); + if ((rv & STRTOG_Retmask) == STRTOG_Denormal) { + for(i = nw1; !b1->x[i]; --i) + if (!i) { + rv1 = STRTOG_Zero | STRTOG_Inexlo | (rv & STRTOG_Neg); + break; + } + goto swapcheck; + } + if (!(b1->x[nw1] & 1L << nb11)) { + if (e1 == fpi->emin) { + if (fpi->sudden_underflow) + rv1 += STRTOG_Zero - STRTOG_Normal; + else + rv1 += STRTOG_Denormal - STRTOG_Normal; + rv1 |= STRTOG_Underflow; + } + else { + b1 = lshift(b1, 1 MTb); + b1->x[0] |= 1; + --e1; + } + } + } + swapcheck: + if (swap ^ (rv & STRTOG_Neg)) { + rvp[0] = rv1; + rvp[1] = rv; + B[0] = b1; + B[1] = b; + exp[1] = exp[0]; + exp[0] = e1; + } + else { + rvp[0] = rv; + rvp[1] = rv1; + B[1] = b1; + exp[1] = e1; + } + return rv; + } diff --git a/third_party/gdtoa/strtoIx.c b/third_party/gdtoa/strtoIx.c new file mode 100644 index 000000000..a4aed8453 --- /dev/null +++ b/third_party/gdtoa/strtoIx.c @@ -0,0 +1,68 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIx(s, sp, a, b) CONST char *s; char **sp; void *a; void *b; +#else +strtoIx(CONST char *s, char **sp, void *a, void *b) +#endif +{ + static const FPI fpi = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; + UShort *L = (UShort *)a, *M = (UShort *)b; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(1 MTb); + B[0]->wds = 2; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtox(L, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtox(M, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else { + M[0] = L[0]; + M[1] = L[1]; + M[2] = L[2]; + M[3] = L[3]; + M[4] = L[4]; + } + return k; + } diff --git a/third_party/gdtoa/strtoIxL.c b/third_party/gdtoa/strtoIxL.c new file mode 100644 index 000000000..291d442c1 --- /dev/null +++ b/third_party/gdtoa/strtoIxL.c @@ -0,0 +1,66 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtoIxL(s, sp, a, b) CONST char *s; char **sp; void *a; void *b; +#else +strtoIxL(CONST char *s, char **sp, void *a, void *b) +#endif +{ + static const FPI fpi = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + Long exp[2]; + Bigint *B[2]; + int k, rv[2]; + ULong *L = (ULong *)a, *M = (ULong *)b; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + + B[0] = Balloc(1 MTb); + B[0]->wds = 2; + k = strtoIg(s, sp, &fpi, exp, B, rv); + ULtoxL(L, B[0]->x, exp[0], rv[0]); + Bfree(B[0] MTb); + if (B[1]) { + ULtoxL(M, B[1]->x, exp[1], rv[1]); + Bfree(B[1] MTb); + } + else { + M[0] = L[0]; + M[1] = L[1]; + M[2] = L[2]; + } + return k; + } diff --git a/third_party/gdtoa/strtod.c b/third_party/gdtoa/strtod.c new file mode 100644 index 000000000..275f2a67e --- /dev/null +++ b/third_party/gdtoa/strtod.c @@ -0,0 +1,1073 @@ +#include "libc/errno.h" +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998-2001 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +#ifdef IEEE_Arith +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#undef tinytens +/* The factor of 2^106 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, + 9007199254740992.*9007199254740992.e-256 + }; +#endif +#endif + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#ifdef Avoid_Underflow /*{*/ + static double +sulp +#ifdef KR_headers + (x, scale) U *x; int scale; +#else + (U *x, int scale) +#endif +{ + U u; + double rv; + int i; + + rv = ulp(x); + if (!scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) + return rv; /* Is there an example where i <= 0 ? */ + word0(&u) = Exp_1 + (i << Exp_shift); + word1(&u) = 0; + return rv * u.d; + } +#endif /*}*/ + + double +strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (CONST char *s00, char **se) +#endif +{ +#ifdef Avoid_Underflow + int scale; +#endif + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, decpt, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj; + Long L; + U adj, aadj1, rv, rv0; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef Avoid_Underflow + ULong Lsb, Lsb1; +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif +#ifdef USE_LOCALE /*{{*/ +#ifdef NO_LOCALE_CACHE + char *decimalpoint = localeconv()->decimal_point; + int dplen = strlen(decimalpoint); +#else + char *decimalpoint; + static char *decimalpoint_cache; + static int dplen; + if (!(s0 = decimalpoint_cache)) { + s0 = localeconv()->decimal_point; + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + dplen = strlen(s0); + } + decimalpoint = (char*)s0; +#endif /*NO_LOCALE_CACHE*/ +#else /*USE_LOCALE}{*/ +#define dplen 1 +#endif /*USE_LOCALE}}*/ + +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + + sign = nz0 = nz = decpt = 0; + dval(&rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + { + static const FPI fpi = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + Long exp; + ULong bits[2]; + switch(s[1]) { + case 'x': + case 'X': + { +#ifdef Honor_FLT_ROUNDS + FPI fpi1 = fpi; + fpi1.rounding = Rounding; +#else +#define fpi1 fpi +#endif + switch((i = gethex(&s, &fpi1, &exp, &bb, sign MTb)) & STRTOG_Retmask) { + case STRTOG_NoNumber: + s = s00; + sign = 0; + case STRTOG_Zero: + break; + default: + if (bb) { + copybits(bits, fpi.nbits, bb); + Bfree(bb MTb); + } + ULtod(((U*)&rv)->L, bits, exp, i); + }} + goto ret; + } + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < DBL_DIG + 2) + z = 10*z + c - '0'; + nd0 = nd; +#ifdef USE_LOCALE + if (c == *decimalpoint) { + for(i = 1; decimalpoint[i]; ++i) + if (s[i] != decimalpoint[i]) + goto dig_done; + s += i; + c = *s; +#else + if (c == '.') { + c = *++s; +#endif + decpt = 1; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 2) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 2) + z = 10*z + c; + nz = 0; + } + } + }/*}*/ + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + ULong bits[2]; + static FPI fpinan = /* only 52 explicit bits */ + { 52, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + if (!decpt) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { +#ifndef No_Hex_NaN + if (*s == '(' /*)*/ + && hexnan(&s, &fpinan, bits) + == STRTOG_NaNbits) { + word0(&rv) = 0x7ff00000 | bits[1]; + word1(&rv) = bits[0]; + } + else { +#endif + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + } +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; +#ifndef ROUND_BIASED_without_Round_Up + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif +#endif /* ROUND_BIASED_without_Round_Up */ + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ( (i = e1 & 15) !=0) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ + range_err: + if (bd0) { + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(bd0 MTb); + Bfree(delta MTb); + } +#ifndef NO_ERRNO + errno = ERANGE; +#endif + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else + word0(&rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ( (i = e1 & 15) !=0) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P+2)*Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j-32); + } + else + word1(&rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { + undfl: + dval(&rv) = 0.; +#ifdef Honor_FLT_ROUNDS + if (Rounding == 2) + word1(&rv) = 1; +#endif + goto range_err; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y, dplen MTb); + + for(;;) { + bd = Balloc(bd0->k MTb); + Bcopy(bd, bd0); + bb = d2b(dval(&rv), &bbe, &bbbits MTb); /* rv = bb * 2^bbe */ + bs = i2b(1 MTb); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (Rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + Lsb = LSB; + Lsb1 = 0; + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + j = P + 1 - bbbits; + if (i < Emin) { /* denormal */ + i = Emin - i; + j -= i; + if (i < 32) + Lsb <<= i; + else + Lsb1 = Lsb << (i-32); + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(&rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5 MTb); + bb1 = mult(bs, bb MTb); + Bfree(bb MTb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2 MTb); + if (bd5 > 0) + bd = pow5mult(bd, bd5 MTb); + if (bd2 > 0) + bd = lshift(bd, bd2 MTb); + if (bs2 > 0) + bs = lshift(bs, bs2 MTb); + delta = diff(bb, bd MTb); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifdef Honor_FLT_ROUNDS + if (Rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (Rounding) { + if (dsign) { + dval(&adj) = 1.; + goto apply_adj; + } + } + else if (!dsign) { + dval(&adj) = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P MTb); + if (cmp(delta, bs) <= 0) + dval(&adj) = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow + if (scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj*ulp(&rv); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + dval(&adj) = ratio(delta, bs); + if (adj.d < 1.) + dval(&adj) = 1.; + if (adj.d <= 0x7ffffffe) { + /* dval(&adj) = Rounding ? ceil(&adj) : floor(&adj); */ + y = adj.d; + if (y != adj.d) { + if (!((Rounding>>1) ^ dsign)) + y++; + dval(&adj) = y; + } + } +#ifdef Avoid_Underflow + if (scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&adj) *= ulp(&rv); + if (dsign) + dval(&rv) += adj; + else + dval(&rv) -= adj; + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(&adj) *= ulp(&rv); + if (dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + dval(&rv) += adj.d; + } + else + dval(&rv) -= adj.d; + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P MTb); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + goto undfl; + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED +#ifdef Avoid_Underflow + if (Lsb1) { + if (!(word0(&rv) & Lsb1)) + break; + } + else if (!(word1(&rv) & Lsb)) + break; +#else + if (!(word1(&rv) & LSB)) + break; +#endif +#endif + if (dsign) +#ifdef Avoid_Underflow + dval(&rv) += sulp(&rv, scale); +#else + dval(&rv) += ulp(&rv); +#endif +#ifndef ROUND_BIASED + else { +#ifdef Avoid_Underflow + dval(&rv) -= sulp(&rv, scale); +#else + dval(&rv) -= ulp(&rv); +#endif +#ifndef Sudden_Underflow + if (!dval(&rv)) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = dval(&aadj1) = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) + goto undfl; +#endif + aadj = 1.; + dval(&aadj1) = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + dval(&aadj1) = -aadj; + } + } + else { + aadj *= 0.5; + dval(&aadj1) = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(Rounding) { + case 2: /* towards +infinity */ + dval(&aadj1) -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + dval(&aadj1) += 0.5; + } +#else + if (Flt_Rounds == 0) + dval(&aadj1) += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&rv) += dval(&adj); + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else + word0(&rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + dval(&aadj1) = dsign ? aadj : -aadj; + } + word0(&aadj1) += (2*P+1)*Exp_msk1 - y; + } + dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&rv) += dval(&adj); +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&rv) += adj; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) + goto undfl; + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else + word0(&rv) -= P*Exp_msk1; + } + else { + dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&rv) += adj; + } +#else /*Sudden_Underflow*/ + /* Compute dval(&adj) so that the IEEE rounding rules will + * correctly round rv + dval(&adj) in some half-way cases. + * If rv * ulp(&rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + dval(&aadj1) = (double)(int)(aadj + 0.5); + if (!dsign) + dval(&aadj1) = -dval(&aadj1); + } + dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&rv) += adj; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } +#endif + cont: + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(delta MTb); + } + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(bd0 MTb); + Bfree(delta MTb); +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (inexact && !(word0(&rv) & Exp_mask)) { + /* set underflow bit */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); + } +#endif + ret: + if (se) + *se = (char *)s; + return sign ? -dval(&rv) : dval(&rv); + } + diff --git a/third_party/gdtoa/strtodI.c b/third_party/gdtoa/strtodI.c new file mode 100644 index 000000000..5a8a4a10e --- /dev/null +++ b/third_party/gdtoa/strtodI.c @@ -0,0 +1,164 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + static double +#ifdef KR_headers +ulpdown(d) U *d; +#else +ulpdown(U *d) +#endif +{ + double u; + ULong *L = d->L; + + u = ulp(d); + if (!(L[_1] | (L[_0] & 0xfffff)) + && (L[_0] & 0x7ff00000) > 0x00100000) + u *= 0.5; + return u; + } + + int +#ifdef KR_headers +strtodI(s, sp, dd) CONST char *s; char **sp; double *dd; +#else +strtodI(CONST char *s, char **sp, double *dd) +#endif +{ + static const FPI fpi = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + ULong bits[2], sign; + Long exp; + int j, k; + U *u; + + k = strtodg(s, sp, &fpi, &exp, bits); + u = (U*)dd; + sign = k & STRTOG_Neg ? 0x80000000L : 0; + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + dval(&u[0]) = dval(&u[1]) = 0.; + break; + + case STRTOG_Zero: + dval(&u[0]) = dval(&u[1]) = 0.; +#ifdef Sudden_Underflow + if (k & STRTOG_Inexact) { + if (sign) + word0(&u[0]) = 0x80100000L; + else + word0(&u[1]) = 0x100000L; + } + break; +#else + goto contain; +#endif + + case STRTOG_Denormal: + word1(&u[0]) = bits[0]; + word0(&u[0]) = bits[1]; + goto contain; + + case STRTOG_Normal: + word1(&u[0]) = bits[0]; + word0(&u[0]) = (bits[1] & ~0x100000) | ((exp + 0x3ff + 52) << 20); + contain: + j = k & STRTOG_Inexact; + if (sign) { + word0(&u[0]) |= sign; + j = STRTOG_Inexact - j; + } + switch(j) { + case STRTOG_Inexlo: +#ifdef Sudden_Underflow + if ((u->L[_0] & 0x7ff00000) < 0x3500000) { + word0(&u[1]) = word0(&u[0]) + 0x3500000; + word1(&u[1]) = word1(&u[0]); + dval(&u[1]) += ulp(&u[1]); + word0(&u[1]) -= 0x3500000; + if (!(word0(&u[1]) & 0x7ff00000)) { + word0(&u[1]) = sign; + word1(&u[1]) = 0; + } + } + else +#endif + dval(&u[1]) = dval(&u[0]) + ulp(&u[0]); + break; + case STRTOG_Inexhi: + dval(&u[1]) = dval(&u[0]); +#ifdef Sudden_Underflow + if ((word0(&u[0]) & 0x7ff00000) < 0x3500000) { + word0(&u[0]) += 0x3500000; + dval(&u[0]) -= ulpdown(u); + word0(&u[0]) -= 0x3500000; + if (!(word0(&u[0]) & 0x7ff00000)) { + word0(&u[0]) = sign; + word1(&u[0]) = 0; + } + } + else +#endif + dval(&u[0]) -= ulpdown(u); + break; + default: + dval(&u[1]) = dval(&u[0]); + } + break; + + case STRTOG_Infinite: + word0(&u[0]) = word0(&u[1]) = sign | 0x7ff00000; + word1(&u[0]) = word1(&u[1]) = 0; + if (k & STRTOG_Inexact) { + if (sign) { + word0(&u[1]) = 0xffefffffL; + word1(&u[1]) = 0xffffffffL; + } + else { + word0(&u[0]) = 0x7fefffffL; + word1(&u[0]) = 0xffffffffL; + } + } + break; + + case STRTOG_NaN: + u->L[0] = (u+1)->L[0] = d_QNAN0; + u->L[1] = (u+1)->L[1] = d_QNAN1; + break; + + case STRTOG_NaNbits: + word0(&u[0]) = word0(&u[1]) = 0x7ff00000 | sign | bits[1]; + word1(&u[0]) = word1(&u[1]) = bits[0]; + } + return k; + } diff --git a/third_party/gdtoa/strtodg.c b/third_party/gdtoa/strtodg.c new file mode 100644 index 000000000..aea9f5c45 --- /dev/null +++ b/third_party/gdtoa/strtodg.c @@ -0,0 +1,1085 @@ +#include "libc/errno.h" +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998-2001 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + static CONST int +fivesbits[] = { 0, 3, 5, 7, 10, 12, 14, 17, 19, 21, + 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, + 47, 49, 52 +#ifdef VAX + , 54, 56 +#endif + }; + + Bigint * +#ifdef KR_headers +increment(b MTa) Bigint *b; MTk +#else +increment(Bigint *b MTd) +#endif +{ + ULong *x, *xe; + Bigint *b1; +#ifdef Pack_16 + ULong carry = 1, y; +#endif + + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); +#else + do { + y = *x + carry; + carry = y >> 16; + *x++ = y & 0xffff; + if (!carry) + return b; + } while(x < xe); + if (carry) +#endif + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1 MTa); + Bcopy(b1,b); + Bfree(b MTa); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; + } + + void +#ifdef KR_headers +decrement(b) Bigint *b; +#else +decrement(Bigint *b) +#endif +{ + ULong *x, *xe; +#ifdef Pack_16 + ULong borrow = 1, y; +#endif + + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + do { + if (*x) { + --*x; + break; + } + *x++ = 0xffffffffL; + } + while(x < xe); +#else + do { + y = *x - borrow; + borrow = (y & 0x10000) >> 16; + *x++ = y & 0xffff; + } while(borrow && x < xe); +#endif + } + + static int +#ifdef KR_headers +all_on(b, n) Bigint *b; int n; +#else +all_on(Bigint *b, int n) +#endif +{ + ULong *x, *xe; + + x = b->x; + xe = x + (n >> kshift); + while(x < xe) + if ((*x++ & ALL_ON) != ALL_ON) + return 0; + if (n &= kmask) + return ((*x | (ALL_ON << n)) & ALL_ON) == ALL_ON; + return 1; + } + + Bigint * +#ifdef KR_headers +set_ones(b, n MTa) Bigint *b; int n; MTk +#else +set_ones(Bigint *b, int n MTd) +#endif +{ + int k; + ULong *x, *xe; + + k = (n + ((1 << kshift) - 1)) >> kshift; + if (b->k < k) { + Bfree(b MTa); + b = Balloc(k MTa); + } + k = n >> kshift; + if (n &= kmask) + k++; + b->wds = k; + x = b->x; + xe = x + k; + while(x < xe) + *x++ = ALL_ON; + if (n) + x[-1] >>= ULbits - n; + return b; + } + + static int +rvOK +#ifdef KR_headers + (d, fpi, exp, bits, exact, rd, irv MTa) + U *d; CONST FPI *fpi; Long *exp; ULong *bits; int exact, rd, *irv; MTk +#else + (U *d, CONST FPI *fpi, Long *exp, ULong *bits, int exact, int rd, int *irv MTd) +#endif +{ + Bigint *b; + ULong carry, inex, lostbits; + int bdif, e, j, k, k1, nb, rv; + + carry = rv = 0; + b = d2b(dval(d), &e, &bdif MTa); + bdif -= nb = fpi->nbits; + e += bdif; + if (bdif <= 0) { + if (exact) + goto trunc; + goto ret; + } + if (P == nb) { + if ( +#ifndef IMPRECISE_INEXACT + exact && +#endif + fpi->rounding == +#ifdef RND_PRODQUOT + FPI_Round_near +#else + Flt_Rounds +#endif + ) goto trunc; + goto ret; + } + switch(rd) { + case 1: /* round down (toward -Infinity) */ + goto trunc; + case 2: /* round up (toward +Infinity) */ + break; + default: /* round near */ + k = bdif - 1; + if (k < 0) + goto trunc; + if (!k) { + if (!exact) + goto ret; + if (b->x[0] & 2) + break; + goto trunc; + } + if (b->x[k>>kshift] & ((ULong)1 << (k & kmask))) + break; + goto trunc; + } + /* "break" cases: round up 1 bit, then truncate; bdif > 0 */ + carry = 1; + trunc: + inex = lostbits = 0; + if (bdif > 0) { + if ( (lostbits = any_on(b, bdif)) !=0) + inex = STRTOG_Inexlo; + rshift(b, bdif); + if (carry) { + inex = STRTOG_Inexhi; + b = increment(b MTa); + if ( (j = nb & kmask) !=0) + j = ULbits - j; + if (hi0bits(b->x[b->wds - 1]) != j) { + if (!lostbits) + lostbits = b->x[0] & 1; + rshift(b, 1); + e++; + } + } + } + else if (bdif < 0) + b = lshift(b, -bdif MTa); + if (e < fpi->emin) { + k = fpi->emin - e; + e = fpi->emin; + if (k > nb || fpi->sudden_underflow) { + b->wds = inex = 0; + *irv = STRTOG_Underflow | STRTOG_Inexlo; + } + else { + k1 = k - 1; + if (k1 > 0 && !lostbits) + lostbits = any_on(b, k1); + if (!lostbits && !exact) + goto ret; + lostbits |= + carry = b->x[k1>>kshift] & (1 << (k1 & kmask)); + rshift(b, k); + *irv = STRTOG_Denormal; + if (carry) { + b = increment(b MTa); + inex = STRTOG_Inexhi | STRTOG_Underflow; + } + else if (lostbits) + inex = STRTOG_Inexlo | STRTOG_Underflow; + } + } + else if (e > fpi->emax) { + e = fpi->emax + 1; + *irv = STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + b->wds = inex = 0; + } + *exp = e; + copybits(bits, nb, b); + *irv |= inex; + rv = 1; + ret: + Bfree(b MTa); + return rv; + } + + static int +#ifdef KR_headers +mantbits(d) U *d; +#else +mantbits(U *d) +#endif +{ + ULong L; +#ifdef VAX + L = word1(d) << 16 | word1(d) >> 16; + if (L) +#else + if ( (L = word1(d)) !=0) +#endif + return P - lo0bits(&L); +#ifdef VAX + L = word0(d) << 16 | word0(d) >> 16 | Exp_msk11; +#else + L = word0(d) | Exp_msk1; +#endif + return P - 32 - lo0bits(&L); + } + + int +strtodg +#ifdef KR_headers + (s00, se, fpi, exp, bits) + CONST char *s00; char **se; CONST FPI *fpi; Long *exp; ULong *bits; +#else + (CONST char *s00, char **se, CONST FPI *fpi, Long *exp, ULong *bits) +#endif +{ + int abe, abits, asub; + int bb0, bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, decpt, denorm; + int dsign, e, e1, e2, emin, esign, finished, i, inex, irv, j, k; + int nbits, nd, nd0, nf, nz, nz0, rd, rvbits, rve, rve1, sign; + int sudden_underflow; + CONST char *s, *s0, *s1; + double adj0, tol; + Long L; + U adj, rv; + ULong *b, *be, y, z; + Bigint *ab, *bb, *bb1, *bd, *bd0, *bs, *delta, *rvb, *rvb0; +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif +#ifdef USE_LOCALE /*{{*/ +#ifdef NO_LOCALE_CACHE + char *decimalpoint = localeconv()->decimal_point; + int dplen = strlen(decimalpoint); +#else + char *decimalpoint; + static char *decimalpoint_cache; + static int dplen; + if (!(s0 = decimalpoint_cache)) { + s0 = localeconv()->decimal_point; + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + dplen = strlen(s0); + } + decimalpoint = (char*)s0; +#endif /*NO_LOCALE_CACHE*/ +#else /*USE_LOCALE}{*/ +#define dplen 1 +#endif /*USE_LOCALE}}*/ + + irv = STRTOG_Zero; + denorm = sign = nz0 = nz = 0; + dval(&rv) = 0.; + rvb = 0; + nbits = fpi->nbits; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + sign = 0; + irv = STRTOG_NoNumber; + s = s00; + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP + switch(s[1]) { + case 'x': + case 'X': + irv = gethex(&s, fpi, exp, &rvb, sign MTb); + if (irv == STRTOG_NoNumber) { + s = s00; + sign = 0; + } + goto ret; + } +#endif + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + sudden_underflow = fpi->sudden_underflow; + s0 = s; + y = z = 0; + for(decpt = nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < DBL_DIG + 2) + z = 10*z + c - '0'; + nd0 = nd; +#ifdef USE_LOCALE + if (c == *decimalpoint) { + for(i = 1; decimalpoint[i]; ++i) + if (s[i] != decimalpoint[i]) + goto dig_done; + s += i; + c = *s; +#else + if (c == '.') { + c = *++s; +#endif + decpt = 1; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 2) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 2) + z = 10*z + c; + nz = 0; + } + } + }/*}*/ + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + irv = STRTOG_NoNumber; + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!decpt) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + irv = STRTOG_Infinite; + goto infnanexp; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + irv = STRTOG_NaN; + *exp = fpi->emax + 1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + irv = hexnan(&s, fpi, bits); +#endif + goto infnanexp; + } + } +#endif /* INFNAN_CHECK */ + irv = STRTOG_NoNumber; + s = s00; + } + goto ret; + } + + irv = STRTOG_Normal; + e1 = e -= nf; + rd = 0; + switch(fpi->rounding & 3) { + case FPI_Round_up: + rd = 2 - sign; + break; + case FPI_Round_zero: + rd = 1; + break; + case FPI_Round_down: + rd = 1 + sign; + } + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2; + dval(&rv) = y; + if (k > 9) + dval(&rv) = tens[k - 9] * dval(&rv) + z; + bd0 = 0; + if (nbits <= P && nd <= DBL_DIG) { + if (!e) { + if (rvOK(&rv, fpi, exp, bits, 1, rd, &irv MTb)) + goto ret; + } + else if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else + i = fivesbits[e] + mantbits(&rv) <= P; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if (rvOK(&rv, fpi, exp, bits, i, rd, &irv MTb)) + goto ret; + e1 -= e; + goto rv_notOK; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e2 = e - i; + e1 -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + dval(&adj) = dval(&rv); + word0(&adj) -= P*Exp_msk1; + /* adj = */ rounded_product(dval(&adj), tens[e2]); + if ((word0(&adj) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto rv_notOK; + word0(&adj) += P*Exp_msk1; + dval(&rv) = dval(&adj); +#else + /* rv = */ rounded_product(dval(&rv), tens[e2]); +#endif + if (rvOK(&rv, fpi, exp, bits, 0, rd, &irv MTb)) + goto ret; + e1 -= e2; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + if (rvOK(&rv, fpi, exp, bits, 0, rd, &irv MTb)) + goto ret; + e1 -= e; + } +#endif + } + rv_notOK: + e1 += nd - k; + + /* Get starting approximation = rv * 10**e1 */ + + e2 = 0; + if (e1 > 0) { + if ( (i = e1 & 15) !=0) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + while(e1 >= (1 << (n_bigtens-1))) { + e2 += ((word0(&rv) & Exp_mask) + >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + dval(&rv) *= bigtens[n_bigtens-1]; + e1 -= 1 << (n_bigtens-1); + } + e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + } + } + else if (e1 < 0) { + e1 = -e1; + if ( (i = e1 & 15) !=0) + dval(&rv) /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + while(e1 >= (1 << (n_bigtens-1))) { + e2 += ((word0(&rv) & Exp_mask) + >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + dval(&rv) *= tinytens[n_bigtens-1]; + e1 -= 1 << (n_bigtens-1); + } + e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + } + } +#ifdef IBM + /* e2 is a correction to the (base 2) exponent of the return + * value, reflecting adjustments above to avoid overflow in the + * native arithmetic. For native IBM (base 16) arithmetic, we + * must multiply e2 by 4 to change from base 16 to 2. + */ + e2 <<= 2; +#endif + rvb = d2b(dval(&rv), &rve, &rvbits MTb); /* rv = rvb * 2^rve */ + rve += e2; + if ((j = rvbits - nbits) > 0) { + rshift(rvb, j); + rvbits = nbits; + rve += j; + } + bb0 = 0; /* trailing zero bits in rvb */ + e2 = rve + rvbits - nbits; + if (e2 > fpi->emax + 1) + goto huge; + rve1 = rve + rvbits - nbits; + if (e2 < (emin = fpi->emin)) { + denorm = 1; + j = rve - emin; + if (j > 0) { + rvb = lshift(rvb, j MTb); + rvbits += j; + } + else if (j < 0) { + rvbits += j; + if (rvbits <= 0) { + if (rvbits < -1) { + ufl: + rvb->wds = 0; + rvb->x[0] = 0; + switch(fpi->rounding) { + case FPI_Round_up: + if (!sign) + goto ret_tiny; + break; + case FPI_Round_down: + if (sign) { + ret_tiny: + rvb->wds = rvb->x[0] = 1; + } + } + *exp = emin; + irv = STRTOG_Underflow | STRTOG_Inexlo; + goto ret; + } + rvb->x[0] = rvb->wds = rvbits = 1; + } + else + rshift(rvb, -j); + } + rve = rve1 = emin; + if (sudden_underflow && e2 + 1 < emin) + goto ufl; + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y, dplen MTb); + + for(;;) { + bd = Balloc(bd0->k MTb); + Bcopy(bd, bd0); + bb = Balloc(rvb->k MTb); + Bcopy(bb, rvb); + bbbits = rvbits - bb0; + bbe = rve + bb0; + bs = i2b(1 MTb); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; + j = nbits + 1 - bbbits; + i = bbe + bbbits - nbits; + if (i < emin) /* denormal */ + j += i - emin; + bb2 += j; + bd2 += j; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5 MTb); + bb1 = mult(bs, bb MTb); + Bfree(bb MTb); + bb = bb1; + } + bb2 -= bb0; + if (bb2 > 0) + bb = lshift(bb, bb2 MTb); + else if (bb2 < 0) + rshift(bb, -bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5 MTb); + if (bd2 > 0) + bd = lshift(bd, bd2 MTb); + if (bs2 > 0) + bs = lshift(bs, bs2 MTb); + asub = 1; + inex = STRTOG_Inexhi; + delta = diff(bb, bd MTb); + if (delta->wds <= 1 && !delta->x[0]) + break; + dsign = delta->sign; + delta->sign = finished = 0; + L = 0; + i = cmp(delta, bs); + if (rd && i <= 0) { + irv = STRTOG_Normal; + if ( (finished = dsign ^ (rd&1)) !=0) { + if (dsign != 0) { + irv |= STRTOG_Inexhi; + goto adj1; + } + irv |= STRTOG_Inexlo; + if (rve1 == emin) + goto adj1; + for(i = 0, j = nbits; j >= ULbits; + i++, j -= ULbits) { + if (rvb->x[i] & ALL_ON) + goto adj1; + } + if (j > 1 && lo0bits(rvb->x + i) < j - 1) + goto adj1; + rve = rve1 - 1; + rvb = set_ones(rvb, rvbits = nbits MTb); + break; + } + irv |= dsign ? STRTOG_Inexlo : STRTOG_Inexhi; + break; + } + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + irv = dsign + ? STRTOG_Normal | STRTOG_Inexlo + : STRTOG_Normal | STRTOG_Inexhi; + if (dsign || bbbits > 1 || denorm || rve1 == emin) + break; + delta = lshift(delta,1 MTb); + if (cmp(delta, bs) > 0) { + irv = STRTOG_Normal | STRTOG_Inexlo; + goto drop_down; + } + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if (denorm && all_on(rvb, rvbits)) { + /*boundary case -- increment exponent*/ + rvb->wds = 1; + rvb->x[0] = 1; + rve = emin + nbits - (rvbits = 1); + irv = STRTOG_Normal | STRTOG_Inexhi; + denorm = 0; + break; + } + irv = STRTOG_Normal | STRTOG_Inexlo; + } + else if (bbbits == 1) { + irv = STRTOG_Normal; + drop_down: + /* boundary case -- decrement exponent */ + if (rve1 == emin) { + irv = STRTOG_Normal | STRTOG_Inexhi; + if (rvb->wds == 1 && rvb->x[0] == 1) + sudden_underflow = 1; + break; + } + rve -= nbits; + rvb = set_ones(rvb, rvbits = nbits MTb); + break; + } + else + irv = STRTOG_Normal | STRTOG_Inexhi; + if ((bbbits < nbits && !denorm) || !(rvb->x[0] & 1)) + break; + if (dsign) { + rvb = increment(rvb MTb); + j = kmask & (ULbits - (rvbits & kmask)); + if (hi0bits(rvb->x[rvb->wds - 1]) != j) + rvbits++; + irv = STRTOG_Normal | STRTOG_Inexhi; + } + else { + if (bbbits == 1) + goto undfl; + decrement(rvb); + irv = STRTOG_Normal | STRTOG_Inexlo; + } + break; + } + if ((dval(&adj) = ratio(delta, bs)) <= 2.) { + adj1: + inex = STRTOG_Inexlo; + if (dsign) { + asub = 0; + inex = STRTOG_Inexhi; + } + else if (denorm && bbbits <= 1) { + undfl: + rvb->wds = 0; + rve = emin; + irv = STRTOG_Underflow | STRTOG_Inexlo; + if (fpi->rounding == 2) { + rvb->wds = 1; + rvb->x[0] = 1; + irv = STRTOG_Underflow | STRTOG_Inexhi; + } + break; + } + adj0 = dval(&adj) = 1.; + } + else { + adj0 = dval(&adj) *= 0.5; + if (dsign) { + asub = 0; + inex = STRTOG_Inexlo; + } + if (dval(&adj) < 2147483647.) { + L = adj0; + adj0 -= L; + switch(rd) { + case 0: + if (adj0 >= .5) + goto inc_L; + break; + case 1: + if (asub && adj0 > 0.) + goto inc_L; + break; + case 2: + if (!asub && adj0 > 0.) { + inc_L: + L++; + inex = STRTOG_Inexact - inex; + } + } + dval(&adj) = L; + } + } + y = rve + rvbits; + + /* adj *= ulp(dval(&rv)); */ + /* if (asub) rv -= adj; else rv += adj; */ + + if (!denorm && rvbits < nbits) { + rvb = lshift(rvb, j = nbits - rvbits MTb); + rve -= j; + rvbits = nbits; + } + ab = d2b(dval(&adj), &abe, &abits MTb); + if (abe < 0) + rshift(ab, -abe); + else if (abe > 0) + ab = lshift(ab, abe MTb); + rvb0 = rvb; + if (asub) { + /* rv -= adj; */ + j = hi0bits(rvb->x[rvb->wds-1]); + rvb = diff(rvb, ab MTb); + k = rvb0->wds - 1; + if (denorm) + /* do nothing */; + else if (rvb->wds <= k + || hi0bits( rvb->x[k]) > + hi0bits(rvb0->x[k])) { + /* unlikely; can only have lost 1 high bit */ + if (rve1 == emin) { + --rvbits; + denorm = 1; + } + else { + rvb = lshift(rvb, 1 MTb); + --rve; + --rve1; + L = finished = 0; + } + } + } + else { + rvb = sum(rvb, ab MTb); + k = rvb->wds - 1; + if (k >= rvb0->wds + || hi0bits(rvb->x[k]) < hi0bits(rvb0->x[k])) { + if (denorm) { + if (++rvbits == nbits) + denorm = 0; + } + else { + rshift(rvb, 1); + rve++; + rve1++; + L = 0; + } + } + } + Bfree(ab MTb); + Bfree(rvb0 MTb); + if (finished) + break; + + z = rve + rvbits; + if (y == z && L) { + /* Can we stop now? */ + tol = dval(&adj) * 5e-16; /* > max rel error */ + dval(&adj) = adj0 - .5; + if (dval(&adj) < -tol) { + if (adj0 > tol) { + irv |= inex; + break; + } + } + else if (dval(&adj) > tol && adj0 < 1. - tol) { + irv |= inex; + break; + } + } + bb0 = denorm ? 0 : trailz(rvb); + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(delta MTb); + } + if (!denorm && (j = nbits - rvbits)) { + if (j > 0) + rvb = lshift(rvb, j MTb); + else + rshift(rvb, -j); + rve -= j; + } + *exp = rve; + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(bd0 MTb); + Bfree(delta MTb); + if (rve > fpi->emax) { + huge: + Bfree(rvb MTb); + rvb = 0; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + switch(fpi->rounding & 3) { + case FPI_Round_up: + if (!sign) + goto ret_inf; + break; + case FPI_Round_down: + if (!sign) + break; + case FPI_Round_near: + ret_inf: + irv = STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; + k = nbits >> kshift; + if (nbits & kmask) + ++k; + memset(bits, 0, k*sizeof(ULong)); + infnanexp: + *exp = fpi->emax + 1; + goto ret; + } + /* Round to largest representable magnitude */ + irv = STRTOG_Normal | STRTOG_Inexlo; + *exp = fpi->emax; + b = bits; + be = b + ((fpi->nbits + 31) >> 5); + while(b < be) + *b++ = -1; + if ((j = fpi->nbits & 0x1f)) + *--be >>= (32 - j); + } + ret: + if (denorm) { + if (sudden_underflow) { + rvb->wds = 0; + irv = STRTOG_Underflow | STRTOG_Inexlo; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + } + else { + irv = (irv & ~STRTOG_Retmask) | + (rvb->wds > 0 ? STRTOG_Denormal : STRTOG_Zero); + if (irv & STRTOG_Inexact) { + irv |= STRTOG_Underflow; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + } + } + } + if (se) + *se = (char *)s; + if (sign) + irv |= STRTOG_Neg; + if (rvb) { + copybits(bits, nbits, rvb); + Bfree(rvb MTb); + } + return irv; + } diff --git a/third_party/gdtoa/strtodnrp.c b/third_party/gdtoa/strtodnrp.c new file mode 100644 index 000000000..6f4152152 --- /dev/null +++ b/third_party/gdtoa/strtodnrp.c @@ -0,0 +1,88 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2004 by David M. Gay. +All Rights Reserved +Based on material in the rest of /netlib/fp/gdota.tar.gz, +which is copyright (C) 1998, 2000 by Lucent Technologies. + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* This is a variant of strtod that works on Intel ia32 systems */ +/* with the default extended-precision arithmetic -- it does not */ +/* require setting the precision control to 53 bits. */ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + double +#ifdef KR_headers +strtod(s, sp) CONST char *s; char **sp; +#else +strtod(CONST char *s, char **sp) +#endif +{ + static const FPI fpi = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + ULong bits[2]; + Long exp; + int k; + union { ULong L[2]; double d; } u; + + k = strtodg(s, sp, &fpi, &exp, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + u.L[0] = u.L[1] = 0; + break; + + case STRTOG_Normal: + u.L[_1] = bits[0]; + u.L[_0] = (bits[1] & ~0x100000) | ((exp + 0x3ff + 52) << 20); + break; + + case STRTOG_Denormal: + u.L[_1] = bits[0]; + u.L[_0] = bits[1]; + break; + + case STRTOG_Infinite: + u.L[_0] = 0x7ff00000; + u.L[_1] = 0; + break; + + case STRTOG_NaN: + u.L[0] = d_QNAN0; + u.L[1] = d_QNAN1; + break; + + case STRTOG_NaNbits: + u.L[_0] = 0x7ff00000 | bits[1]; + u.L[_1] = bits[0]; + } + if (k & STRTOG_Neg) + u.L[_0] |= 0x80000000L; + return u.d; + } diff --git a/third_party/gdtoa/strtof.c b/third_party/gdtoa/strtof.c new file mode 100644 index 000000000..51509a6ba --- /dev/null +++ b/third_party/gdtoa/strtof.c @@ -0,0 +1,80 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + float +#ifdef KR_headers +strtof(s, sp) CONST char *s; char **sp; +#else +strtof(CONST char *s, char **sp) +#endif +{ + static FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, SI, 0 /*unused*/ }; + ULong bits[1]; + Long exp; + int k; + union { ULong L[1]; float f; } u; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + switch(k & STRTOG_Retmask) { + default: /* unused */ + case STRTOG_NoNumber: + case STRTOG_Zero: + u.L[0] = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + u.L[0] = (bits[0] & 0x7fffff) | ((exp + 0x7f + 23) << 23); + break; + + case STRTOG_Denormal: + u.L[0] = bits[0]; + break; + + case STRTOG_Infinite: + u.L[0] = 0x7f800000; + break; + + case STRTOG_NaN: + u.L[0] = f_QNAN; + } + if (k & STRTOG_Neg) + u.L[0] |= 0x80000000L; + return u.f; + } diff --git a/libc/bits/min.c b/third_party/gdtoa/strtold.c similarity index 91% rename from libc/bits/min.c rename to third_party/gdtoa/strtold.c index 431302549..58cee249f 100644 --- a/libc/bits/min.c +++ b/third_party/gdtoa/strtold.c @@ -17,6 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" +#include "third_party/gdtoa/gdtoa.h" -long(min)(long x, long y) { return MIN(x, y); } +long double strtold(const char *s, char **endptr) { + long double result; + strtorQ(s, endptr, FPI_Round_near, &result); + return result; +} diff --git a/third_party/gdtoa/strtopQ.c b/third_party/gdtoa/strtopQ.c new file mode 100644 index 000000000..5a53013c2 --- /dev/null +++ b/third_party/gdtoa/strtopQ.c @@ -0,0 +1,110 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#endif +#ifdef IEEE_8087 +#define _0 3 +#define _1 2 +#define _2 1 +#define _3 0 +#endif + + extern ULong NanDflt_Q_D2A[4]; + + + int +#ifdef KR_headers +strtopQ(s, sp, V) CONST char *s; char **sp; void *V; +#else +strtopQ(CONST char *s, char **sp, void *V) +#endif +{ + static const FPI fpi0 = { 113, 1-16383-113+1, 32766 - 16383 - 113 + 1, 1, SI, 0 /*unused*/ }; + ULong bits[4]; + Long exp; + int k; + ULong *L = (ULong*)V; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_3] = bits[0]; + L[_2] = bits[1]; + L[_1] = bits[2]; + L[_0] = (bits[3] & ~0x10000) | ((exp + 0x3fff + 112) << 16); + break; + + case STRTOG_Denormal: + L[_3] = bits[0]; + L[_2] = bits[1]; + L[_1] = bits[2]; + L[_0] = bits[3]; + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff0000; + L[_1] = L[_2] = L[_3] = 0; + break; + + case STRTOG_NaN: + L[_0] = NanDflt_Q_D2A[3]; + L[_1] = NanDflt_Q_D2A[2]; + L[_2] = NanDflt_Q_D2A[1]; + L[_3] = NanDflt_Q_D2A[0]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x80000000L; + return k; + } diff --git a/third_party/gdtoa/strtopd.c b/third_party/gdtoa/strtopd.c new file mode 100644 index 000000000..a601eea8c --- /dev/null +++ b/third_party/gdtoa/strtopd.c @@ -0,0 +1,55 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtopd(s, sp, d) char *s; char **sp; double *d; +#else +strtopd(CONST char *s, char **sp, double *d) +#endif +{ + static const FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + ULong bits[2]; + Long exp; + int k; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + ULtod((ULong*)d, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtopdd.c b/third_party/gdtoa/strtopdd.c new file mode 100644 index 000000000..b23499332 --- /dev/null +++ b/third_party/gdtoa/strtopdd.c @@ -0,0 +1,184 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtopdd(s, sp, dd) CONST char *s; char **sp; double *dd; +#else +strtopdd(CONST char *s, char **sp, double *dd) +#endif +{ +#ifdef Sudden_Underflow + static const FPI fpi0 = { 106, 1-1023, 2046-1023-106+1, 1, 1, 0 /*unused*/ }; +#else + static const FPI fpi0 = { 106, 1-1023-53+1, 2046-1023-106+1, 1, 0, 0 /*unused*/ }; +#endif + ULong bits[4]; + Long exp; + int i, j, rv; + typedef union { + double d[2]; + ULong L[4]; + } U; + U *u; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + rv = strtodg(s, sp, fpi, &exp, bits); + u = (U*)dd; + switch(rv & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + u->d[0] = u->d[1] = 0.; + break; + + case STRTOG_Normal: + u->L[_1] = (bits[1] >> 21 | bits[2] << 11) & 0xffffffffL; + u->L[_0] = (bits[2] >> 21) | ((bits[3] << 11) & 0xfffff) + | ((exp + 0x3ff + 105) << 20); + exp += 0x3ff + 52; + if (bits[1] &= 0x1fffff) { + i = hi0bits(bits[1]) - 11; + if (i >= exp) { + i = exp - 1; + exp = 0; + } + else + exp -= i; + if (i > 0) { + bits[1] = bits[1] << i | bits[0] >> (32-i); + bits[0] = bits[0] << i & 0xffffffffL; + } + } + else if (bits[0]) { + i = hi0bits(bits[0]) + 21; + if (i >= exp) { + i = exp - 1; + exp = 0; + } + else + exp -= i; + if (i < 32) { + bits[1] = bits[0] >> (32 - i); + bits[0] = bits[0] << i & 0xffffffffL; + } + else { + bits[1] = bits[0] << (i - 32); + bits[0] = 0; + } + } + else { + u->L[2] = u->L[3] = 0; + break; + } + u->L[2+_1] = bits[0]; + u->L[2+_0] = (bits[1] & 0xfffff) | (exp << 20); + break; + + case STRTOG_Denormal: + if (bits[3]) + goto nearly_normal; + if (bits[2]) + goto partly_normal; + if (bits[1] & 0xffe00000) + goto hardly_normal; + /* completely denormal */ + u->L[2] = u->L[3] = 0; + u->L[_1] = bits[0]; + u->L[_0] = bits[1]; + break; + + nearly_normal: + i = hi0bits(bits[3]) - 11; /* i >= 12 */ + j = 32 - i; + u->L[_0] = ((bits[3] << i | bits[2] >> j) & 0xfffff) + | ((65 - i) << 20); + u->L[_1] = (bits[2] << i | bits[1] >> j) & 0xffffffffL; + u->L[2+_0] = bits[1] & ((1L << j) - 1); + u->L[2+_1] = bits[0]; + break; + + partly_normal: + i = hi0bits(bits[2]) - 11; + if (i < 0) { + j = -i; + i += 32; + u->L[_0] = (bits[2] >> j & 0xfffff) | (33 + j) << 20; + u->L[_1] = ((bits[2] << i) | (bits[1] >> j)) & 0xffffffffL; + u->L[2+_0] = bits[1] & ((1L << j) - 1); + u->L[2+_1] = bits[0]; + break; + } + if (i == 0) { + u->L[_0] = (bits[2] & 0xfffff) | (33 << 20); + u->L[_1] = bits[1]; + u->L[2+_0] = 0; + u->L[2+_1] = bits[0]; + break; + } + j = 32 - i; + u->L[_0] = (((bits[2] << i) | (bits[1] >> j)) & 0xfffff) + | ((j + 1) << 20); + u->L[_1] = (bits[1] << i | bits[0] >> j) & 0xffffffffL; + u->L[2+_0] = 0; + u->L[2+_1] = bits[0] & ((1L << j) - 1); + break; + + hardly_normal: + j = 11 - hi0bits(bits[1]); + i = 32 - j; + u->L[_0] = (bits[1] >> j & 0xfffff) | ((j + 1) << 20); + u->L[_1] = (bits[1] << i | bits[0] >> j) & 0xffffffffL; + u->L[2+_0] = 0; + u->L[2+_1] = bits[0] & ((1L << j) - 1); + break; + + case STRTOG_Infinite: + u->L[_0] = u->L[2+_0] = 0x7ff00000; + u->L[_1] = u->L[2+_1] = 0; + break; + + case STRTOG_NaN: + u->L[0] = u->L[2] = d_QNAN0; + u->L[1] = u->L[3] = d_QNAN1; + } + if (rv & STRTOG_Neg) { + u->L[ _0] |= 0x80000000L; + u->L[2+_0] |= 0x80000000L; + } + return rv; + } diff --git a/third_party/gdtoa/strtopf.c b/third_party/gdtoa/strtopf.c new file mode 100644 index 000000000..c84709d55 --- /dev/null +++ b/third_party/gdtoa/strtopf.c @@ -0,0 +1,79 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + int +#ifdef KR_headers +strtopf(s, sp, f) CONST char *s; char **sp; float *f; +#else +strtopf(CONST char *s, char **sp, float *f) +#endif +{ + static const FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, SI, 0 /*unused*/ }; + ULong bits[1], *L; + Long exp; + int k; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + L = (ULong*)f; + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[0] = (bits[0] & 0x7fffff) | ((exp + 0x7f + 23) << 23); + break; + + case STRTOG_Denormal: + L[0] = bits[0]; + break; + + case STRTOG_Infinite: + L[0] = 0x7f800000; + break; + + case STRTOG_NaN: + L[0] = f_QNAN; + } + if (k & STRTOG_Neg) + L[0] |= 0x80000000L; + return k; + } diff --git a/third_party/gdtoa/strtopx.c b/third_party/gdtoa/strtopx.c new file mode 100644 index 000000000..9b0c3ed7f --- /dev/null +++ b/third_party/gdtoa/strtopx.c @@ -0,0 +1,112 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern UShort NanDflt_ldus_D2A[5]; + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + + int +#ifdef KR_headers +strtopx(s, sp, V) CONST char *s; char **sp; void *V; +#else +strtopx(CONST char *s, char **sp, void *V) +#endif +{ + const static FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + ULong bits[2]; + Long exp; + int k; + UShort *L = (UShort*)V; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = L[4] = 0; + break; + + case STRTOG_Denormal: + L[_0] = 0; + goto normal_bits; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_0] = exp + 0x3fff + 63; + normal_bits: + L[_4] = (UShort)bits[0]; + L[_3] = (UShort)(bits[0] >> 16); + L[_2] = (UShort)bits[1]; + L[_1] = (UShort)(bits[1] >> 16); + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff; + L[_1] = 0x8000; + L[_2] = L[_3] = L[_4] = 0; + break; + + case STRTOG_NaN: + L[_4] = NanDflt_ldus_D2A[0]; + L[_3] = NanDflt_ldus_D2A[1]; + L[_2] = NanDflt_ldus_D2A[2]; + L[_1] = NanDflt_ldus_D2A[3]; + L[_0] = NanDflt_ldus_D2A[4]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x8000; + return k; + } diff --git a/third_party/gdtoa/strtopxL.c b/third_party/gdtoa/strtopxL.c new file mode 100644 index 000000000..c742a7c24 --- /dev/null +++ b/third_party/gdtoa/strtopxL.c @@ -0,0 +1,100 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_xL_D2A[3]; + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#endif +#ifdef IEEE_8087 +#define _0 2 +#define _1 1 +#define _2 0 +#endif + + int +#ifdef KR_headers +strtopxL(s, sp, V) CONST char *s; char **sp; void *V; +#else +strtopxL(CONST char *s, char **sp, void *V) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + ULong bits[2]; + Long exp; + int k; + ULong *L = (ULong*)V; +#ifdef Honor_FLT_ROUNDS +#include "third_party/gdtoa/gdtoa_fltrnds.inc" +#else +#define fpi &fpi0 +#endif + + k = strtodg(s, sp, fpi, &exp, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = 0; + break; + + case STRTOG_Normal: + case STRTOG_Denormal: + case STRTOG_NaNbits: + L[_2] = bits[0]; + L[_1] = bits[1]; + L[_0] = (exp + 0x3fff + 63) << 16; + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff << 16; + L[_1] = 0x80000000; + L[_2] = 0; + break; + + case STRTOG_NaN: + L[_0] = NanDflt_xL_D2A[2]; + L[_1] = NanDflt_xL_D2A[1]; + L[_2] = NanDflt_xL_D2A[0]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x80000000L; + return k; + } diff --git a/third_party/gdtoa/strtorQ.c b/third_party/gdtoa/strtorQ.c new file mode 100644 index 000000000..f718c234f --- /dev/null +++ b/third_party/gdtoa/strtorQ.c @@ -0,0 +1,120 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#endif +#ifdef IEEE_8087 +#define _0 3 +#define _1 2 +#define _2 1 +#define _3 0 +#endif + + extern ULong NanDflt_Q_D2A[4]; + + void +#ifdef KR_headers +ULtoQ(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k; +#else +ULtoQ(ULong *L, ULong *bits, Long exp, int k) +#endif +{ + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_3] = bits[0]; + L[_2] = bits[1]; + L[_1] = bits[2]; + L[_0] = (bits[3] & ~0x10000) | ((exp + 0x3fff + 112) << 16); + break; + + case STRTOG_Denormal: + L[_3] = bits[0]; + L[_2] = bits[1]; + L[_1] = bits[2]; + L[_0] = bits[3]; + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff0000; + L[_1] = L[_2] = L[_3] = 0; + break; + + case STRTOG_NaN: + L[_0] = NanDflt_Q_D2A[3]; + L[_1] = NanDflt_Q_D2A[2]; + L[_2] = NanDflt_Q_D2A[1]; + L[_3] = NanDflt_Q_D2A[0]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x80000000L; + } + + int +#ifdef KR_headers +strtorQ(s, sp, rounding, L) CONST char *s; char **sp; int rounding; void *L; +#else +strtorQ(CONST char *s, char **sp, int rounding, void *L) +#endif +{ + static const FPI fpi0 = { 113, 1-16383-113+1, 32766-16383-113+1, 1, SI, 0 /*unused*/ }; + FPI *fpi, fpi1; + ULong bits[4]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtoQ((ULong*)L, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtord.c b/third_party/gdtoa/strtord.c new file mode 100644 index 000000000..e43e2a17f --- /dev/null +++ b/third_party/gdtoa/strtord.c @@ -0,0 +1,96 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_d_D2A[2]; + + void +#ifdef KR_headers +ULtod(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k; +#else +ULtod(ULong *L, ULong *bits, Long exp, int k) +#endif +{ + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = 0; + break; + + case STRTOG_Denormal: + L[_1] = bits[0]; + L[_0] = bits[1]; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_1] = bits[0]; + L[_0] = (bits[1] & ~0x100000) | ((exp + 0x3ff + 52) << 20); + break; + + case STRTOG_Infinite: + L[_0] = 0x7ff00000; + L[_1] = 0; + break; + + case STRTOG_NaN: + L[_0] = NanDflt_d_D2A[1]; + L[_1] = NanDflt_d_D2A[0]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x80000000L; + } + + int +#ifdef KR_headers +strtord(s, sp, rounding, d) CONST char *s; char **sp; int rounding; double *d; +#else +strtord(CONST char *s, char **sp, int rounding, double *d) +#endif +{ + static const FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, SI, 0 /*unused*/ }; + FPI *fpi, fpi1; + ULong bits[2]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtod((ULong*)d, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtordd.c b/third_party/gdtoa/strtordd.c new file mode 100644 index 000000000..e24aaa3eb --- /dev/null +++ b/third_party/gdtoa/strtordd.c @@ -0,0 +1,203 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_d_D2A[2]; + + void +#ifdef KR_headers +ULtodd(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k; +#else +ULtodd(ULong *L, ULong *bits, Long exp, int k) +#endif +{ + int i, j; + + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = 0; + break; + + case STRTOG_Normal: + L[_1] = (bits[1] >> 21 | bits[2] << 11) & (ULong)0xffffffffL; + L[_0] = (bits[2] >> 21) | (bits[3] << 11 & 0xfffff) + | ((exp + 0x3ff + 105) << 20); + exp += 0x3ff + 52; + if (bits[1] &= 0x1fffff) { + i = hi0bits(bits[1]) - 11; + if (i >= exp) { + i = exp - 1; + exp = 0; + } + else + exp -= i; + if (i > 0) { + bits[1] = bits[1] << i | bits[0] >> (32-i); + bits[0] = bits[0] << i & (ULong)0xffffffffL; + } + } + else if (bits[0]) { + i = hi0bits(bits[0]) + 21; + if (i >= exp) { + i = exp - 1; + exp = 0; + } + else + exp -= i; + if (i < 32) { + bits[1] = bits[0] >> (32 - i); + bits[0] = bits[0] << i & (ULong)0xffffffffL; + } + else { + bits[1] = bits[0] << (i - 32); + bits[0] = 0; + } + } + else { + L[2] = L[3] = 0; + break; + } + L[2+_1] = bits[0]; + L[2+_0] = (bits[1] & 0xfffff) | (exp << 20); + break; + + case STRTOG_Denormal: + if (bits[3]) + goto nearly_normal; + if (bits[2]) + goto partly_normal; + if (bits[1] & 0xffe00000) + goto hardly_normal; + /* completely denormal */ + L[2] = L[3] = 0; + L[_1] = bits[0]; + L[_0] = bits[1]; + break; + + nearly_normal: + i = hi0bits(bits[3]) - 11; /* i >= 12 */ + j = 32 - i; + L[_0] = ((bits[3] << i | bits[2] >> j) & 0xfffff) + | ((65 - i) << 20); + L[_1] = (bits[2] << i | bits[1] >> j) & 0xffffffffL; + L[2+_0] = bits[1] & (((ULong)1L << j) - 1); + L[2+_1] = bits[0]; + break; + + partly_normal: + i = hi0bits(bits[2]) - 11; + if (i < 0) { + j = -i; + i += 32; + L[_0] = (bits[2] >> j & 0xfffff) | ((33 + j) << 20); + L[_1] = (bits[2] << i | bits[1] >> j) & 0xffffffffL; + L[2+_0] = bits[1] & (((ULong)1L << j) - 1); + L[2+_1] = bits[0]; + break; + } + if (i == 0) { + L[_0] = (bits[2] & 0xfffff) | (33 << 20); + L[_1] = bits[1]; + L[2+_0] = 0; + L[2+_1] = bits[0]; + break; + } + j = 32 - i; + L[_0] = (((bits[2] << i) | (bits[1] >> j)) & 0xfffff) + | ((j + 1) << 20); + L[_1] = (bits[1] << i | bits[0] >> j) & 0xffffffffL; + L[2+_0] = 0; + L[2+_1] = bits[0] & ((1L << j) - 1); + break; + + hardly_normal: + j = 11 - hi0bits(bits[1]); + i = 32 - j; + L[_0] = (bits[1] >> j & 0xfffff) | ((j + 1) << 20); + L[_1] = (bits[1] << i | bits[0] >> j) & 0xffffffffL; + L[2+_0] = 0; + L[2+_1] = bits[0] & (((ULong)1L << j) - 1); + break; + + case STRTOG_Infinite: + L[_0] = L[2+_0] = 0x7ff00000; + L[_1] = L[2+_1] = 0; + break; + + case STRTOG_NaN: + L[_0] = L[_0+2] = NanDflt_d_D2A[1]; + L[_1] = L[_1+2] = NanDflt_d_D2A[0]; + break; + + case STRTOG_NaNbits: + L[_1] = (bits[1] >> 20 | bits[2] << 12) & (ULong)0xffffffffL; + L[_0] = bits[2] >> 20 | bits[3] << 12; + L[_0] |= (L[_1] | L[_0]) ? (ULong)0x7ff00000L : (ULong)0x7ff80000L; + L[2+_1] = bits[0] & (ULong)0xffffffffL; + L[2+_0] = bits[1] & 0xfffffL; + L[2+_0] |= (L[2+_1] | L[2+_0]) ? (ULong)0x7ff00000L : (ULong)0x7ff80000L; + } + if (k & STRTOG_Neg) { + L[_0] |= 0x80000000L; + L[2+_0] |= 0x80000000L; + } + } + + int +#ifdef KR_headers +strtordd(s, sp, rounding, dd) CONST char *s; char **sp; int rounding; double *dd; +#else +strtordd(CONST char *s, char **sp, int rounding, double *dd) +#endif +{ +#ifdef Sudden_Underflow + static const FPI fpi0 = { 106, 1-1023, 2046-1023-106+1, 1, 1, 0 /*unused*/ }; +#else + static const FPI fpi0 = { 106, 1-1023-53+1, 2046-1023-106+1, 1, 0, 0 /*unused*/ }; +#endif + FPI *fpi, fpi1; + ULong bits[4]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtodd((ULong*)dd, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtorf.c b/third_party/gdtoa/strtorf.c new file mode 100644 index 000000000..b77a94074 --- /dev/null +++ b/third_party/gdtoa/strtorf.c @@ -0,0 +1,92 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + extern ULong NanDflt_f_D2A[1]; + + void +#ifdef KR_headers +ULtof(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k; +#else +ULtof(ULong *L, ULong *bits, Long exp, int k) +#endif +{ + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + *L = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[0] = (bits[0] & 0x7fffff) | ((exp + 0x7f + 23) << 23); + break; + + case STRTOG_Denormal: + L[0] = bits[0]; + break; + + case STRTOG_Infinite: + L[0] = 0x7f800000; + break; + + case STRTOG_NaN: + L[0] = NanDflt_f_D2A[0]; + } + if (k & STRTOG_Neg) + L[0] |= 0x80000000L; + } + + int +#ifdef KR_headers +strtorf(s, sp, rounding, f) CONST char *s; char **sp; int rounding; float *f; +#else +strtorf(CONST char *s, char **sp, int rounding, float *f) +#endif +{ + static const FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, SI, 0 /*unused*/ }; + FPI *fpi, fpi1; + ULong bits[1]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtof((ULong*)f, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtorx.c b/third_party/gdtoa/strtorx.c new file mode 100644 index 000000000..48891491d --- /dev/null +++ b/third_party/gdtoa/strtorx.c @@ -0,0 +1,123 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + + extern UShort NanDflt_ldus_D2A[5]; + + void +#ifdef KR_headers +ULtox(L, bits, exp, k) UShort *L; ULong *bits; Long exp; int k; +#else +ULtox(UShort *L, ULong *bits, Long exp, int k) +#endif +{ + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = L[4] = 0; + break; + + case STRTOG_Denormal: + L[_0] = 0; + goto normal_bits; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_0] = exp + 0x3fff + 63; + normal_bits: + L[_4] = (UShort)bits[0]; + L[_3] = (UShort)(bits[0] >> 16); + L[_2] = (UShort)bits[1]; + L[_1] = (UShort)(bits[1] >> 16); + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff; + L[_1] = 0x8000; + L[_2] = L[_3] = L[_4] = 0; + break; + + case STRTOG_NaN: + L[_4] = NanDflt_ldus_D2A[0]; + L[_3] = NanDflt_ldus_D2A[1]; + L[_2] = NanDflt_ldus_D2A[2]; + L[_1] = NanDflt_ldus_D2A[3]; + L[_0] = NanDflt_ldus_D2A[4]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x8000; + } + + int +#ifdef KR_headers +strtorx(s, sp, rounding, L) CONST char *s; char **sp; int rounding; void *L; +#else +strtorx(CONST char *s, char **sp, int rounding, void *L) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + FPI *fpi, fpi1; + ULong bits[2]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtox((UShort*)L, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/strtorxL.c b/third_party/gdtoa/strtorxL.c new file mode 100644 index 000000000..7be151adf --- /dev/null +++ b/third_party/gdtoa/strtorxL.c @@ -0,0 +1,111 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#endif +#ifdef IEEE_8087 +#define _0 2 +#define _1 1 +#define _2 0 +#endif + + extern ULong NanDflt_xL_D2A[3]; + + void +#ifdef KR_headers +ULtoxL(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k; +#else +ULtoxL(ULong *L, ULong *bits, Long exp, int k) +#endif +{ + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = 0; + break; + + case STRTOG_Normal: + case STRTOG_Denormal: + case STRTOG_NaNbits: + L[_0] = (exp + 0x3fff + 63) << 16; + L[_1] = bits[1]; + L[_2] = bits[0]; + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff0000; + L[_1] = 0x80000000; + L[_2] = 0; + break; + + case STRTOG_NaN: + L[_0] = NanDflt_xL_D2A[2]; + L[_1] = NanDflt_xL_D2A[1]; + L[_2] = NanDflt_xL_D2A[0]; + } + if (k & STRTOG_Neg) + L[_0] |= 0x80000000L; + } + + int +#ifdef KR_headers +strtorxL(s, sp, rounding, L) CONST char *s; char **sp; int rounding; void *L; +#else +strtorxL(CONST char *s, char **sp, int rounding, void *L) +#endif +{ + static const FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, 0 /*unused*/ }; + FPI *fpi, fpi1; + ULong bits[2]; + Long exp; + int k; + + fpi = &fpi0; + if (rounding != FPI_Round_near) { + fpi1 = fpi0; + fpi1.rounding = rounding; + fpi = &fpi1; + } + k = strtodg(s, sp, fpi, &exp, bits); + ULtoxL((ULong*)L, bits, exp, k); + return k; + } diff --git a/third_party/gdtoa/sum.c b/third_party/gdtoa/sum.c new file mode 100644 index 000000000..175eae99b --- /dev/null +++ b/third_party/gdtoa/sum.c @@ -0,0 +1,99 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + Bigint * +#ifdef KR_headers +sum(a, b MTa) Bigint *a; Bigint *b; MTk +#else +sum(Bigint *a, Bigint *b MTd) +#endif +{ + Bigint *c; + ULong carry, *xc, *xa, *xb, *xe, y; +#ifdef Pack_32 + ULong z; +#endif + + if (a->wds < b->wds) { + c = b; b = a; a = c; + } + c = Balloc(a->k MTa); + c->wds = a->wds; + carry = 0; + xa = a->x; + xb = b->x; + xc = c->x; + xe = xc + b->wds; +#ifdef Pack_32 + do { + y = (*xa & 0xffff) + (*xb & 0xffff) + carry; + carry = (y & 0x10000) >> 16; + z = (*xa++ >> 16) + (*xb++ >> 16) + carry; + carry = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xc < xe); + xe += a->wds - b->wds; + while(xc < xe) { + y = (*xa & 0xffff) + carry; + carry = (y & 0x10000) >> 16; + z = (*xa++ >> 16) + carry; + carry = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ + *xb++ + carry; + carry = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xc < xe); + xe += a->wds - b->wds; + while(xc < xe) { + y = *xa++ + carry; + carry = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif + if (carry) { + if (c->wds == c->maxwds) { + b = Balloc(c->k + 1 MTa); + Bcopy(b, c); + Bfree(c MTa); + c = b; + } + c->x[c->wds++] = 1; + } + return c; + } diff --git a/third_party/gdtoa/ulp.c b/third_party/gdtoa/ulp.c new file mode 100644 index 000000000..31b9badd5 --- /dev/null +++ b/third_party/gdtoa/ulp.c @@ -0,0 +1,71 @@ +#include "third_party/gdtoa/gdtoaimp.h" + +/* clang-format off */ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + double +ulp +#ifdef KR_headers + (x) U *x; +#else + (U *x) +#endif +{ + Long L; + U a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&a) = L; + word1(&a) = 0; +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&a) = 0x80000 >> L; + word1(&a) = 0; + } + else { + word0(&a) = 0; + L -= Exp_shift; + word1(&a) = L >= 31 ? 1 : 1 << (31 - L); + } + } +#endif + return dval(&a); + } diff --git a/third_party/third_party.mk b/third_party/third_party.mk index 2b4b381fd..1d0f55aa1 100644 --- a/third_party/third_party.mk +++ b/third_party/third_party.mk @@ -6,10 +6,12 @@ o/$(MODE)/third_party: \ o/$(MODE)/third_party/avir \ o/$(MODE)/third_party/blas \ o/$(MODE)/third_party/bzip2 \ + o/$(MODE)/third_party/chibicc \ o/$(MODE)/third_party/compiler_rt \ o/$(MODE)/third_party/ctags \ o/$(MODE)/third_party/dlmalloc \ o/$(MODE)/third_party/dtoa \ + o/$(MODE)/third_party/gdtoa \ o/$(MODE)/third_party/duktape \ o/$(MODE)/third_party/editline \ o/$(MODE)/third_party/f2c \ diff --git a/tool/build/lib/disarg.c b/tool/build/lib/disarg.c index 4bfc3508e..e556e1c47 100644 --- a/tool/build/lib/disarg.c +++ b/tool/build/lib/disarg.c @@ -420,6 +420,14 @@ static char *DisRdx(struct Dis *d, uint32_t rde, char *p) { return DisRegister(p, kGreg[Osz(rde)][Rexw(rde)][0][2]); } +static char *DisPort(struct Dis *d, uint32_t rde, char *p) { + *p++ = '('; + p = DisRegister(p, kGreg[1][0][0][2]); + *p++ = ')'; + *p = '\0'; + return p; +} + static char *DisCd(struct Dis *d, uint32_t rde, char *p) { return DisRegister(p, kCtl[ModrmReg(rde)]); } @@ -653,6 +661,7 @@ static const struct DisArg { {"%rAX", DisRax}, // {"%rDX", DisRdx}, // {"BBb", DisBBb}, // + {"DX", DisPort}, // {"EST", DisEst}, // {"EST1", DisEst1}, // {"ESsr", DisEssr}, // diff --git a/tool/build/lib/disinst.c b/tool/build/lib/disinst.c index 77eb212ac..0b8d0348d 100644 --- a/tool/build/lib/disinst.c +++ b/tool/build/lib/disinst.c @@ -18,7 +18,6 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/log/check.h" -#include "libc/nexgen32e/tinystrcmp.internal.h" #include "libc/str/str.h" #include "third_party/zlib/zlib.h" #include "tool/build/lib/dis.h" @@ -97,30 +96,30 @@ static char *DisName(struct Dis *d, char *bp, const char *name, rde = d->xedd->op.rde; if (d->xedd->op.lock) p = stpcpy(p, "lock "); p = DisRepPrefix(d, p); - if (tinystrcmp(name, "BIT") == 0) { + if (strcmp(name, "BIT") == 0) { p = stpcpy(p, kBitOp[ModrmReg(rde)]); - } else if (tinystrcmp(name, "nop") == 0 && d->xedd->op.rep) { + } else if (strcmp(name, "nop") == 0 && d->xedd->op.rep) { p = stpcpy(p, "pause"); - } else if (tinystrcmp(name, "CALL") == 0) { + } else if (strcmp(name, "CALL") == 0) { p = stpcpy(p, "call"); - } else if (tinystrcmp(name, "JMP") == 0) { + } else if (strcmp(name, "JMP") == 0) { p = stpcpy(p, "jmp"); - } else if (tinystrcmp(name, "jcxz") == 0) { + } else if (strcmp(name, "jcxz") == 0) { p = stpcpy(p, kJcxz[Eamode(rde)]); p = DisBranchTaken(d, p); - } else if (tinystrcmp(name, "loop") == 0 || tinystrcmp(name, "loope") == 0 || - tinystrcmp(name, "loopne") == 0) { + } else if (strcmp(name, "loop") == 0 || strcmp(name, "loope") == 0 || + strcmp(name, "loopne") == 0) { p = stpcpy(p, name); if (Eamode(rde) != Mode(rde)) { *p++ = "wl"[Eamode(rde)]; *p = '\0'; } p = DisBranchTaken(d, p); - } else if (tinystrcmp(name, "cwtl") == 0) { + } else if (strcmp(name, "cwtl") == 0) { if (Osz(rde)) name = "cbtw"; if (Rexw(rde)) name = "cltq"; p = stpcpy(p, name); - } else if (tinystrcmp(name, "cltd") == 0) { + } else if (strcmp(name, "cltd") == 0) { if (Osz(rde)) name = "cwtd"; if (Rexw(rde)) name = "cqto"; p = stpcpy(p, name); @@ -132,27 +131,27 @@ static char *DisName(struct Dis *d, char *bp, const char *name, for (np = name; *np && (islower(*np) || isdigit(*np)); ++np) { *p++ = *np; } - if (tinystrcmp(name, "ALU") == 0) { + if (strcmp(name, "ALU") == 0) { p = stpcpy(p, kAluOp[(d->xedd->op.opcode & 070) >> 3]); - } else if (tinystrcmp(name, "ALU2") == 0) { + } else if (strcmp(name, "ALU2") == 0) { p = stpcpy(p, kAluOp[ModrmReg(rde)]); - } else if (tinystrcmp(np, "WLQ") == 0) { + } else if (strcmp(np, "WLQ") == 0) { notbyte = true; wantsuffix = true; - } else if (tinystrcmp(np, "CC") == 0) { + } else if (strcmp(np, "CC") == 0) { p = stpcpy(p, kCc[d->xedd->op.opcode & 15]); p = DisBranchTaken(d, p); - } else if (tinystrcmp(np, "WQ") == 0) { + } else if (strcmp(np, "WQ") == 0) { notbyte = true; notlong = Eamode(rde) != XED_MODE_REAL; wantsuffix = true; - } else if (tinystrcmp(np, "LQ") == 0 || tinystrcmp(np, "WL") == 0) { + } else if (strcmp(np, "LQ") == 0 || strcmp(np, "WL") == 0) { notbyte = true; wantsuffix = true; - } else if (tinystrcmp(np, "SD") == 0) { + } else if (strcmp(np, "SD") == 0) { notbyte = true; wantsuffixsd = true; - } else if (tinystrcmp(np, "ABS") == 0) { + } else if (strcmp(np, "ABS") == 0) { if (Rexw(rde)) p = stpcpy(p, "abs"); } if (wantsuffixsd) { diff --git a/tool/build/lib/disspec.c b/tool/build/lib/disspec.c index 2ded30e30..9addf5dc4 100644 --- a/tool/build/lib/disspec.c +++ b/tool/build/lib/disspec.c @@ -177,10 +177,10 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { RCASE(0x69, "imul %Gvqp Evqp Ivds"); RCASE(0x6A, "pushWQ Ibss"); RCASE(0x6B, "imul %Gvqp Evqp Ibs"); - RCASE(0x6C, "insb Yb %dx"); - RCASE(0x6D, "insWL Yv %dx"); - RCASE(0x6E, "outsb %dx Xb"); - RCASE(0x6F, "outsWL %dx Xv"); + RCASE(0x6C, "insb Yb DX"); + RCASE(0x6D, "insWL Yv DX"); + RCASE(0x6E, "outsb DX Xb"); + RCASE(0x6F, "outsWL DX Xv"); RCASE(0x70 ... 0x7f, "jCC Jbs"); RCASE(0x80, "ALU2 Eb Ib"); RCASE(0x81, "ALU2 Evqp Ivds"); @@ -258,10 +258,10 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { RCASE(0xE9, "jmp Jvds"); RCASE(0xEA, "ljmp Rvds Kvds"); RCASE(0xEB, "jmp Jbs"); - RCASE(0xEC, "in %al %dx"); - RCASE(0xED, "in %eAX %dx"); - RCASE(0xEE, "out %dx %al"); - RCASE(0xEF, "out %dx %eAX"); + RCASE(0xEC, "in %al DX"); + RCASE(0xED, "in %eAX DX"); + RCASE(0xEE, "out DX %al"); + RCASE(0xEF, "out DX %eAX"); RCASE(0xF1, "int1"); RCASE(0xF4, "hlt"); RCASE(0xF5, "cmc"); diff --git a/tool/build/lib/interner.c b/tool/build/lib/interner.c index 109b87cce..ee41819a5 100644 --- a/tool/build/lib/interner.c +++ b/tool/build/lib/interner.c @@ -21,7 +21,7 @@ #include "libc/alg/arraylist2.internal.h" #include "libc/bits/safemacros.internal.h" #include "libc/mem/mem.h" -#include "libc/str/knuthmultiplicativehash.h" +#include "libc/str/knuthmultiplicativehash.internal.h" #include "libc/str/str.h" #include "libc/x/x.h" #include "tool/build/lib/interner.h" diff --git a/tool/build/lz4toasm.c b/tool/build/lz4toasm.c index b6bdfda49..5e248bb64 100644 --- a/tool/build/lz4toasm.c +++ b/tool/build/lz4toasm.c @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) { fprintf(fout, "/\t%s -o %s -s %s %s\n" - ".include \"libc/macros.inc\"\n" + ".include \"libc/macros.internal.inc\"\n" "\n", argv[0], outpath, symbol, lz4path); diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 0e56abdb6..9e4b769b2 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -36,7 +36,7 @@ #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" -#include "libc/str/knuthmultiplicativehash.h" +#include "libc/str/knuthmultiplicativehash.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" diff --git a/tool/build/rollup.c b/tool/build/rollup.c index 951c44ee6..18d110306 100644 --- a/tool/build/rollup.c +++ b/tool/build/rollup.c @@ -26,6 +26,7 @@ #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" @@ -122,7 +123,10 @@ static void Visit(const char *path) { APPENDSTR(path); APPENDSTR(" */\n\n"); APPEND(&visited.p, &visited.i, &visited.n, &path); - CHECK_NE(-1, (fd = open(path, O_RDONLY))); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "error: %s: failed to open\n", path); + exit(1); + } CHECK_NE(-1, fstat(fd, &st)); if (st.st_size) { CHECK_NE(MAP_FAILED, @@ -137,12 +141,12 @@ int main(int argc, char *argv[]) { int i; APPENDSTR("#ifndef COSMOPOLITAN_H_\n"); APPENDSTR("#define COSMOPOLITAN_H_\n"); - APPENDSTR("#define IMAGE_BASE_VIRTUAL "); - AppendInt(IMAGE_BASE_VIRTUAL); - APPENDSTR("\n"); - APPENDSTR("#define IMAGE_BASE_PHYSICAL "); - AppendInt(IMAGE_BASE_PHYSICAL); - APPENDSTR("\n"); + /* APPENDSTR("#define IMAGE_BASE_VIRTUAL "); */ + /* AppendInt(IMAGE_BASE_VIRTUAL); */ + /* APPENDSTR("\n"); */ + /* APPENDSTR("#define IMAGE_BASE_PHYSICAL "); */ + /* AppendInt(IMAGE_BASE_PHYSICAL); */ + /* APPENDSTR("\n"); */ for (i = 1; i < argc; ++i) { Visit(argv[i]); } diff --git a/tool/viz/generatematrix.c b/tool/viz/generatematrix.c index 017468206..0250e4ee9 100644 --- a/tool/viz/generatematrix.c +++ b/tool/viz/generatematrix.c @@ -26,7 +26,7 @@ #include "libc/macros.h" #include "libc/math.h" #include "libc/mem/mem.h" -#include "libc/rand/lcg.h" +#include "libc/rand/lcg.internal.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h"