Auto-generate some documentation

main
Justine Tunney 2020-12-26 02:09:07 -08:00
parent 117d0111ab
commit 13437dd19b
97 changed files with 2033 additions and 661 deletions

View File

@ -303,6 +303,7 @@ COSMOPOLITAN_HEADERS = \
THIRD_PARTY_GDTOA \ THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \ THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \ THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB \
THIRD_PARTY_REGEX THIRD_PARTY_REGEX
o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_OBJS))) o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_OBJS)))
@ -312,6 +313,10 @@ o/cosmopolitan.h: \
$(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS)) $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS))
@ACTION=ROLLUP TARGET=$@ build/do $^ >$@ @ACTION=ROLLUP TARGET=$@ build/do $^ >$@
o/cosmopolitan.html: \
o//third_party/chibicc/chibicc.com.dbg
o//third_party/chibicc/chibicc.com.dbg -J -fno-common -include libc/integral/normalize.inc -o $@ $(filter %.c,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
# UNSPECIFIED PREREQUISITES TUTORIAL # UNSPECIFIED PREREQUISITES TUTORIAL
# #
# A build rule must exist for all files that make needs to consider in # A build rule must exist for all files that make needs to consider in

View File

@ -3,11 +3,7 @@
#include "ape/relocations.h" #include "ape/relocations.h"
#include "libc/macros.h" #include "libc/macros.h"
/** /*
* @fileverview αcτµαlly pδrταblε εxεcµταblε configuration.
*/
/**
* Post-Initialization Read-Only Code Size Threshold. * Post-Initialization Read-Only Code Size Threshold.
* *
* An executable needs to have at least this much code, before the * An executable needs to have at least this much code, before the
@ -22,9 +18,6 @@
#endif #endif
#endif #endif
/**
* PC Standard I/O Configuration.
*/
#ifndef METAL_STDIN #ifndef METAL_STDIN
#define METAL_STDIN COM1 #define METAL_STDIN COM1
#endif #endif
@ -35,11 +28,6 @@
#define METAL_STDERR COM1 #define METAL_STDERR COM1
#endif #endif
/**
* PC Display Configuration (MDA/CGA)
* @see www.lammertbies.nl/comm/info/serial-uart.html
* @see ape/lib/vidya.internal.h
*/
#ifndef VIDYA_MODE #ifndef VIDYA_MODE
#define VIDYA_MODE VIDYA_MODE_CGA #define VIDYA_MODE VIDYA_MODE_CGA
#endif #endif
@ -66,10 +54,6 @@
#define X87_DEFAULT X87_NORMAL #define X87_DEFAULT X87_NORMAL
#endif #endif
/**
* Serial Line Configuration (8250 UART 16550)
* @see ape/lib/uart.h
*/
#ifndef UART_BAUD_RATE #ifndef UART_BAUD_RATE
#define UART_BAUD_RATE 9600 /* bits per second ∈ [50,115200] */ #define UART_BAUD_RATE 9600 /* bits per second ∈ [50,115200] */
#endif #endif
@ -93,9 +77,6 @@
#define UART_CONF_LCR 0b01000011 #define UART_CONF_LCR 0b01000011
#endif #endif
/**
* eXtreme Low Memory.
*/
#define XLM(VAR) (XLM_BASE_REAL + XLM_##VAR) #define XLM(VAR) (XLM_BASE_REAL + XLM_##VAR)
#define XLMV(VAR) (__xlm + XLM_##VAR) #define XLMV(VAR) (__xlm + XLM_##VAR)
#define XLM_BASE_REAL 0x1000 #define XLM_BASE_REAL 0x1000

View File

@ -19,23 +19,23 @@
#define IMAGE_BASE_PHYSICAL 0x100000 #define IMAGE_BASE_PHYSICAL 0x100000
#endif #endif
#ifndef REAL_SCRATCH_AREA
/** /**
* Location of anything goes memory for real mode. * Location of anything goes memory for real mode.
* *
* The MBR won't load program content beyond this address, so we have * The MBR won't load program content beyond this address, so we have
* room for buffers, page tables, etc. before we reach the stack frame. * room for buffers, page tables, etc. before we reach the stack frame.
*/ */
#ifndef REAL_SCRATCH_AREA
#define REAL_SCRATCH_AREA 0x40000 #define REAL_SCRATCH_AREA 0x40000
#endif #endif
#ifndef REAL_STACK_FRAME
/** /**
* Location of real mode 64kb stack frame. * Location of real mode 64kb stack frame.
* *
* This address was chosen because memory beyond 0x80000 can't be * This address was chosen because memory beyond 0x80000 can't be
* accessed safely without consulting e820. * accessed safely without consulting e820.
*/ */
#ifndef REAL_STACK_FRAME
#define REAL_STACK_FRAME 0x70000 #define REAL_STACK_FRAME 0x70000
#endif #endif

View File

@ -21,12 +21,8 @@
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
/* clang-format off */
static void KnuthMorrisPrattInit(m, T, W) static void KnuthMorrisPrattInit(ssize_t m, ssize_t *T, const char *W) {
ssize_t m, T[m + 1];
const char W[m];
{
ssize_t i = 2; ssize_t i = 2;
ssize_t j = 0; ssize_t j = 0;
T[0] = -1; T[0] = -1;
@ -43,10 +39,8 @@ static void KnuthMorrisPrattInit(m, T, W)
T[m] = 0; T[m] = 0;
} }
static size_t KnuthMorrisPratt(m, T, W, n, S) static size_t KnuthMorrisPratt(long m, const long *T, const char *W, long n,
const long n, m, T[m + 1]; const char *S) {
const char W[m], S[n];
{
long i = 0, j = 0; long i = 0, j = 0;
while (i + j < n) { while (i + j < n) {
if (W[i] == S[i + j]) { if (W[i] == S[i + j]) {
@ -60,8 +54,6 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
return j; return j;
} }
/* clang-format on */
/** /**
* Searches for fixed-length substring in memory region. * Searches for fixed-length substring in memory region.
* *

View File

@ -20,6 +20,9 @@
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/macros.h" #include "libc/macros.h"
/**
* Returns absolute value of x.
*/
int(abs)(int x) { int(abs)(int x) {
return ABS(x); return ABS(x);
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
/**
* Reverses bits in 16-bit word.
*/
uint16_t(bitreverse16)(uint16_t x) { uint16_t(bitreverse16)(uint16_t x) {
return kReverseBits[x & 0x00FF] << 8 | kReverseBits[(x & 0xFF00) >> 8]; return kReverseBits[x & 0x00FF] << 8 | kReverseBits[(x & 0xFF00) >> 8];
} }

View File

@ -20,6 +20,9 @@
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/bits/bswap.h" #include "libc/bits/bswap.h"
/**
* Reverses bits in 32-bit word.
*/
uint32_t(bitreverse32)(uint32_t x) { uint32_t(bitreverse32)(uint32_t x) {
x = bswap_32(x); x = bswap_32(x);
x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1); x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1);

View File

@ -20,6 +20,9 @@
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/bits/bswap.h" #include "libc/bits/bswap.h"
/**
* Reverses bits in 64-bit word.
*/
uint64_t bitreverse64(uint64_t x) { uint64_t bitreverse64(uint64_t x) {
x = bswap_64(x); x = bswap_64(x);
x = ((x & 0xaaaaaaaaaaaaaaaa) >> 1) | ((x & 0x5555555555555555) << 1); x = ((x & 0xaaaaaaaaaaaaaaaa) >> 1) | ((x & 0x5555555555555555) << 1);

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
/**
* Reverses bits in 8-bit word.
*/
uint8_t(bitreverse8)(uint8_t x) { uint8_t(bitreverse8)(uint8_t x) {
return kReverseBits[x]; return kReverseBits[x];
} }

View File

@ -17,5 +17,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/bits/safemacros.internal.h"
const char *emptytonull(const char *s) { return s && !*s ? 0 : s; } /**
* Returns string where empty string is made null.
* @see nulltoempty()
*/
const char *(emptytonull)(const char *s) {
return s && !*s ? 0 : s;
}

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
/**
* Returns a or b or aborts if both are null.
*/
const char *(firstnonnull)(const char *a, const char *b) { const char *(firstnonnull)(const char *a, const char *b) {
if (a) return a; if (a) return a;
if (b) return b; if (b) return b;

View File

@ -19,6 +19,11 @@
*/ */
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
/**
* Returns gray code for x.
* @see https://en.wikipedia.org/wiki/Gray_code
* @see ungray()
*/
uint32_t gray(uint32_t x) { uint32_t gray(uint32_t x) {
return x ^ (x >> 1); return x ^ (x >> 1);
} }

View File

@ -21,6 +21,7 @@
/** /**
* Counts number of different bits. * Counts number of different bits.
* @see https://en.wikipedia.org/wiki/Hamming_code
*/ */
unsigned long hamming(unsigned long x, unsigned long y) { unsigned long hamming(unsigned long x, unsigned long y) {
return popcnt(x ^ y); return popcnt(x ^ y);

View File

@ -36,7 +36,8 @@ static axdx_t RotateQuadrant(long n, long y, long x, long ry, long rx) {
/** /**
* Generates Hilbert space-filling curve. * Generates Hilbert space-filling curve.
* *
* @see morton() * @see https://en.wikipedia.org/wiki/Hilbert_curve
* @see unhilbert()
*/ */
long hilbert(long n, long y, long x) { long hilbert(long n, long y, long x) {
axdx_t m; axdx_t m;
@ -56,7 +57,8 @@ long hilbert(long n, long y, long x) {
/** /**
* Decodes Hilbert space-filling curve. * Decodes Hilbert space-filling curve.
* *
* @see unmorton() * @see https://en.wikipedia.org/wiki/Hilbert_curve
* @see hilbert()
*/ */
axdx_t unhilbert(long n, long i) { axdx_t unhilbert(long n, long i) {
axdx_t m; axdx_t m;

View File

@ -18,4 +18,9 @@
02110-1301 USA 02110-1301 USA
*/ */
bool isempty(const char *s) { return !s || !*s; } /**
* Returns true if s is empty string or null.
*/
bool isempty(const char *s) {
return !s || !*s;
}

View File

@ -21,6 +21,8 @@
/** /**
* Interleaves bits. * Interleaves bits.
* @see https://en.wikipedia.org/wiki/Z-order_curve
* @see unmorton()
*/ */
unsigned long(morton)(unsigned long y, unsigned long x) { unsigned long(morton)(unsigned long y, unsigned long x) {
x = (x | x << 020) & 0x0000FFFF0000FFFF; x = (x | x << 020) & 0x0000FFFF0000FFFF;

View File

@ -17,5 +17,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/bits/safemacros.internal.h"
const char *nulltoempty(const char *s) { return s ? s : ""; } /**
* Returns 𝑠 converting null to empty string.
* @see emptytonull()
*/
const char *(nulltoempty)(const char *s) {
return s ? s : "";
}

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/bits/popcnt.h" #include "libc/bits/popcnt.h"
/**
* Returns number of bits set in integer.
*/
uint64_t(popcnt)(uint64_t x) { uint64_t(popcnt)(uint64_t x) {
x = x - ((x >> 1) & 0x5555555555555555); x = x - ((x >> 1) & 0x5555555555555555);
x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333); x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333);

View File

@ -3,13 +3,13 @@
#include "libc/macros.h" #include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
#if !defined(__GNUC__) || defined(__STRICT_ANSI__)
#define pushpop(x) (x)
#else
/** /**
* PushPop * PushPop
* An elegant weapon for a more civilized age. * An elegant weapon for a more civilized age.
*/ */
#if !defined(__GNUC__) || defined(__STRICT_ANSI__)
#define pushpop(x) (x)
#else
#define pushpop(x) \ #define pushpop(x) \
({ \ ({ \
typeof(x) Popped; \ typeof(x) Popped; \

View File

@ -19,4 +19,9 @@
*/ */
#include "libc/macros.h" #include "libc/macros.h"
long(rounddown)(long w, long k) { return ROUNDDOWN(w, k); } /**
* Rounds down 𝑤 to next two power 𝑘.
*/
long(rounddown)(long w, long k) {
return ROUNDDOWN(w, k);
}

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/macros.h" #include "libc/macros.h"
/**
* Rounds up 𝑤 to next two power 𝑘.
*/
long(roundup)(long w, long k) { long(roundup)(long w, long k) {
return ROUNDUP(w, k); return ROUNDUP(w, k);
} }

View File

@ -22,7 +22,7 @@
/** /**
* Returns 𝑥 rounded up to next two power and log'd. * Returns 𝑥 rounded up to next two power and log'd.
* @see roundup2pow * @see roundup2pow()
*/ */
unsigned long roundup2log(unsigned long x) { unsigned long roundup2log(unsigned long x) {
return x > 1 ? (bsrl(x - 1) + 1) : x ? 1 : 0; return x > 1 ? (bsrl(x - 1) + 1) : x ? 1 : 0;

View File

@ -24,7 +24,7 @@
* Returns 𝑥 rounded up to next two power. * Returns 𝑥 rounded up to next two power.
* *
* @define (𝑥>02^logx, x=00, 𝑇) * @define (𝑥>02^logx, x=00, 𝑇)
* @see rounddown2pow)() * @see rounddown2pow()
*/ */
unsigned long roundup2pow(unsigned long x) { unsigned long roundup2pow(unsigned long x) {
return x > 1 ? 1ul << (bsrl(x - 1) + 1) : x ? 1 : 0; return x > 1 ? 1ul << (bsrl(x - 1) + 1) : x ? 1 : 0;

View File

@ -19,6 +19,11 @@
*/ */
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
/**
* Decodes gray code.
* @see https://en.wikipedia.org/wiki/Gray_code
* @see gray()
*/
uint32_t ungray(uint32_t x) { uint32_t ungray(uint32_t x) {
x ^= x >> 16; x ^= x >> 16;
x ^= x >> 8; x ^= x >> 8;

View File

@ -35,6 +35,7 @@ static unsigned long GetOddBits(unsigned long x) {
* @param 𝑖 is interleaved index * @param 𝑖 is interleaved index
* @return deinterleaved coordinate {ax := 𝑦, dx := 𝑥} * @return deinterleaved coordinate {ax := 𝑦, dx := 𝑥}
* @see en.wikipedia.org/wiki/Z-order_curve * @see en.wikipedia.org/wiki/Z-order_curve
* @see morton()
*/ */
axdx_t(unmorton)(unsigned long i) { axdx_t(unmorton)(unsigned long i) {
return (axdx_t){GetOddBits(i >> 1), GetOddBits(i)}; return (axdx_t){GetOddBits(i >> 1), GetOddBits(i)};

View File

@ -25,15 +25,15 @@
/** /**
* Changes file permissions via open()'d file descriptor, e.g.: * Changes file permissions via open()'d file descriptor, e.g.:
* *
* CHECK_NE(-1, chmod("foo/bar.txt", 0644)); * CHECK_NE(-1, chmod("foo/bar.txt", 0644));
* CHECK_NE(-1, chmod("o/default/program.com", 0755)); * CHECK_NE(-1, chmod("o/default/program.com", 0755));
* CHECK_NE(-1, chmod("privatefolder/", 0700)); * CHECK_NE(-1, chmod("privatefolder/", 0700));
* *
* The esoteric bits generally available on System V are: * The esoteric bits generally available on System V are:
* *
* CHECK_NE(-1, chmod("/opt/", 01000)); // sticky bit * CHECK_NE(-1, chmod("/opt/", 01000)); // sticky bit
* CHECK_NE(-1, chmod("/usr/bin/sudo", 04755)); // setuid bit * CHECK_NE(-1, chmod("/usr/bin/sudo", 04755)); // setuid bit
* CHECK_NE(-1, chmod("/usr/bin/wall", 02755)); // setgid bit * CHECK_NE(-1, chmod("/usr/bin/wall", 02755)); // setgid bit
* *
* This works on Windows NT if you ignore the error ;-) * This works on Windows NT if you ignore the error ;-)
* *

View File

@ -24,7 +24,7 @@
/** /**
* Does things with file descriptor, via re-imagined hourglass api, e.g. * Does things with file descriptor, via re-imagined hourglass api, e.g.
* *
* CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC)); * CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC));
* *
* @param cmd can be F_{GET,SET}{FD,FL}, etc. * @param cmd can be F_{GET,SET}{FD,FL}, etc.
* @param arg can be FD_CLOEXEC, etc. depending * @param arg can be FD_CLOEXEC, etc. depending

View File

@ -118,13 +118,13 @@ static textwindows noinline struct dirent *readdir$nt(DIR *dir) {
/** /**
* Opens directory, e.g. * Opens directory, e.g.
* *
* DIR *d; * DIR *d;
* struct dirent *e; * struct dirent *e;
* CHECK((d = opendir(path))); * CHECK((d = opendir(path)));
* while ((e = readdir(d))) { * while ((e = readdir(d))) {
* printf("%s/%s\n", path, e->d_name); * printf("%s/%s\n", path, e->d_name);
* } * }
* LOGIFNEG1(closedir(d)); * LOGIFNEG1(closedir(d));
* *
* @returns newly allocated DIR object, or NULL w/ errno * @returns newly allocated DIR object, or NULL w/ errno
* @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM * @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM

View File

@ -27,18 +27,18 @@
/** /**
* Launches program, e.g. * Launches program, e.g.
* *
* char buf[2]; * char buf[2];
* int ws, pid, fds[3] = {-1, -1, STDERR_FILENO}; * int ws, pid, fds[3] = {-1, -1, STDERR_FILENO};
* CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"), * CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"),
* (char *const[]){"ssh", hostname, "cat", NULL}, * (char *const[]){"ssh", hostname, "cat", 0},
* environ))); * environ)));
* CHECK_EQ(+2, write(fds[0], "hi", 2)); * CHECK_EQ(+2, write(fds[0], "hi", 2));
* CHECK_NE(-1, close(fds[0])); * CHECK_NE(-1, close(fds[0]));
* CHECK_EQ(+2, read(fds[1], buf, 2))); * CHECK_EQ(+2, read(fds[1], buf, 2)));
* CHECK_NE(-1, close(fds[1])); * CHECK_NE(-1, close(fds[1]));
* CHECK_EQ(+0, memcmp(buf, "hi", 2))); * CHECK_EQ(+0, memcmp(buf, "hi", 2)));
* CHECK_NE(-1, waitpid(pid, &ws, 0)); * CHECK_NE(-1, waitpid(pid, &ws, 0));
* CHECK_EQ(+0, WEXITSTATUS(ws)); * CHECK_EQ(+0, WEXITSTATUS(ws));
* *
* @param stdiofds may optionally be passed to customize standard i/o * @param stdiofds may optionally be passed to customize standard i/o
* @param stdiofds[𝑖] may be -1 to receive a pipe() fd * @param stdiofds[𝑖] may be -1 to receive a pipe() fd
@ -49,6 +49,7 @@
* @param envp[0,n-2] specifies "foo=bar" environment variables * @param envp[0,n-2] specifies "foo=bar" environment variables
* @param envp[n-1] is NULL * @param envp[n-1] is NULL
* @return pid of child, or -1 w/ errno * @return pid of child, or -1 w/ errno
* @deprecated just use vfork() and execve()
*/ */
int spawnve(unsigned flags, int stdiofds[3], const char *program, int spawnve(unsigned flags, int stdiofds[3], const char *program,
char *const argv[], char *const envp[]) { char *const argv[], char *const envp[]) {

View File

@ -27,7 +27,7 @@
* @asyncsignalsafe * @asyncsignalsafe
*/ */
bool32 isatty(int fd) { bool32 isatty(int fd) {
char buf[sizeof(uint16_t) * 4] forcealign(2); _Alignas(short) char buf[sizeof(uint16_t) * 4];
if (!IsWindows()) { if (!IsWindows()) {
return ioctl$sysv(fd, TIOCGWINSZ, &buf) != -1; return ioctl$sysv(fd, TIOCGWINSZ, &buf) != -1;
} else { } else {

View File

@ -21,38 +21,37 @@
#include "libc/calls/kntprioritycombos.internal.h" #include "libc/calls/kntprioritycombos.internal.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/nexgen32e/ffs.h"
#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/threadpriority.h" #include "libc/nt/enum/threadpriority.h"
#define FFS(x) __builtin_ffs(x)
const struct NtPriorityCombo kNtPriorityCombos[] = { const struct NtPriorityCombo kNtPriorityCombos[] = {
{-20, FFS(kNtHighPriorityClass), kNtThreadPriorityHighest, 15}, {-20, ffs(kNtHighPriorityClass), kNtThreadPriorityHighest, 15},
{-18, FFS(kNtHighPriorityClass), kNtThreadPriorityTimeCritical, 15}, {-18, ffs(kNtHighPriorityClass), kNtThreadPriorityTimeCritical, 15},
{-17, FFS(kNtNormalPriorityClass), kNtThreadPriorityTimeCritical, 15}, {-17, ffs(kNtNormalPriorityClass), kNtThreadPriorityTimeCritical, 15},
{-15, FFS(kNtIdlePriorityClass), kNtThreadPriorityTimeCritical, 15}, {-15, ffs(kNtIdlePriorityClass), kNtThreadPriorityTimeCritical, 15},
{-13, FFS(kNtHighPriorityClass), kNtThreadPriorityAboveNormal, 14}, {-13, ffs(kNtHighPriorityClass), kNtThreadPriorityAboveNormal, 14},
{-11, FFS(kNtHighPriorityClass), kNtThreadPriorityNormal, 13}, {-11, ffs(kNtHighPriorityClass), kNtThreadPriorityNormal, 13},
{-9, FFS(kNtHighPriorityClass), kNtThreadPriorityBelowNormal, 12}, {-9, ffs(kNtHighPriorityClass), kNtThreadPriorityBelowNormal, 12},
{-7, FFS(kNtNormalPriorityClass), kNtThreadPriorityHighest, 11}, {-7, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 11},
{-5, FFS(kNtHighPriorityClass), kNtThreadPriorityLowest, 11}, {-5, ffs(kNtHighPriorityClass), kNtThreadPriorityLowest, 11},
{-3, FFS(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 10}, {-3, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 10},
{-1, FFS(kNtNormalPriorityClass), kNtThreadPriorityHighest, 9}, {-1, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 9},
{0, FFS(kNtNormalPriorityClass), kNtThreadPriorityNormal, 9}, {0, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 9},
{1, FFS(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 8}, {1, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 8},
{2, FFS(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 8}, {2, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 8},
{3, FFS(kNtNormalPriorityClass), kNtThreadPriorityNormal, 7}, {3, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 7},
{4, FFS(kNtNormalPriorityClass), kNtThreadPriorityLowest, 7}, {4, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 7},
{5, FFS(kNtIdlePriorityClass), kNtThreadPriorityHighest, 6}, {5, ffs(kNtIdlePriorityClass), kNtThreadPriorityHighest, 6},
{6, FFS(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 6}, {6, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 6},
{7, FFS(kNtIdlePriorityClass), kNtThreadPriorityAboveNormal, 5}, {7, ffs(kNtIdlePriorityClass), kNtThreadPriorityAboveNormal, 5},
{9, FFS(kNtNormalPriorityClass), kNtThreadPriorityLowest, 5}, {9, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 5},
{11, FFS(kNtIdlePriorityClass), kNtThreadPriorityNormal, 4}, {11, ffs(kNtIdlePriorityClass), kNtThreadPriorityNormal, 4},
{13, FFS(kNtIdlePriorityClass), kNtThreadPriorityBelowNormal, 3}, {13, ffs(kNtIdlePriorityClass), kNtThreadPriorityBelowNormal, 3},
{15, FFS(kNtIdlePriorityClass), kNtThreadPriorityLowest, 2}, {15, ffs(kNtIdlePriorityClass), kNtThreadPriorityLowest, 2},
{17, FFS(kNtHighPriorityClass), kNtThreadPriorityIdle, 1}, {17, ffs(kNtHighPriorityClass), kNtThreadPriorityIdle, 1},
{18, FFS(kNtNormalPriorityClass), kNtThreadPriorityIdle, 1}, {18, ffs(kNtNormalPriorityClass), kNtThreadPriorityIdle, 1},
{19, FFS(kNtIdlePriorityClass), kNtThreadPriorityIdle, 1}, {19, ffs(kNtIdlePriorityClass), kNtThreadPriorityIdle, 1},
}; };
const unsigned kNtPriorityCombosLen = ARRAYLEN(kNtPriorityCombos); const unsigned kNtPriorityCombosLen = ARRAYLEN(kNtPriorityCombos);

View File

@ -29,11 +29,11 @@
* how long each sequence is, so that each read consumes a single thing * how long each sequence is, so that each read consumes a single thing
* from the underlying file descriptor, e.g. * from the underlying file descriptor, e.g.
* *
* "a" ALFA * "a" ALFA
* "\316\261" ALPHA * "\316\261" ALPHA
* "\033[A" CURSOR UP * "\033[A" CURSOR UP
* "\033[38;5;202m" ORANGERED * "\033[38;5;202m" ORANGERED
* "\eOP" PF1 * "\eOP" PF1
* *
* This routine generalizes to ascii, utf-8, chorded modifier keys, * This routine generalizes to ascii, utf-8, chorded modifier keys,
* function keys, color codes, c0/c1 control codes, cursor movement, * function keys, color codes, c0/c1 control codes, cursor movement,
@ -48,9 +48,9 @@
* tokenized as a single read. Lastly note, this function has limited * tokenized as a single read. Lastly note, this function has limited
* support for UNICODE representations of C0/C1 control codes, e.g. * support for UNICODE representations of C0/C1 control codes, e.g.
* *
* "\000" NUL * "\000" NUL
* "\300\200" NUL * "\300\200" NUL
* "\302\233A" CURSOR UP * "\302\233A" CURSOR UP
* *
* @param buf is guaranteed to receive a NUL terminator if size>0 * @param buf is guaranteed to receive a NUL terminator if size>0
* @return number of bytes read (helps differentiate "\0" vs. "") * @return number of bytes read (helps differentiate "\0" vs. "")

View File

@ -28,27 +28,28 @@
* *
* Raise SIGALRM every 1.5s: * Raise SIGALRM every 1.5s:
* *
* CHECK_NE(-1, sigaction(SIGALRM, * CHECK_NE(-1, sigaction(SIGALRM,
* &(struct sigaction){.sa_sigaction = missingno}, * &(struct sigaction){.sa_sigaction = missingno},
* NULL)); * NULL));
* CHECK_NE(-1, setitimer(ITIMER_REAL, * CHECK_NE(-1, setitimer(ITIMER_REAL,
* &(const struct itimerval){{1, 500000}, {1, 500000}}, * &(const struct itimerval){{1, 500000},
* NULL)); * {1, 500000}},
* NULL));
* *
* Set single-shot 50ms timer callback to interrupt laggy connect(): * Set single-shot 50ms timer callback to interrupt laggy connect():
* *
* CHECK_NE(-1, sigaction(SIGALRM, * CHECK_NE(-1, sigaction(SIGALRM,
* &(struct sigaction){.sa_sigaction = missingno, * &(struct sigaction){.sa_sigaction = missingno,
* .sa_flags = SA_RESETHAND}, * .sa_flags = SA_RESETHAND},
* NULL)); * NULL));
* CHECK_NE(-1, setitimer(ITIMER_REAL, * CHECK_NE(-1, setitimer(ITIMER_REAL,
* &(const struct itimerval){{0, 0}, {0, 50000}}, * &(const struct itimerval){{0, 0}, {0, 50000}},
* NULL)); * NULL));
* if (connect(...) == -1 && errno == EINTR) { ... } * if (connect(...) == -1 && errno == EINTR) { ... }
* *
* Disarm timer: * Disarm timer:
* *
* CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL)); * CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL));
* *
* Be sure to check for EINTR on your i/o calls, for best low latency. * Be sure to check for EINTR on your i/o calls, for best low latency.
* *

View File

@ -112,10 +112,10 @@ static void sigaction$native2cosmo(union metasigaction *sa) {
/** /**
* Installs handler for kernel interrupt, e.g.: * Installs handler for kernel interrupt, e.g.:
* *
* void GotCtrlC(int sig, siginfo_t *si, ucontext_t *ctx); * void GotCtrlC(int sig, siginfo_t *si, ucontext_t *ctx);
* struct sigaction sa = {.sa_sigaction = GotCtrlC, * struct sigaction sa = {.sa_sigaction = GotCtrlC,
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO}; * .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL)); * CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
* *
* @see xsigaction() for a much better api * @see xsigaction() for a much better api
* @asyncsignalsafe * @asyncsignalsafe

View File

@ -26,9 +26,9 @@
/** /**
* Changes program signal blocking state, e.g.: * Changes program signal blocking state, e.g.:
* *
* sigset_t oldmask; * sigset_t oldmask;
* sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask); * sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
* sigprocmask(SIG_SETMASK, &oldmask, NULL); * sigprocmask(SIG_SETMASK, &oldmask, NULL);
* *
* @param how can be SIG_BLOCK (U), SIG_UNBLOCK (/), SIG_SETMASK (=) * @param how can be SIG_BLOCK (U), SIG_UNBLOCK (/), SIG_SETMASK (=)
* @param set is the new mask content (optional) * @param set is the new mask content (optional)

View File

@ -5,6 +5,7 @@
cosmopolitan § autotune » dead code elimination cosmopolitan § autotune » dead code elimination
*/ */
#ifndef SUPPORT_VECTOR
/** /**
* Supported Platforms Tuning Knob (Runtime & Compile-Time) * Supported Platforms Tuning Knob (Runtime & Compile-Time)
* *
@ -17,7 +18,6 @@
* offer marginal improvements in terms of code size and performance, at * offer marginal improvements in terms of code size and performance, at
* the cost of portability. * the cost of portability.
*/ */
#ifndef SUPPORT_VECTOR
#define SUPPORT_VECTOR 0b11111111 #define SUPPORT_VECTOR 0b11111111
#endif #endif
#define LINUX 1 #define LINUX 1

View File

@ -10,7 +10,7 @@ typedef struct Elf64_Rela {
* ELF64_R_TYPE(r_info) R_X86_64_{64,PC32,GOTPCRELX,...} * ELF64_R_TYPE(r_info) R_X86_64_{64,PC32,GOTPCRELX,...}
* ELF64_R_INFO(sym, type) r_info * ELF64_R_INFO(sym, type) r_info
*/ */
/*u64*/ Elf64_Xword r_info; /** @see ELF64_R_{SYM,SIZE,INFO} */ /*u64*/ Elf64_Xword r_info; /* ELF64_R_{SYM,SIZE,INFO} */
/*i64*/ Elf64_Sxword r_addend; /*i64*/ Elf64_Sxword r_addend;
} Elf64_Rela; } Elf64_Rela;

View File

@ -26,23 +26,23 @@
* *
* Cosmopolitan displays RADIX-256 numbers using these digits: * Cosmopolitan displays RADIX-256 numbers using these digits:
* *
* 0123456789abcdef * 0123456789abcdef
* 0  * 0 
* 1§ * 1§
* 2 !"#$%&'()*+,-./ * 2 !"#$%&'()*+,-./
* 30123456789:;<=>? * 30123456789:;<=>?
* 4@ABCDEFGHIJKLMNO * 4@ABCDEFGHIJKLMNO
* 5PQRSTUVWXYZ[\]^_ * 5PQRSTUVWXYZ[\]^_
* 6`abcdefghijklmno * 6`abcdefghijklmno
* 7pqrstuvwxyz{|}~ * 7pqrstuvwxyz{|}~
* 8ÇüéâäàåçêëèïîìÄÅ * 8ÇüéâäàåçêëèïîìÄÅ
* 9ÉæÆôöòûùÿÖÜ¢£¥ƒ * 9ÉæÆôöòûùÿÖÜ¢£¥ƒ
* aáíóúñѪº¿¬½¼¡«» * aáíóúñѪº¿¬½¼¡«»
* b * b
* c * c
* d * d
* eαßΓπΣσμτΦΘΩδφε * eαßΓπΣσμτΦΘΩδφε
* f±÷°·²λ * f±÷°·²λ
* *
* IBM designed these glyphs for the PC to map onto the display bytes at * IBM designed these glyphs for the PC to map onto the display bytes at
* (char *)0xb8000. Because IBM authorized buyers of its PCs to inspect * (char *)0xb8000. Because IBM authorized buyers of its PCs to inspect

View File

@ -18,10 +18,6 @@
#define __far #define __far
#endif #endif
/**
* Gets type of expression.
* @see autotype() which is a better alternative for certain use cases
*/
#if !defined(__GNUC__) && __cplusplus + 0 >= 201103L #if !defined(__GNUC__) && __cplusplus + 0 >= 201103L
#define typeof(x) decltype(x) #define typeof(x) decltype(x)
#elif (defined(__STRICT_ANSI__) || !defined(__GNUC__)) && \ #elif (defined(__STRICT_ANSI__) || !defined(__GNUC__)) && \
@ -143,35 +139,10 @@ typedef __UINT32_TYPE__ uint32_t;
typedef __INT64_TYPE__ int64_t; typedef __INT64_TYPE__ int64_t;
typedef __UINT64_TYPE__ uint64_t; typedef __UINT64_TYPE__ uint64_t;
/**
* AX:DX register pair.
*
* Every ABI we support permits functions to return two machine words.
* Normally it's best to define a one-off struct. Sometimes we don't
* want the boilerplate.
*
* @see System V Application Binary Interface NexGen32e Architecture
* Processor Supplement, Version 1.0, December 5th, 2018
* @see agner.org/optimize/calling_conventions.pdf (chapter 6)
* @see LISP primitives CONS[CAR,CDR] w/ IBM 704 naming
* @see int128_t
*/
typedef struct { typedef struct {
intptr_t ax, dx; intptr_t ax, dx;
} axdx_t; } axdx_t;
/*
* GCC, Clang, and System V ABI all incorrectly define intmax_t.
*
* [intmax_t] designates a signed integer type capable of
* representing any value of any signed integer type.
* ──Quoth ISO/IEC 9899:201x 7.20.1.5
*
* This surprising contradiction is most likely due to Linux distro
* practices of using dynamic shared objects which needs to change.
*
* http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2303.pdf
*/
#ifdef __SIZEOF_INTMAX__ #ifdef __SIZEOF_INTMAX__
#undef __SIZEOF_INTMAX__ #undef __SIZEOF_INTMAX__
#endif #endif
@ -210,11 +181,6 @@ typedef uint64_t uintmax_t;
#define mallocesque reallocesque returnspointerwithnoaliases #define mallocesque reallocesque returnspointerwithnoaliases
#define interruptfn nocallersavedregisters forcealignargpointer #define interruptfn nocallersavedregisters forcealignargpointer
/**
* Declares combinator, i.e. never reads/writes global memory.
* Thus enabling LICM, CSE, DCE, etc. optimizations.
* @see nosideeffect
*/
#ifndef pureconst #ifndef pureconst
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#define pureconst __attribute__((__const__)) #define pureconst __attribute__((__const__))
@ -223,9 +189,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Aligns automatic or static variable.
*/
#ifndef forcealign #ifndef forcealign
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#define forcealign(bytes) __attribute__((__aligned__(bytes))) #define forcealign(bytes) __attribute__((__aligned__(bytes)))
@ -234,21 +197,12 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Disables alignment.
* @param opt_bytes defaults to __BIGGEST_ALIGNMENT__
* @see nosideeffect
*/
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#define thatispacked __attribute__((__packed__)) #define thatispacked __attribute__((__packed__))
#else #else
#define thatispacked #define thatispacked
#endif #endif
/**
* Declares prototype as using well-known format string DSL.
* Thereby allowing compiler to identify certain bugs.
*/
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#define printfesque(n) __attribute__((__format__(__gnu_printf__, n, n + 1))) #define printfesque(n) __attribute__((__format__(__gnu_printf__, n, n + 1)))
#define scanfesque(n) __attribute__((__format__(__gnu_scanf__, n, n + 1))) #define scanfesque(n) __attribute__((__format__(__gnu_scanf__, n, n + 1)))
@ -297,11 +251,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares prototype as never mutating global memory.
* Thus enabling CSE, DCE, LICM [clang-only?], etc. optimizations.
* @see pureconst
*/
#ifndef nosideeffect #ifndef nosideeffect
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__pure__) || \ (__has_attribute(__pure__) || \
@ -332,18 +281,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Makes function behave as much like macro as possible, meaning:
*
* 1. always inlined, i.e. even with -fno-inline
* 2. unlinkable, i.e. elf data is not generated
* 3. steppable, i.e. dwarf data is generated
* 4. unprofilable
* 5. unhookable
*
* @note consider static or writing a macro
* @see externinline
*/
#ifndef forceinline #ifndef forceinline
#ifdef __cplusplus #ifdef __cplusplus
#define forceinline inline #define forceinline inline
@ -372,19 +309,6 @@ typedef uint64_t uintmax_t;
#endif /* __cplusplus */ #endif /* __cplusplus */
#endif /* forceinline */ #endif /* forceinline */
/**
* Permits untyped or punned memory manipulation w/o asm.
*
* “The fundamental problem is that it is not possible to write real
* programs using the X3J11 definition of C. The committee has created
* an unreal language that no one can or will actually use. While the
* problems of `const' may owe to careless drafting of the
* specification, `noalias' is an altogether mistaken notion, and must
* not survive. ──Dennis Ritchie in 1988-03-20.
*
* @see asm(), memcpy(), memset(), read32be(), etc.
* @see unsigned char
*/
#ifndef mayalias #ifndef mayalias
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__may_alias__) || \ (__has_attribute(__may_alias__) || \
@ -395,11 +319,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares prototype as returning freeable resource.
* Compilation will fail if caller ignores return value.
* @see gc(), free(), close(), etc.
*/
#ifndef nodiscard #ifndef nodiscard
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 304 || \ ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 304 || \
@ -410,10 +329,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares variadic function as needing NULL sentinel argument.
* @see execl() for example
*/
#ifndef nullterminated #ifndef nullterminated
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__sentinel__) || __GNUC__ >= 4) (__has_attribute(__sentinel__) || __GNUC__ >= 4)
@ -448,10 +363,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Relocates function to .text.unlikely section of binary.
* @note can be used to minimize page-faults and improve locality
*/
#ifndef relegated #ifndef relegated
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__cold__) || \ (__has_attribute(__cold__) || \
@ -470,11 +381,6 @@ typedef uint64_t uintmax_t;
#define warnifused(s) #define warnifused(s)
#endif #endif
/**
* Relocates function to .text.hot section of binary.
* @note can be used to minimize page-faults w/ improved locality
* @note most appropriately automated by profile-guided opts
*/
#ifndef firstclass #ifndef firstclass
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__hot__) || \ (__has_attribute(__hot__) || \
@ -485,12 +391,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares all or specific parameters as never receiving NULL.
*
* This can be checked at both compile-time (only for constexprs) and
* runtime too (only in MODE=dbg mode) by synthetic Ubsan code.
*/
#ifndef paramsnonnull #ifndef paramsnonnull
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__nonnull__) || \ (__has_attribute(__nonnull__) || \
@ -501,22 +401,12 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares array argument w/ minimum size contract, e.g.
*
* int foo(int bar[hasatleast 2]) { ... }
*/
#if __STDC_VERSION__ + 0 >= 199901L #if __STDC_VERSION__ + 0 >= 199901L
#define hasatleast static #define hasatleast static
#else #else
#define hasatleast #define hasatleast
#endif #endif
/**
* Qualifies char pointer so it's treated like every other type.
*
* int foo(int bar[hasatleast 2]) { ... }
*/
#if __STDC_VERSION__ + 0 < 199901L && !defined(restrict) #if __STDC_VERSION__ + 0 < 199901L && !defined(restrict)
#if !defined(__STRICT_ANSI__) && !defined(__cplusplus) && \ #if !defined(__STRICT_ANSI__) && !defined(__cplusplus) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 301 || defined(_MSC_VER)) ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 301 || defined(_MSC_VER))
@ -527,10 +417,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares prototype that can't mutate caller's static variables.
* @note consider more .c files or declare in function
*/
#ifndef nocallback #ifndef nocallback
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__leaf__) || \ (__has_attribute(__leaf__) || \
@ -554,11 +440,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Asks compiler to not optimize function definition.
*
* @todo this is dangerous delete?
*/
#ifndef nooptimize #ifndef nooptimize
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \ #if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -572,13 +453,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Asks compiler to generate as little code as possible for function.
*
* This does the same thing as relegated, but without relocation.
*
* @todo this is dangerous delete?
*/
#ifndef optimizesize #ifndef optimizesize
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \ #if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -592,15 +466,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Asks compiler to always heavily optimize function.
*
* This keyword provides an alternative to build flag tuning, in cases
* where the compiler is reluctant to vectorize mathematical code that's
* written in standards-compliant C rather than GCC extensions.
*
* @todo this is dangerous delete?
*/
#ifndef optimizespeed #ifndef optimizespeed
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \ ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -611,9 +476,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Declares prototype that behaves similar to setjmp() or vfork().
*/
#ifndef returnstwice #ifndef returnstwice
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__returns_twice__) || \ (__has_attribute(__returns_twice__) || \
@ -624,10 +486,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Asks compiler to not emit DWARF assembly for function.
* @see artificial
*/
#ifndef nodebuginfo #ifndef nodebuginfo
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__nodebug__) || defined(__llvm__)) (__has_attribute(__nodebug__) || defined(__llvm__))
@ -637,10 +495,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Associates debug information with call site.
* @see nodebuginfo
*/
#ifndef artificial #ifndef artificial
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__artificial__) || \ (__has_attribute(__artificial__) || \
@ -651,11 +505,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Defines function as specially compiled for newer cpu model.
* @see -ftree-vectorize and consider assembly
* @see libc/dce.h
*/
#ifndef microarchitecture #ifndef microarchitecture
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__target__) || \ (__has_attribute(__target__) || \
@ -666,10 +515,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Compiles function multiple times for different cpu models.
* @see libc/dce.h
*/
#ifndef targetclones #ifndef targetclones
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__target_clones__) || __GNUC__ >= 6) (__has_attribute(__target_clones__) || __GNUC__ >= 6)
@ -679,10 +524,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Defines function with prologue that fixes misaligned stack.
* @see nocallersavedregisters and consider assembly
*/
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 408 || \ #if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 408 || \
__has_attribute(__force_align_arg_pointer__) __has_attribute(__force_align_arg_pointer__)
#define forcealignargpointer __attribute__((__force_align_arg_pointer__)) #define forcealignargpointer __attribute__((__force_align_arg_pointer__))
@ -690,12 +531,6 @@ typedef uint64_t uintmax_t;
#define forcealignargpointer "need modern compiler" #define forcealignargpointer "need modern compiler"
#endif #endif
/**
* Declares prototype as never returning NULL.
*
* This is checked at compile-time for constexprs. It'll be checked at
* runtime too by synthetic code, only in MODE=dbg mode.
*/
#ifndef returnsnonnull #ifndef returnsnonnull
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__returns_nonnull__) || \ (__has_attribute(__returns_nonnull__) || \
@ -706,13 +541,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Attests return value is aligned.
*
* @param (alignment)
* @param (alignment, misalignment)
* @see attributeallocalign(), returnspointerwithnoaliases, mallocesque
*/
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__assume_aligned__) || \ (__has_attribute(__assume_aligned__) || \
(__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 409) (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 409)
@ -721,10 +549,6 @@ typedef uint64_t uintmax_t;
#define returnsaligned(x) #define returnsaligned(x)
#endif #endif
/**
* Declares prototype as behaving similar to malloc().
* @see attributeallocsize(), attributeallocalign()
*/
#ifndef returnspointerwithnoaliases #ifndef returnspointerwithnoaliases
#if !defined(__STRICT_ANSI__) && \ #if !defined(__STRICT_ANSI__) && \
(__has_attribute(__malloc__) || \ (__has_attribute(__malloc__) || \
@ -757,20 +581,6 @@ typedef uint64_t uintmax_t;
#endif #endif
#endif #endif
/**
* Defines variable as having same type as right-hand side.
*
* This enables safe, backwards-compatible, non-explosive macros, e.g.:
*
* #define bar(FOO) \
* ({ \
* autotype(FOO) Foo = (FOO); \
* Foo + Foo * 2; \
* })
*
* @param x must be identical to rhs
* @note typeof goes back to gcc 2.x
*/
#if __cplusplus + 0 >= 201103L #if __cplusplus + 0 >= 201103L
#define autotype(x) auto #define autotype(x) auto
#elif ((__has_builtin(auto_type) || defined(__llvm__) || \ #elif ((__has_builtin(auto_type) || defined(__llvm__) || \
@ -781,27 +591,16 @@ typedef uint64_t uintmax_t;
#define autotype(x) typeof(x) #define autotype(x) typeof(x)
#endif #endif
/**
* Defines interrupt handler that can call non-interrupt functions.
* @see forcealignargpointer, -mgeneral-regs-only and consider assembly
*/
#if __GNUC__ >= 7 || __has_attribute(__no_caller_saved_registers__) #if __GNUC__ >= 7 || __has_attribute(__no_caller_saved_registers__)
#define nocallersavedregisters __attribute__((__no_caller_saved_registers__)) #define nocallersavedregisters __attribute__((__no_caller_saved_registers__))
#else #else
#define nocallersavedregisters "need modern compiler" #define nocallersavedregisters "need modern compiler"
#endif #endif
/**
* Attests that execution of statement is impossible.
*/
#ifndef unreachable #ifndef unreachable
#define unreachable __builtin_unreachable() #define unreachable __builtin_unreachable()
#endif #endif
/**
* Statement that does nothing.
* @note can help avoid drama w/ linters, warnings, formatters, etc.
*/
#define donothing \ #define donothing \
do { \ do { \
} while (0) } while (0)
@ -857,9 +656,6 @@ typedef uint64_t uintmax_t;
#define initarray _Section(".init_array") #define initarray _Section(".init_array")
#endif #endif
/**
* Systemic suppressions.
*/
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#if defined(__GNUC__) || defined(__llvm__) #if defined(__GNUC__) || defined(__llvm__)
#pragma GCC diagnostic ignored "-Wsign-compare" /* lint needs to change */ #pragma GCC diagnostic ignored "-Wsign-compare" /* lint needs to change */
@ -908,17 +704,6 @@ typedef uint64_t uintmax_t;
#endif /* !GCC && LLVM */ #endif /* !GCC && LLVM */
#endif /* ANSI */ #endif /* ANSI */
/**
* Elevate warnings of material consequence.
*
* These aren't stylistic in nature; but are perfectly fine to disable,
* assuming we're ok with the compiler simply generating a runtime crash
* instead. Otherwise what usually happens with these is that a weakness
* is introduced, important optimizations can't be performed; or worst
* of all: the code will need patching if ported to a toy or any machine
* designed by an engineer who hadn't understood John von Neumann at the
* time, e.g. 1's complement, big endian, under 32bit word size, etc.
*/
#ifndef __W__ #ifndef __W__
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#if defined(__GNUC__) || defined(__llvm__) #if defined(__GNUC__) || defined(__llvm__)
@ -965,10 +750,6 @@ typedef uint64_t uintmax_t;
#endif /* ANSI */ #endif /* ANSI */
#endif /* -w */ #endif /* -w */
/**
* Sets manual breakpoint.
* @see showcrashreports() for auto gdb attach
*/
#define DebugBreak() asm("int3") #define DebugBreak() asm("int3")
#define VEIL(CONSTRAINT, EXPRESSION) \ #define VEIL(CONSTRAINT, EXPRESSION) \
@ -991,10 +772,6 @@ typedef uint64_t uintmax_t;
0; \ 0; \
}) })
/**
* Pulls another module, by symbol, into linkage.
* @note nop is discarded by ape/ape.lds
*/
#define YOINK(SYMBOL) \ #define YOINK(SYMBOL) \
do { \ do { \
_Static_assert(!__builtin_types_compatible_p(typeof(SYMBOL), char[]), \ _Static_assert(!__builtin_types_compatible_p(typeof(SYMBOL), char[]), \
@ -1006,29 +783,15 @@ typedef uint64_t uintmax_t;
: "X"(SYMBOL)); \ : "X"(SYMBOL)); \
} while (0) } while (0)
/**
* Pulls another module into linkage from top-level scope.
* @note nop is discarded by ape/ape.lds
*/
#define STATIC_YOINK(SYMBOLSTR) \ #define STATIC_YOINK(SYMBOLSTR) \
asm(".pushsection .yoink\n\tnop\t\"" SYMBOLSTR "\"\n\t.popsection") asm(".pushsection .yoink\n\tnop\t\"" SYMBOLSTR "\"\n\t.popsection")
/**
* Pulls source file into ZIP portion of binary.
* @see build/rules.mk which defines the wildcard build rule %.zip.o
*/
#if !defined(IM_FEELING_NAUGHTY) && !defined(__STRICT_ANSI__) #if !defined(IM_FEELING_NAUGHTY) && !defined(__STRICT_ANSI__)
#define STATIC_YOINK_SOURCE(PATH) STATIC_YOINK(PATH) #define STATIC_YOINK_SOURCE(PATH) STATIC_YOINK(PATH)
#else #else
#define STATIC_YOINK_SOURCE(PATH) #define STATIC_YOINK_SOURCE(PATH)
#endif #endif
/**
* Pulls source of object being compiled into zip.
* @note automates most compliance with gpl terms
* @see libc/zipos/zipcentraldir.S
* @see ape/ape.lds
*/
#ifdef __BASE_FILE__ #ifdef __BASE_FILE__
STATIC_YOINK_SOURCE(__BASE_FILE__); STATIC_YOINK_SOURCE(__BASE_FILE__);
#endif #endif

View File

@ -67,4 +67,4 @@
#define __INT_FAST64_TYPE__ __INT64_TYPE__ #define __INT_FAST64_TYPE__ __INT64_TYPE__
#define __UINT_FAST64_TYPE__ __UINT64_TYPE__ #define __UINT_FAST64_TYPE__ __UINT64_TYPE__
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif

View File

@ -1,12 +1,3 @@
/**
* @fileoverview Cosmopolitan Preprocessor / Language Normalization.
*
* This is our lowest-level header file. You don't need to include it,
* since we require that compilers be configured to do so automatically,
* and the -include flag is the safest bet. Further note polyfills here
* shouldn't be taken as indicators of intent to support.
*/
#define __COSMOPOLITAN__ 1 #define __COSMOPOLITAN__ 1
#ifndef __COUNTER__ #ifndef __COUNTER__

View File

@ -289,11 +289,6 @@ void sincosl(long double, long double *, long double *);
cosmopolitan § mathematics » x87 cosmopolitan § mathematics » x87
*/ */
#if 0
/**
* Asks FPU to push well-known numbers to its own stack.
*/
#endif
#define fldz() __X87_CONST(fldz, 0x0p+0) #define fldz() __X87_CONST(fldz, 0x0p+0)
#define fld1() __X87_CONST(fld1, 0x8p-3) #define fld1() __X87_CONST(fld1, 0x8p-3)
#define fldpi() __X87_CONST(fldpi, M_PI) #define fldpi() __X87_CONST(fldpi, M_PI)

View File

@ -30,7 +30,7 @@
* @see xasprintf() for a better API * @see xasprintf() for a better API
*/ */
int(vasprintf)(char **strp, const char *fmt, va_list va) { int(vasprintf)(char **strp, const char *fmt, va_list va) {
/** /*
* This implementation guarantees the smallest possible allocation, * This implementation guarantees the smallest possible allocation,
* using an optimistic approach w/o changing asymptotic complexity. * using an optimistic approach w/o changing asymptotic complexity.
*/ */

View File

@ -38,8 +38,8 @@
/ @asyncsignalsafe / @asyncsignalsafe
ffs: .leafprologue ffs: .leafprologue
.profilable .profilable
bsf %edi,%eax
or $-1,%edx or $-1,%edx
bsf %edi,%eax
cmovz %edx,%eax cmovz %edx,%eax
inc %eax inc %eax
.leafepilogue .leafepilogue

View File

@ -3,9 +3,29 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
/*
* BIT SCANNING 101
* ctz(𝑥) 31^clz(𝑥) clz(𝑥)
* uint32 𝑥 bsf(𝑥) tzcnt(𝑥) ffs(𝑥) bsr(𝑥) lzcnt(𝑥)
* 0x00000000 wut 32 0 wut 32
* 0x00000001 0 0 1 0 31
* 0x80000001 0 0 1 31 0
* 0x80000000 31 31 32 31 0
* 0x00000010 4 4 5 4 27
* 0x08000010 4 4 5 27 4
* 0x08000000 27 27 28 27 4
* 0xffffffff 0 0 1 31 0
*/
int ffs(int) pureconst; int ffs(int) pureconst;
int ffsl(long int) pureconst; int ffsl(long) pureconst;
int ffsll(long long int) pureconst; int ffsll(long long) pureconst;
#ifdef __GNUC__
#define ffs(u) __builtin_ffs(u)
#define ffsl(u) __builtin_ffsl(u)
#define ffsll(u) __builtin_ffsll(u)
#endif
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -38,8 +38,8 @@
/ @asyncsignalsafe / @asyncsignalsafe
ffsl: .leafprologue ffsl: .leafprologue
.profilable .profilable
bsf %rdi,%rax
or $-1,%edx or $-1,%edx
bsf %rdi,%rax
cmovz %edx,%eax cmovz %edx,%eax
inc %eax inc %eax
.leafepilogue .leafepilogue

View File

@ -29,13 +29,6 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
/**
* Globally precomputed x86 CPUID values.
*
* @note Referencing Is Initialization (RII)
* @note Protected with PIRO
* @see X86_HAVE()
*/
extern const unsigned kCpuids[KCPUIDS_LEN][4]; extern const unsigned kCpuids[KCPUIDS_LEN][4];
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_

View File

@ -2,6 +2,9 @@
#define COSMOPOLITAN_LIBC_NT_THUNK_MSABI_H_ #define COSMOPOLITAN_LIBC_NT_THUNK_MSABI_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
#if !defined(__STRICT_ANSI__) && \
(__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \
(__has_attribute(__ms_abi__) || defined(__llvm__)))
/** /**
* Defines function as using Microsoft x64 calling convention. * Defines function as using Microsoft x64 calling convention.
* *
@ -9,13 +12,10 @@
* generate code that calls MS ABI functions directly, without needing * generate code that calls MS ABI functions directly, without needing
* to jump through the assembly thunks. * to jump through the assembly thunks.
*/ */
#if !defined(__STRICT_ANSI__) && \
(__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \
(__has_attribute(__ms_abi__) || defined(__llvm__)))
#define __msabi __attribute__((__ms_abi__)) #define __msabi __attribute__((__ms_abi__))
#endif #endif
/** /*
* Returns true if header should provide MS-ABI overrides. * Returns true if header should provide MS-ABI overrides.
*/ */
#ifndef ShouldUseMsabiAttribute #ifndef ShouldUseMsabiAttribute

View File

@ -35,7 +35,7 @@ nodebuginfo uint32_t(rand32)(void) {
} else { } else {
devrand(&res, sizeof(res)); devrand(&res, sizeof(res));
} }
hidden extern uint32_t g_rando32; extern uint32_t g_rando32 hidden;
res ^= MarsagliaXorshift32(&g_rando32); res ^= MarsagliaXorshift32(&g_rando32);
} }
return res; return res;

View File

@ -42,9 +42,9 @@
/** /**
* Beseeches system for page-table entries. * Beseeches system for page-table entries.
* *
* char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, * char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
* MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); * MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
* munmap(p, 65536); * munmap(p, 65536);
* *
* @param addr optionally requests a particular virtual base address, * @param addr optionally requests a particular virtual base address,
* which needs to be 64kb aligned if passed (for NT compatibility) * which needs to be 64kb aligned if passed (for NT compatibility)

View File

@ -157,29 +157,29 @@ static textwindows wontreturn void WinMainNew(void) {
* The Cosmopolitan Runtime provides the following services, which aim * The Cosmopolitan Runtime provides the following services, which aim
* to bring Windows NT behavior closer in harmony with System Five: * to bring Windows NT behavior closer in harmony with System Five:
* *
* 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10. * 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10.
* *
* 2. Command line arguments are passed as a blob of UTF-16 text. We * 2. Command line arguments are passed as a blob of UTF-16 text. We
* chop them up into an char *argv[] UTF-8 data structure, in * chop them up into an char *argv[] UTF-8 data structure, in
* accordance with the DOS conventions for argument quoting. * accordance with the DOS conventions for argument quoting.
* *
* 3. Environment variables are passed to us as a sorted UTF-16 double * 3. Environment variables are passed to us as a sorted UTF-16 double
* NUL terminated list. We translate this to char ** using UTF-8. * NUL terminated list. We translate this to char ** using UTF-8.
* *
* 4. Allocates new stack at a high address. NT likes to choose a * 4. Allocates new stack at a high address. NT likes to choose a
* stack address that's beneath the program image. We want to be * stack address that's beneath the program image. We want to be
* able to assume that stack addresses are located at higher * able to assume that stack addresses are located at higher
* addresses than heap and program memory. * addresses than heap and program memory.
* *
* 5. Windows users are afraid of "drive-by downloads" where someone * 5. Windows users are afraid of "drive-by downloads" where someone
* might accidentally an evil DLL to their Downloads folder which * might accidentally an evil DLL to their Downloads folder which
* then overrides the behavior of a legitimate EXE being run from * then overrides the behavior of a legitimate EXE being run from
* the downloads folder. Since we don't even use dynamic linking, * the downloads folder. Since we don't even use dynamic linking,
* we've cargo culted some API calls, that may harden against it. * we've cargo culted some API calls, that may harden against it.
* *
* 6. Finally, we need fork. Microsoft designed Windows to prevent us * 6. Finally, we need fork. Microsoft designed Windows to prevent us
* from having fork() so we pass pipe handles in an environment * from having fork() so we pass pipe handles in an environment
* variable literally copy all the memory. * variable literally copy all the memory.
* *
* @param hInstance call GetModuleHandle(NULL) from main if you need it * @param hInstance call GetModuleHandle(NULL) from main if you need it
*/ */

View File

@ -22,7 +22,11 @@
STATIC_YOINK("_init_g_stderr"); STATIC_YOINK("_init_g_stderr");
/**
* Pointer to standard error stream.
*/
FILE *stderr; FILE *stderr;
hidden FILE g_stderr; hidden FILE g_stderr;
hidden unsigned char g_stderr_buf[BUFSIZ] forcealign(PAGESIZE); hidden unsigned char g_stderr_buf[BUFSIZ] forcealign(PAGESIZE);

View File

@ -22,7 +22,11 @@
STATIC_YOINK("_init_g_stdin"); STATIC_YOINK("_init_g_stdin");
/**
* Pointer to standard input stream.
*/
FILE *stdin; FILE *stdin;
hidden FILE g_stdin; hidden FILE g_stdin;
hidden unsigned char g_stdin_buf[BUFSIZ] forcealign(PAGESIZE); hidden unsigned char g_stdin_buf[BUFSIZ] forcealign(PAGESIZE);

View File

@ -25,7 +25,11 @@
STATIC_YOINK("_init_g_stdout"); STATIC_YOINK("_init_g_stdout");
/**
* Pointer to standard output stream.
*/
FILE *stdout; FILE *stdout;
hidden FILE g_stdout; hidden FILE g_stdout;
hidden unsigned char g_stdout_buf[BUFSIZ] forcealign(PAGESIZE); hidden unsigned char g_stdout_buf[BUFSIZ] forcealign(PAGESIZE);

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is lower, alpha, or digit.
*/
int isalnum(int c) { int isalnum(int c) {
return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') ||
('a' <= c && c <= 'z'); ('a' <= c && c <= 'z');

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is upper or lower.
*/
int isalpha(int c) { int isalpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is ascii.
*/
int isascii(int c) { int isascii(int c) {
return 0x00 <= c && c <= 0x7F; return 0x00 <= c && c <= 0x7F;
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is space or tab.
*/
int isblank(int c) { int isblank(int c) {
return c == ' ' || c == '\t'; return c == ' ' || c == '\t';
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is C0 ASCII control code or DEL.
*/
int iscntrl(int c) { int iscntrl(int c) {
return (0x00 <= c && c <= 0x1F) || c == 0x7F; return (0x00 <= c && c <= 0x1F) || c == 0x7F;
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is decimal digit.
*/
int isdigit(int c) { int isdigit(int c) {
return '0' <= c && c <= '9'; return '0' <= c && c <= '9';
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is lowercase alpha ascii character.
*/
int islower(int c) { int islower(int c) {
return 'a' <= c && c <= 'z'; return 'a' <= c && c <= 'z';
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns true if c is space, \t, \r, \n, \f, or \v.
*/
int isspace(int c) { int isspace(int c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' ||
c == '\v'; c == '\v';

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if c is uppercase alpha ascii character.
*/
int isupper(int c) { int isupper(int c) {
return 'A' <= c && c <= 'Z'; return 'A' <= c && c <= 'Z';
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns nonzero if wc is C0 or C1 control code.
*/
int iswcntrl(wint_t wc) { int iswcntrl(wint_t wc) {
return (0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F); return (0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F);
} }

View File

@ -19,6 +19,9 @@
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
/**
* Returns true if c is hexadecimal digit.
*/
int isxdigit(int c) { int isxdigit(int c) {
return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') ||
('a' <= c && c <= 'f'); ('a' <= c && c <= 'f');

View File

@ -28,17 +28,17 @@
* *
* For example, strictly: * For example, strictly:
* *
* char buf[16]; * char buf[16];
* CHECK_NOTNULL(memccpy(buf, s, '\0', sizeof(buf))); * CHECK_NOTNULL(memccpy(buf, s, '\0', sizeof(buf)));
* *
* Or unstrictly: * Or unstrictly:
* *
* if (!memccpy(buf, s, '\0', sizeof(buf))) strcpy(buf, "?"); * if (!memccpy(buf, s, '\0', sizeof(buf))) strcpy(buf, "?");
* *
* Are usually more sensible than the following: * Are usually more sensible than the following:
* *
* char cstrbuf[16]; * char cstrbuf[16];
* snprintf(cstrbuf, sizeof(cstrbuf), "%s", CSTR); * snprintf(cstrbuf, sizeof(cstrbuf), "%s", CSTR);
* *
* @return 𝑑 + idx(𝑐) + 1, or NULL if 𝑐 𝑠 * @return 𝑑 + idx(𝑐) + 1, or NULL if 𝑐 𝑠
* @note 𝑑 and 𝑠 can't overlap * @note 𝑑 and 𝑠 can't overlap

View File

@ -23,6 +23,7 @@
* Prepares static search buffer. * Prepares static search buffer.
* *
* 1. If SRC is too long, it's truncated and *not* NUL-terminated. * 1. If SRC is too long, it's truncated and *not* NUL-terminated.
*
* 2. If SRC is too short, the remainder is zero-filled. * 2. If SRC is too short, the remainder is zero-filled.
* *
* Please note this function isn't designed to prevent untrustworthy * Please note this function isn't designed to prevent untrustworthy

View File

@ -23,6 +23,7 @@
* Prepares static search buffer. * Prepares static search buffer.
* *
* 1. If SRC is too long, it's truncated and *not* NUL-terminated. * 1. If SRC is too long, it's truncated and *not* NUL-terminated.
*
* 2. If SRC is too short, the remainder is zero-filled. * 2. If SRC is too short, the remainder is zero-filled.
* *
* Please note this function isn't designed to prevent untrustworthy * Please note this function isn't designed to prevent untrustworthy

View File

@ -46,7 +46,7 @@ wontreturn void testlib_abort(void) {
*/ */
testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, testonly void testlib_runtestcases(testfn_t *start, testfn_t *end,
testfn_t warmup) { testfn_t warmup) {
/** /*
* getpid() calls are inserted to help visually see tests in traces * getpid() calls are inserted to help visually see tests in traces
* which can be performed on Linux, FreeBSD, OpenBSD, and XNU: * which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
* *

View File

@ -370,13 +370,13 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format,
/** /**
* Converts time to string, e.g. * Converts time to string, e.g.
* *
* char b[64]; * char b[64];
* int64_t sec; * int64_t sec;
* struct tm tm; * struct tm tm;
* time(&sec); * time(&sec);
* localtime_r(&sec, &tm); * localtime_r(&sec, &tm);
* strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601 * strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601
* strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123 * strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123
* *
* @return bytes copied excluding nul, or 0 on error * @return bytes copied excluding nul, or 0 on error
*/ */

View File

@ -25,7 +25,9 @@
* Returns monospace display width of UTF-8 string. * Returns monospace display width of UTF-8 string.
* *
* - Control codes are discounted * - Control codes are discounted
*
* - ANSI escape sequences are discounted * - ANSI escape sequences are discounted
*
* - East asian glyphs, emoji, etc. count as two * - East asian glyphs, emoji, etc. count as two
* *
* @param s is NUL-terminated string * @param s is NUL-terminated string

View File

@ -27,10 +27,10 @@
/** /**
* Decodes human-readable CP437 glyphs into binary, e.g. * Decodes human-readable CP437 glyphs into binary, e.g.
* *
* char binged[5]; * char binged[5];
* char golden[5] = "\0\1\2\3\4"; * char golden[5] = "\0\1\2\3\4";
* unbingbuf(binged, sizeof(binged), u" ☺☻♥♦", -1); * unbingbuf(binged, sizeof(binged), u" ☺☻♥♦", -1);
* CHECK_EQ(0, memcmp(binged, golden, 5)); * CHECK_EQ(0, memcmp(binged, golden, 5));
* *
* @param buf is caller owned * @param buf is caller owned
* @param size is byte length of buf * @param size is byte length of buf

View File

@ -24,7 +24,7 @@
/** /**
* Decodes human-readable CP437 glyphs into binary, e.g. * Decodes human-readable CP437 glyphs into binary, e.g.
* *
* CHECK_EQ(0, memcmp(gc(unbingstr(u" ☺☻♥♦")), "\0\1\2\3\4", 5)); * CHECK_EQ(0, memcmp(gc(unbingstr(u" ☺☻♥♦")), "\0\1\2\3\4", 5));
* *
* @param buf is caller owned * @param buf is caller owned
* @param size is byte length of buf * @param size is byte length of buf

View File

@ -24,12 +24,12 @@
/** /**
* Joins paths, e.g. * Joins paths, e.g.
* *
* "a" + "b" "a/b" * "a" + "b" "a/b"
* "a/" + "b" "a/b" * "a/" + "b" "a/b"
* "a" + "b/" "a/b/" * "a" + "b/" "a/b/"
* "a" + "/b" "/b" * "a" + "/b" "/b"
* "." + "b" "b" * "." + "b" "b"
* "" + "b" "b" * "" + "b" "b"
* *
* @return newly allocated string of resulting path * @return newly allocated string of resulting path
*/ */

View File

@ -26,8 +26,8 @@
/** /**
* Installs handler for kernel interrupt, e.g.: * Installs handler for kernel interrupt, e.g.:
* *
* onctrlc(sig) { exit(128+sig); } * onctrlc(sig) { exit(128+sig); }
* CHECK_NE(-1, xsigaction(SIGINT, onctrlc, SA_RESETHAND, 0, 0)); * CHECK_NE(-1, xsigaction(SIGINT, onctrlc, SA_RESETHAND, 0, 0));
* *
* @param sig can be SIGINT, SIGTERM, etc. * @param sig can be SIGINT, SIGTERM, etc.
* @param handler is SIG_DFL, SIG_IGN, or a pointer to a 0arity3 * @param handler is SIG_DFL, SIG_IGN, or a pointer to a 0arity3

View File

@ -0,0 +1,45 @@
/*-*- 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/nexgen32e/ffs.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/testlib/testlib.h"
TEST(ffs, test) {
EXPECT_EQ(__builtin_ffs(0), (ffs)(0));
EXPECT_EQ(__builtin_ffs(1), (ffs)(1));
EXPECT_EQ(__builtin_ffs(0x00100000), (ffs)(0x00100000));
EXPECT_EQ(__builtin_ffs(-1), (ffs)(-1));
}
TEST(ffsl, test) {
EXPECT_EQ(__builtin_ffsl(0), (ffsl)(0));
EXPECT_EQ(__builtin_ffsl(1), (ffsl)(1));
EXPECT_EQ(__builtin_ffsl(0x00100000), (ffsl)(0x00100000));
EXPECT_EQ(__builtin_ffsl(0x0010000000000000), (ffsl)(0x0010000000000000));
EXPECT_EQ(__builtin_ffsl(-1), (ffsl)(-1));
}
TEST(ffsll, test) {
EXPECT_EQ(__builtin_ffsll(0), (ffsll)(0));
EXPECT_EQ(__builtin_ffsll(1), (ffsll)(1));
EXPECT_EQ(__builtin_ffsll(0x00100000), (ffsll)(0x00100000));
EXPECT_EQ(__builtin_ffsll(0x0010000000000000), (ffsll)(0x0010000000000000));
EXPECT_EQ(__builtin_ffsll(-1), (ffsll)(-1));
}

View File

@ -0,0 +1,230 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
#include "tool/build/lib/javadown.h"
TEST(ParseJavadown, testOneLiner) {
const char *comment = "/** Parses javadown. */";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ("Parses javadown.", jd->title);
EXPECT_STREQ("", jd->text);
EXPECT_EQ(0, jd->tags.n);
FreeJavadown(jd);
}
TEST(ParseJavadown, testShortAndTerse) {
const char *comment = "\
/**\n\
* Parses javadown.\n\
* @see love\n\
*/";
const char *title = "Parses javadown and that is the bottom line.";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ("Parses javadown.", jd->title);
EXPECT_STREQ("", jd->text);
EXPECT_EQ(1, jd->tags.n);
EXPECT_STREQ("see", jd->tags.p[0].tag);
EXPECT_STREQ("love", jd->tags.p[0].text);
FreeJavadown(jd);
}
TEST(ParseJavadown, testBlankLineOmitted) {
const char *comment = "\
/**\n\
* Parses javadown.\n\
*\n\
* Description.\n\
* @see love\n\
*/";
const char *title = "Parses javadown and that is the bottom line.";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ("Parses javadown.", jd->title);
EXPECT_STREQ("Description.", jd->text);
EXPECT_EQ(1, jd->tags.n);
EXPECT_STREQ("see", jd->tags.p[0].tag);
EXPECT_STREQ("love", jd->tags.p[0].text);
FreeJavadown(jd);
}
TEST(ParseJavadown, testContentInterpretation) {
const char *comment = "\
/**\n\
* Parses javadown \n\
* and that is the bottom line.\n\
*\n\
* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
* eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
* ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
* aliquip ex ea commodo consequat.\n\
*\n\
* Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
* dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
* non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
* laborum\n\
*\n\
* @param data should point to text inside the slash star markers\n\
* lorem ipsum dolla dollaz yo\n\
* @param size is length of data in bytes\n\
* @return object that should be passed to FreeJavadown()\n\
* @asyncsignalsafe\n\
*/";
const char *title = "Parses javadown and that is the bottom line.";
const char *description = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
aliquip ex ea commodo consequat.\n\
\n\
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
laborum\n";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ(title, jd->title);
EXPECT_STREQ(description, jd->text);
EXPECT_EQ(4, jd->tags.n);
EXPECT_STREQ("param", jd->tags.p[0].tag);
EXPECT_STREQ("data should point to text inside the slash star markers\n"
"lorem ipsum dolla dollaz yo",
jd->tags.p[0].text);
EXPECT_STREQ("param", jd->tags.p[1].tag);
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
EXPECT_STREQ("return", jd->tags.p[2].tag);
EXPECT_STREQ("object that should be passed to FreeJavadown()",
jd->tags.p[2].text);
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
FreeJavadown(jd);
}
TEST(ParseJavadown, testTabFormatting1) {
const char *comment = "\
/**\n\
\tParses javadown \n\
\tand that is the bottom line.\n\
\n\
\tLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
\taliquip ex ea commodo consequat.\n\
\n\
\tDuis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
\tdolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
\tnon proident, sunt in culpa qui officia deserunt mollit anim id est\n\
\tlaborum\n\
\n\
\t@param data should point to text inside the slash star markers\n\
\t\tlorem ipsum dolla dollaz yo\n\
\t@param size is length of data in bytes\n\
\t@return object that should be passed to FreeJavadown()\n\
\t@asyncsignalsafe\n\
*/";
const char *title = "Parses javadown and that is the bottom line.";
const char *description = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
aliquip ex ea commodo consequat.\n\
\n\
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
laborum\n";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ(title, jd->title);
EXPECT_STREQ(description, jd->text);
EXPECT_EQ(4, jd->tags.n);
EXPECT_STREQ("param", jd->tags.p[0].tag);
EXPECT_STREQ("data should point to text inside the slash star markers\n"
"lorem ipsum dolla dollaz yo",
jd->tags.p[0].text);
EXPECT_STREQ("param", jd->tags.p[1].tag);
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
EXPECT_STREQ("return", jd->tags.p[2].tag);
EXPECT_STREQ("object that should be passed to FreeJavadown()",
jd->tags.p[2].text);
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
FreeJavadown(jd);
}
TEST(ParseJavadown, testTabFormatting2) {
const char *comment = "\
/**\n\
\tParses javadown \n\
\tand that is the bottom line.\n\
\t\n\
\tLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
\t\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
\t\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
\t\taliquip ex ea commodo consequat.\n\
\t\n\
\tDuis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
\tdolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
\tnon proident, sunt in culpa qui officia deserunt mollit anim id est\n\
\tlaborum\n\
\t\n\
\t@param data should point to text inside the slash star markers\n\
\t\tlorem ipsum dolla dollaz yo\n\
\t@param size is length of data in bytes\n\
\t@return object that should be passed to FreeJavadown()\n\
\t@asyncsignalsafe\n\
*/";
const char *title = "Parses javadown and that is the bottom line.";
const char *description = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
\taliquip ex ea commodo consequat.\n\
\n\
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
laborum\n";
struct Javadown *jd;
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
EXPECT_FALSE(jd->isfileoverview);
EXPECT_STREQ(title, jd->title);
EXPECT_STREQ(description, jd->text);
EXPECT_EQ(4, jd->tags.n);
EXPECT_STREQ("param", jd->tags.p[0].tag);
EXPECT_STREQ("data should point to text inside the slash star markers\n"
"lorem ipsum dolla dollaz yo",
jd->tags.p[0].text);
EXPECT_STREQ("param", jd->tags.p[1].tag);
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
EXPECT_STREQ("return", jd->tags.p[2].tag);
EXPECT_STREQ("object that should be passed to FreeJavadown()",
jd->tags.p[2].text);
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
FreeJavadown(jd);
}

View File

@ -16,18 +16,22 @@ local enhancements
- support __builtin_constant_p, __builtin_likely, etc. - support __builtin_constant_p, __builtin_likely, etc.
- support __builtin_isunordered, __builtin_islessgreater, etc. - support __builtin_isunordered, __builtin_islessgreater, etc.
- support __builtin_ctz, __builtin_bswap, __builtin_popcount, etc. - support __builtin_ctz, __builtin_bswap, __builtin_popcount, etc.
- support __force_align_arg_pointer__, __no_caller_saved_registers__, etc.
- support __constructor__, __section__, __cold__, -ffunction-sections, etc. - support __constructor__, __section__, __cold__, -ffunction-sections, etc.
- support building -x assembler-with-cpp a.k.a. .S files - support building -x assembler-with-cpp a.k.a. .S files
- support profiling w/ -mcount / -mfentry / -mnop-mcount - support profiling w/ -mcount / -mfentry / -mnop-mcount
- improve error messages to trace macro expansions - improve error messages to trace macro expansions
- reduce #lines of generated assembly by a third - reduce #lines of generated assembly by a third
- reduce #bytes of generated binary by a third - reduce #bytes of generated binary by a third
- report divide errors in constexprs
local bug fixes local bug fixes
- allow casted values to be lvalues - allow casted values to be lvalues
- permit remainder operator in constexprs
- permit parentheses around string-initializer - permit parentheses around string-initializer
- fix 64-bit bug in generated code for struct bitfields - fix 64-bit bug in generated code for struct bitfields
- fix struct_designator() so it won't crash on anonymous union members
- fix bug where last statement in statement expression couldn't have label - fix bug where last statement in statement expression couldn't have label
- print_tokens (chibicc -E) now works in the case of adjacent string literals - print_tokens (chibicc -E) now works in the case of adjacent string literals
- make enums unsigned (like gcc) so we don't suffer the msvc enum bitfield bug - make enums unsigned (like gcc) so we don't suffer the msvc enum bitfield bug
@ -35,6 +39,8 @@ local bug fixes
local changes local changes
- use tabs in generated output - use tabs in generated output
- parse javadoc-style markdown comments
- don't fold backslash newline in comments
- generated code no longer assumes red zone - generated code no longer assumes red zone
- emit .size directives for function definitions - emit .size directives for function definitions
- use fisttp long double conversions if built w/ -msse3 - use fisttp long double conversions if built w/ -msse3

View File

@ -177,8 +177,8 @@ struct As {
struct Sauces { struct Sauces {
unsigned long n; unsigned long n;
struct Sauce { struct Sauce {
int path; // strings unsigned path; // strings
int line; // 1-indexed unsigned line; // 1-indexed
} * p; } * p;
} sauces; } sauces;
struct Things { struct Things {
@ -192,14 +192,14 @@ struct As {
TT_FORWARD, TT_FORWARD,
TT_BACKWARD, TT_BACKWARD,
} t : 4; } t : 4;
int s : 28; // sauces unsigned s : 28; // sauces
int i; // identity,ints,floats,slices unsigned i; // identity,ints,floats,slices
} * p; } * p;
} things; } things;
struct Sections { struct Sections {
unsigned long n; unsigned long n;
struct Section { struct Section {
int name; // strings unsigned name; // strings
int flags; int flags;
int type; int type;
int align; int align;
@ -210,11 +210,11 @@ struct As {
unsigned long n; unsigned long n;
struct Symbol { struct Symbol {
bool isused; bool isused;
int name; // slices unsigned char stb; // STB_*
int section; // sections unsigned char stv; // STV_*
int stb; // STB_* unsigned char type; // STT_*
int stv; // STV_* unsigned name; // slices
int type; // STT_* unsigned section; // sections
long offset; long offset;
long size; long size;
struct ElfWriterSymRef ref; struct ElfWriterSymRef ref;
@ -223,25 +223,25 @@ struct As {
struct HashTable { struct HashTable {
unsigned i, n; unsigned i, n;
struct HashEntry { struct HashEntry {
int h; unsigned h;
int i; unsigned i;
} * p; } * p;
} symbolindex; } symbolindex;
struct Labels { struct Labels {
unsigned long n; unsigned long n;
struct Label { struct Label {
int id; unsigned id;
int tok; // things unsigned tok; // things
int symbol; // symbols unsigned symbol; // symbols
} * p; } * p;
} labels; } labels;
struct Relas { struct Relas {
unsigned long n; unsigned long n;
struct Rela { struct Rela {
bool isdead; bool isdead;
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...} int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
int expr; // exprs unsigned expr; // exprs
int section; // sections unsigned section; // sections
long offset; long offset;
long addend; long addend;
} * p; } * p;
@ -251,7 +251,7 @@ struct As {
struct Expr { struct Expr {
enum ExprKind { enum ExprKind {
EX_INT, // integer EX_INT, // integer
EX_SYM, // slice, forward, backward, then symbol EX_SYM, // things (then symbols after eval)
EX_NEG, // unary - EX_NEG, // unary -
EX_NOT, // unary ! EX_NOT, // unary !
EX_BITNOT, // unary ~ EX_BITNOT, // unary ~
@ -276,7 +276,7 @@ struct As {
EM_DTPOFF, EM_DTPOFF,
EM_TPOFF, EM_TPOFF,
} em; } em;
int tok; unsigned tok;
int lhs; int lhs;
int rhs; int rhs;
long x; long x;
@ -456,7 +456,7 @@ static bool EndsWith(const char *s, const char *suffix) {
n = strlen(s); n = strlen(s);
m = strlen(suffix); m = strlen(suffix);
if (m > n) return false; if (m > n) return false;
return memcmp(s + n - m, suffix, m) == 0; return !memcmp(s + n - m, suffix, m);
} }
static char *Format(const char *fmt, ...) { static char *Format(const char *fmt, ...) {
@ -1192,21 +1192,21 @@ static int ParseMul(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '*')) { if (IsPunct(a, i, '*')) {
y = ParseUnary(a, &i, i + 1); y = ParseUnary(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x *= a->exprs.p[y].x; a->exprs.p[x].x *= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_MUL, x, y); x = NewBinary(a, EX_MUL, x, y);
} }
} else if (IsPunct(a, i, '/')) { } else if (IsPunct(a, i, '/')) {
y = ParseUnary(a, &i, i + 1); y = ParseUnary(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x /= a->exprs.p[y].x; a->exprs.p[x].x /= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_DIV, x, y); x = NewBinary(a, EX_DIV, x, y);
} }
} else if (IsPunct(a, i, '%')) { } else if (IsPunct(a, i, '%')) {
y = ParseUnary(a, &i, i + 1); y = ParseUnary(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x %= a->exprs.p[y].x; a->exprs.p[x].x %= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_REM, x, y); x = NewBinary(a, EX_REM, x, y);
@ -1225,14 +1225,14 @@ static int ParseAdd(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '+')) { if (IsPunct(a, i, '+')) {
y = ParseMul(a, &i, i + 1); y = ParseMul(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x += a->exprs.p[y].x; a->exprs.p[x].x += a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_ADD, x, y); x = NewBinary(a, EX_ADD, x, y);
} }
} else if (IsPunct(a, i, '-')) { } else if (IsPunct(a, i, '-')) {
y = ParseMul(a, &i, i + 1); y = ParseMul(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x -= a->exprs.p[y].x; a->exprs.p[x].x -= a->exprs.p[y].x;
} else if (a->exprs.p[y].kind == EX_INT) { } else if (a->exprs.p[y].kind == EX_INT) {
a->exprs.p[y].x = -a->exprs.p[y].x; a->exprs.p[y].x = -a->exprs.p[y].x;
@ -1254,14 +1254,14 @@ static int ParseShift(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '<' << 8 | '<')) { if (IsPunct(a, i, '<' << 8 | '<')) {
y = ParseAdd(a, &i, i + 1); y = ParseAdd(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x <<= a->exprs.p[y].x & 63; a->exprs.p[x].x <<= a->exprs.p[y].x & 63;
} else { } else {
x = NewBinary(a, EX_SHL, x, y); x = NewBinary(a, EX_SHL, x, y);
} }
} else if (IsPunct(a, i, '>' << 8 | '>')) { } else if (IsPunct(a, i, '>' << 8 | '>')) {
y = ParseAdd(a, &i, i + 1); y = ParseAdd(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x >>= a->exprs.p[y].x & 63; a->exprs.p[x].x >>= a->exprs.p[y].x & 63;
} else { } else {
x = NewBinary(a, EX_SHR, x, y); x = NewBinary(a, EX_SHR, x, y);
@ -1280,28 +1280,28 @@ static int ParseRelational(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '<')) { if (IsPunct(a, i, '<')) {
y = ParseShift(a, &i, i + 1); y = ParseShift(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[x].x < a->exprs.p[y].x; a->exprs.p[x].x = a->exprs.p[x].x < a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_LT, x, y); x = NewBinary(a, EX_LT, x, y);
} }
} else if (IsPunct(a, i, '>')) { } else if (IsPunct(a, i, '>')) {
y = ParseShift(a, &i, i + 1); y = ParseShift(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[y].x < a->exprs.p[x].x; a->exprs.p[x].x = a->exprs.p[y].x < a->exprs.p[x].x;
} else { } else {
x = NewBinary(a, EX_LT, y, x); x = NewBinary(a, EX_LT, y, x);
} }
} else if (IsPunct(a, i, '<' << 8 | '=')) { } else if (IsPunct(a, i, '<' << 8 | '=')) {
y = ParseShift(a, &i, i + 1); y = ParseShift(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[x].x <= a->exprs.p[y].x; a->exprs.p[x].x = a->exprs.p[x].x <= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_LE, x, y); x = NewBinary(a, EX_LE, x, y);
} }
} else if (IsPunct(a, i, '>' << 8 | '=')) { } else if (IsPunct(a, i, '>' << 8 | '=')) {
y = ParseShift(a, &i, i + 1); y = ParseShift(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[y].x <= a->exprs.p[x].x; a->exprs.p[x].x = a->exprs.p[y].x <= a->exprs.p[x].x;
} else { } else {
x = NewBinary(a, EX_LE, y, x); x = NewBinary(a, EX_LE, y, x);
@ -1320,14 +1320,14 @@ static int ParseEquality(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '=' << 8 | '=')) { if (IsPunct(a, i, '=' << 8 | '=')) {
y = ParseRelational(a, &i, i + 1); y = ParseRelational(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[x].x == a->exprs.p[y].x; a->exprs.p[x].x = a->exprs.p[x].x == a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_EQ, x, y); x = NewBinary(a, EX_EQ, x, y);
} }
} else if (IsPunct(a, i, '!' << 8 | '=')) { } else if (IsPunct(a, i, '!' << 8 | '=')) {
y = ParseRelational(a, &i, i + 1); y = ParseRelational(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x = a->exprs.p[x].x != a->exprs.p[y].x; a->exprs.p[x].x = a->exprs.p[x].x != a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_NE, x, y); x = NewBinary(a, EX_NE, x, y);
@ -1346,7 +1346,7 @@ static int ParseAnd(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '&')) { if (IsPunct(a, i, '&')) {
y = ParseEquality(a, &i, i + 1); y = ParseEquality(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x &= a->exprs.p[y].x; a->exprs.p[x].x &= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_AND, x, y); x = NewBinary(a, EX_AND, x, y);
@ -1365,7 +1365,7 @@ static int ParseXor(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '^')) { if (IsPunct(a, i, '^')) {
y = ParseAnd(a, &i, i + 1); y = ParseAnd(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x ^= a->exprs.p[y].x; a->exprs.p[x].x ^= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_XOR, x, y); x = NewBinary(a, EX_XOR, x, y);
@ -1384,7 +1384,7 @@ static int ParseOr(struct As *a, int *rest, int i) {
for (;;) { for (;;) {
if (IsPunct(a, i, '|')) { if (IsPunct(a, i, '|')) {
y = ParseXor(a, &i, i + 1); y = ParseXor(a, &i, i + 1);
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) { if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
a->exprs.p[x].x |= a->exprs.p[y].x; a->exprs.p[x].x |= a->exprs.p[y].x;
} else { } else {
x = NewBinary(a, EX_OR, x, y); x = NewBinary(a, EX_OR, x, y);
@ -3745,8 +3745,7 @@ static int ResolveSymbol(struct As *a, int i) {
case TT_FORWARD: case TT_FORWARD:
return FindLabelForward(a, a->ints.p[a->things.p[i].i]); return FindLabelForward(a, a->ints.p[a->things.p[i].i]);
default: default:
DebugBreak(); Fail(a, "this corruption %d", a->things.p[i].t);
Fail(a, "this corruption");
} }
} }
@ -3777,15 +3776,6 @@ static void Write32(char b[4], int x) {
b[3] = x >> 030; b[3] = x >> 030;
} }
static void MarkUndefinedSymbolsGlobal(struct As *a) {
int i;
for (i = 0; i < a->symbols.n; ++i) {
if (!a->symbols.p[i].section && a->symbols.p[i].stb == STB_LOCAL) {
a->symbols.p[i].stb = STB_GLOBAL;
}
}
}
static void MarkUsedSymbols(struct As *a, int i) { static void MarkUsedSymbols(struct As *a, int i) {
if (i == -1) return; if (i == -1) return;
MarkUsedSymbols(a, a->exprs.p[i].lhs); MarkUsedSymbols(a, a->exprs.p[i].lhs);
@ -3818,6 +3808,16 @@ static void Evaluate(struct As *a) {
} }
} }
static void MarkUndefinedSymbolsGlobal(struct As *a) {
int i;
for (i = 0; i < a->symbols.n; ++i) {
if (a->symbols.p[i].isused && !a->symbols.p[i].section &&
a->symbols.p[i].stb == STB_LOCAL) {
a->symbols.p[i].stb = STB_GLOBAL;
}
}
}
static bool IsLocal(struct As *a, int name) { static bool IsLocal(struct As *a, int name) {
if (name < 0) return true; if (name < 0) return true;
return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2); return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2);
@ -3829,17 +3829,18 @@ static bool IsLiveSymbol(struct As *a, int i) {
} }
static void Objectify(struct As *a, int path) { static void Objectify(struct As *a, int path) {
int i, j, s; char *p;
int i, j, s, e;
struct ElfWriter *elf; struct ElfWriter *elf;
elf = elfwriter_open(a->strings.p[path], 0644); elf = elfwriter_open(a->strings.p[path], 0644);
for (i = 0; i < a->symbols.n; ++i) { for (i = 0; i < a->symbols.n; ++i) {
if (!IsLiveSymbol(a, i)) continue; if (!IsLiveSymbol(a, i)) continue;
p = strndup(a->slices.p[a->symbols.p[i].name].p,
a->slices.p[a->symbols.p[i].name].n);
a->symbols.p[i].ref = elfwriter_appendsym( a->symbols.p[i].ref = elfwriter_appendsym(
elf, elf, p, ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type),
strndup(a->slices.p[a->symbols.p[i].name].p,
a->slices.p[a->symbols.p[i].name].n),
ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type),
a->symbols.p[i].stv, a->symbols.p[i].offset, a->symbols.p[i].size); a->symbols.p[i].stv, a->symbols.p[i].offset, a->symbols.p[i].size);
free(p);
} }
for (i = 0; i < a->sections.n; ++i) { for (i = 0; i < a->sections.n; ++i) {
elfwriter_align(elf, a->sections.p[i].align, 0); elfwriter_align(elf, a->sections.p[i].align, 0);
@ -3853,24 +3854,24 @@ static void Objectify(struct As *a, int path) {
for (j = 0; j < a->relas.n; ++j) { for (j = 0; j < a->relas.n; ++j) {
if (a->relas.p[j].isdead) continue; if (a->relas.p[j].isdead) continue;
if (a->relas.p[j].section != i) continue; if (a->relas.p[j].section != i) continue;
a->i = a->exprs.p[a->relas.p[j].expr].tok; e = a->relas.p[j].expr;
switch (a->exprs.p[a->relas.p[j].expr].kind) { a->i = a->exprs.p[e].tok;
switch (a->exprs.p[e].kind) {
case EX_INT: case EX_INT:
break; break;
case EX_SYM: case EX_SYM:
elfwriter_appendrela( elfwriter_appendrela(elf, a->relas.p[j].offset,
elf, a->relas.p[j].offset, a->symbols.p[a->exprs.p[e].x].ref,
a->symbols.p[a->exprs.p[a->relas.p[j].expr].x].ref, a->relas.p[j].kind, a->relas.p[j].addend);
a->relas.p[j].kind, a->relas.p[j].addend);
break; break;
case EX_ADD: case EX_ADD:
if (a->exprs.p[a->exprs.p[j].lhs].kind == EX_SYM && if (a->exprs.p[a->exprs.p[e].lhs].kind == EX_SYM &&
a->exprs.p[a->exprs.p[j].rhs].kind == EX_INT) { a->exprs.p[a->exprs.p[e].rhs].kind == EX_INT) {
elfwriter_appendrela( elfwriter_appendrela(
elf, a->relas.p[j].offset, elf, a->relas.p[j].offset,
a->symbols.p[a->exprs.p[a->exprs.p[j].lhs].x].ref, a->symbols.p[a->exprs.p[a->exprs.p[e].lhs].x].ref,
a->relas.p[j].kind, a->relas.p[j].kind,
a->relas.p[j].addend + a->exprs.p[a->exprs.p[j].rhs].x); a->relas.p[j].addend + a->exprs.p[a->exprs.p[e].rhs].x);
} else { } else {
Fail(a, "bad addend"); Fail(a, "bad addend");
} }
@ -3887,6 +3888,58 @@ static void Objectify(struct As *a, int path) {
elfwriter_close(elf); elfwriter_close(elf);
} }
static void CheckIntegrity(struct As *a) {
int i;
for (i = 0; i < a->things.n; ++i) {
CHECK_LT((int)a->things.p[i].s, a->sauces.n);
switch (a->things.p[i].t) {
case TT_INT:
case TT_FORWARD:
case TT_BACKWARD:
CHECK_LT(a->things.p[i].i, a->ints.n);
break;
case TT_FLOAT:
CHECK_LT(a->things.p[i].i, a->floats.n);
break;
case TT_SLICE:
CHECK_LT(a->things.p[i].i, a->slices.n);
break;
default:
break;
}
}
for (i = 0; i < a->sections.n; ++i) {
CHECK_LT(a->sections.p[i].name, a->strings.n);
}
for (i = 0; i < a->symbols.n; ++i) {
CHECK_LT(a->symbols.p[i].name, a->slices.n);
CHECK_LT(a->symbols.p[i].section, a->sections.n);
}
for (i = 0; i < a->labels.n; ++i) {
CHECK_LT(a->labels.p[i].tok, a->things.n);
CHECK_LT(a->labels.p[i].symbol, a->symbols.n);
}
for (i = 0; i < a->relas.n; ++i) {
CHECK_LT(a->relas.p[i].expr, a->exprs.n);
CHECK_LT(a->relas.p[i].section, a->sections.n);
}
for (i = 0; i < a->exprs.n; ++i) {
CHECK_LT(a->exprs.p[i].tok, a->things.n);
if (a->exprs.p[i].lhs != -1) CHECK_LT(a->exprs.p[i].lhs, a->exprs.n);
if (a->exprs.p[i].rhs != -1) CHECK_LT(a->exprs.p[i].rhs, a->exprs.n);
switch (a->exprs.p[i].kind) {
case EX_SYM:
CHECK_LT(a->exprs.p[i].x, a->things.n);
CHECK(a->things.p[a->exprs.p[i].x].t == TT_SLICE ||
a->things.p[a->exprs.p[i].x].t == TT_FORWARD ||
a->things.p[a->exprs.p[i].x].t == TT_BACKWARD);
break;
default:
break;
}
}
}
static void PrintThings(struct As *a) { static void PrintThings(struct As *a) {
int i; int i;
char pbuf[4], fbuf[32]; char pbuf[4], fbuf[32];
@ -3929,17 +3982,18 @@ void Assembler(int argc, char *argv[]) {
Tokenize(a, a->inpath); Tokenize(a, a->inpath);
/* PrintThings(a); */ /* PrintThings(a); */
Assemble(a); Assemble(a);
/* CheckIntegrity(a); */
Evaluate(a); Evaluate(a);
MarkUndefinedSymbolsGlobal(a); MarkUndefinedSymbolsGlobal(a);
Objectify(a, a->outpath); Objectify(a, a->outpath);
malloc_stats(); /* malloc_stats(); */
FreeAssembler(a); FreeAssembler(a);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
showcrashreports(); showcrashreports();
if (argc == 1) { if (argc == 1) {
system("o//third_party/chibicc/as.com -o /tmp/o third_party/chibicc/hog.s"); system("o//third_party/chibicc/as.com -o /tmp/o /home/jart/trash/hog.s");
system("objdump -xwd /tmp/o"); system("objdump -xwd /tmp/o");
exit(0); exit(0);
} }

View File

@ -32,6 +32,7 @@ bool opt_verbose;
static bool opt_A; static bool opt_A;
static bool opt_E; static bool opt_E;
static bool opt_J;
static bool opt_M; static bool opt_M;
static bool opt_MD; static bool opt_MD;
static bool opt_MMD; static bool opt_MMD;
@ -202,6 +203,8 @@ static void parse_args(int argc, char **argv) {
opt_c = true; opt_c = true;
} else if (!strcmp(argv[i], "-E")) { } else if (!strcmp(argv[i], "-E")) {
opt_E = true; opt_E = true;
} else if (!strcmp(argv[i], "-J")) {
opt_J = true;
} else if (!strcmp(argv[i], "-A")) { } else if (!strcmp(argv[i], "-A")) {
opt_A = true; opt_A = true;
} else if (!strcmp(argv[i], "-I")) { } else if (!strcmp(argv[i], "-I")) {
@ -364,7 +367,14 @@ static char *create_tmpfile(void) {
return path; return path;
} }
static void run_subprocess(char **argv) { static void handle_exit(bool ok) {
if (!ok) {
opt_save_temps = true;
exit(1);
}
}
static bool run_subprocess(char **argv) {
// If -### is given, dump the subprocess's command line. // If -### is given, dump the subprocess's command line.
if (opt_hash_hash_hash) { if (opt_hash_hash_hash) {
fprintf(stderr, "%s", argv[0]); fprintf(stderr, "%s", argv[0]);
@ -384,13 +394,10 @@ static void run_subprocess(char **argv) {
break; break;
} }
} }
if (status != 0) { return !status;
opt_save_temps = true;
exit(1);
}
} }
static void run_cc1(int argc, char **argv, char *input, char *output) { static bool run_cc1(int argc, char **argv, char *input, char *output) {
char **args = calloc(argc + 10, sizeof(char *)); char **args = calloc(argc + 10, sizeof(char *));
memcpy(args, argv, argc * sizeof(char *)); memcpy(args, argv, argc * sizeof(char *));
args[argc++] = "-cc1"; args[argc++] = "-cc1";
@ -402,7 +409,7 @@ static void run_cc1(int argc, char **argv, char *input, char *output) {
args[argc++] = "-cc1-output"; args[argc++] = "-cc1-output";
args[argc++] = output; args[argc++] = output;
} }
run_subprocess(args); return run_subprocess(args);
} }
static void print_token(FILE *out, Token *tok) { static void print_token(FILE *out, Token *tok) {
@ -540,6 +547,10 @@ static void cc1(void) {
print_ast(stdout, prog); print_ast(stdout, prog);
return; return;
} }
if (opt_J) {
output_javadown(output_file, prog);
return;
}
FILE *out = open_file(output_file); FILE *out = open_file(output_file);
codegen(prog, out); codegen(prog, out);
fclose(out); fclose(out);
@ -561,7 +572,7 @@ static void assemble(char *input, char *output) {
strarray_push(&arr, "-o"); strarray_push(&arr, "-o");
strarray_push(&arr, output); strarray_push(&arr, output);
strarray_push(&arr, NULL); strarray_push(&arr, NULL);
run_subprocess(arr.data); handle_exit(run_subprocess(arr.data));
} }
static void run_linker(StringArray *inputs, char *output) { static void run_linker(StringArray *inputs, char *output) {
@ -591,7 +602,7 @@ static void run_linker(StringArray *inputs, char *output) {
strarray_push(&arr, inputs->data[i]); strarray_push(&arr, inputs->data[i]);
} }
strarray_push(&arr, NULL); strarray_push(&arr, NULL);
run_subprocess(arr.data); handle_exit(run_subprocess(arr.data));
} }
int chibicc(int argc, char **argv) { int chibicc(int argc, char **argv) {
@ -608,6 +619,7 @@ int chibicc(int argc, char **argv) {
error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files"); error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files");
} }
StringArray ld_args = {}; StringArray ld_args = {};
StringArray dox_args = {};
for (int i = 0; i < input_paths.len; i++) { for (int i = 0; i < input_paths.len; i++) {
char *input = input_paths.data[i]; char *input = input_paths.data[i];
if (!strncmp(input, "-l", 2)) { if (!strncmp(input, "-l", 2)) {
@ -647,25 +659,33 @@ int chibicc(int argc, char **argv) {
assert(type == FILE_C || type == FILE_ASM_CPP); assert(type == FILE_C || type == FILE_ASM_CPP);
// Just preprocess // Just preprocess
if (opt_E || opt_M) { if (opt_E || opt_M) {
run_cc1(argc, argv, input, NULL); handle_exit(run_cc1(argc, argv, input, NULL));
continue; continue;
} }
// Compile // Compile
if (opt_S) { if (opt_S) {
run_cc1(argc, argv, input, output); handle_exit(run_cc1(argc, argv, input, output));
continue; continue;
} }
// Compile and assemble // Compile and assemble
if (opt_c) { if (opt_c) {
char *tmp = create_tmpfile(); char *tmp = create_tmpfile();
run_cc1(argc, argv, input, tmp); handle_exit(run_cc1(argc, argv, input, tmp));
assemble(tmp, output); assemble(tmp, output);
continue; continue;
} }
// Dox
if (opt_J) {
char *tmp = create_tmpfile();
if (run_cc1(argc, argv, input, tmp)) {
strarray_push(&dox_args, tmp);
}
continue;
}
// Compile, assemble and link // Compile, assemble and link
char *tmp1 = create_tmpfile(); char *tmp1 = create_tmpfile();
char *tmp2 = create_tmpfile(); char *tmp2 = create_tmpfile();
run_cc1(argc, argv, input, tmp1); handle_exit(run_cc1(argc, argv, input, tmp1));
assemble(tmp1, tmp2); assemble(tmp1, tmp2);
strarray_push(&ld_args, tmp2); strarray_push(&ld_args, tmp2);
continue; continue;
@ -673,5 +693,8 @@ int chibicc(int argc, char **argv) {
if (ld_args.len > 0) { if (ld_args.len > 0) {
run_linker(&ld_args, opt_o ? opt_o : "a.out"); run_linker(&ld_args, opt_o ? opt_o : "a.out");
} }
if (dox_args.len > 0) {
drop_dox(&dox_args, opt_o ? opt_o : "/dev/stdout");
}
return 0; return 0;
} }

View File

@ -26,6 +26,7 @@
#include "libc/unicode/unicode.h" #include "libc/unicode/unicode.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "third_party/gdtoa/gdtoa.h" #include "third_party/gdtoa/gdtoa.h"
#include "tool/build/lib/javadown.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
@ -35,7 +36,10 @@ typedef struct Asm Asm;
typedef struct AsmOperand AsmOperand; typedef struct AsmOperand AsmOperand;
typedef struct File File; typedef struct File File;
typedef struct FpClassify FpClassify; typedef struct FpClassify FpClassify;
typedef struct HashMap HashMap;
typedef struct Hideset Hideset; typedef struct Hideset Hideset;
typedef struct Macro Macro;
typedef struct MacroParam MacroParam;
typedef struct Member Member; typedef struct Member Member;
typedef struct Node Node; typedef struct Node Node;
typedef struct Obj Obj; typedef struct Obj Obj;
@ -47,6 +51,8 @@ typedef struct Token Token;
typedef struct TokenStack TokenStack; typedef struct TokenStack TokenStack;
typedef struct Type Type; typedef struct Type Type;
typedef Token *macro_handler_fn(Token *);
// //
// strarray.c // strarray.c
// //
@ -64,13 +70,14 @@ void strarray_push(StringArray *, char *);
// //
typedef enum { typedef enum {
TK_IDENT, // Identifiers TK_IDENT, // Identifiers
TK_PUNCT, // Punctuators TK_PUNCT, // Punctuators
TK_KEYWORD, // Keywords TK_KEYWORD, // Keywords
TK_STR, // String literals TK_STR, // String literals
TK_NUM, // Numeric literals TK_NUM, // Numeric literals
TK_PP_NUM, // Preprocessing numbers TK_PP_NUM, // Preprocessing numbers
TK_EOF, // End-of-file markers TK_JAVADOWN, // /** ... */ comments
TK_EOF, // End-of-file markers
} TokenKind; } TokenKind;
struct File { struct File {
@ -80,6 +87,7 @@ struct File {
// For #line directive // For #line directive
char *display_name; char *display_name;
int line_delta; int line_delta;
struct Javadown *javadown;
}; };
struct thatispacked Token { struct thatispacked Token {
@ -96,6 +104,7 @@ struct thatispacked Token {
char *filename; // Filename char *filename; // Filename
Hideset *hideset; // For macro expansion Hideset *hideset; // For macro expansion
Token *origin; // If this is expanded from a macro, the original token Token *origin; // If this is expanded from a macro, the original token
struct Javadown *javadown;
union { union {
int64_t val; // If kind is TK_NUM, its value int64_t val; // If kind is TK_NUM, its value
long double fval; // If kind is TK_NUM, its value long double fval; // If kind is TK_NUM, its value
@ -134,6 +143,23 @@ int read_escaped_char(char **, char *);
// preprocess.c // preprocess.c
// //
struct MacroParam {
MacroParam *next;
char *name;
};
struct Macro {
char *name;
bool is_objlike; // Object-like or function-like
MacroParam *params;
char *va_args_name;
Token *body;
macro_handler_fn *handler;
Token *javadown;
};
extern HashMap macros;
char *search_include_paths(char *); char *search_include_paths(char *);
void init_macros(void); void init_macros(void);
void define_macro(char *, char *); void define_macro(char *, char *);
@ -232,6 +258,7 @@ struct Obj {
char *asmname; char *asmname;
char *section; char *section;
char *visibility; char *visibility;
Token *javadown;
// Global variable // Global variable
bool is_tentative; bool is_tentative;
bool is_string_literal; bool is_string_literal;
@ -244,6 +271,9 @@ struct Obj {
bool is_noreturn; bool is_noreturn;
bool is_destructor; bool is_destructor;
bool is_constructor; bool is_constructor;
bool is_ms_abi; /* TODO */
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int stack_size; int stack_size;
Obj *params; Obj *params;
Node *body; Node *body;
@ -419,6 +449,7 @@ struct Type {
int align; // alignment int align; // alignment
bool is_unsigned; // unsigned or signed bool is_unsigned; // unsigned or signed
bool is_atomic; // true if _Atomic bool is_atomic; // true if _Atomic
bool is_ms_abi; // microsoft abi
Type *origin; // for type compatibility check Type *origin; // for type compatibility check
// Pointer-to or array-of type. We intentionally use the same member // Pointer-to or array-of type. We intentionally use the same member
// to represent pointer/array duality in C. // to represent pointer/array duality in C.
@ -534,11 +565,11 @@ typedef struct {
void *val; void *val;
} HashEntry; } HashEntry;
typedef struct { struct HashMap {
HashEntry *buckets; HashEntry *buckets;
int capacity; int capacity;
int used; int used;
} HashMap; };
void *hashmap_get(HashMap *, char *); void *hashmap_get(HashMap *, char *);
void *hashmap_get2(HashMap *, char *, int); void *hashmap_get2(HashMap *, char *, int);
@ -584,6 +615,13 @@ Token *alloc_token(void);
Obj *alloc_obj(void); Obj *alloc_obj(void);
Type *alloc_type(void); Type *alloc_type(void);
//
// javadown.c
//
void output_javadown(const char *, Obj *);
void drop_dox(const StringArray *, const char *);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ */ #endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ */

View File

@ -49,6 +49,7 @@ THIRD_PARTY_CHIBICC_A_CHECKS = \
$(THIRD_PARTY_CHIBICC_A_HDRS:%=o/$(MODE)/%.ok) $(THIRD_PARTY_CHIBICC_A_HDRS:%=o/$(MODE)/%.ok)
THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \
LIBC_ALG \
LIBC_BITS \ LIBC_BITS \
LIBC_CALLS \ LIBC_CALLS \
LIBC_CALLS_HEFTY \ LIBC_CALLS_HEFTY \

View File

@ -2265,7 +2265,7 @@ static void emit_data(Obj *prog) {
} }
} }
static void store_fp(int r, int offset, int sz) { static void store_fp(Obj *fn, int r, int offset, int sz) {
switch (sz) { switch (sz) {
case 4: case 4:
println("\tmovss\t%%xmm%d,%d(%%rbp)", r, offset); println("\tmovss\t%%xmm%d,%d(%%rbp)", r, offset);
@ -2274,7 +2274,8 @@ static void store_fp(int r, int offset, int sz) {
println("\tmovsd\t%%xmm%d,%d(%%rbp)", r, offset); println("\tmovsd\t%%xmm%d,%d(%%rbp)", r, offset);
return; return;
case 16: case 16:
println("\tmovaps\t%%xmm%d,%d(%%rbp)", r, offset); println("\t%s\t%%xmm%d,%d(%%rbp)",
fn->is_force_align_arg_pointer ? "movups" : "movaps", r, offset);
return; return;
} }
UNREACHABLE(); UNREACHABLE();
@ -2381,13 +2382,13 @@ static void emit_text(Obj *prog) {
case TY_UNION: case TY_UNION:
assert(ty->size <= 16); assert(ty->size <= 16);
if (has_flonum(ty, 0, 8, 0)) { if (has_flonum(ty, 0, 8, 0)) {
store_fp(fp++, var->offset, MIN(8, ty->size)); store_fp(fn, fp++, var->offset, MIN(8, ty->size));
} else { } else {
store_gp(gp++, var->offset, MIN(8, ty->size)); store_gp(gp++, var->offset, MIN(8, ty->size));
} }
if (ty->size > 8) { if (ty->size > 8) {
if (has_flonum(ty, 8, 16, 0)) { if (has_flonum(ty, 8, 16, 0)) {
store_fp(fp++, var->offset + 8, ty->size - 8); store_fp(fn, fp++, var->offset + 8, ty->size - 8);
} else { } else {
store_gp(gp++, var->offset + 8, ty->size - 8); store_gp(gp++, var->offset + 8, ty->size - 8);
} }
@ -2395,7 +2396,7 @@ static void emit_text(Obj *prog) {
break; break;
case TY_FLOAT: case TY_FLOAT:
case TY_DOUBLE: case TY_DOUBLE:
store_fp(fp++, var->offset, ty->size); store_fp(fn, fp++, var->offset, ty->size);
break; break;
case TY_INT128: case TY_INT128:
store_gp(gp++, var->offset + 0, 8); store_gp(gp++, var->offset + 0, 8);
@ -2405,6 +2406,20 @@ static void emit_text(Obj *prog) {
store_gp(gp++, var->offset, ty->size); store_gp(gp++, var->offset, ty->size);
} }
} }
if (fn->is_force_align_arg_pointer) {
emitlin("\tand\t$-16,%rsp");
}
if (fn->is_no_caller_saved_registers) {
emitlin("\
\tpush\t%rdi\n\
\tpush\t%rsi\n\
\tpush\t%rdx\n\
\tpush\t%rcx\n\
\tpush\t%r8\n\
\tpush\t%r9\n\
\tpush\t%r10\n\
\tpush\t%r11");
}
// Emit code // Emit code
gen_stmt(fn->body); gen_stmt(fn->body);
assert(!depth); assert(!depth);
@ -2420,6 +2435,17 @@ static void emit_text(Obj *prog) {
if (fn->is_noreturn) { if (fn->is_noreturn) {
emitlin("\tud2"); emitlin("\tud2");
} else { } else {
if (fn->is_no_caller_saved_registers) {
emitlin("\
\tpop\t%r11\n\
\tpop\t%r10\n\
\tpop\t%r9\n\
\tpop\t%r8\n\
\tpop\t%rcx\n\
\tpop\t%rdx\n\
\tpop\t%rsi\n\
\tpop\t%rdi");
}
emitlin("\tleave"); emitlin("\tleave");
emitlin("\tret"); emitlin("\tret");
} }

248
third_party/chibicc/dox1.c vendored 100644
View File

@ -0,0 +1,248 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/gc.h"
#include "third_party/chibicc/chibicc.h"
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
struct DoxWriter {
struct Buffer {
size_t n;
char *p;
} buf;
struct Macros {
size_t n;
Macro **p;
} macros;
struct Objects {
size_t n;
Obj **p;
} objects;
};
static void SerializeData(struct Buffer *buf, const void *p, unsigned long n) {
struct Slice *s;
buf->p = realloc(buf->p, buf->n + n);
memcpy(buf->p + buf->n, p, n);
buf->n += n;
}
static int SerializeInt(struct Buffer *buf, int x) {
unsigned char b[4];
b[0] = x >> 000;
b[1] = x >> 010;
b[2] = x >> 020;
b[3] = x >> 030;
SerializeData(buf, b, 4);
return x;
}
static void SerializeStr(struct Buffer *buf, const char *s) {
size_t n;
if (!s) s = "";
n = strlen(s);
n = MIN(INT_MAX, n);
SerializeInt(buf, n);
SerializeData(buf, s, n);
}
static void SerializeJavadown(struct Buffer *buf, struct Javadown *jd) {
int i;
SerializeInt(buf, jd->isfileoverview);
SerializeStr(buf, jd->title);
SerializeStr(buf, jd->text);
SerializeInt(buf, jd->tags.n);
for (i = 0; i < jd->tags.n; ++i) {
SerializeStr(buf, jd->tags.p[i].tag);
SerializeStr(buf, jd->tags.p[i].text);
}
}
static char *DescribeScalar(struct Type *ty, char *name) {
return xasprintf("%s%s%s", ty->is_atomic ? "_Atomic " : "",
ty->is_unsigned ? "unsigned " : "", name);
}
static char *DescribeType(struct Type *ty) {
switch (ty->kind) {
case TY_VOID:
return strdup("void");
case TY_BOOL:
return strdup("_Bool");
case TY_CHAR:
return DescribeScalar(ty, "char");
case TY_SHORT:
return DescribeScalar(ty, "short");
case TY_INT:
return DescribeScalar(ty, "int");
case TY_LONG:
return DescribeScalar(ty, "long");
case TY_INT128:
return DescribeScalar(ty, "__int128");
case TY_FLOAT:
return DescribeScalar(ty, "float");
case TY_DOUBLE:
return DescribeScalar(ty, "double");
case TY_LDOUBLE:
return DescribeScalar(ty, "long double");
case TY_PTR:
return xasprintf("%s*", gc(DescribeType(ty->base)));
case TY_ARRAY:
return xasprintf("%s[%d]", gc(DescribeType(ty->base)), ty->array_len);
case TY_ENUM:
if (ty->name_pos) {
return xasprintf("enum %.*s", ty->name_pos->len, ty->name_pos->loc);
} else {
return strdup("ANONYMOUS-ENUM");
}
case TY_STRUCT:
if (ty->name_pos) {
return xasprintf("struct %.*s", ty->name_pos->len, ty->name_pos->loc);
} else {
return strdup("ANONYMOUS-STRUCT");
}
case TY_UNION:
if (ty->name_pos) {
return xasprintf("union %.*s", ty->name_pos->len, ty->name_pos->loc);
} else {
return strdup("ANONYMOUS-UNION");
}
case TY_FUNC:
return xasprintf("%s(*)()", gc(DescribeType(ty->return_ty)));
default:
return "UNKNOWN";
}
}
static int CountParams(Obj *params) {
int n;
for (n = 0; params; params = params->next) ++n;
return n;
}
static const char *GetFileName(Obj *obj) {
if (obj->javadown && obj->javadown->file) return obj->javadown->file->name;
if (obj->tok && obj->tok->file) return obj->tok->file->name;
return "missingno.c";
}
static int GetLine(Obj *obj) {
if (obj->javadown && obj->javadown->file) return obj->javadown->line_no;
if (obj->tok && obj->tok->file) return obj->tok->line_no;
return 0;
}
static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
int i;
char *s;
Obj *param;
MacroParam *mparam;
SerializeInt(&dox->buf, dox->objects.n);
for (i = 0; i < dox->objects.n; ++i) {
s = DescribeType(dox->objects.p[i]->ty);
SerializeStr(&dox->buf, s);
free(s);
SerializeStr(&dox->buf, dox->objects.p[i]->name);
SerializeStr(&dox->buf, GetFileName(dox->objects.p[i]));
SerializeInt(&dox->buf, GetLine(dox->objects.p[i]));
SerializeInt(&dox->buf, dox->objects.p[i]->is_function);
SerializeInt(&dox->buf, dox->objects.p[i]->is_weak);
SerializeInt(&dox->buf, dox->objects.p[i]->is_inline);
SerializeInt(&dox->buf, dox->objects.p[i]->is_noreturn);
SerializeInt(&dox->buf, dox->objects.p[i]->is_destructor);
SerializeInt(&dox->buf, dox->objects.p[i]->is_constructor);
SerializeInt(&dox->buf, dox->objects.p[i]->is_force_align_arg_pointer);
SerializeInt(&dox->buf, dox->objects.p[i]->is_no_caller_saved_registers);
SerializeStr(&dox->buf, dox->objects.p[i]->visibility);
SerializeJavadown(&dox->buf, dox->objects.p[i]->javadown->javadown);
SerializeInt(&dox->buf, CountParams(dox->objects.p[i]->params));
for (param = dox->objects.p[i]->params; param; param = param->next) {
s = DescribeType(param->ty);
SerializeStr(&dox->buf, s);
free(s);
SerializeStr(&dox->buf, param->name);
}
}
SerializeInt(&dox->buf, dox->macros.n);
for (i = 0; i < dox->macros.n; ++i) {
SerializeStr(&dox->buf, dox->macros.p[i]->name);
SerializeStr(&dox->buf, dox->macros.p[i]->javadown->file->name);
SerializeInt(&dox->buf, dox->macros.p[i]->javadown->line_no);
SerializeJavadown(&dox->buf, dox->macros.p[i]->javadown->javadown);
}
SerializeInt(&dox->buf, 31337);
}
static void LoadPublicDefinitions(struct DoxWriter *dox, Obj *prog) {
int i;
Obj *obj;
Macro *macro;
for (obj = prog; obj; obj = obj->next) {
if (obj->is_static) continue;
if (*obj->name == '_') continue;
if (!obj->javadown) continue;
if (obj->is_string_literal) continue;
if (obj->visibility && !strcmp(obj->visibility, "hidden")) continue;
if (strchr(obj->name, '$')) continue;
APPEND(dox->objects);
dox->objects.p[dox->objects.n - 1] = obj;
}
for (i = 0; i < macros.capacity; ++i) {
if (!macros.buckets[i].key) continue;
if (macros.buckets[i].key == (char *)-1) continue;
macro = macros.buckets[i].val;
if (!macro->javadown) continue;
if (!macro->javadown->javadown) continue;
if (*macro->name == '_') continue;
if (strchr(macro->name, '$')) continue;
APPEND(dox->macros);
dox->macros.p[dox->macros.n - 1] = macro;
}
}
static struct DoxWriter *NewDoxWriter(void) {
return calloc(1, sizeof(struct DoxWriter));
}
static void FreeDoxWriter(struct DoxWriter *dox) {
if (dox) {
free(dox->buf.p);
free(dox->macros.p);
free(dox->objects.p);
free(dox);
}
}
static void WriteDox(struct DoxWriter *dox, const char *path) {
int fd;
CHECK_NE(-1, (fd = creat(path, 0644)));
CHECK_EQ(dox->buf.n, write(fd, dox->buf.p, dox->buf.n));
close(fd);
}
/**
* Emits documentation datum for compilation unit just parsed.
*/
void output_javadown(const char *path, Obj *prog) {
struct DoxWriter *dox = NewDoxWriter();
LoadPublicDefinitions(dox, prog);
SerializeDox(dox, prog);
WriteDox(dox, path);
FreeDoxWriter(dox);
}

392
third_party/chibicc/dox2.c vendored 100644
View File

@ -0,0 +1,392 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/alg.h"
#include "libc/bits/bits.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "third_party/chibicc/chibicc.h"
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
struct Dox {
unsigned char *p;
struct Freelist {
size_t n;
void **p;
} freelist;
struct Set {
size_t n;
struct SetEntry {
unsigned h;
char *s;
} * p;
} names;
struct DoxObjects {
size_t n;
struct DoxObject {
bool ignore;
char *type;
char *name;
char *path;
int line;
bool is_function;
bool is_weak;
bool is_inline;
bool is_noreturn;
bool is_destructor;
bool is_constructor;
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
char *visibility;
struct Javadown *javadown;
struct DoxObjectParams {
size_t n;
struct DoxObjectParam {
char *type;
char *name;
} * p;
} params;
} * p;
} objects;
struct {
size_t n;
int *p;
} objectindex;
};
static unsigned Hash(const void *p, unsigned long n) {
unsigned h, i;
for (h = i = 0; i < n; i++) {
h += ((unsigned char *)p)[i];
h *= 0x9e3779b1;
}
return MAX(1, h);
}
static struct Dox *NewDox(void) {
return calloc(1, sizeof(struct Dox));
}
static void FreeDox(struct Dox *dox) {
int i;
if (dox) {
for (i = 0; i < dox->freelist.n; ++i) {
free(dox->freelist.p[i]);
}
free(dox->names.p);
free(dox->freelist.p);
free(dox->objects.p);
free(dox);
}
}
static void *FreeLater(struct Dox *dox, void *p) {
APPEND(dox->freelist);
dox->freelist.p[dox->freelist.n - 1] = p;
return p;
}
static int DeserializeInt(struct Dox *dox) {
int x;
x = (unsigned)dox->p[0] << 000 | (unsigned)dox->p[1] << 010 |
(unsigned)dox->p[2] << 020 | (unsigned)dox->p[3] << 030;
dox->p += 4;
return x;
}
static char *DeserializeStr(struct Dox *dox) {
char *s;
size_t n;
n = DeserializeInt(dox);
s = malloc(n + 1);
memcpy(s, dox->p, n);
s[n] = '\0';
dox->p += n;
return FreeLater(dox, s);
}
static struct Javadown *DeserializeJavadown(struct Dox *dox) {
int i;
struct Javadown *jd;
jd = FreeLater(dox, calloc(1, sizeof(struct Javadown)));
jd->isfileoverview = DeserializeInt(dox);
jd->title = DeserializeStr(dox);
jd->text = DeserializeStr(dox);
jd->tags.n = DeserializeInt(dox);
jd->tags.p = FreeLater(dox, malloc(jd->tags.n * sizeof(*jd->tags.p)));
for (i = 0; i < jd->tags.n; ++i) {
jd->tags.p[i].tag = DeserializeStr(dox);
jd->tags.p[i].text = DeserializeStr(dox);
}
return jd;
}
static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
int i;
o->ignore = false;
o->type = DeserializeStr(dox);
o->name = DeserializeStr(dox);
o->path = DeserializeStr(dox);
o->line = DeserializeInt(dox);
o->is_function = DeserializeInt(dox);
o->is_weak = DeserializeInt(dox);
o->is_inline = DeserializeInt(dox);
o->is_noreturn = DeserializeInt(dox);
o->is_destructor = DeserializeInt(dox);
o->is_constructor = DeserializeInt(dox);
o->is_force_align_arg_pointer = DeserializeInt(dox);
o->is_no_caller_saved_registers = DeserializeInt(dox);
o->visibility = DeserializeStr(dox);
o->javadown = DeserializeJavadown(dox);
o->params.n = DeserializeInt(dox);
o->params.p = FreeLater(dox, malloc(o->params.n * sizeof(*o->params.p)));
for (i = 0; i < o->params.n; ++i) {
o->params.p[i].type = DeserializeStr(dox);
o->params.p[i].name = DeserializeStr(dox);
}
}
static void DeserializeDox(struct Dox *dox) {
int i, j, n;
i = dox->objects.n;
n = DeserializeInt(dox);
dox->objects.p =
realloc(dox->objects.p, (dox->objects.n + n) * sizeof(*dox->objects.p));
for (j = 0; j < n; ++j) {
DeserializeObject(dox, dox->objects.p + i + j);
}
dox->objects.n += n;
}
static void ReadDox(struct Dox *dox, const StringArray *files) {
int i, fd;
void *map;
struct stat st;
for (i = 0; i < files->len; ++i) {
CHECK_NE(-1, (fd = open(files->data[i], O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
if (st.st_size) {
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)));
dox->p = map;
DeserializeDox(dox);
munmap(map, st.st_size);
}
close(fd);
}
}
static bool AddSet(struct Set *set, char *s) {
unsigned i, h, k;
h = Hash(s, strlen(s));
k = 0;
for (k = 0;; ++k) {
i = (h + k + ((k + 1) >> 1)) & (set->n - 1);
if (!set->p[i].h) {
set->p[i].h = h;
set->p[i].s = s;
return true;
}
if (h == set->p[i].h && !strcmp(s, set->p[i].s)) {
return false;
}
}
}
static int CompareObjectNames(const void *a, const void *b, void *arg) {
int *i1, *i2;
struct Dox *dox;
i1 = a, i2 = b, dox = arg;
return strcmp(dox->objects.p[*i1].name, dox->objects.p[*i2].name);
}
static void IndexDox(struct Dox *dox) {
size_t i, j, n;
dox->names.n = roundup2pow(dox->objects.n) << 1;
dox->names.p = calloc(dox->names.n, sizeof(*dox->names.p));
for (n = i = 0; i < dox->objects.n; ++i) {
if (AddSet(&dox->names, dox->objects.p[i].name)) {
++n;
} else {
dox->objects.p[i].ignore = true;
}
}
dox->objectindex.n = n;
dox->objectindex.p = malloc(n * sizeof(*dox->objectindex.p));
for (j = i = 0; i < dox->objects.n; ++i) {
if (dox->objects.p[i].ignore) continue;
dox->objectindex.p[j++] = i;
}
qsort_r(dox->objectindex.p, dox->objectindex.n, sizeof(*dox->objectindex.p),
CompareObjectNames, dox);
}
static void PrintText(FILE *f, const char *s) {
int c;
bool bol, pre;
for (pre = false, bol = true;;) {
switch ((c = *s++)) {
case '\0':
if (pre) {
fprintf(f, "</pre>");
}
return;
case '&':
fprintf(f, "&amp;");
bol = false;
break;
case '<':
fprintf(f, "&lt;");
bol = false;
break;
case '>':
fprintf(f, "&gt;");
bol = false;
break;
case '"':
fprintf(f, "&quot;");
bol = false;
break;
case '\'':
fprintf(f, "&apos;");
bol = false;
break;
case '\n':
if (!pre && *s == '\n') {
++s;
fprintf(f, "\n<p>");
} else if (pre &&
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
fprintf(f, "</pre>\n");
pre = false;
} else {
fprintf(f, "\n");
}
bol = true;
break;
case ' ':
if (bol && !pre && s[0] == ' ' && s[1] == ' ' && s[2] == ' ') {
pre = true;
fprintf(f, "<pre>");
}
fprintf(f, " ");
bol = false;
break;
default:
fprintf(f, "%c", c);
bol = false;
break;
}
}
}
static void PrintDox(struct Dox *dox, FILE *f) {
int i, j, k;
char *prefix;
struct DoxObject *o;
fprintf(f, "\
<!doctype html>\n\
<meta charset=\"utf-8\">\n\
<style>\n\
.indent {\n\
padding-left: 1em;\n\
}\n\
</style>\n\
\n\
<table width=\"100%%\"><tr><td width=\"33%%\" valign=\"top\">\n\
<p class=\"toc\">\n\
");
for (i = 0; i < dox->objectindex.n; ++i) {
o = dox->objects.p + dox->objectindex.p[i];
if (o->ignore || !o->is_function) continue;
fprintf(f, "<a href=\"#%s\">%s</a><br>\n", o->name, o->name);
fprintf(f, "<br>\n");
}
fprintf(f, "<td width=\"67%%\" valign=\"top\">\n");
for (i = 0; i < dox->objectindex.n; ++i) {
o = dox->objects.p + dox->objectindex.p[i];
if (o->ignore || !o->is_function) continue;
fprintf(f, "\n<div id=\"%s\" class=\"func\">\n", o->name, o->name);
fprintf(f, "<h3><a href=\"#%s\">", o->name);
fprintf(f, "<strong class=\"name\">%s</strong></a></h3>", o->name);
fprintf(f, "<p>");
PrintText(f, o->javadown->title);
fprintf(f, "\n");
if (*o->javadown->text) {
fprintf(f, "<p>");
PrintText(f, o->javadown->text);
fprintf(f, "\n");
}
fprintf(f, "<p><strong>@param</strong>\n");
fprintf(f, "<div class=\"params indent\">\n");
if (o->params.n) {
fprintf(f, "<dl>\n");
for (j = 0; j < o->params.n; ++j) {
fprintf(f, "<dt>");
PrintText(f, o->params.p[j].type);
fprintf(f, " <em>");
PrintText(f, o->params.p[j].name);
fprintf(f, "</em>\n");
prefix = xasprintf("%s ", o->params.p[j].name);
for (k = 0; k < o->javadown->tags.n; ++k) {
if (!strcmp(o->javadown->tags.p[k].tag, "param") &&
startswith(o->javadown->tags.p[k].text, prefix)) {
fprintf(f, "<dd>");
PrintText(f, o->javadown->tags.p[k].text + strlen(prefix));
fprintf(f, "\n");
break;
}
}
free(prefix);
}
fprintf(f, "</dl>\n");
} else {
fprintf(f, "<p>None.\n");
}
fprintf(f, "</div>\n");
for (k = 0; k < o->javadown->tags.n; ++k) {
if (!strcmp(o->javadown->tags.p[k].tag, "param")) continue;
fprintf(f, "<p><strong>@");
PrintText(f, o->javadown->tags.p[k].tag);
fprintf(f, "</strong>\n");
if (*o->javadown->tags.p[k].text) {
PrintText(f, o->javadown->tags.p[k].text);
fprintf(f, "\n");
}
}
fprintf(f, "</div>\n");
}
fprintf(f, "</table>\n");
}
/**
* Merges documentation data and outputs HTML.
*/
void drop_dox(const StringArray *files, const char *path) {
FILE *f;
struct Dox *dox;
dox = NewDox();
ReadDox(dox, files);
IndexDox(dox);
f = fopen(path, "w");
PrintDox(dox, f);
fclose(f);
FreeDox(dox);
}

View File

@ -16,6 +16,7 @@
// So it is very easy to lookahead arbitrary number of tokens in this // So it is very easy to lookahead arbitrary number of tokens in this
// parser. // parser.
#include "libc/nexgen32e/ffs.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "third_party/chibicc/chibicc.h" #include "third_party/chibicc/chibicc.h"
@ -50,11 +51,14 @@ typedef struct {
bool is_const; bool is_const;
bool is_tls; bool is_tls;
bool is_weak; bool is_weak;
bool is_ms_abi;
bool is_aligned; bool is_aligned;
bool is_noreturn; bool is_noreturn;
bool is_destructor; bool is_destructor;
bool is_constructor; bool is_constructor;
bool is_externally_visible; bool is_externally_visible;
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int align; int align;
char *section; char *section;
char *visibility; char *visibility;
@ -113,6 +117,8 @@ static Node *current_switch;
static Obj *builtin_alloca; static Obj *builtin_alloca;
static Token *current_javadown;
static Initializer *initializer(Token **, Token *, Type *, Type **); static Initializer *initializer(Token **, Token *, Type *, Type **);
static Member *get_struct_member(Type *, Token *); static Member *get_struct_member(Type *, Token *);
static Node *binor(Token **, Token *); static Node *binor(Token **, Token *);
@ -382,6 +388,10 @@ static Token *type_attributes(Token *tok, void *arg) {
ty->is_packed = true; ty->is_packed = true;
return tok; return tok;
} }
if (consume_attribute(&tok, tok, "ms_abi")) {
ty->is_ms_abi = true;
return tok;
}
if (consume_attribute(&tok, tok, "aligned")) { if (consume_attribute(&tok, tok, "aligned")) {
ty->is_aligned = true; ty->is_aligned = true;
if (CONSUME(&tok, tok, "(")) { if (CONSUME(&tok, tok, "(")) {
@ -466,6 +476,18 @@ static Token *thing_attributes(Token *tok, void *arg) {
attr->is_externally_visible = true; attr->is_externally_visible = true;
return tok; return tok;
} }
if (consume_attribute(&tok, tok, "force_align_arg_pointer")) {
attr->is_force_align_arg_pointer = true;
return tok;
}
if (consume_attribute(&tok, tok, "no_caller_saved_registers")) {
attr->is_no_caller_saved_registers = true;
return tok;
}
if (consume_attribute(&tok, tok, "ms_abi")) {
attr->is_ms_abi = true;
return tok;
}
if (consume_attribute(&tok, tok, "constructor")) { if (consume_attribute(&tok, tok, "constructor")) {
attr->is_constructor = true; attr->is_constructor = true;
if (CONSUME(&tok, tok, "(")) { if (CONSUME(&tok, tok, "(")) {
@ -1080,6 +1102,7 @@ static Node *declaration(Token **rest, Token *tok, Type *basety,
// even if ty is not VLA because ty may be a pointer to VLA // 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.) // (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); cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok);
tok = attribute_list(tok, attr, thing_attributes);
if (ty->kind == TY_VLA) { if (ty->kind == TY_VLA) {
if (EQUAL(tok, "=")) if (EQUAL(tok, "="))
error_tok(tok, "variable-sized object may not be initialized"); error_tok(tok, "variable-sized object may not be initialized");
@ -1198,7 +1221,8 @@ static Member *struct_designator(Token **rest, Token *tok, Type *ty) {
if (tok->kind != TK_IDENT) error_tok(tok, "expected a field designator"); if (tok->kind != TK_IDENT) error_tok(tok, "expected a field designator");
for (Member *mem = ty->members; mem; mem = mem->next) { for (Member *mem = ty->members; mem; mem = mem->next) {
// Anonymous struct member // Anonymous struct member
if (mem->ty->kind == TY_STRUCT && !mem->name) { if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) &&
!mem->name) {
if (get_struct_member(mem->ty, tok)) { if (get_struct_member(mem->ty, tok)) {
*rest = start; *rest = start;
return mem; return mem;
@ -1920,6 +1944,7 @@ int64_t eval(Node *node) {
// number. The latter form is accepted only as an initialization // number. The latter form is accepted only as an initialization
// expression for a global variable. // expression for a global variable.
int64_t eval2(Node *node, char ***label) { int64_t eval2(Node *node, char ***label) {
int64_t x, y;
add_type(node); add_type(node);
if (is_flonum(node->ty)) return eval_double(node); if (is_flonum(node->ty)) return eval_double(node);
switch (node->kind) { switch (node->kind) {
@ -1930,15 +1955,25 @@ int64_t eval2(Node *node, char ***label) {
case ND_MUL: case ND_MUL:
return eval(node->lhs) * eval(node->rhs); return eval(node->lhs) * eval(node->rhs);
case ND_DIV: case ND_DIV:
if (node->ty->is_unsigned) y = eval(node->rhs);
return (uint64_t)eval(node->lhs) / eval(node->rhs); if (!y) error_tok(node->rhs->tok, "constexpr div by zero");
return eval(node->lhs) / eval(node->rhs); if (node->ty->is_unsigned) {
return (uint64_t)eval(node->lhs) / y;
}
x = eval(node->lhs);
if (x == 0x8000000000000000 && y == -1) {
error_tok(node->rhs->tok, "constexpr divide error");
}
return x / y;
case ND_NEG: case ND_NEG:
return -eval(node->lhs); return -eval(node->lhs);
case ND_REM: case ND_REM:
if (node->ty->is_unsigned) y = eval(node->rhs);
return (uint64_t)eval(node->lhs) % eval(node->rhs); if (!y) error_tok(node->rhs->tok, "constexpr rem by zero");
return eval(node->lhs) % eval(node->rhs); if (node->ty->is_unsigned) {
return (uint64_t)eval(node->lhs) % y;
}
return eval(node->lhs) % y;
case ND_BINAND: case ND_BINAND:
return eval(node->lhs) & eval(node->rhs); return eval(node->lhs) & eval(node->rhs);
case ND_BINOR: case ND_BINOR:
@ -2044,8 +2079,9 @@ int64_t eval2(Node *node, char ***label) {
static int64_t eval_rval(Node *node, char ***label) { static int64_t eval_rval(Node *node, char ***label) {
switch (node->kind) { switch (node->kind) {
case ND_VAR: case ND_VAR:
if (node->var->is_local) if (node->var->is_local) {
error_tok(node->tok, "not a compile-time constant"); error_tok(node->tok, "not a compile-time constant");
}
*label = &node->var->name; *label = &node->var->name;
return 0; return 0;
case ND_DEREF: case ND_DEREF:
@ -2063,6 +2099,7 @@ bool is_const_expr(Node *node) {
case ND_SUB: case ND_SUB:
case ND_MUL: case ND_MUL:
case ND_DIV: case ND_DIV:
case ND_REM:
case ND_BINAND: case ND_BINAND:
case ND_BINOR: case ND_BINOR:
case ND_BINXOR: case ND_BINXOR:
@ -2884,8 +2921,9 @@ static Node *postfix(Token **rest, Token *tok) {
static Node *funcall(Token **rest, Token *tok, Node *fn) { static Node *funcall(Token **rest, Token *tok, Node *fn) {
add_type(fn); add_type(fn);
if (fn->ty->kind != TY_FUNC && if (fn->ty->kind != TY_FUNC &&
(fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) (fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) {
error_tok(fn->tok, "not a function"); error_tok(fn->tok, "not a function");
}
Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base; Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base;
Type *param_ty = ty->params; Type *param_ty = ty->params;
Node head = {}; Node head = {};
@ -2896,8 +2934,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
add_type(arg); add_type(arg);
if (!param_ty && !ty->is_variadic) error_tok(tok, "too many arguments"); if (!param_ty && !ty->is_variadic) error_tok(tok, "too many arguments");
if (param_ty) { if (param_ty) {
if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) {
arg = new_cast(arg, param_ty); arg = new_cast(arg, param_ty);
}
param_ty = param_ty->next; param_ty = param_ty->next;
} else if (arg->ty->kind == TY_FLOAT) { } else if (arg->ty->kind == TY_FLOAT) {
// If parameter type is omitted (e.g. in "..."), float // If parameter type is omitted (e.g. in "..."), float
@ -2914,8 +2953,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
node->args = head.next; node->args = head.next;
// If a function returns a struct, it is caller's responsibility // If a function returns a struct, it is caller's responsibility
// to allocate a space for the return value. // to allocate a space for the return value.
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) {
node->ret_buffer = new_lvar("", node->ty); node->ret_buffer = new_lvar("", node->ty);
}
return node; return node;
} }
@ -3138,13 +3178,37 @@ static Node *primary(Token **rest, Token *tok) {
*rest = skip(tok, ')'); *rest = skip(tok, ')');
return node; return node;
} }
if (EQUAL(tok, "__builtin_popcount") || EQUAL(tok, "__builtin_popcountl") || if (EQUAL(tok, "__builtin_popcount")) {
Token *t = skip(tok->next, '(');
Node *node = assign(&t, t);
if (is_const_expr(node)) {
*rest = skip(t, ')');
return new_num(__builtin_popcount(eval(node)), t);
}
}
if (EQUAL(tok, "__builtin_popcountl") ||
EQUAL(tok, "__builtin_popcountll")) { EQUAL(tok, "__builtin_popcountll")) {
Token *t = skip(tok->next, '('); Token *t = skip(tok->next, '(');
Node *node = assign(&t, t); Node *node = assign(&t, t);
if (is_const_expr(node)) { if (is_const_expr(node)) {
*rest = skip(t, ')'); *rest = skip(t, ')');
return new_num(popcnt(eval(node)), t); return new_num(__builtin_popcountl(eval(node)), t);
}
}
if (EQUAL(tok, "__builtin_ffs")) {
Token *t = skip(tok->next, '(');
Node *node = assign(&t, t);
if (is_const_expr(node)) {
*rest = skip(t, ')');
return new_num(__builtin_ffs(eval(node)), t);
}
}
if (EQUAL(tok, "__builtin_ffsl") || EQUAL(tok, "__builtin_ffsll")) {
Token *t = skip(tok->next, '(');
Node *node = assign(&t, t);
if (is_const_expr(node)) {
*rest = skip(t, ')');
return new_num(__builtin_ffsl(eval(node)), t);
} }
} }
} }
@ -3328,15 +3392,19 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern); fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern);
fn->is_inline = attr->is_inline; fn->is_inline = attr->is_inline;
fn->is_weak = attr->is_weak; fn->is_weak = attr->is_weak;
fn->is_ms_abi = attr->is_ms_abi;
fn->is_aligned = attr->is_aligned; fn->is_aligned = attr->is_aligned;
fn->is_noreturn = attr->is_noreturn; fn->is_noreturn = attr->is_noreturn;
fn->is_destructor = attr->is_destructor; fn->is_destructor = attr->is_destructor;
fn->is_constructor = attr->is_constructor; fn->is_constructor = attr->is_constructor;
fn->is_externally_visible = attr->is_externally_visible; fn->is_externally_visible = attr->is_externally_visible;
fn->is_force_align_arg_pointer = attr->is_force_align_arg_pointer;
fn->is_no_caller_saved_registers = attr->is_no_caller_saved_registers;
fn->align = attr->align; fn->align = attr->align;
fn->section = attr->section; fn->section = attr->section;
fn->visibility = attr->visibility; fn->visibility = attr->visibility;
} }
fn->javadown = fn->javadown ?: current_javadown;
fn->is_root = !(fn->is_static && fn->is_inline); fn->is_root = !(fn->is_static && fn->is_inline);
if (consume_attribute(&tok, tok, "asm")) { if (consume_attribute(&tok, tok, "asm")) {
tok = skip(tok, '('); tok = skip(tok, '(');
@ -3352,11 +3420,13 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
// A buffer for a struct/union return value is passed // A buffer for a struct/union return value is passed
// as the hidden first parameter. // as the hidden first parameter.
Type *rty = ty->return_ty; Type *rty = ty->return_ty;
if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) {
new_lvar("", pointer_to(rty)); new_lvar("", pointer_to(rty));
}
fn->params = locals; fn->params = locals;
if (ty->is_variadic) if (ty->is_variadic) {
fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136)); fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136));
}
fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char)); fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char));
tok = skip(tok, '{'); tok = skip(tok, '{');
// [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is // [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is
@ -3382,6 +3452,7 @@ static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
Type *ty = declarator(&tok, tok, basety); Type *ty = declarator(&tok, tok, basety);
if (!ty->name) error_tok(ty->name_pos, "variable name omitted"); if (!ty->name) error_tok(ty->name_pos, "variable name omitted");
Obj *var = new_gvar(get_ident(ty->name), ty); Obj *var = new_gvar(get_ident(ty->name), ty);
var->javadown = current_javadown;
if (consume_attribute(&tok, tok, "asm")) { if (consume_attribute(&tok, tok, "asm")) {
tok = skip(tok, '('); tok = skip(tok, '(');
var->asmname = ConsumeStringLiteral(&tok, tok); var->asmname = ConsumeStringLiteral(&tok, tok);
@ -3528,6 +3599,12 @@ Obj *parse(Token *tok) {
tok = static_assertion(tok); tok = static_assertion(tok);
continue; continue;
} }
if (tok->kind == TK_JAVADOWN) {
current_javadown = tok;
tok = tok->next;
} else {
current_javadown = NULL;
}
VarAttr attr = {}; VarAttr attr = {};
tok = attribute_list(tok, &attr, thing_attributes); tok = attribute_list(tok, &attr, thing_attributes);
Type *basety = declspec(&tok, tok, &attr); Type *basety = declspec(&tok, tok, &attr);

View File

@ -26,11 +26,7 @@
typedef struct CondIncl CondIncl; typedef struct CondIncl CondIncl;
typedef struct Hideset Hideset; typedef struct Hideset Hideset;
typedef struct Macro Macro;
typedef struct MacroArg MacroArg; typedef struct MacroArg MacroArg;
typedef struct MacroParam MacroParam;
typedef Token *macro_handler_fn(Token *);
typedef enum { typedef enum {
STR_NONE, STR_NONE,
@ -40,11 +36,6 @@ typedef enum {
STR_WIDE, STR_WIDE,
} StringKind; } StringKind;
struct MacroParam {
MacroParam *next;
char *name;
};
struct MacroArg { struct MacroArg {
MacroArg *next; MacroArg *next;
char *name; char *name;
@ -52,15 +43,6 @@ struct MacroArg {
Token *tok; Token *tok;
}; };
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. // `#if` can be nested, so we use a stack to manage nested `#if`s.
struct CondIncl { struct CondIncl {
CondIncl *next; CondIncl *next;
@ -74,7 +56,8 @@ struct Hideset {
char *name; char *name;
}; };
static HashMap macros; HashMap macros;
static CondIncl *cond_incl; static CondIncl *cond_incl;
static HashMap pragma_once; static HashMap pragma_once;
static int include_next_idx; static int include_next_idx;
@ -338,21 +321,24 @@ static MacroParam *read_macro_params(Token **rest, Token *tok,
return head.next; return head.next;
} }
static void read_macro_definition(Token **rest, Token *tok) { static Macro *read_macro_definition(Token **rest, Token *tok) {
Macro *m;
char *name;
if (tok->kind != TK_IDENT) error_tok(tok, "macro name must be an identifier"); if (tok->kind != TK_IDENT) error_tok(tok, "macro name must be an identifier");
char *name = strndup(tok->loc, tok->len); name = strndup(tok->loc, tok->len);
tok = tok->next; tok = tok->next;
if (!tok->has_space && EQUAL(tok, "(")) { if (!tok->has_space && EQUAL(tok, "(")) {
// Function-like macro // Function-like macro
char *va_args_name = NULL; char *va_args_name = NULL;
MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name); MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
Macro *m = add_macro(name, false, copy_line(rest, tok)); m = add_macro(name, false, copy_line(rest, tok));
m->params = params; m->params = params;
m->va_args_name = va_args_name; m->va_args_name = va_args_name;
} else { } else {
// Object-like macro // Object-like macro
add_macro(name, true, copy_line(rest, tok)); m = add_macro(name, true, copy_line(rest, tok));
} }
return m;
} }
static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) { static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) {
@ -742,6 +728,12 @@ static Token *preprocess2(Token *tok) {
while (tok->kind != TK_EOF) { while (tok->kind != TK_EOF) {
// If it is a macro, expand it. // If it is a macro, expand it.
if (expand_macro(&tok, tok)) continue; if (expand_macro(&tok, tok)) continue;
// make sure javadown is removed if it's for a macro definition
if (tok->kind == TK_JAVADOWN && is_hash(tok->next) &&
EQUAL(tok->next->next, "define")) {
read_macro_definition(&tok, tok->next->next->next)->javadown = tok;
continue;
}
// Pass through if it is not a "#". // Pass through if it is not a "#".
if (!is_hash(tok)) { if (!is_hash(tok)) {
tok->line_delta = tok->file->line_delta; tok->line_delta = tok->file->line_delta;
@ -936,6 +928,8 @@ __GNUC_PATCHLEVEL__\000\
0\000\ 0\000\
__NO_INLINE__\000\ __NO_INLINE__\000\
16\000\ 16\000\
__GNUC_STDC_INLINE__\000\
1\000\
__BIGGEST_ALIGNMENT__\000\ __BIGGEST_ALIGNMENT__\000\
16\000\ 16\000\
__C99_MACRO_WITH_VA_ARGS\000\ __C99_MACRO_WITH_VA_ARGS\000\
@ -1010,6 +1004,10 @@ __UINT32_MAX__\000\
0xffffffffu\000\ 0xffffffffu\000\
__INT64_MAX__\000\ __INT64_MAX__\000\
0x7fffffffffffffffl\000\ 0x7fffffffffffffffl\000\
__LONG_MAX__\000\
0x7fffffffffffffffl\000\
__LONG_LONG_MAX__\000\
0x7fffffffffffffffl\000\
__UINT64_MAX__\000\ __UINT64_MAX__\000\
0xfffffffffffffffful\000\ 0xfffffffffffffffful\000\
__SIZE_MAX__\000\ __SIZE_MAX__\000\

View File

@ -12,7 +12,8 @@ __attribute__((__nonnull__(1))) void cate2(char *);
__attribute__((__section__(".data.var"))) int var2; __attribute__((__section__(".data.var"))) int var2;
__attribute__((__section__(".data.var"))) int ar2[4]; __attribute__((__section__(".data.var"))) int ar2[4];
int main() { __attribute__((__force_align_arg_pointer__, __no_caller_saved_registers__)) int
main() {
int2 a; int2 a;
ASSERT(64, _Alignof(int2)); ASSERT(64, _Alignof(int2));
ASSERT(64, _Alignof(a)); ASSERT(64, _Alignof(a));

View File

@ -1,3 +1,4 @@
#include "libc/macros.h"
#include "third_party/chibicc/test/test.h" #include "third_party/chibicc/test/test.h"
int main() { int main() {
@ -6,6 +7,11 @@ int main() {
int x[n]; int x[n];
sizeof(x); sizeof(x);
})); }));
ASSERT(5, ({
int n = 5;
int x[n];
ARRAYLEN(x);
}));
ASSERT((5 + 1) * (8 * 2) * 4, ({ ASSERT((5 + 1) * (8 * 2) * 4, ({
int m = 5, n = 8; int m = 5, n = 8;
int x[m + 1][n * 2]; int x[m + 1][n * 2];

View File

@ -470,6 +470,7 @@ Token *tokenize(File *file) {
char *p = file->contents; char *p = file->contents;
Token head = {}; Token head = {};
Token *cur = &head; Token *cur = &head;
struct Javadown *javadown;
at_bol = true; at_bol = true;
has_space = false; has_space = false;
while (*p) { while (*p) {
@ -480,6 +481,22 @@ Token *tokenize(File *file) {
has_space = true; has_space = true;
continue; continue;
} }
// Javadoc-style markdown comments.
if (LOOKINGAT(p, "/**") && p[3] != '/' && p[3] != '*') {
char *q = strstr(p + 3, "*/");
if (!q) error_at(p, "unclosed javadown");
javadown = ParseJavadown(p + 3, q - p - 3 - 2);
if (javadown->isfileoverview) {
FreeJavadown(file->javadown);
file->javadown = javadown;
} else {
cur = cur->next = new_token(TK_JAVADOWN, p, q + 2);
cur->javadown = javadown;
}
p = q + 2;
has_space = true;
continue;
}
// Skip block comments. // Skip block comments.
if (LOOKINGAT(p, "/*")) { if (LOOKINGAT(p, "/*")) {
char *q = strstr(p + 2, "*/"); char *q = strstr(p + 2, "*/");
@ -505,12 +522,13 @@ Token *tokenize(File *file) {
if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) { if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) {
char *q = p++; char *q = p++;
for (;;) { for (;;) {
if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1])) if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1])) {
p += 2; p += 2;
else if (isalnum(*p) || *p == '.') } else if (isalnum(*p) || *p == '.') {
p++; p++;
else } else {
break; break;
}
} }
cur = cur->next = new_token(TK_PP_NUM, q, p); cur = cur->next = new_token(TK_PP_NUM, q, p);
continue; continue;
@ -663,7 +681,30 @@ void remove_backslash_newline(char *p) {
// the logical line number matches the physical one. // the logical line number matches the physical one.
// This counter maintain the number of newlines we have removed. // This counter maintain the number of newlines we have removed.
int n = 0; int n = 0;
bool instring = false;
while (p[i]) { while (p[i]) {
if (instring) {
if (p[i] == '"' && p[i - 1] != '\\') {
instring = false;
}
} else {
if (p[i] == '"') {
instring = true;
} else if (p[i] == '/' && p[i + 1] == '*') {
p[j++] = p[i++];
p[j++] = p[i++];
while (p[i]) {
if (p[i] == '*' && p[i + 1] == '/') {
p[j++] = p[i++];
p[j++] = p[i++];
break;
} else {
p[j++] = p[i++];
}
}
continue;
}
}
if (p[i] == '\\' && p[i + 1] == '\n') { if (p[i] == '\\' && p[i + 1] == '\n') {
i += 2; i += 2;
n++; n++;

View File

@ -4,19 +4,28 @@
/** /**
* Returns (by copy) a struct containing various summary statistics: * Returns (by copy) a struct containing various summary statistics:
* *
* arena: current total non-mmapped bytes allocated from system * - arena: current total non-mmapped bytes allocated from system
* ordblks: the number of free chunks *
* smblks: always zero. * - ordblks: the number of free chunks
* hblks: current number of mmapped regions *
* hblkhd: total bytes held in mmapped regions * - smblks: always zero.
* usmblks: the maximum total allocated space. This will be greater *
* than current total if trimming has occurred. * - hblks: current number of mmapped regions
* fsmblks: always zero *
* uordblks: current total allocated space (normal or mmapped) * - hblkhd: total bytes held in mmapped regions
* fordblks: total free space *
* keepcost: the maximum number of bytes that could ideally be released * - usmblks: the maximum total allocated space. This will be greater
* back to system via malloc_trim. ("ideally" means that * than current total if trimming has occurred.
* it ignores page restrictions etc.) *
* - fsmblks: always zero
*
* - uordblks: current total allocated space (normal or mmapped)
*
* - fordblks: total free space
*
* - keepcost: the maximum number of bytes that could ideally be
* released back to system via malloc_trim. ("ideally" means that it
* ignores page restrictions etc.)
* *
* Because these fields are ints, but internal bookkeeping may * Because these fields are ints, but internal bookkeeping may
* be kept as longs, the reported values may wrap around zero and * be kept as longs, the reported values may wrap around zero and

View File

@ -227,13 +227,13 @@ static int GlobPredicate(const void *a, const void *b) {
* *
* For example: * For example:
* *
* glob_t g = {.gl_offs = 2}; * glob_t g = {.gl_offs = 2};
* glob("*.*", GLOB_DOOFFS, NULL, &g); * glob("*.*", GLOB_DOOFFS, NULL, &g);
* glob("../.*", GLOB_DOOFFS | GLOB_APPEND, NULL, &g); * glob("../.*", GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
* g.gl_pathv[0] = "ls"; * g.gl_pathv[0] = "ls";
* g.gl_pathv[1] = "-l"; * g.gl_pathv[1] = "-l";
* execvp("ls", &g.gl_pathv[0]); * execvp("ls", &g.gl_pathv[0]);
* globfree(g); * globfree(g);
* *
* @param pat can have star wildcard see fnmatch() * @param pat can have star wildcard see fnmatch()
* @param g will receive matching entries and needs globfree() * @param g will receive matching entries and needs globfree()

View File

@ -2387,10 +2387,10 @@ static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node,
/** /**
* Compiles regular expression, e.g. * Compiles regular expression, e.g.
* *
* regex_t rx; * regex_t rx;
* EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z\x7f-\uFFFF]{2}$", REG_EXTENDED)); * EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z]{2}$", REG_EXTENDED));
* EXPECT_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0)); * EXPECT_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0));
* regfree(&rx); * regfree(&rx);
* *
* @param preg points to state, and needs regfree() afterwards * @param preg points to state, and needs regfree() afterwards
* @param regex is utf-8 regular expression string * @param regex is utf-8 regular expression string

View File

@ -38,11 +38,11 @@
* even in the case of corrupted input. * even in the case of corrupted input.
*/ */
#define ZLIB_VERSION "1.2.11" #define ZLIB_VERSION "1.2.11"
#define ZLIB_VERNUM 0x12b0 #define ZLIB_VERNUM 0x12b0
#define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MAJOR 1
#define ZLIB_VER_MINOR 2 #define ZLIB_VER_MINOR 2
#define ZLIB_VER_REVISION 11 #define ZLIB_VER_REVISION 11
#define ZLIB_VER_SUBREVISION 0 #define ZLIB_VER_SUBREVISION 0
/** /**
@ -81,25 +81,25 @@
* (particularly if the decompressor wants to decompress everything in a * (particularly if the decompressor wants to decompress everything in a
* single step). * single step).
*/ */
#define Z_NO_FLUSH 0 #define Z_NO_FLUSH 0
#define Z_PARTIAL_FLUSH 1 #define Z_PARTIAL_FLUSH 1
#define Z_SYNC_FLUSH 2 #define Z_SYNC_FLUSH 2
#define Z_FULL_FLUSH 3 #define Z_FULL_FLUSH 3
#define Z_FINISH 4 #define Z_FINISH 4
#define Z_BLOCK 5 #define Z_BLOCK 5
#define Z_TREES 6 #define Z_TREES 6
/** /**
* Allowed flush values; see deflate() and inflate() below for details. * Allowed flush values; see deflate() and inflate() below for details.
*/ */
#define Z_OK 0 #define Z_OK 0
#define Z_STREAM_END 1 #define Z_STREAM_END 1
#define Z_NEED_DICT 2 #define Z_NEED_DICT 2
#define Z_ERRNO (-1) #define Z_ERRNO (-1)
#define Z_STREAM_ERROR (-2) #define Z_STREAM_ERROR (-2)
#define Z_DATA_ERROR (-3) #define Z_DATA_ERROR (-3)
#define Z_MEM_ERROR (-4) #define Z_MEM_ERROR (-4)
#define Z_BUF_ERROR (-5) #define Z_BUF_ERROR (-5)
#define Z_VERSION_ERROR (-6) #define Z_VERSION_ERROR (-6)
/** /**
@ -107,26 +107,26 @@
* values are errors, positive values are used for special but normal * values are errors, positive values are used for special but normal
* events. * events.
*/ */
#define Z_NO_COMPRESSION 0 #define Z_NO_COMPRESSION 0
#define Z_BEST_SPEED 1 #define Z_BEST_SPEED 1
#define Z_BEST_COMPRESSION 9 #define Z_BEST_COMPRESSION 9
#define Z_DEFAULT_COMPRESSION (-1) #define Z_DEFAULT_COMPRESSION (-1)
/** /**
* Compression levels. * Compression levels.
*/ */
#define Z_FILTERED 1 #define Z_FILTERED 1
#define Z_HUFFMAN_ONLY 2 #define Z_HUFFMAN_ONLY 2
#define Z_RLE 3 #define Z_RLE 3
#define Z_FIXED 4 #define Z_FIXED 4
#define Z_DEFAULT_STRATEGY 0 #define Z_DEFAULT_STRATEGY 0
/** /**
* Compression strategy; see deflateInit2() below for details * Compression strategy; see deflateInit2() below for details
*/ */
#define Z_BINARY 0 #define Z_BINARY 0
#define Z_TEXT 1 #define Z_TEXT 1
#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
#define Z_UNKNOWN 2 #define Z_UNKNOWN 2
/** /**
@ -1139,7 +1139,7 @@ int inflateBack(z_streamp strm, in_func in, void *in_desc, out_func out,
*/ */
int inflateBackEnd(z_streamp strm); int inflateBackEnd(z_streamp strm);
/** /*
* Return flags indicating compile-time options. * Return flags indicating compile-time options.
* *
* Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: * Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
@ -1160,10 +1160,13 @@ int inflateBackEnd(z_streamp strm);
* 14,15: 0 (reserved) * 14,15: 0 (reserved)
* *
* Library content (indicates missing functionality): * Library content (indicates missing functionality):
*
* 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking * 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
* deflate code when not needed) * deflate code when not needed)
* 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect *
* and decode gzip streams (to avoid linking crc code) * 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't
* detect and decode gzip streams (to avoid linking crc code)
*
* 18-19: 0 (reserved) * 18-19: 0 (reserved)
* *
* Operation variations (changes in library functionality): * Operation variations (changes in library functionality):
@ -1685,12 +1688,11 @@ uLong adler32_combine(uLong adler1, uLong adler2, int64_t len2);
* *
* Usage example: * Usage example:
* *
* uLong crc = crc32(0L, Z_NULL, 0); * uLong crc = crc32(0L, Z_NULL, 0);
* * while (read_buffer(buffer, length) != EOF) {
* while (read_buffer(buffer, length) != EOF) { * crc = crc32(crc, buffer, length);
* crc = crc32(crc, buffer, length); * }
* } * if (crc != original_crc) error();
* if (crc != original_crc) error();
*/ */
uLong crc32(uLong crc, const Bytef *buf, uInt len); uLong crc32(uLong crc, const Bytef *buf, uInt len);

View File

@ -0,0 +1,276 @@
/*-*- 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/limits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "tool/build/lib/javadown.h"
#define FILEOVERVIEW "@fileoverview"
struct Lines {
size_t n;
struct Line {
char *p;
size_t n;
} * p;
};
static char *SkipEmptyFirstLine(char *p) {
int i = 0;
while (p[i] == ' ' || p[i] == '\t') ++i;
return p[i] == '\n' ? p + i + 1 : p;
}
static void DeleteLastEmptyLine(char *p, size_t n) {
while (n && (p[n - 1] == ' ' || p[n - 1] == '\t')) --n;
if (n && p[n - 1] == '\n') p[n - 1] = '\0';
}
static void AppendLine(struct Lines *lines) {
lines->p = realloc(lines->p, ++lines->n * sizeof(*lines->p));
memset(lines->p + lines->n - 1, 0, sizeof(*lines->p));
}
static void AppendTag(struct JavadownTags *tags) {
tags->p = realloc(tags->p, ++tags->n * sizeof(*tags->p));
memset(tags->p + tags->n - 1, 0, sizeof(*tags->p));
}
static unsigned GetSpacePrefixLen(const char *p, size_t n) {
int i;
for (i = 0; i < n; ++i) {
if (p[i] != ' ' && p[i] != '\t') {
break;
}
}
return i;
}
static unsigned GetSpaceStarPrefixLen(const char *p, size_t n) {
int i;
i = GetSpacePrefixLen(p, n);
return i < n && (p[i] == '*') ? i + 1 : 0;
}
static unsigned GetTagLen(const char *p, size_t n) {
int i;
for (i = 0; i < n; ++i) {
if (!islower(p[i])) {
break;
}
}
return i;
}
static unsigned GetMinPrefixLen(struct Lines *lines,
unsigned GetPrefixLen(const char *, size_t)) {
int i;
unsigned n, m;
for (m = -1, i = 0; i < lines->n; ++i) {
if (lines->p[i].n) {
n = GetPrefixLen(lines->p[i].p, lines->p[i].n);
if (n < m) m = n;
}
}
return m == -1 ? 0 : m;
}
static void RemovePrefixes(struct Lines *lines, unsigned m) {
int i;
for (i = 0; i < lines->n; ++i) {
if (m <= lines->p[i].n) {
lines->p[i].p += m;
lines->p[i].n -= m;
}
}
}
static void SplitLines(struct Lines *lines, char *p) {
char *q;
DeleteLastEmptyLine(p, strlen(p));
p = SkipEmptyFirstLine(p);
for (;;) {
AppendLine(lines);
lines->p[lines->n - 1].p = p;
lines->p[lines->n - 1].n = (q = strchr(p, '\n')) ? q - p : strlen(p);
if (!q) break;
p = q + 1;
}
RemovePrefixes(lines, GetMinPrefixLen(lines, GetSpaceStarPrefixLen));
RemovePrefixes(lines, GetMinPrefixLen(lines, GetSpacePrefixLen));
}
static bool ConsumeFileOverview(struct Lines *lines) {
int i;
if (lines->n && lines->p[0].n >= strlen(FILEOVERVIEW) &&
startswith(lines->p[0].p, FILEOVERVIEW)) {
lines->p[0].p += strlen(FILEOVERVIEW);
lines->p[0].n -= strlen(FILEOVERVIEW);
while (lines->p[0].n &&
(lines->p[0].p[0] == ' ' || lines->p[0].p[0] == '\t')) {
lines->p[0].p += 1;
lines->p[0].n -= 1;
}
return true;
} else {
return false;
}
}
static void RemoveTrailingWhitespace(struct Lines *lines) {
int i;
for (i = 0; i < lines->n; ++i) {
while (lines->p[i].n && isspace(lines->p[i].p[lines->p[i].n - 1])) {
--lines->p[i].n;
}
}
}
static int ExtractTitle(struct Javadown *jd, struct Lines *lines) {
int i;
char *p;
size_t n;
for (p = NULL, n = i = 0; i < lines->n; ++i) {
if (!lines->p[i].n) {
++i;
break;
}
if (*lines->p[i].p == '@') {
break;
}
if (i) {
p = realloc(p, ++n);
p[n - 1] = ' ';
}
p = realloc(p, n + lines->p[i].n);
memcpy(p + n, lines->p[i].p, lines->p[i].n);
n += lines->p[i].n;
}
p = realloc(p, n + 1);
p[n] = '\0';
jd->title = p;
return i;
}
static int ExtractText(struct Javadown *jd, struct Lines *lines, int i) {
int j;
char *p;
size_t n;
for (p = NULL, n = j = 0; i + j < lines->n; ++j) {
if (lines->p[i + j].n && lines->p[i + j].p[0] == '@') break;
if (j) {
p = realloc(p, ++n);
p[n - 1] = '\n';
}
p = realloc(p, n + lines->p[i + j].n);
memcpy(p + n, lines->p[i + j].p, lines->p[i + j].n);
n += lines->p[i + j].n;
}
p = realloc(p, n + 1);
p[n] = '\0';
jd->text = p;
return i;
}
static void ExtractTags(struct Javadown *jd, struct Lines *lines, int i) {
size_t n;
char *p, *tag, *text, *p2;
unsigned taglen, textlen, n2;
for (p = NULL, n = 0; i < lines->n; ++i) {
if (!lines->p[i].n) continue;
if (lines->p[i].p[0] != '@') continue;
tag = lines->p[i].p + 1;
taglen = GetTagLen(tag, lines->p[i].n - 1);
if (!taglen) continue;
text = tag + taglen;
tag = strndup(tag, taglen);
textlen = lines->p[i].n - 1 - taglen;
while (textlen && isspace(*text)) {
++text;
--textlen;
}
text = strndup(text, textlen);
while (i + 1 < lines->n &&
(!lines->p[i + 1].n || lines->p[i + 1].p[0] != '@')) {
++i;
p2 = lines->p[i].p;
n2 = lines->p[i].n;
if (n2 && *p2 == '\t') {
p2 += 1;
n2 -= 1;
}
if (n2 >= 4 && !memcmp(p2, " ", 4)) {
p2 += 4;
n2 -= 4;
}
text = realloc(text, textlen + 1 + n2 + 1);
text[textlen] = '\n';
memcpy(text + textlen + 1, p2, n2);
textlen += 1 + n2;
text[textlen] = '\0';
}
AppendTag(&jd->tags);
jd->tags.p[jd->tags.n - 1].tag = tag;
jd->tags.p[jd->tags.n - 1].text = text;
}
}
/**
* Parses javadown.
*
* @param data should point to text inside the slash star markers
* @param size is length of data in bytes
* @return object that should be passed to FreeJavadown()
*/
struct Javadown *ParseJavadown(const char *data, size_t size) {
int i;
char *p;
struct Lines lines;
struct Javadown *jd;
memset(&lines, 0, sizeof(lines));
jd = calloc(1, sizeof(struct Javadown));
p = strndup(data, size);
SplitLines(&lines, p);
RemoveTrailingWhitespace(&lines);
jd->isfileoverview = ConsumeFileOverview(&lines);
i = ExtractTitle(jd, &lines);
i = ExtractText(jd, &lines, i);
ExtractTags(jd, &lines, i);
free(lines.p);
free(p);
return jd;
}
/**
* Frees object returned by ParseJavadown().
*/
void FreeJavadown(struct Javadown *jd) {
int i;
if (jd) {
for (i = 0; i < jd->tags.n; ++i) {
free(jd->tags.p[i].tag);
free(jd->tags.p[i].text);
}
free(jd->tags.p);
free(jd->title);
free(jd->text);
free(jd);
}
}

View File

@ -0,0 +1,24 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Javadown {
bool isfileoverview;
char *title;
char *text;
struct JavadownTags {
size_t n;
struct JavadownTag {
char *tag;
char *text;
} * p;
} tags;
};
struct Javadown *ParseJavadown(const char *, size_t);
void FreeJavadown(struct Javadown *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_ */

View File

@ -43,6 +43,27 @@
"\\)*>") "\\)*>")
0 ,c-doc-markup-face-name prepend nil))) 0 ,c-doc-markup-face-name prepend nil)))
;; (defconst javadown-font-lock-doc-comments
;; `(("{@[a-z]+[^}\n\r]*}" ; "{@foo ...}" markup.
;; 0 ,c-doc-markup-face-name prepend nil)
;; ("^\\(/\\*\\)?\\(\\s \\|\\*\\)*\\(@[a-z]+\\)" ; "@foo ..." markup.
;; 3 ,c-doc-markup-face-name prepend nil)
;; (,(concat "</?\\sw" ; HTML tags.
;; "\\("
;; (concat "\\sw\\|\\s \\|[=\n\r*.:]\\|"
;; "\"[^\"]*\"\\|'[^']*'")
;; "\\)*>")
;; 0 ,c-doc-markup-face-name prepend nil)
;; ;; ("&\\(\\sw\\|[.:]\\)+;" ; HTML entities.
;; ;; 0 ,c-doc-markup-face-name prepend nil)
;; ;; Fontify remaining markup characters as invalid. Note
;; ;; that the Javadoc spec is hazy about when "@" is
;; ;; allowed in non-markup use. [jart: we like markdown]
;; ;; (,(lambda (limit)
;; ;; (c-find-invalid-doc-markup "[<>&]\\|{@" limit))
;; ;; 0 'font-lock-warning-face prepend nil)
;; ))
(defconst javadown-font-lock-keywords (defconst javadown-font-lock-keywords
`((,(lambda (limit) `((,(lambda (limit)
(c-font-lock-doc-comments "/\\*\\*" limit (c-font-lock-doc-comments "/\\*\\*" limit