Auto-generate some documentation

This commit is contained in:
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

@ -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,25 +223,25 @@ 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 {
unsigned long n;
struct Rela {
bool isdead;
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
int expr; // exprs
int section; // sections
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
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,
a->relas.p[j].kind, a->relas.p[j].addend);
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
//
@ -64,13 +70,14 @@ void strarray_push(StringArray *, char *);
//
typedef enum {
TK_IDENT, // Identifiers
TK_PUNCT, // Punctuators
TK_KEYWORD, // Keywords
TK_STR, // String literals
TK_NUM, // Numeric literals
TK_PP_NUM, // Preprocessing numbers
TK_EOF, // End-of-file markers
TK_IDENT, // Identifiers
TK_PUNCT, // Punctuators
TK_KEYWORD, // Keywords
TK_STR, // String literals
TK_NUM, // Numeric literals
TK_PP_NUM, // Preprocessing numbers
TK_JAVADOWN, // /** ... */ comments
TK_EOF, // End-of-file markers
} TokenKind;
struct File {
@ -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 Normal file
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 Normal file
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,12 +522,13 @@ 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++;