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

View File

@ -19,23 +19,23 @@
#define IMAGE_BASE_PHYSICAL 0x100000
#endif
#ifndef REAL_SCRATCH_AREA
/**
* Location of anything goes memory for real mode.
*
* 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.
*/
#ifndef REAL_SCRATCH_AREA
#define REAL_SCRATCH_AREA 0x40000
#endif
#ifndef REAL_STACK_FRAME
/**
* Location of real mode 64kb stack frame.
*
* This address was chosen because memory beyond 0x80000 can't be
* accessed safely without consulting e820.
*/
#ifndef REAL_STACK_FRAME
#define REAL_STACK_FRAME 0x70000
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,5 +17,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
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"
/**
* Returns a or b or aborts if both are null.
*/
const char *(firstnonnull)(const char *a, const char *b) {
if (a) return a;
if (b) return b;

View File

@ -19,6 +19,11 @@
*/
#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) {
return x ^ (x >> 1);
}

View File

@ -21,6 +21,7 @@
/**
* Counts number of different bits.
* @see https://en.wikipedia.org/wiki/Hamming_code
*/
unsigned long hamming(unsigned long x, unsigned long 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.
*
* @see morton()
* @see https://en.wikipedia.org/wiki/Hilbert_curve
* @see unhilbert()
*/
long hilbert(long n, long y, long x) {
axdx_t m;
@ -56,7 +57,8 @@ long hilbert(long n, long y, long x) {
/**
* 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 m;

View File

@ -18,4 +18,9 @@
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.
* @see https://en.wikipedia.org/wiki/Z-order_curve
* @see unmorton()
*/
unsigned long(morton)(unsigned long y, unsigned long x) {
x = (x | x << 020) & 0x0000FFFF0000FFFF;

View File

@ -17,5 +17,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
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"
/**
* Returns number of bits set in integer.
*/
uint64_t(popcnt)(uint64_t x) {
x = x - ((x >> 1) & 0x5555555555555555);
x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333);

View File

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

View File

@ -19,4 +19,9 @@
*/
#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"
/**
* Rounds up 𝑤 to next two power 𝑘.
*/
long(roundup)(long w, long k) {
return ROUNDUP(w, k);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@
* char buf[2];
* int ws, pid, fds[3] = {-1, -1, STDERR_FILENO};
* CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"),
* (char *const[]){"ssh", hostname, "cat", NULL},
* (char *const[]){"ssh", hostname, "cat", 0},
* environ)));
* CHECK_EQ(+2, write(fds[0], "hi", 2));
* CHECK_NE(-1, close(fds[0]));
@ -49,6 +49,7 @@
* @param envp[0,n-2] specifies "foo=bar" environment variables
* @param envp[n-1] is NULL
* @return pid of child, or -1 w/ errno
* @deprecated just use vfork() and execve()
*/
int spawnve(unsigned flags, int stdiofds[3], const char *program,
char *const argv[], char *const envp[]) {

View File

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

View File

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

View File

@ -32,7 +32,8 @@
* &(struct sigaction){.sa_sigaction = missingno},
* NULL));
* CHECK_NE(-1, setitimer(ITIMER_REAL,
* &(const struct itimerval){{1, 500000}, {1, 500000}},
* &(const struct itimerval){{1, 500000},
* {1, 500000}},
* NULL));
*
* Set single-shot 50ms timer callback to interrupt laggy connect():

View File

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

View File

@ -18,10 +18,6 @@
#define __far
#endif
/**
* Gets type of expression.
* @see autotype() which is a better alternative for certain use cases
*/
#if !defined(__GNUC__) && __cplusplus + 0 >= 201103L
#define typeof(x) decltype(x)
#elif (defined(__STRICT_ANSI__) || !defined(__GNUC__)) && \
@ -143,35 +139,10 @@ typedef __UINT32_TYPE__ uint32_t;
typedef __INT64_TYPE__ int64_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 {
intptr_t ax, dx;
} 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__
#undef __SIZEOF_INTMAX__
#endif
@ -210,11 +181,6 @@ typedef uint64_t uintmax_t;
#define mallocesque reallocesque returnspointerwithnoaliases
#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 __STRICT_ANSI__
#define pureconst __attribute__((__const__))
@ -223,9 +189,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Aligns automatic or static variable.
*/
#ifndef forcealign
#ifndef __STRICT_ANSI__
#define forcealign(bytes) __attribute__((__aligned__(bytes)))
@ -234,21 +197,12 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Disables alignment.
* @param opt_bytes defaults to __BIGGEST_ALIGNMENT__
* @see nosideeffect
*/
#ifndef __STRICT_ANSI__
#define thatispacked __attribute__((__packed__))
#else
#define thatispacked
#endif
/**
* Declares prototype as using well-known format string DSL.
* Thereby allowing compiler to identify certain bugs.
*/
#ifndef __STRICT_ANSI__
#define printfesque(n) __attribute__((__format__(__gnu_printf__, n, n + 1)))
#define scanfesque(n) __attribute__((__format__(__gnu_scanf__, n, n + 1)))
@ -297,11 +251,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares prototype as never mutating global memory.
* Thus enabling CSE, DCE, LICM [clang-only?], etc. optimizations.
* @see pureconst
*/
#ifndef nosideeffect
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__pure__) || \
@ -332,18 +281,6 @@ typedef uint64_t uintmax_t;
#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
#ifdef __cplusplus
#define forceinline inline
@ -372,19 +309,6 @@ typedef uint64_t uintmax_t;
#endif /* __cplusplus */
#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
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__may_alias__) || \
@ -395,11 +319,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares prototype as returning freeable resource.
* Compilation will fail if caller ignores return value.
* @see gc(), free(), close(), etc.
*/
#ifndef nodiscard
#if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 304 || \
@ -410,10 +329,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares variadic function as needing NULL sentinel argument.
* @see execl() for example
*/
#ifndef nullterminated
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__sentinel__) || __GNUC__ >= 4)
@ -448,10 +363,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Relocates function to .text.unlikely section of binary.
* @note can be used to minimize page-faults and improve locality
*/
#ifndef relegated
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__cold__) || \
@ -470,11 +381,6 @@ typedef uint64_t uintmax_t;
#define warnifused(s)
#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
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__hot__) || \
@ -485,12 +391,6 @@ typedef uint64_t uintmax_t;
#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
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__nonnull__) || \
@ -501,22 +401,12 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares array argument w/ minimum size contract, e.g.
*
* int foo(int bar[hasatleast 2]) { ... }
*/
#if __STDC_VERSION__ + 0 >= 199901L
#define hasatleast static
#else
#define hasatleast
#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 !defined(__STRICT_ANSI__) && !defined(__cplusplus) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 301 || defined(_MSC_VER))
@ -527,10 +417,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares prototype that can't mutate caller's static variables.
* @note consider more .c files or declare in function
*/
#ifndef nocallback
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__leaf__) || \
@ -554,11 +440,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Asks compiler to not optimize function definition.
*
* @todo this is dangerous delete?
*/
#ifndef nooptimize
#ifndef __STRICT_ANSI__
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -572,13 +453,6 @@ typedef uint64_t uintmax_t;
#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 __STRICT_ANSI__
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -592,15 +466,6 @@ typedef uint64_t uintmax_t;
#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
#if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
@ -611,9 +476,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Declares prototype that behaves similar to setjmp() or vfork().
*/
#ifndef returnstwice
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__returns_twice__) || \
@ -624,10 +486,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Asks compiler to not emit DWARF assembly for function.
* @see artificial
*/
#ifndef nodebuginfo
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__nodebug__) || defined(__llvm__))
@ -637,10 +495,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Associates debug information with call site.
* @see nodebuginfo
*/
#ifndef artificial
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__artificial__) || \
@ -651,11 +505,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Defines function as specially compiled for newer cpu model.
* @see -ftree-vectorize and consider assembly
* @see libc/dce.h
*/
#ifndef microarchitecture
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__target__) || \
@ -666,10 +515,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Compiles function multiple times for different cpu models.
* @see libc/dce.h
*/
#ifndef targetclones
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__target_clones__) || __GNUC__ >= 6)
@ -679,10 +524,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Defines function with prologue that fixes misaligned stack.
* @see nocallersavedregisters and consider assembly
*/
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 408 || \
__has_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"
#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
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__returns_nonnull__) || \
@ -706,13 +541,6 @@ typedef uint64_t uintmax_t;
#endif
#endif
/**
* Attests return value is aligned.
*
* @param (alignment)
* @param (alignment, misalignment)
* @see attributeallocalign(), returnspointerwithnoaliases, mallocesque
*/
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__assume_aligned__) || \
(__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 409)
@ -721,10 +549,6 @@ typedef uint64_t uintmax_t;
#define returnsaligned(x)
#endif
/**
* Declares prototype as behaving similar to malloc().
* @see attributeallocsize(), attributeallocalign()
*/
#ifndef returnspointerwithnoaliases
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__malloc__) || \
@ -757,20 +581,6 @@ typedef uint64_t uintmax_t;
#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
#define autotype(x) auto
#elif ((__has_builtin(auto_type) || defined(__llvm__) || \
@ -781,27 +591,16 @@ typedef uint64_t uintmax_t;
#define autotype(x) typeof(x)
#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__)
#define nocallersavedregisters __attribute__((__no_caller_saved_registers__))
#else
#define nocallersavedregisters "need modern compiler"
#endif
/**
* Attests that execution of statement is impossible.
*/
#ifndef unreachable
#define unreachable __builtin_unreachable()
#endif
/**
* Statement that does nothing.
* @note can help avoid drama w/ linters, warnings, formatters, etc.
*/
#define donothing \
do { \
} while (0)
@ -857,9 +656,6 @@ typedef uint64_t uintmax_t;
#define initarray _Section(".init_array")
#endif
/**
* Systemic suppressions.
*/
#ifndef __STRICT_ANSI__
#if defined(__GNUC__) || defined(__llvm__)
#pragma GCC diagnostic ignored "-Wsign-compare" /* lint needs to change */
@ -908,17 +704,6 @@ typedef uint64_t uintmax_t;
#endif /* !GCC && LLVM */
#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 __STRICT_ANSI__
#if defined(__GNUC__) || defined(__llvm__)
@ -965,10 +750,6 @@ typedef uint64_t uintmax_t;
#endif /* ANSI */
#endif /* -w */
/**
* Sets manual breakpoint.
* @see showcrashreports() for auto gdb attach
*/
#define DebugBreak() asm("int3")
#define VEIL(CONSTRAINT, EXPRESSION) \
@ -991,10 +772,6 @@ typedef uint64_t uintmax_t;
0; \
})
/**
* Pulls another module, by symbol, into linkage.
* @note nop is discarded by ape/ape.lds
*/
#define YOINK(SYMBOL) \
do { \
_Static_assert(!__builtin_types_compatible_p(typeof(SYMBOL), char[]), \
@ -1006,29 +783,15 @@ typedef uint64_t uintmax_t;
: "X"(SYMBOL)); \
} while (0)
/**
* Pulls another module into linkage from top-level scope.
* @note nop is discarded by ape/ape.lds
*/
#define STATIC_YOINK(SYMBOLSTR) \
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__)
#define STATIC_YOINK_SOURCE(PATH) STATIC_YOINK(PATH)
#else
#define STATIC_YOINK_SOURCE(PATH)
#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__
STATIC_YOINK_SOURCE(__BASE_FILE__);
#endif

View File

@ -67,4 +67,4 @@
#define __INT_FAST64_TYPE__ __INT64_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
#ifndef __COUNTER__

View File

@ -289,11 +289,6 @@ void sincosl(long double, long double *, long double *);
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 fld1() __X87_CONST(fld1, 0x8p-3)
#define fldpi() __X87_CONST(fldpi, M_PI)

View File

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

View File

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

View File

@ -3,9 +3,29 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
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 ffsl(long int) pureconst;
int ffsll(long long int) pureconst;
int ffsl(long) 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_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

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

View File

@ -29,13 +29,6 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
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];
COSMOPOLITAN_C_END_

View File

@ -2,6 +2,9 @@
#define COSMOPOLITAN_LIBC_NT_THUNK_MSABI_H_
#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.
*
@ -9,13 +12,10 @@
* generate code that calls MS ABI functions directly, without needing
* 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__))
#endif
/**
/*
* Returns true if header should provide MS-ABI overrides.
*/
#ifndef ShouldUseMsabiAttribute

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@
* Prepares static search buffer.
*
* 1. If SRC is too long, it's truncated and *not* NUL-terminated.
*
* 2. If SRC is too short, the remainder is zero-filled.
*
* 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,
testfn_t warmup) {
/**
/*
* getpid() calls are inserted to help visually see tests in traces
* which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
*

View File

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

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_isunordered, __builtin_islessgreater, 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 building -x assembler-with-cpp a.k.a. .S files
- support profiling w/ -mcount / -mfentry / -mnop-mcount
- improve error messages to trace macro expansions
- reduce #lines of generated assembly by a third
- reduce #bytes of generated binary by a third
- report divide errors in constexprs
local bug fixes
- allow casted values to be lvalues
- permit remainder operator in constexprs
- permit parentheses around string-initializer
- 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
- 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
@ -35,6 +39,8 @@ local bug fixes
local changes
- use tabs in generated output
- parse javadoc-style markdown comments
- don't fold backslash newline in comments
- generated code no longer assumes red zone
- emit .size directives for function definitions
- use fisttp long double conversions if built w/ -msse3

View File

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

View File

@ -32,6 +32,7 @@ bool opt_verbose;
static bool opt_A;
static bool opt_E;
static bool opt_J;
static bool opt_M;
static bool opt_MD;
static bool opt_MMD;
@ -202,6 +203,8 @@ static void parse_args(int argc, char **argv) {
opt_c = true;
} else if (!strcmp(argv[i], "-E")) {
opt_E = true;
} else if (!strcmp(argv[i], "-J")) {
opt_J = true;
} else if (!strcmp(argv[i], "-A")) {
opt_A = true;
} else if (!strcmp(argv[i], "-I")) {
@ -364,7 +367,14 @@ static char *create_tmpfile(void) {
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 (opt_hash_hash_hash) {
fprintf(stderr, "%s", argv[0]);
@ -384,13 +394,10 @@ static void run_subprocess(char **argv) {
break;
}
}
if (status != 0) {
opt_save_temps = true;
exit(1);
}
return !status;
}
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 *));
memcpy(args, argv, argc * sizeof(char *));
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++] = output;
}
run_subprocess(args);
return run_subprocess(args);
}
static void print_token(FILE *out, Token *tok) {
@ -540,6 +547,10 @@ static void cc1(void) {
print_ast(stdout, prog);
return;
}
if (opt_J) {
output_javadown(output_file, prog);
return;
}
FILE *out = open_file(output_file);
codegen(prog, out);
fclose(out);
@ -561,7 +572,7 @@ static void assemble(char *input, char *output) {
strarray_push(&arr, "-o");
strarray_push(&arr, output);
strarray_push(&arr, NULL);
run_subprocess(arr.data);
handle_exit(run_subprocess(arr.data));
}
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, NULL);
run_subprocess(arr.data);
handle_exit(run_subprocess(arr.data));
}
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");
}
StringArray ld_args = {};
StringArray dox_args = {};
for (int i = 0; i < input_paths.len; i++) {
char *input = input_paths.data[i];
if (!strncmp(input, "-l", 2)) {
@ -647,25 +659,33 @@ int chibicc(int argc, char **argv) {
assert(type == FILE_C || type == FILE_ASM_CPP);
// Just preprocess
if (opt_E || opt_M) {
run_cc1(argc, argv, input, NULL);
handle_exit(run_cc1(argc, argv, input, NULL));
continue;
}
// Compile
if (opt_S) {
run_cc1(argc, argv, input, output);
handle_exit(run_cc1(argc, argv, input, output));
continue;
}
// Compile and assemble
if (opt_c) {
char *tmp = create_tmpfile();
run_cc1(argc, argv, input, tmp);
handle_exit(run_cc1(argc, argv, input, tmp));
assemble(tmp, output);
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
char *tmp1 = create_tmpfile();
char *tmp2 = create_tmpfile();
run_cc1(argc, argv, input, tmp1);
handle_exit(run_cc1(argc, argv, input, tmp1));
assemble(tmp1, tmp2);
strarray_push(&ld_args, tmp2);
continue;
@ -673,5 +693,8 @@ int chibicc(int argc, char **argv) {
if (ld_args.len > 0) {
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;
}

View File

@ -26,6 +26,7 @@
#include "libc/unicode/unicode.h"
#include "libc/x/x.h"
#include "third_party/gdtoa/gdtoa.h"
#include "tool/build/lib/javadown.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -35,7 +36,10 @@ typedef struct Asm Asm;
typedef struct AsmOperand AsmOperand;
typedef struct File File;
typedef struct FpClassify FpClassify;
typedef struct HashMap HashMap;
typedef struct Hideset Hideset;
typedef struct Macro Macro;
typedef struct MacroParam MacroParam;
typedef struct Member Member;
typedef struct Node Node;
typedef struct Obj Obj;
@ -47,6 +51,8 @@ typedef struct Token Token;
typedef struct TokenStack TokenStack;
typedef struct Type Type;
typedef Token *macro_handler_fn(Token *);
//
// strarray.c
//
@ -70,6 +76,7 @@ typedef enum {
TK_STR, // String literals
TK_NUM, // Numeric literals
TK_PP_NUM, // Preprocessing numbers
TK_JAVADOWN, // /** ... */ comments
TK_EOF, // End-of-file markers
} TokenKind;
@ -80,6 +87,7 @@ struct File {
// For #line directive
char *display_name;
int line_delta;
struct Javadown *javadown;
};
struct thatispacked Token {
@ -96,6 +104,7 @@ struct thatispacked Token {
char *filename; // Filename
Hideset *hideset; // For macro expansion
Token *origin; // If this is expanded from a macro, the original token
struct Javadown *javadown;
union {
int64_t val; // 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
//
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 *);
void init_macros(void);
void define_macro(char *, char *);
@ -232,6 +258,7 @@ struct Obj {
char *asmname;
char *section;
char *visibility;
Token *javadown;
// Global variable
bool is_tentative;
bool is_string_literal;
@ -244,6 +271,9 @@ struct Obj {
bool is_noreturn;
bool is_destructor;
bool is_constructor;
bool is_ms_abi; /* TODO */
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int stack_size;
Obj *params;
Node *body;
@ -419,6 +449,7 @@ struct Type {
int align; // alignment
bool is_unsigned; // unsigned or signed
bool is_atomic; // true if _Atomic
bool is_ms_abi; // microsoft abi
Type *origin; // for type compatibility check
// Pointer-to or array-of type. We intentionally use the same member
// to represent pointer/array duality in C.
@ -534,11 +565,11 @@ typedef struct {
void *val;
} HashEntry;
typedef struct {
struct HashMap {
HashEntry *buckets;
int capacity;
int used;
} HashMap;
};
void *hashmap_get(HashMap *, char *);
void *hashmap_get2(HashMap *, char *, int);
@ -584,6 +615,13 @@ Token *alloc_token(void);
Obj *alloc_obj(void);
Type *alloc_type(void);
//
// javadown.c
//
void output_javadown(const char *, Obj *);
void drop_dox(const StringArray *, const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#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_DIRECTDEPS = \
LIBC_ALG \
LIBC_BITS \
LIBC_CALLS \
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) {
case 4:
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);
return;
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;
}
UNREACHABLE();
@ -2381,13 +2382,13 @@ static void emit_text(Obj *prog) {
case TY_UNION:
assert(ty->size <= 16);
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 {
store_gp(gp++, var->offset, MIN(8, ty->size));
}
if (ty->size > 8) {
if (has_flonum(ty, 8, 16, 0)) {
store_fp(fp++, var->offset + 8, ty->size - 8);
store_fp(fn, fp++, var->offset + 8, ty->size - 8);
} else {
store_gp(gp++, var->offset + 8, ty->size - 8);
}
@ -2395,7 +2396,7 @@ static void emit_text(Obj *prog) {
break;
case TY_FLOAT:
case TY_DOUBLE:
store_fp(fp++, var->offset, ty->size);
store_fp(fn, fp++, var->offset, ty->size);
break;
case TY_INT128:
store_gp(gp++, var->offset + 0, 8);
@ -2405,6 +2406,20 @@ static void emit_text(Obj *prog) {
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
gen_stmt(fn->body);
assert(!depth);
@ -2420,6 +2435,17 @@ static void emit_text(Obj *prog) {
if (fn->is_noreturn) {
emitlin("\tud2");
} 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("\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
// parser.
#include "libc/nexgen32e/ffs.h"
#include "libc/testlib/testlib.h"
#include "third_party/chibicc/chibicc.h"
@ -50,11 +51,14 @@ typedef struct {
bool is_const;
bool is_tls;
bool is_weak;
bool is_ms_abi;
bool is_aligned;
bool is_noreturn;
bool is_destructor;
bool is_constructor;
bool is_externally_visible;
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int align;
char *section;
char *visibility;
@ -113,6 +117,8 @@ static Node *current_switch;
static Obj *builtin_alloca;
static Token *current_javadown;
static Initializer *initializer(Token **, Token *, Type *, Type **);
static Member *get_struct_member(Type *, Token *);
static Node *binor(Token **, Token *);
@ -382,6 +388,10 @@ static Token *type_attributes(Token *tok, void *arg) {
ty->is_packed = true;
return tok;
}
if (consume_attribute(&tok, tok, "ms_abi")) {
ty->is_ms_abi = true;
return tok;
}
if (consume_attribute(&tok, tok, "aligned")) {
ty->is_aligned = true;
if (CONSUME(&tok, tok, "(")) {
@ -466,6 +476,18 @@ static Token *thing_attributes(Token *tok, void *arg) {
attr->is_externally_visible = true;
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")) {
attr->is_constructor = true;
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
// (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);
tok = attribute_list(tok, attr, thing_attributes);
if (ty->kind == TY_VLA) {
if (EQUAL(tok, "="))
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");
for (Member *mem = ty->members; mem; mem = mem->next) {
// 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)) {
*rest = start;
return mem;
@ -1920,6 +1944,7 @@ int64_t eval(Node *node) {
// number. The latter form is accepted only as an initialization
// expression for a global variable.
int64_t eval2(Node *node, char ***label) {
int64_t x, y;
add_type(node);
if (is_flonum(node->ty)) return eval_double(node);
switch (node->kind) {
@ -1930,15 +1955,25 @@ int64_t eval2(Node *node, char ***label) {
case ND_MUL:
return eval(node->lhs) * eval(node->rhs);
case ND_DIV:
if (node->ty->is_unsigned)
return (uint64_t)eval(node->lhs) / eval(node->rhs);
return eval(node->lhs) / eval(node->rhs);
y = eval(node->rhs);
if (!y) error_tok(node->rhs->tok, "constexpr div by zero");
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:
return -eval(node->lhs);
case ND_REM:
if (node->ty->is_unsigned)
return (uint64_t)eval(node->lhs) % eval(node->rhs);
return eval(node->lhs) % eval(node->rhs);
y = eval(node->rhs);
if (!y) error_tok(node->rhs->tok, "constexpr rem by zero");
if (node->ty->is_unsigned) {
return (uint64_t)eval(node->lhs) % y;
}
return eval(node->lhs) % y;
case ND_BINAND:
return eval(node->lhs) & eval(node->rhs);
case ND_BINOR:
@ -2044,8 +2079,9 @@ int64_t eval2(Node *node, char ***label) {
static int64_t eval_rval(Node *node, char ***label) {
switch (node->kind) {
case ND_VAR:
if (node->var->is_local)
if (node->var->is_local) {
error_tok(node->tok, "not a compile-time constant");
}
*label = &node->var->name;
return 0;
case ND_DEREF:
@ -2063,6 +2099,7 @@ bool is_const_expr(Node *node) {
case ND_SUB:
case ND_MUL:
case ND_DIV:
case ND_REM:
case ND_BINAND:
case ND_BINOR:
case ND_BINXOR:
@ -2884,8 +2921,9 @@ static Node *postfix(Token **rest, Token *tok) {
static Node *funcall(Token **rest, Token *tok, Node *fn) {
add_type(fn);
if (fn->ty->kind != TY_FUNC &&
(fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC))
(fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) {
error_tok(fn->tok, "not a function");
}
Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base;
Type *param_ty = ty->params;
Node head = {};
@ -2896,8 +2934,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
add_type(arg);
if (!param_ty && !ty->is_variadic) error_tok(tok, "too many arguments");
if (param_ty) {
if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION)
if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) {
arg = new_cast(arg, param_ty);
}
param_ty = param_ty->next;
} else if (arg->ty->kind == TY_FLOAT) {
// If parameter type is omitted (e.g. in "..."), float
@ -2914,8 +2953,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
node->args = head.next;
// If a function returns a struct, it is caller's responsibility
// to allocate a space for the return value.
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION)
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) {
node->ret_buffer = new_lvar("", node->ty);
}
return node;
}
@ -3138,13 +3178,37 @@ static Node *primary(Token **rest, Token *tok) {
*rest = skip(tok, ')');
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")) {
Token *t = skip(tok->next, '(');
Node *node = assign(&t, t);
if (is_const_expr(node)) {
*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_inline = attr->is_inline;
fn->is_weak = attr->is_weak;
fn->is_ms_abi = attr->is_ms_abi;
fn->is_aligned = attr->is_aligned;
fn->is_noreturn = attr->is_noreturn;
fn->is_destructor = attr->is_destructor;
fn->is_constructor = attr->is_constructor;
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->section = attr->section;
fn->visibility = attr->visibility;
}
fn->javadown = fn->javadown ?: current_javadown;
fn->is_root = !(fn->is_static && fn->is_inline);
if (consume_attribute(&tok, tok, "asm")) {
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
// as the hidden first parameter.
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));
}
fn->params = locals;
if (ty->is_variadic)
if (ty->is_variadic) {
fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136));
}
fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char));
tok = skip(tok, '{');
// [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is
@ -3382,6 +3452,7 @@ static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
Type *ty = declarator(&tok, tok, basety);
if (!ty->name) error_tok(ty->name_pos, "variable name omitted");
Obj *var = new_gvar(get_ident(ty->name), ty);
var->javadown = current_javadown;
if (consume_attribute(&tok, tok, "asm")) {
tok = skip(tok, '(');
var->asmname = ConsumeStringLiteral(&tok, tok);
@ -3528,6 +3599,12 @@ Obj *parse(Token *tok) {
tok = static_assertion(tok);
continue;
}
if (tok->kind == TK_JAVADOWN) {
current_javadown = tok;
tok = tok->next;
} else {
current_javadown = NULL;
}
VarAttr attr = {};
tok = attribute_list(tok, &attr, thing_attributes);
Type *basety = declspec(&tok, tok, &attr);

View File

@ -26,11 +26,7 @@
typedef struct CondIncl CondIncl;
typedef struct Hideset Hideset;
typedef struct Macro Macro;
typedef struct MacroArg MacroArg;
typedef struct MacroParam MacroParam;
typedef Token *macro_handler_fn(Token *);
typedef enum {
STR_NONE,
@ -40,11 +36,6 @@ typedef enum {
STR_WIDE,
} StringKind;
struct MacroParam {
MacroParam *next;
char *name;
};
struct MacroArg {
MacroArg *next;
char *name;
@ -52,15 +43,6 @@ struct MacroArg {
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.
struct CondIncl {
CondIncl *next;
@ -74,7 +56,8 @@ struct Hideset {
char *name;
};
static HashMap macros;
HashMap macros;
static CondIncl *cond_incl;
static HashMap pragma_once;
static int include_next_idx;
@ -338,21 +321,24 @@ static MacroParam *read_macro_params(Token **rest, Token *tok,
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");
char *name = strndup(tok->loc, tok->len);
name = strndup(tok->loc, tok->len);
tok = tok->next;
if (!tok->has_space && EQUAL(tok, "(")) {
// Function-like macro
char *va_args_name = NULL;
MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
Macro *m = add_macro(name, false, copy_line(rest, tok));
m = add_macro(name, false, copy_line(rest, tok));
m->params = params;
m->va_args_name = va_args_name;
} else {
// Object-like macro
add_macro(name, true, copy_line(rest, tok));
m = add_macro(name, true, copy_line(rest, tok));
}
return m;
}
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) {
// If it is a macro, expand it.
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 "#".
if (!is_hash(tok)) {
tok->line_delta = tok->file->line_delta;
@ -936,6 +928,8 @@ __GNUC_PATCHLEVEL__\000\
0\000\
__NO_INLINE__\000\
16\000\
__GNUC_STDC_INLINE__\000\
1\000\
__BIGGEST_ALIGNMENT__\000\
16\000\
__C99_MACRO_WITH_VA_ARGS\000\
@ -1010,6 +1004,10 @@ __UINT32_MAX__\000\
0xffffffffu\000\
__INT64_MAX__\000\
0x7fffffffffffffffl\000\
__LONG_MAX__\000\
0x7fffffffffffffffl\000\
__LONG_LONG_MAX__\000\
0x7fffffffffffffffl\000\
__UINT64_MAX__\000\
0xfffffffffffffffful\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 ar2[4];
int main() {
__attribute__((__force_align_arg_pointer__, __no_caller_saved_registers__)) int
main() {
int2 a;
ASSERT(64, _Alignof(int2));
ASSERT(64, _Alignof(a));

View File

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

View File

@ -470,6 +470,7 @@ Token *tokenize(File *file) {
char *p = file->contents;
Token head = {};
Token *cur = &head;
struct Javadown *javadown;
at_bol = true;
has_space = false;
while (*p) {
@ -480,6 +481,22 @@ Token *tokenize(File *file) {
has_space = true;
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.
if (LOOKINGAT(p, "/*")) {
char *q = strstr(p + 2, "*/");
@ -505,13 +522,14 @@ Token *tokenize(File *file) {
if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) {
char *q = p++;
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;
else if (isalnum(*p) || *p == '.')
} else if (isalnum(*p) || *p == '.') {
p++;
else
} else {
break;
}
}
cur = cur->next = new_token(TK_PP_NUM, q, p);
continue;
}
@ -663,7 +681,30 @@ void remove_backslash_newline(char *p) {
// the logical line number matches the physical one.
// This counter maintain the number of newlines we have removed.
int n = 0;
bool instring = false;
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') {
i += 2;
n++;

View File

@ -4,19 +4,28 @@
/**
* Returns (by copy) a struct containing various summary statistics:
*
* arena: current total non-mmapped bytes allocated from system
* ordblks: the number of free chunks
* smblks: always zero.
* hblks: current number of mmapped regions
* hblkhd: total bytes held in mmapped regions
* usmblks: the maximum total allocated space. This will be greater
* - arena: current total non-mmapped bytes allocated from system
*
* - ordblks: the number of free chunks
*
* - smblks: always zero.
*
* - hblks: current number of mmapped regions
*
* - hblkhd: total bytes held in mmapped regions
*
* - usmblks: the maximum total allocated space. This will be greater
* than current total if trimming has occurred.
* 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.)
*
* - 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
* be kept as longs, the reported values may wrap around zero and

View File

@ -2388,7 +2388,7 @@ static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node,
* Compiles regular expression, e.g.
*
* 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));
* regfree(&rx);
*

View File

@ -1139,7 +1139,7 @@ int inflateBack(z_streamp strm, in_func in, void *in_desc, out_func out,
*/
int inflateBackEnd(z_streamp strm);
/**
/*
* Return flags indicating compile-time options.
*
* 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)
*
* Library content (indicates missing functionality):
*
* 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
* 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)
*
* Operation variations (changes in library functionality):
@ -1686,7 +1689,6 @@ uLong adler32_combine(uLong adler1, uLong adler2, int64_t len2);
* Usage example:
*
* uLong crc = crc32(0L, Z_NULL, 0);
*
* while (read_buffer(buffer, length) != EOF) {
* crc = crc32(crc, buffer, length);
* }

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)))
;; (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
`((,(lambda (limit)
(c-font-lock-doc-comments "/\\*\\*" limit