Improve documentation

The Cosmo API documentation page is pretty good now
https://justine.lol/cosmopolitan/documentation.html
This commit is contained in:
Justine Tunney 2020-12-27 07:02:35 -08:00
parent 13437dd19b
commit 1bc3a25505
367 changed files with 2542 additions and 26178 deletions

View file

@ -2707,15 +2707,6 @@ static noinline void OpFpu1(struct As *a, int op, int reg) {
OpFpu1Impl(a, op, reg);
}
static void OnFucomi(struct As *a, struct Slice s) {
int reg, rm;
rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1;
reg = !IsPunct(a, a->i, ';') ? GetRegisterReg(a) : 0;
if (reg & 7) Fail(a, "bad register");
EmitByte(a, 0xDB);
EmitByte(a, 0350 | rm & 7);
}
static void OnFxch(struct As *a, struct Slice s) {
int rm;
rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1;
@ -2731,6 +2722,18 @@ static void OnBswap(struct As *a, struct Slice s) {
EmitByte(a, 0310 | srm & 7);
}
static noinline void OpFcomImpl(struct As *a, int op) {
int reg, rm;
rm = !IsPunct(a, a->i, ';') ? GetRegisterRm(a) : 1;
reg = !IsPunct(a, a->i, ';') ? GetRegisterReg(a) : 0;
if (reg & 7) Fail(a, "bad register");
EmitVarword(a, op | rm & 7);
}
static noinline void OpFcom(struct As *a, int op) {
OpFcomImpl(a, op);
}
// clang-format off
static void OnAdc(struct As *a, struct Slice s) { OpAlu(a, s, 2); }
static void OnAdd(struct As *a, struct Slice s) { OpAlu(a, s, 0); }
@ -2803,13 +2806,22 @@ static void OnDivps(struct As *a, struct Slice s) { OpSse(a, 0x0F5E); }
static void OnDivsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5E); }
static void OnDivss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5E); }
static void OnDppd(struct As *a, struct Slice s) { OpSse(a, 0x660F3A41); }
static void OnFabs(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e1); }
static void OnFabs(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E1); }
static void OnFaddl(struct As *a, struct Slice s) { OpFpu1(a, 0xDC, 0); }
static void OnFaddp(struct As *a, struct Slice s) { EmitVarword(a, 0xdec1); }
static void OnFaddp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEC1); }
static void OnFadds(struct As *a, struct Slice s) { OpFpu1(a, 0xD8, 0); }
static void OnFchs(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e0); }
static void OnFcomip(struct As *a, struct Slice s) { EmitVarword(a, 0xdff1); }
static void OnFdivrp(struct As *a, struct Slice s) { EmitVarword(a, 0xdef9); }
static void OnFchs(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E0); }
static void OnFcmovb(struct As *a, struct Slice s) { OpFcom(a, 0xDAC0); }
static void OnFcmovbe(struct As *a, struct Slice s) { OpFcom(a, 0xDAD0); }
static void OnFcmove(struct As *a, struct Slice s) { OpFcom(a, 0xDAC8); }
static void OnFcmovnb(struct As *a, struct Slice s) { OpFcom(a, 0xDBC0); }
static void OnFcmovnbe(struct As *a, struct Slice s) { OpFcom(a, 0xDBD0); }
static void OnFcmovne(struct As *a, struct Slice s) { OpFcom(a, 0xDBC8); }
static void OnFcmovnu(struct As *a, struct Slice s) { OpFcom(a, 0xDBD8); }
static void OnFcmovu(struct As *a, struct Slice s) { OpFcom(a, 0xDAD8); }
static void OnFcomi(struct As *a, struct Slice s) { OpFcom(a, 0xDBF0); }
static void OnFcomip(struct As *a, struct Slice s) { OpFcom(a, 0xDFF0); }
static void OnFdivrp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEF9); }
static void OnFildl(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 0); }
static void OnFildll(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); }
static void OnFildq(struct As *a, struct Slice s) { OpFpu1(a, 0xDF, 5); }
@ -2837,9 +2849,11 @@ static void OnFstps(struct As *a, struct Slice s) { OpFpu1(a, 0xD9, 3); }
static void OnFstpt(struct As *a, struct Slice s) { OpFpu1(a, 0xDB, 7); }
static void OnFsubrp(struct As *a, struct Slice s) { EmitVarword(a, 0xDEE9); }
static void OnFtst(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E4); }
static void OnFucomip(struct As *a, struct Slice s) { EmitVarword(a, 0xDFE9); }
static void OnFucomi(struct As *a, struct Slice s) { OpFcom(a, 0xDBE8); }
static void OnFucomip(struct As *a, struct Slice s) { OpFcom(a, 0xDFE8); }
static void OnFwait(struct As *a, struct Slice s) { EmitByte(a, 0x9B); }
static void OnFxam(struct As *a, struct Slice s) { EmitVarword(a, 0xd9e5); }
static void OnFxam(struct As *a, struct Slice s) { EmitVarword(a, 0xD9E5); }
static void OnFxtract(struct As *a, struct Slice s) { EmitVarword(a, 0xD9F4); }
static void OnHaddpd(struct As *a, struct Slice s) { OpSse(a, 0x660F7C); }
static void OnHaddps(struct As *a, struct Slice s) { OpSse(a, 0xF20F7C); }
static void OnHlt(struct As *a, struct Slice s) { EmitByte(a, 0xF4); }
@ -3211,6 +3225,15 @@ static const struct Directive8 {
{"faddp", OnFaddp}, //
{"fadds", OnFadds}, //
{"fchs", OnFchs}, //
{"fcmovb", OnFcmovb}, //
{"fcmovbe", OnFcmovbe}, //
{"fcmove", OnFcmove}, //
{"fcmovnb", OnFcmovnb}, //
{"fcmovnbe", OnFcmovnbe}, //
{"fcmovne", OnFcmovne}, //
{"fcmovnu", OnFcmovnu}, //
{"fcmovu", OnFcmovu}, //
{"fcomi", OnFcomi}, //
{"fcomip", OnFcomip}, //
{"fdivrp", OnFdivrp}, //
{"fildl", OnFildl}, //
@ -3246,6 +3269,7 @@ static const struct Directive8 {
{"fwait", OnFwait}, //
{"fxam", OnFxam}, //
{"fxch", OnFxch}, //
{"fxtract", OnFxtract}, //
{"haddpd", OnHaddpd}, //
{"haddps", OnHaddps}, //
{"hlt", OnHlt}, //

View file

@ -1,3 +1,5 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
#include "third_party/chibicc/chibicc.h"
asm(".ident\t\"\\n\\n\
@ -381,10 +383,9 @@ static bool run_subprocess(char **argv) {
for (int i = 1; argv[i]; i++) fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n");
}
if (fork() == 0) {
if (!vfork()) {
// Child process. Run a new command.
execvp(argv[0], argv);
fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno));
_exit(1);
}
// Wait for the child process to finish.
@ -503,7 +504,7 @@ static Token *append_tokens(Token *tok1, Token *tok2) {
return tok1;
}
static FileType get_file_type(char *filename) {
static FileType get_file_type(const char *filename) {
if (opt_x != FILE_NONE) return opt_x;
if (endswith(filename, ".a")) return FILE_AR;
if (endswith(filename, ".o")) return FILE_OBJ;
@ -514,7 +515,13 @@ static FileType get_file_type(char *filename) {
}
static void cc1(void) {
FileType ft;
Token *tok = NULL;
ft = get_file_type(base_file);
if (opt_J && (ft == FILE_ASM || ft == FILE_ASM_CPP)) {
output_javadown_asm(output_file, base_file);
return;
}
// Process -include option
for (int i = 0; i < opt_include.len; i++) {
char *incl = opt_include.data[i];
@ -538,7 +545,7 @@ static void cc1(void) {
if (opt_M) return;
}
// If -E is given, print out preprocessed C code as a result.
if (opt_E || get_file_type(base_file) == FILE_ASM_CPP) {
if (opt_E || ft == FILE_ASM_CPP) {
print_tokens(tok);
return;
}
@ -605,8 +612,13 @@ static void run_linker(StringArray *inputs, char *output) {
handle_exit(run_subprocess(arr.data));
}
static void OnCtrlC(int sig, siginfo_t *si, ucontext_t *ctx) {
exit(1);
}
int chibicc(int argc, char **argv) {
showcrashreports();
sigaction(SIGINT, &(struct sigaction){.sa_sigaction = OnCtrlC}, NULL);
atexit(cleanup);
init_macros();
parse_args(argc, argv);
@ -649,6 +661,18 @@ int chibicc(int argc, char **argv) {
strarray_push(&ld_args, input);
continue;
}
// Dox
if (opt_J) {
if (opt_c) {
handle_exit(run_cc1(argc, argv, input, output));
} else {
char *tmp = create_tmpfile();
if (run_cc1(argc, argv, input, tmp)) {
strarray_push(&dox_args, tmp);
}
}
continue;
}
// Handle .s
if (type == FILE_ASM) {
if (!opt_S) {
@ -657,6 +681,11 @@ int chibicc(int argc, char **argv) {
continue;
}
assert(type == FILE_C || type == FILE_ASM_CPP);
// Just print ast.
if (opt_A) {
handle_exit(run_cc1(argc, argv, input, NULL));
continue;
}
// Just preprocess
if (opt_E || opt_M) {
handle_exit(run_cc1(argc, argv, input, NULL));
@ -674,14 +703,6 @@ int chibicc(int argc, char **argv) {
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();

View file

@ -272,6 +272,7 @@ struct Obj {
bool is_destructor;
bool is_constructor;
bool is_ms_abi; /* TODO */
bool is_no_instrument_function;
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int stack_size;
@ -616,10 +617,16 @@ Obj *alloc_obj(void);
Type *alloc_type(void);
//
// javadown.c
// dox1.c
//
void output_javadown(const char *, Obj *);
void output_javadown_asm(const char *, const char *);
//
// dox2.c
//
void drop_dox(const StringArray *, const char *);
COSMOPOLITAN_C_END_

View file

@ -116,15 +116,6 @@ o/$(MODE)/third_party/chibicc/as.com.dbg: \
$(THIRD_PARTY_CHIBICC_A).pkg
@$(APELINK)
o/$(MODE)/third_party/chibicc/hello.com.dbg: \
$(THIRD_PARTY_CHIBICC_A_DEPS) \
$(THIRD_PARTY_CHIBICC_A) \
$(APE) \
$(CRT) \
o/$(MODE)/third_party/chibicc/hello.chibicc.o \
$(THIRD_PARTY_CHIBICC_A).pkg
@$(APELINK)
o/$(MODE)/third_party/chibicc/chibicc.o: \
CPPFLAGS += $(THIRD_PARTY_CHIBICC_DEFINES)

View file

@ -973,6 +973,12 @@ static bool gen_builtin_funcall(Node *node, const char *name) {
pop("%rax");
return true;
}
} else if (!strcmp(name, "logbl")) {
gen_expr(node->args);
emitlin("\
\tfxtract\n\
\tfstp\t%st");
return true;
} else if (!strcmp(name, "isgreater")) {
gen_comis(node, "comisd", 1, 0, "a");
return true;
@ -1010,17 +1016,17 @@ static bool gen_builtin_funcall(Node *node, const char *name) {
\tflds\t(%rsp)\n\
\tpop\t%rax");
return true;
} else if (!strcmp(name, "inff")) {
} else if (!strcmp(name, "inff") || !strcmp(name, "huge_valf")) {
emitlin("\
\tmov\t$0x7f800000,%eax\n\
\tmovd\t%eax,%xmm0");
return true;
} else if (!strcmp(name, "inf")) {
} else if (!strcmp(name, "inf") || !strcmp(name, "huge_val")) {
emitlin("\
\tmov\t$0x7ff0000000000000,%rax\n\
\tmovq\t%rax,%xmm0");
return true;
} else if (!strcmp(name, "infl")) {
} else if (!strcmp(name, "infl") || !strcmp(name, "huge_vall")) {
emitlin("\
\tpush\t$0x7f800000\n\
\tflds\t(%rsp)\n\
@ -2304,6 +2310,42 @@ static void store_gp(int r, int offset, int sz) {
}
}
static void emit_function_hook(void) {
if (opt_nop_mcount) {
print_profiling_nop();
} else if (opt_fentry) {
emitlin("\tcall\t__fentry__@gotpcrel(%rip)");
} else if (opt_pg) {
emitlin("\tcall\tmcount@gotpcrel(%rip)");
} else {
print_profiling_nop();
}
}
static void save_caller_saved_registers(void) {
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");
}
static void restore_caller_saved_registers(void) {
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");
}
static void emit_text(Obj *prog) {
for (Obj *fn = prog; fn; fn = fn->next) {
if (!fn->is_function || !fn->is_definition) continue;
@ -2327,14 +2369,8 @@ static void emit_text(Obj *prog) {
// Prologue
emitlin("\tpush\t%rbp");
emitlin("\tmov\t%rsp,%rbp");
if (opt_nop_mcount) {
print_profiling_nop();
} else if (opt_fentry) {
emitlin("\tcall\t__fentry__@gotpcrel(%rip)");
} else if (opt_pg) {
emitlin("\tcall\tmcount@gotpcrel(%rip)");
} else {
print_profiling_nop();
if (!fn->is_no_instrument_function) {
emit_function_hook();
}
println("\tsub\t$%d,%%rsp", fn->stack_size);
println("\tmov\t%%rsp,%d(%%rbp)", fn->alloca_bottom->offset);
@ -2410,15 +2446,7 @@ static void emit_text(Obj *prog) {
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");
save_caller_saved_registers();
}
// Emit code
gen_stmt(fn->body);
@ -2436,15 +2464,7 @@ static void emit_text(Obj *prog) {
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");
restore_caller_saved_registers();
}
emitlin("\tleave");
emitlin("\tret");

View file

@ -17,7 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/gc.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"
#include "tool/build/lib/asmdown.h"
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
@ -101,30 +105,34 @@ static char *DescribeType(struct Type *ty) {
return DescribeScalar(ty, "double");
case TY_LDOUBLE:
return DescribeScalar(ty, "long double");
case TY_FUNC:
return xasprintf("%s(*)()", gc(DescribeType(ty->return_ty)));
case TY_PTR:
return xasprintf("%s*", gc(DescribeType(ty->base)));
if (ty->base->kind == TY_FUNC) {
return DescribeType(ty->base);
} else {
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);
if (ty->name) {
return xasprintf("enum %.*s", ty->name->len, ty->name->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);
if (ty->name) {
return xasprintf("struct %.*s", ty->name->len, ty->name->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);
if (ty->name) {
return xasprintf("union %.*s", ty->name->len, ty->name->loc);
} else {
return strdup("ANONYMOUS-UNION");
}
case TY_FUNC:
return xasprintf("%s(*)()", gc(DescribeType(ty->return_ty)));
default:
return "UNKNOWN";
}
@ -136,6 +144,12 @@ static int CountParams(Obj *params) {
return n;
}
static int CountMacroParams(struct MacroParam *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;
@ -155,7 +169,9 @@ static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
MacroParam *mparam;
SerializeInt(&dox->buf, dox->objects.n);
for (i = 0; i < dox->objects.n; ++i) {
s = DescribeType(dox->objects.p[i]->ty);
s = DescribeType(dox->objects.p[i]->is_function
? dox->objects.p[i]->ty->return_ty
: dox->objects.p[i]->ty);
SerializeStr(&dox->buf, s);
free(s);
SerializeStr(&dox->buf, dox->objects.p[i]->name);
@ -170,7 +186,10 @@ static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
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, !!dox->objects.p[i]->javadown);
if (dox->objects.p[i]->javadown) {
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);
@ -184,22 +203,93 @@ static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
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, dox->macros.p[i]->is_objlike);
SerializeStr(&dox->buf, dox->macros.p[i]->va_args_name);
SerializeInt(&dox->buf, !!dox->macros.p[i]->javadown);
if (dox->macros.p[i]->javadown) {
SerializeJavadown(&dox->buf, dox->macros.p[i]->javadown->javadown);
}
SerializeInt(&dox->buf, CountMacroParams(dox->macros.p[i]->params));
for (mparam = dox->macros.p[i]->params; mparam; mparam = mparam->next) {
SerializeStr(&dox->buf, mparam->name);
}
}
SerializeInt(&dox->buf, 31337);
}
static int IsJavadownParam(struct JavadownTag *jt) {
return !strcmp(jt->tag, "param") && strchr(jt->text, ' ');
}
static char *ExtractJavadownParamName(const char *text) {
char *space;
space = strchr(text, ' ');
return strndup(text, space - text);
}
static int CountJavadownParams(struct Javadown *jd) {
int i, n;
for (n = i = 0; i < jd->tags.n; ++i) {
if (IsJavadownParam(jd->tags.p + i)) {
++n;
}
}
return n;
}
static void SerializeAsmdown(struct DoxWriter *dox, struct Asmdown *ad,
const char *filename) {
char *s;
int i, j;
SerializeInt(&dox->buf, ad->symbols.n);
for (i = 0; i < ad->symbols.n; ++i) {
SerializeStr(&dox->buf, ""); // type
SerializeStr(&dox->buf, ad->symbols.p[i].name);
SerializeStr(&dox->buf, filename);
SerializeInt(&dox->buf, ad->symbols.p[i].line);
SerializeInt(&dox->buf, true); // TODO: is_function
SerializeInt(&dox->buf, false); // TODO: is_weak
SerializeInt(&dox->buf, false); // is_inline
SerializeInt(&dox->buf, false); // is_noreturn
SerializeInt(&dox->buf, false); // is_destructor
SerializeInt(&dox->buf, false); // is_constructor
SerializeInt(&dox->buf, false); // is_force_align_arg_pointer
SerializeInt(&dox->buf, false); // is_no_caller_saved_registers
SerializeStr(&dox->buf, ""); // TODO: visibility
SerializeInt(&dox->buf, true); // has_javadown
SerializeJavadown(&dox->buf, ad->symbols.p[i].javadown);
SerializeInt(&dox->buf, CountJavadownParams(ad->symbols.p[i].javadown));
for (j = 0; j < ad->symbols.p[i].javadown->tags.n; ++j) {
if (IsJavadownParam(ad->symbols.p[i].javadown->tags.p + j)) {
SerializeStr(&dox->buf, ""); // type
s = ExtractJavadownParamName(ad->symbols.p[i].javadown->tags.p[j].text);
SerializeStr(&dox->buf, s); // name
free(s);
}
}
}
SerializeInt(&dox->buf, 0); // macros
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->javadown) {
if (*obj->name == '_') continue;
if (strchr(obj->name, '$')) continue;
if (startswith(obj->name, "__gdtoa_")) continue;
if (obj->visibility && !strcmp(obj->visibility, "hidden")) continue;
if (!obj->is_definition && (!obj->is_function || !obj->params ||
!obj->params->name || !*obj->params->name)) {
continue;
}
}
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;
if (obj->section && startswith(obj->section, ".init_array")) continue;
APPEND(dox->objects);
dox->objects.p[dox->objects.n - 1] = obj;
}
@ -209,8 +299,8 @@ static void LoadPublicDefinitions(struct DoxWriter *dox, Obj *prog) {
macro = macros.buckets[i].val;
if (!macro->javadown) continue;
if (!macro->javadown->javadown) continue;
if (*macro->name == '_') continue;
if (strchr(macro->name, '$')) continue;
/* if (*macro->name == '_') continue; */
/* if (strchr(macro->name, '$')) continue; */
APPEND(dox->macros);
dox->macros.p[dox->macros.n - 1] = macro;
}
@ -237,7 +327,7 @@ static void WriteDox(struct DoxWriter *dox, const char *path) {
}
/**
* Emits documentation datum for compilation unit just parsed.
* Emits documentation data for compilation unit just parsed.
*/
void output_javadown(const char *path, Obj *prog) {
struct DoxWriter *dox = NewDoxWriter();
@ -246,3 +336,30 @@ void output_javadown(const char *path, Obj *prog) {
WriteDox(dox, path);
FreeDoxWriter(dox);
}
/**
* Emits documentation data for assembly source file.
*/
void output_javadown_asm(const char *path, const char *source) {
int fd;
void *map;
struct stat st;
struct Asmdown *ad;
struct DoxWriter *dox;
CHECK_NE(-1, (fd = open(source, 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)));
ad = ParseAsmdown(map, st.st_size);
munmap(map, st.st_size);
} else {
ad = ParseAsmdown("", 0);
}
close(fd);
dox = NewDoxWriter();
SerializeAsmdown(dox, ad, source);
WriteDox(dox, path);
FreeDoxWriter(dox);
FreeAsmdown(ad);
}

View file

@ -65,10 +65,34 @@ struct Dox {
} params;
} * p;
} objects;
struct {
struct DoxMacros {
size_t n;
int *p;
} objectindex;
struct DoxMacro {
bool ignore;
char *name;
char *path;
int line;
bool is_objlike;
char *va_args_name;
struct Javadown *javadown;
struct DoxMacroParams {
size_t n;
struct DoxMacroParam {
char *name;
} * p;
} params;
} * p;
} macros;
struct DoxIndex {
size_t n;
struct DoxIndexEntry {
enum DoxIndexType {
kObject,
kMacro,
} t;
int i;
} * p;
} index;
};
static unsigned Hash(const void *p, unsigned long n) {
@ -93,6 +117,8 @@ static void FreeDox(struct Dox *dox) {
free(dox->names.p);
free(dox->freelist.p);
free(dox->objects.p);
free(dox->macros.p);
free(dox->index.p);
free(dox);
}
}
@ -124,18 +150,23 @@ static char *DeserializeStr(struct Dox *dox) {
static struct Javadown *DeserializeJavadown(struct Dox *dox) {
int i;
bool present;
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);
if (DeserializeInt(dox)) {
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;
} else {
return NULL;
}
return jd;
}
static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
@ -163,6 +194,22 @@ static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
}
}
static void DeserializeMacro(struct Dox *dox, struct DoxMacro *m) {
int i;
m->ignore = false;
m->name = DeserializeStr(dox);
m->path = DeserializeStr(dox);
m->line = DeserializeInt(dox);
m->is_objlike = DeserializeInt(dox);
m->va_args_name = DeserializeStr(dox);
m->javadown = DeserializeJavadown(dox);
m->params.n = DeserializeInt(dox);
m->params.p = FreeLater(dox, malloc(m->params.n * sizeof(*m->params.p)));
for (i = 0; i < m->params.n; ++i) {
m->params.p[i].name = DeserializeStr(dox);
}
}
static void DeserializeDox(struct Dox *dox) {
int i, j, n;
i = dox->objects.n;
@ -172,7 +219,16 @@ static void DeserializeDox(struct Dox *dox) {
for (j = 0; j < n; ++j) {
DeserializeObject(dox, dox->objects.p + i + j);
}
i = dox->macros.n;
dox->objects.n += n;
n = DeserializeInt(dox);
dox->macros.p =
realloc(dox->macros.p, (dox->macros.n + n) * sizeof(*dox->macros.p));
for (j = 0; j < n; ++j) {
DeserializeMacro(dox, dox->macros.p + i + j);
}
dox->macros.n += n;
CHECK_EQ(31337, DeserializeInt(dox));
}
static void ReadDox(struct Dox *dox, const StringArray *files) {
@ -210,43 +266,66 @@ static bool AddSet(struct Set *set, char *s) {
}
}
static int CompareObjectNames(const void *a, const void *b, void *arg) {
int *i1, *i2;
static int CompareDoxIndexEntry(const void *p1, const void *p2, void *arg) {
struct Dox *dox;
i1 = a, i2 = b, dox = arg;
return strcmp(dox->objects.p[*i1].name, dox->objects.p[*i2].name);
const char *s1, *s2;
struct DoxIndexEntry *a, *b;
dox = arg, a = p1, b = p2;
s1 = a->t == kObject ? dox->objects.p[a->i].name : dox->macros.p[a->i].name;
s2 = b->t == kObject ? dox->objects.p[b->i].name : dox->macros.p[b->i].name;
while (*s1 == '_') ++s1;
while (*s2 == '_') ++s2;
return strcasecmp(s1, s2);
}
static void IndexDox(struct Dox *dox) {
size_t i, j, n;
dox->names.n = roundup2pow(dox->objects.n) << 1;
dox->names.n = roundup2pow(dox->objects.n + dox->macros.n) << 1;
dox->names.p = calloc(dox->names.n, sizeof(*dox->names.p));
for (n = i = 0; i < dox->objects.n; ++i) {
n = 0;
for (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;
for (i = 0; i < dox->macros.n; ++i) {
if (AddSet(&dox->names, dox->macros.p[i].name)) {
++n;
} else {
dox->macros.p[i].ignore = true;
}
}
qsort_r(dox->objectindex.p, dox->objectindex.n, sizeof(*dox->objectindex.p),
CompareObjectNames, dox);
dox->index.n = n;
dox->index.p = malloc(n * sizeof(*dox->index.p));
j = 0;
for (i = 0; i < dox->objects.n; ++i) {
if (dox->objects.p[i].ignore) continue;
dox->index.p[j].t = kObject;
dox->index.p[j].i = i;
++j;
}
for (i = 0; i < dox->macros.n; ++i) {
if (dox->macros.p[i].ignore) continue;
dox->index.p[j].t = kMacro;
dox->index.p[j].i = i;
++j;
}
CHECK_EQ(n, j);
qsort_r(dox->index.p, dox->index.n, sizeof(*dox->index.p),
CompareDoxIndexEntry, dox);
}
static void PrintText(FILE *f, const char *s) {
int c;
bool bol, pre;
for (pre = false, bol = true;;) {
bool bol, pre, ul0, ul2, bt1, bt2;
for (bt1 = bt2 = ul2 = ul0 = pre = false, bol = true;;) {
switch ((c = *s++)) {
case '\0':
if (pre) {
fprintf(f, "</pre>");
}
if (bt1 || bt2) fprintf(f, "</code>");
if (pre) fprintf(f, "</pre>");
if (ul0 || ul2) fprintf(f, "</ul>");
return;
case '&':
fprintf(f, "&amp;");
@ -268,25 +347,79 @@ static void PrintText(FILE *f, const char *s) {
fprintf(f, "&apos;");
bol = false;
break;
case '\n':
if (!pre && *s == '\n') {
case '`':
if (!pre && !bt1 && !bt2 && *s != '`') {
fprintf(f, "<code>");
bt1 = true;
} else if (!pre && !bt1 && !bt2 && *s == '`') {
fprintf(f, "<code>");
bt2 = true;
++s;
} else if (bt1) {
fprintf(f, "</code>");
bt1 = false;
} else if (bt2 && *s == '`') {
fprintf(f, "</code>");
bt2 = false;
++s;
} else {
fprintf(f, "`");
}
bol = false;
break;
case '\n':
if (!pre && !ul0 && !ul2 && *s == '\n') {
fprintf(f, "\n<p>");
} else if (pre &&
bol = true;
} else if (pre && s[0] != '\n' &&
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
fprintf(f, "</pre>\n");
pre = false;
bol = true;
} else if (ul0 && s[0] == '-' && s[1] == ' ') {
fprintf(f, "\n<li>");
s += 2;
bol = false;
} else if (ul2 && s[0] == ' ' && s[1] == ' ' && s[2] == '-' &&
s[3] == ' ') {
fprintf(f, "\n<li>");
s += 4;
bol = false;
} else if (ul0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) {
fprintf(f, "\n</ul>\n");
bol = true;
ul0 = false;
} else if (ul2 && s[0] != '\n' &&
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
fprintf(f, "\n</ul>\n");
bol = true;
ul2 = false;
} else {
fprintf(f, "\n");
bol = true;
}
bol = true;
break;
case '-':
if (bol && !ul0 && !ul2 && s[0] == ' ') {
ul0 = true;
fprintf(f, "<ul><li>");
} else {
fprintf(f, "-");
}
bol = false;
break;
case ' ':
if (bol && !pre && s[0] == ' ' && s[1] == ' ' && s[2] == ' ') {
pre = true;
fprintf(f, "<pre>");
fprintf(f, "<pre> ");
} else if (bol && !ul0 && !ul2 && s[0] == ' ' && s[1] == '-' &&
s[2] == ' ') {
ul2 = true;
fprintf(f, "<ul><li>");
s += 3;
} else {
fprintf(f, " ");
}
fprintf(f, " ");
bol = false;
break;
default:
@ -297,83 +430,364 @@ static void PrintText(FILE *f, const char *s) {
}
}
static bool HasTag(struct Javadown *jd, const char *tag) {
int k;
if (jd) {
for (k = 0; k < jd->tags.n; ++k) {
if (!strcmp(jd->tags.p[k].tag, tag)) {
return true;
}
}
}
return false;
}
static bool IsNoReturn(struct DoxObject *o) {
return o->is_noreturn || HasTag(o->javadown, "noreturn");
}
static void PrintDox(struct Dox *dox, FILE *f) {
int i, j, k;
char *prefix;
bool was_outputted;
struct DoxMacro *m;
struct DoxObject *o;
// header
fprintf(f, "\
<!doctype html>\n\
<html lang=\"en\">\n\
<meta charset=\"utf-8\">\n\
<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-43182592-5\"></script>\n\
<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-43182592-5');</script>\n\
<title>Cosmopolitan C Library</title>\n\
<meta name=\"viewport\" content=\"width=1024\">\n\
<link rel=\"canonical\" href=\"https://justine.lol/cosmopolitan/documentation.html\">\n\
<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Roboto&display=swap\">\n\
<link rel=\"stylesheet\" href=\"style.css\">\n\
<style>\n\
.indent {\n\
padding-left: 1em;\n\
}\n\
.nav {\n\
margin-bottom: 0;\n\
}\n\
.toc a {\n\
text-decoration: none;\n\
}\n\
h3 a {\n\
color: inherit;\n\
text-decoration: none;\n\
}\n\
pre {\n\
margin-left: 0;\n\
padding: 12px;\n\
background: #f6f6f6;\n\
width: 100%;\n\
overflow-x: auto;\n\
border-radius: 5px;\n\
}\n\
code {\n\
padding: 2px 4px;\n\
background: #e4e6e8;\n\
border-radius: 3px;\n\
}\n\
hr {\n\
height: 1px;\n\
margin-bottom: 16px;\n\
color: #d6d9dc;\n\
background: #d6d9dc;\n\
border: 0;\n\
}\n\
.category {\n\
font-weight: bold;\n\
}\n\
.tagname {\n\
font-size: 12pt;\n\
font-weight: bold;\n\
}\n\
.tag {\n\
margin-top: .5em;\n\
}\n\
.tag dl {\n\
margin-top: .5em;\n\
margin-bottom: .5em;\n\
margin-left: 1em;\n\
}\n\
</style>\n\
\n\
<table width=\"100%%\"><tr><td width=\"33%%\" valign=\"top\">\n\
<p class=\"toc\">\n\
<header>\n\
<img width=\"196\" height=\"105\" src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\" alt=\"honeybadger\">\n\
<h1>cosmopolitan libc</h1>\n\
<span>build-once run-anywhere c without devops</span>\n\
</header>\n\
\n\
<nav class=\"nav\">\n\
<ul>\n\
<li><a href=\"index.html\">Intro</a>\n\
<li><a href=\"download.html\">Download</a>\n\
<li><a class=\"active\" href=\"documentation.html\">Documentation</a>\n\
<li><a href=\"sources.html\">Sources</a>\n\
<li><a href=\"https://github.com/jart/cosmopolitan\">GitHub</a>\n\
<li><a href=\"license.html\">License</a>\n\
<li class=\"right\"><a href=\"../index.html\">» jart's web page</a>\n\
</ul>\n\
</nav>\n\
\n\
<table class=\"dox\" width=\"960\">\n\
<tr>\n\
<td width=\"320\" valign=\"top\" 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;
/* // lefthand index: objects */
/* fprintf(f, "<p><span class=\"category\">macro objects</span>\n"); */
/* fprintf(f, "<p>\n"); */
/* for (i = 0; i < dox->index.n; ++i) { */
/* if (dox->index.p[i].t != kMacro) continue; */
/* m = dox->macros.p + dox->index.p[i].i; */
/* if (m->ignore) continue; */
/* if (!m->is_objlike) continue; */
/* fprintf(f, "<a href=\"#%s\">%s</a><br>\n", m->name, m->name); */
/* } */
/* // lefthand index: functions */
/* fprintf(f, "<p><span class=\"category\">macro functions</span>\n"); */
/* fprintf(f, "<p>\n"); */
/* for (i = 0; i < dox->index.n; ++i) { */
/* if (dox->index.p[i].t != kMacro) continue; */
/* m = dox->macros.p + dox->index.p[i].i; */
/* if (m->ignore) continue; */
/* if (m->is_objlike) continue; */
/* fprintf(f, "<a href=\"#%s\">%s</a><br>\n", m->name, m->name); */
/* } */
// lefthand index: objects
fprintf(f, "<p><span class=\"category\">objects</span>\n");
fprintf(f, "<p>\n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t != kObject) continue;
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
if (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);
// lefthand index: functions
fprintf(f, "<p><span class=\"category\">functions</span>\n");
fprintf(f, "<p>\n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t != kObject) continue;
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
if (!o->is_function) continue;
fprintf(f, "<a href=\"#%s\">%s</a><br>\n", o->name, o->name);
}
// righthand contents
fprintf(f, "<td width=\"640\" valign=\"top\">\n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t == kObject) {
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
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);
if (i) fprintf(f, "<hr>");
fprintf(f, "<div id=\"%s\" class=\"api\">\n", o->name);
fprintf(f, "<h3><a href=\"#%s\">%s</a></h3>", o->name, o->name);
// title
if (o->javadown && *o->javadown->title) {
fprintf(f, "<p>");
PrintText(f, o->javadown->title);
fprintf(f, "\n");
}
// text
if (o->javadown && *o->javadown->text) {
fprintf(f, "<p>");
PrintText(f, o->javadown->text);
fprintf(f, "\n");
}
// parameters
if (o->is_function && (o->params.n || HasTag(o->javadown, "param"))) {
fprintf(f, "<div class=\"tag\">\n");
fprintf(f, "<span class=\"tagname\">@param</span>\n");
fprintf(f, "<dl>\n");
if (o->params.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");
if (o->javadown) {
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);
}
}
} else {
for (k = 0; k < o->javadown->tags.n; ++k) {
if (!strcmp(o->javadown->tags.p[k].tag, "param")) {
fprintf(f, "<dd>");
PrintText(f, o->javadown->tags.p[k].text);
fprintf(f, "\n");
break;
}
}
}
fprintf(f, "</dl>\n");
fprintf(f, "</div>\n"); // .tag
}
// return
if (o->is_function) {
fprintf(f, "<div class=\"tag\">\n");
if (IsNoReturn(o)) {
fprintf(f, "<span class=\"tagname\">@noreturn</span>\n");
} else {
fprintf(f, "<span class=\"tagname\">@return</span>\n");
was_outputted = false;
fprintf(f, "<dl>\n");
if (o->javadown) {
for (k = 0; k < o->javadown->tags.n; ++k) {
if (strcmp(o->javadown->tags.p[k].tag, "return")) continue;
if (!was_outputted) {
fprintf(f, "<dt>");
PrintText(f, o->type);
was_outputted = true;
}
fprintf(f, "\n<dd>");
PrintText(f, o->javadown->tags.p[k].text);
fprintf(f, "\n");
}
}
if (!was_outputted) {
fprintf(f, "<dt>");
PrintText(f, o->type);
}
fprintf(f, "</dl>\n");
fprintf(f, "</div>\n"); // .tag
}
}
// tags
if (o->javadown) {
for (k = 0; k < o->javadown->tags.n; ++k) {
if (!strcmp(o->javadown->tags.p[k].tag, "param")) continue;
if (!strcmp(o->javadown->tags.p[k].tag, "return")) continue;
if (!strcmp(o->javadown->tags.p[k].tag, "noreturn")) continue;
fprintf(f, "<div class=\"tag\">\n");
fprintf(f, "<span class=\"tagname\">@");
PrintText(f, o->javadown->tags.p[k].tag);
fprintf(f, "</span>\n");
if (*o->javadown->tags.p[k].text) {
PrintText(f, o->javadown->tags.p[k].text);
fprintf(f, "\n");
}
fprintf(f, "</div>\n"); // .tag
}
}
// sauce
if (strcmp(o->path, "missingno.c")) {
fprintf(f, "<div class=\"tag\">\n");
fprintf(f,
"<span class=\"tagname\">@see</span> <a "
"href=\"https://github.com/jart/cosmopolitan/blob/master/"
"%s#L%d\">%s</a>",
o->path, o->line, o->path);
fprintf(f, "</div>\n"); // .tag
}
fprintf(f, "</div>\n"); /* class=".api" */
} else {
continue;
m = dox->macros.p + dox->index.p[i].i;
if (m->ignore) continue;
fprintf(f, "\n");
if (i) fprintf(f, "<hr>");
fprintf(f, "<div id=\"%s\" class=\"api\">\n", m->name);
fprintf(f, "<h3><a href=\"#%s\">%s</a></h3>", m->name, m->name);
// title
if (m->javadown && *m->javadown->title) {
fprintf(f, "<p>");
PrintText(f, m->javadown->title);
fprintf(f, "\n");
}
// text
if (m->javadown && *m->javadown->text) {
fprintf(f, "<p>");
PrintText(f, m->javadown->text);
fprintf(f, "\n");
}
// parameters
if (!m->is_objlike && (m->params.n || HasTag(m->javadown, "param"))) {
fprintf(f, "<div class=\"tag\">\n");
fprintf(f, "<span class=\"tagname\">@param</span>\n");
fprintf(f, "<dl>\n");
if (m->params.n) {
for (j = 0; j < m->params.n; ++j) {
fprintf(f, "<dt>");
fprintf(f, "<em>");
PrintText(f, m->params.p[j].name);
fprintf(f, "</em>\n");
if (m->javadown) {
prefix = xasprintf("%s ", m->params.p[j].name);
for (k = 0; k < m->javadown->tags.n; ++k) {
if (!strcmp(m->javadown->tags.p[k].tag, "param") &&
startswith(m->javadown->tags.p[k].text, prefix)) {
fprintf(f, "<dd>");
PrintText(f, m->javadown->tags.p[k].text + strlen(prefix));
fprintf(f, "\n");
break;
}
}
free(prefix);
}
}
} else {
for (k = 0; k < m->javadown->tags.n; ++k) {
if (!strcmp(m->javadown->tags.p[k].tag, "param")) {
fprintf(f, "<dd>");
PrintText(f, m->javadown->tags.p[k].text);
fprintf(f, "\n");
break;
}
}
}
fprintf(f, "</dl>\n");
fprintf(f, "</div>\n"); // .tag
}
fprintf(f, "</div>\n"); /* class=".api" */
}
fprintf(f, "</div>\n");
}
fprintf(f, "</table>\n");
// footer
fprintf(f, "\
\n\
<footer>\n\
<p>\n\
<div style=\"float:right;text-align:right\">\n\
Free Libre &amp; Open Source<br>\n\
<a href=\"https://github.com/jart\">github.com/jart/cosmopolitan</a>\n\
</div>\n\
Feedback<br>\n\
jtunney@gmail.com\n\
</p>\n\
<div style=\"clear:both\"></div>\n\
</footer>\n\
");
}
/**

View file

@ -57,6 +57,7 @@ typedef struct {
bool is_destructor;
bool is_constructor;
bool is_externally_visible;
bool is_no_instrument_function;
bool is_force_align_arg_pointer;
bool is_no_caller_saved_registers;
int align;
@ -476,6 +477,10 @@ static Token *thing_attributes(Token *tok, void *arg) {
attr->is_externally_visible = true;
return tok;
}
if (consume_attribute(&tok, tok, "no_instrument_function")) {
attr->is_no_instrument_function = true;
return tok;
}
if (consume_attribute(&tok, tok, "force_align_arg_pointer")) {
attr->is_force_align_arg_pointer = true;
return tok;
@ -555,7 +560,6 @@ static Token *thing_attributes(Token *tok, void *arg) {
consume_attribute(&tok, tok, "no_split_stack") ||
consume_attribute(&tok, tok, "no_stack_limit") ||
consume_attribute(&tok, tok, "no_sanitize_undefined") ||
consume_attribute(&tok, tok, "no_instrument_function") ||
consume_attribute(&tok, tok, "no_profile_instrument_function")) {
return tok;
}
@ -1018,6 +1022,7 @@ static Type *enum_specifier(Token **rest, Token *tok) {
*rest = tok;
return ty;
}
ty->name = tag;
tok = skip(tok, '{');
// Read an enum-list.
int i = 0;
@ -2066,8 +2071,9 @@ int64_t eval2(Node *node, char ***label) {
}
error_tok(node->tok, "not a compile-time constant");
}
if (node->var->ty->kind != TY_ARRAY && node->var->ty->kind != TY_FUNC)
if (node->var->ty->kind != TY_ARRAY && node->var->ty->kind != TY_FUNC) {
error_tok(node->tok, "invalid initializer");
}
*label = &node->var->name;
return 0;
case ND_NUM:
@ -2727,6 +2733,7 @@ static Type *struct_union_decl(Token **rest, Token *tok) {
push_tag_scope(tag, ty);
return ty;
}
ty->name = tag;
tok = skip(tok, '{');
// Construct a struct object.
struct_members(&tok, tok, ty);
@ -3361,10 +3368,12 @@ static Obj *find_func(char *name) {
}
static void mark_live(Obj *var) {
int i;
Obj *fn;
if (!var->is_function || var->is_live) return;
var->is_live = true;
for (int i = 0; i < var->refs.len; i++) {
Obj *fn = find_func(var->refs.data[i]);
for (i = 0; i < var->refs.len; i++) {
fn = find_func(var->refs.data[i]);
if (fn) mark_live(fn);
}
}
@ -3385,25 +3394,28 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
fn->is_definition = fn->is_definition || EQUAL(tok, "{");
fn->is_weak |= attr->is_weak;
fn->is_noreturn |= attr->is_noreturn;
fn->tok = ty->name;
} else {
fn = new_gvar(name_str, ty);
fn->tok = ty->name;
fn->is_function = true;
fn->is_definition = EQUAL(tok, "{");
fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern);
fn->is_inline = attr->is_inline;
fn->is_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->align = MAX(fn->align, attr->align);
fn->is_weak |= attr->is_weak;
fn->section = fn->section ?: attr->section;
fn->is_ms_abi |= attr->is_ms_abi;
fn->visibility = fn->visibility ?: attr->visibility;
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_no_instrument_function |= attr->is_no_instrument_function;
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->javadown = fn->javadown ?: current_javadown;
fn->is_root = !(fn->is_static && fn->is_inline);
if (consume_attribute(&tok, tok, "asm")) {
@ -3452,6 +3464,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);
if (!var->tok) var->tok = ty->name;
var->javadown = current_javadown;
if (consume_attribute(&tok, tok, "asm")) {
tok = skip(tok, '(');
@ -3459,9 +3472,16 @@ static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
tok = skip(tok, ')');
}
tok = attribute_list(tok, attr, thing_attributes);
var->align = MAX(var->align, attr->align);
var->is_weak = attr->is_weak;
var->section = attr->section;
var->visibility = attr->visibility;
var->is_aligned = var->is_aligned | attr->is_aligned;
var->is_externally_visible = attr->is_externally_visible;
var->is_definition = !attr->is_extern;
var->is_static = attr->is_static;
var->is_tls = attr->is_tls;
var->section = attr->section;
if (attr->align) var->align = attr->align;
if (EQUAL(tok, "=")) {
gvar_initializer(&tok, tok->next, var);
@ -3535,15 +3555,30 @@ static Obj *declare3(char *s, Type *r, Type *a, Type *b, Type *c) {
return new_gvar(xstrcat("__builtin_", s), ty);
}
static void math0(char *name) {
declare0(name, ty_double);
declare0(xstrcat(name, 'f'), ty_float);
declare0(xstrcat(name, 'l'), ty_ldouble);
}
static void math1(char *name) {
declare1(name, ty_double, ty_double);
declare1(xstrcat(name, 'f'), ty_float, ty_float);
declare1(xstrcat(name, 'l'), ty_ldouble, ty_ldouble);
}
static void math2(char *name) {
declare2(name, ty_double, ty_double, ty_double);
declare2(xstrcat(name, 'f'), ty_float, ty_float, ty_float);
declare2(xstrcat(name, 'l'), ty_ldouble, ty_ldouble, ty_ldouble);
}
void declare_builtin_functions(void) {
Type *pvoid = pointer_to(ty_void);
Type *pchar = pointer_to(ty_char);
builtin_alloca = declare1("alloca", pointer_to(ty_void), ty_int);
declare0("trap", ty_int);
declare0("unreachable", ty_int);
declare0("inff", ty_float);
declare0("inf", ty_double);
declare0("infl", ty_ldouble);
declare1("ctz", ty_int, ty_int);
declare1("ctzl", ty_long, ty_long);
declare1("ctzll", ty_long, ty_long);
@ -3581,6 +3616,16 @@ void declare_builtin_functions(void) {
declare2("strchr", pchar, pchar, ty_int);
declare2("strstr", pchar, pchar, pchar);
declare1("frame_address", pvoid, ty_int);
declare2("scalbnf", ty_float, ty_float, ty_int);
declare2("scalbn", ty_double, ty_double, ty_int);
declare2("scalbnl", ty_ldouble, ty_ldouble, ty_int);
math0("inf");
math0("huge_val");
math1("fabs");
math1("logb");
math2("fmax");
math2("fmin");
math2("copysign");
}
// program = (typedef | function-definition | global-variable)*

View file

@ -119,7 +119,6 @@ static void PrintType(FILE *f, int l, const char *s, Type *t) {
PrintInt(f, l + 2, "align: ", t->align);
PrintBool(f, l + 2, "is_unsigned: ", t->is_unsigned);
PrintBool(f, l + 2, "is_atomic: ", t->is_atomic);
PrintType(f, l + 2, "origin: ", t->origin);
PrintType(f, l + 2, "base: ", t->base);
PrintTokStr(f, l + 2, "name: ", t->name);
PrintTokStr(f, l + 2, "name_pos: ", t->name_pos);
@ -231,6 +230,13 @@ static void PrintObj(FILE *f, int l, const char *s, Obj *o) {
PrintBool(f, l + 2, "is_noreturn: ", o->is_noreturn);
PrintBool(f, l + 2, "is_destructor: ", o->is_destructor);
PrintBool(f, l + 2, "is_constructor: ", o->is_constructor);
PrintBool(f, l + 2, "is_externally_visible: ", o->is_externally_visible);
PrintBool(f, l + 2,
"is_no_instrument_function: ", o->is_no_instrument_function);
PrintBool(f, l + 2,
"is_force_align_arg_pointer: ", o->is_force_align_arg_pointer);
PrintBool(f, l + 2,
"is_no_caller_saved_registers: ", o->is_no_caller_saved_registers);
PrintInt(f, l + 2, "stack_size: ", o->stack_size);
PrintObj(f, l + 2, "params: ", o->params);
PrintNode(f, l + 2, "body: ", o->body);

View file

@ -1,3 +1,4 @@
#include "libc/math.h"
#include "third_party/chibicc/test/test.h"
#define FPNAN 0
@ -115,6 +116,31 @@ void test_fpclassify(void) {
ASSERT(FPNAN, FPCLASSIFY(__builtin_nanl("")));
}
void test_logb(void) {
ASSERT(6, __builtin_logbl(123.456));
ASSERT(logbl(123.456L), __builtin_logbl(123.456L));
ASSERT(logbl(__LDBL_MIN__), __builtin_logbl(__LDBL_MIN__));
ASSERT(logbl(__LDBL_MAX__), __builtin_logbl(__LDBL_MAX__));
}
void test_fmax(void) {
ASSERT(fmaxl(1, 2), __builtin_fmaxl(1, 2));
ASSERT(2, __builtin_fmaxl(__builtin_nanl(""), 2));
ASSERT(1, __builtin_fmaxl(1, __builtin_nanl("")));
ASSERT(2, fmaxl(nanl(""), 2));
ASSERT(1, fmaxl(1, nanl("")));
ASSERT(fmaxf(1, 2), __builtin_fmaxf(1, 2));
ASSERT(2, __builtin_fmaxf(__builtin_nanl(""), 2));
ASSERT(1, __builtin_fmaxf(1, __builtin_nanl("")));
ASSERT(2, fmaxf(nanl(""), 2));
ASSERT(1, fmaxf(1, nanl("")));
ASSERT(fmax(1, 2), __builtin_fmax(1, 2));
ASSERT(2, __builtin_fmax(__builtin_nanl(""), 2));
ASSERT(1, __builtin_fmax(1, __builtin_nanl("")));
ASSERT(2, fmax(nanl(""), 2));
ASSERT(1, fmax(1, nanl("")));
}
void test_strlen(void) {
ASSERT(5, strlen("hello"));
ASSERT(5, __builtin_strlen("hello"));
@ -414,5 +440,7 @@ int main() {
test_strchr();
test_strpbrk();
test_strstr();
test_logb();
test_fmax();
return 0;
}

View file

@ -49,6 +49,7 @@ THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_UNICODE \
LIBC_MEM \
LIBC_TINYMATH \
LIBC_X \
THIRD_PARTY_CHIBICC \
THIRD_PARTY_COMPILER_RT