ww: import toolchain — C bootstrap + ww-side self-host (phases 0-10)
C bootstrap (phases 0-9):
cmd/wwc, cmd/6c, cmd/6a, cmd/6l, cmd/ww, rt, lib/*.
ww-side self-host (phase 10):
selfhost/cmd/wwc — ww-cgen frontend; bootstrap fixed point.
selfhost/cmd/6a — assembler; byte-identical to C 6a (test 991).
selfhost/cmd/6l — linker w/ archive (.a) support; byte-identical
to C 6l (test 992).
selfhost/cmd/ww — driver (build/run/version); byte-identical to
C ww (test 993).
make test: 15/15. make bootstrap: ww2.s == ww3.s, ww2.o == ww3.o,
ww2 == ww3 byte-identical, with the full ww-tooled chain.
This commit is contained in:
107
cmd/6c/6.out.h
Normal file
107
cmd/6c/6.out.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 6.out.h — amd64 instruction enum + register names. Mirrors the
|
||||
* Plan 9 6c shape (cmd/6c/6.out.h) but trimmed to the subset that
|
||||
* 6c emits and 6a consumes in this bootstrap. Each new opcode added
|
||||
* here must also gain encoding support in cmd/6a/asm.c.
|
||||
*/
|
||||
#ifndef SIX_OUT_H
|
||||
#define SIX_OUT_H
|
||||
|
||||
/* registers — Plan 9 names; lowercase = 8-bit, etc. We use 64-bit. */
|
||||
enum {
|
||||
D_NONE = 0,
|
||||
|
||||
/* general purpose 64-bit */
|
||||
D_AX, D_CX, D_DX, D_BX,
|
||||
D_SP, D_BP, D_SI, D_DI,
|
||||
D_R8, D_R9, D_R10, D_R11,
|
||||
D_R12, D_R13, D_R14, D_R15,
|
||||
|
||||
/* SSE/XMM 64-bit float regs */
|
||||
D_X0, D_X1, D_X2, D_X3,
|
||||
D_X4, D_X5, D_X6, D_X7,
|
||||
D_X8, D_X9, D_X10, D_X11,
|
||||
D_X12, D_X13, D_X14, D_X15,
|
||||
|
||||
/* pseudo regs (Plan 9) */
|
||||
D_PSP, /* SP pseudo (frame-relative) */
|
||||
D_PFP, /* FP pseudo (incoming args) */
|
||||
D_PSB, /* SB pseudo (static base) */
|
||||
|
||||
/* operand kinds; not registers but share the slot */
|
||||
D_CONST, /* $N immediate */
|
||||
D_BRANCH, /* label reference */
|
||||
D_EXTERN, /* external symbol */
|
||||
D_INDIR /* offset(reg) memory */
|
||||
};
|
||||
|
||||
/* opcodes — the small set we currently emit & encode */
|
||||
enum {
|
||||
A_NOP = 0,
|
||||
A_TEXT,
|
||||
A_DATA,
|
||||
A_GLOBL,
|
||||
A_END,
|
||||
|
||||
A_MOVQ,
|
||||
A_MOVL,
|
||||
A_MOVB,
|
||||
A_MOVZBQ, /* movzx r64, r/m8 — load byte zero-extended */
|
||||
A_MOVSXD, /* movsxd r64, r/m32 — load i32 sign-extended */
|
||||
|
||||
/* SSE2 scalar double-precision float */
|
||||
A_MOVSD, /* xmm/m → xmm and xmm → m */
|
||||
A_ADDSD,
|
||||
A_SUBSD,
|
||||
A_MULSD,
|
||||
A_DIVSD,
|
||||
A_UCOMISD,
|
||||
A_CVTTSD2SI, /* truncate f64 → i64 */
|
||||
A_CVTSI2SD, /* convert i64 → f64 */
|
||||
|
||||
/* SSE scalar single-precision float (f32). Same xmm regs. */
|
||||
A_MOVSS,
|
||||
A_ADDSS,
|
||||
A_SUBSS,
|
||||
A_MULSS,
|
||||
A_DIVSS,
|
||||
A_UCOMISS,
|
||||
A_CVTTSS2SI,
|
||||
A_CVTSI2SS,
|
||||
A_CVTSD2SS, /* f64 → f32 truncate */
|
||||
A_CVTSS2SD, /* f32 → f64 widen */
|
||||
A_ADDQ,
|
||||
A_SUBQ,
|
||||
A_IMULQ,
|
||||
A_IDIVQ,
|
||||
A_DIVQ, /* unsigned 64-bit divide; sibling of IDIVQ */
|
||||
A_NEGQ,
|
||||
A_NOTQ,
|
||||
A_ANDQ,
|
||||
A_ORQ,
|
||||
A_XORQ,
|
||||
A_SHLQ,
|
||||
A_SHRQ,
|
||||
A_CMPQ,
|
||||
|
||||
A_PUSHQ,
|
||||
A_POPQ,
|
||||
A_LEAQ,
|
||||
|
||||
A_CALL,
|
||||
A_RET,
|
||||
A_JMP,
|
||||
A_JE, A_JNE,
|
||||
A_JL, A_JLE, A_JG, A_JGE,
|
||||
A_JB, A_JBE, A_JA, A_JAE,
|
||||
A_JZ, A_JNZ,
|
||||
|
||||
A_SYSCALL,
|
||||
|
||||
A_LAST
|
||||
};
|
||||
|
||||
const char *anames(int); /* opcode -> mnemonic */
|
||||
const char *rnames(int); /* register -> name */
|
||||
|
||||
#endif
|
||||
2622
cmd/6c/cgen.c
Normal file
2622
cmd/6c/cgen.c
Normal file
File diff suppressed because it is too large
Load Diff
56
cmd/6c/gc.h
Normal file
56
cmd/6c/gc.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* gc.h — 6c-private header: Prog/Adr structs, scratch register set,
|
||||
* stack-frame state. Plan 9 cmd/6c/gc.h shape, trimmed.
|
||||
*/
|
||||
#ifndef SIX_GC_H
|
||||
#define SIX_GC_H
|
||||
|
||||
#include "ww.h"
|
||||
#include "6.out.h"
|
||||
|
||||
typedef struct Prog Prog;
|
||||
typedef struct Adr Adr;
|
||||
|
||||
/* one operand: register, immediate, indirect, or symbolic. */
|
||||
struct Adr {
|
||||
int type; /* D_AX, D_CONST, D_INDIR, ... */
|
||||
int reg; /* base register for D_INDIR */
|
||||
long long offset; /* immediate value or memory displacement */
|
||||
const char *sym; /* symbol name for D_EXTERN/D_BRANCH */
|
||||
};
|
||||
|
||||
struct Prog {
|
||||
int as; /* opcode (A_MOVQ, ...) */
|
||||
Adr from; /* source operand */
|
||||
Adr to; /* destination operand (Plan 9 order) */
|
||||
int line;
|
||||
const char *label; /* if non-NULL, this prog is preceded by label: */
|
||||
Prog *link;
|
||||
};
|
||||
|
||||
/* per-fn codegen state */
|
||||
typedef struct Cg Cg;
|
||||
struct Cg {
|
||||
Arena *a;
|
||||
Prog *head, *tail;
|
||||
const char *fnname;
|
||||
int framesize; /* bytes of locals; 16-byte aligned */
|
||||
int curoff; /* current top of locals */
|
||||
Scope *locals; /* (name → offset) tracked via Sym */
|
||||
int labelseq;
|
||||
};
|
||||
|
||||
/* cgen.c */
|
||||
void cg_init(Cg*, Arena*);
|
||||
void cg_file(Cg*, FILE *out, Node *file);
|
||||
Prog *newprog(Cg*, int op);
|
||||
void emit(Cg*, Prog*);
|
||||
|
||||
/* txt.c */
|
||||
void txt_emit(FILE*, Prog *head);
|
||||
|
||||
/* swt.c, peep.c, reg.c — placeholders for now */
|
||||
void peephole(Cg*);
|
||||
void regalloc_init(Cg*);
|
||||
|
||||
#endif
|
||||
93
cmd/6c/main.c
Normal file
93
cmd/6c/main.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 6c — amd64 compiler driver. Reads a .ww source file, runs the
|
||||
* libwwc frontend (lex → parse → check), then walks the typed AST
|
||||
* via cgen.c and writes Plan 9-flavoured amd64 asm to stdout (or
|
||||
* the file given by -o).
|
||||
*/
|
||||
#include "gc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, u64 *outlen)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (f == NULL) return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (n < 0) { fclose(f); return -1; }
|
||||
char *b = malloc((size_t)n + 1);
|
||||
if (b == NULL) { fclose(f); return -1; }
|
||||
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
||||
b[n] = '\0';
|
||||
fclose(f);
|
||||
*outbuf = b;
|
||||
*outlen = (u64)n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char *src = NULL;
|
||||
const char *out = NULL;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
const char *a = argv[i];
|
||||
if (strcmp(a, "-o") == 0 && i + 1 < argc) {
|
||||
out = argv[++i];
|
||||
} else if (a[0] == '-') {
|
||||
fprintf(stderr, "6c: unknown flag %s\n", a);
|
||||
return 2;
|
||||
} else if (src == NULL) {
|
||||
src = a;
|
||||
} else {
|
||||
fprintf(stderr, "6c: only one input supported\n");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if (src == NULL) {
|
||||
fputs("usage: 6c [-o out.s] file.ww\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *buf;
|
||||
u64 len;
|
||||
if (slurp(src, &buf, &len) < 0) {
|
||||
fprintf(stderr, "6c: %s: cannot read\n", src);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Arena *a = newarena();
|
||||
Lex l;
|
||||
Parser p;
|
||||
Checker c;
|
||||
Cg cg;
|
||||
|
||||
lexinit(&l, a, src, buf, len);
|
||||
parserinit(&p, a, &l);
|
||||
Node *file = parsefile(&p);
|
||||
if (l.errs || p.errs) return 1;
|
||||
|
||||
check_init(&c, a);
|
||||
check_file(&c, file);
|
||||
if (c.errs) return 1;
|
||||
|
||||
FILE *of = stdout;
|
||||
if (out) {
|
||||
of = fopen(out, "wb");
|
||||
if (of == NULL) {
|
||||
fprintf(stderr, "6c: cannot open %s\n", out);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
cg_init(&cg, a);
|
||||
cg_file(&cg, of, file);
|
||||
|
||||
if (of != stdout) fclose(of);
|
||||
freearena(a);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
8
cmd/6c/peep.c
Normal file
8
cmd/6c/peep.c
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* peep.c — peephole pass. Currently a no-op; reserved for the kind of
|
||||
* cleanup Plan 9 6c does (folding adjacent moves, removing redundant
|
||||
* compares). Wire in `peephole(c)` from cgen.c after the main walk.
|
||||
*/
|
||||
#include "gc.h"
|
||||
|
||||
void peep_run(Cg *c) { (void)c; }
|
||||
9
cmd/6c/reg.c
Normal file
9
cmd/6c/reg.c
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* reg.c — register allocator. The current cgen pins everything to AX
|
||||
* with BX as a scratch top-of-stack — no real allocation. This file
|
||||
* is the seam where a linear-scan or graph-colouring pass would land
|
||||
* later; today it's empty.
|
||||
*/
|
||||
#include "gc.h"
|
||||
|
||||
void reg_run(Cg *c) { (void)c; }
|
||||
8
cmd/6c/swt.c
Normal file
8
cmd/6c/swt.c
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* swt.c — switch-statement lowering. Stub for now: cgen falls
|
||||
* through to a no-op for N_SWITCH. When we add a real lowering, it
|
||||
* will live here, mirroring Plan 9 6c's pswt.c.
|
||||
*/
|
||||
#include "gc.h"
|
||||
|
||||
void swt_lower(Cg *c, Node *n) { (void)c; (void)n; }
|
||||
189
cmd/6c/txt.c
Normal file
189
cmd/6c/txt.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* txt.c — print a Prog list as Plan 9-flavoured amd64 asm text.
|
||||
*
|
||||
* Format we emit (and that 6a expects):
|
||||
* TEXT name<framesize>
|
||||
* MOVQ $1, AX
|
||||
* MOVQ AX, name(SB) ; extern symbol
|
||||
* MOVQ off(BP), AX ; local
|
||||
* CMPQ AX, $0
|
||||
* JE label
|
||||
* RET
|
||||
* label:
|
||||
*
|
||||
* Operand order is Plan 9-ish: source first, dest second. (Same as
|
||||
* AT&T; the convention diverges from Plan 9 only on a few items we
|
||||
* don't yet emit.)
|
||||
*/
|
||||
#include "gc.h"
|
||||
#include <string.h>
|
||||
|
||||
const char *
|
||||
anames(int op)
|
||||
{
|
||||
switch (op) {
|
||||
case A_NOP: return "NOP";
|
||||
case A_TEXT: return "TEXT";
|
||||
case A_DATA: return "DATA";
|
||||
case A_GLOBL: return "GLOBL";
|
||||
case A_END: return "END";
|
||||
case A_MOVQ: return "MOVQ";
|
||||
case A_MOVL: return "MOVL";
|
||||
case A_MOVB: return "MOVB";
|
||||
case A_MOVZBQ: return "MOVZBQ";
|
||||
case A_MOVSXD: return "MOVSXD";
|
||||
case A_MOVSD: return "MOVSD";
|
||||
case A_ADDSD: return "ADDSD";
|
||||
case A_SUBSD: return "SUBSD";
|
||||
case A_MULSD: return "MULSD";
|
||||
case A_DIVSD: return "DIVSD";
|
||||
case A_UCOMISD: return "UCOMISD";
|
||||
case A_CVTTSD2SI: return "CVTTSD2SI";
|
||||
case A_CVTSI2SD:return "CVTSI2SD";
|
||||
case A_MOVSS: return "MOVSS";
|
||||
case A_ADDSS: return "ADDSS";
|
||||
case A_SUBSS: return "SUBSS";
|
||||
case A_MULSS: return "MULSS";
|
||||
case A_DIVSS: return "DIVSS";
|
||||
case A_UCOMISS: return "UCOMISS";
|
||||
case A_CVTTSS2SI: return "CVTTSS2SI";
|
||||
case A_CVTSI2SS:return "CVTSI2SS";
|
||||
case A_CVTSD2SS:return "CVTSD2SS";
|
||||
case A_CVTSS2SD:return "CVTSS2SD";
|
||||
case A_ADDQ: return "ADDQ";
|
||||
case A_SUBQ: return "SUBQ";
|
||||
case A_IMULQ: return "IMULQ";
|
||||
case A_IDIVQ: return "IDIVQ";
|
||||
case A_DIVQ: return "DIVQ";
|
||||
case A_NEGQ: return "NEGQ";
|
||||
case A_NOTQ: return "NOTQ";
|
||||
case A_ANDQ: return "ANDQ";
|
||||
case A_ORQ: return "ORQ";
|
||||
case A_XORQ: return "XORQ";
|
||||
case A_SHLQ: return "SHLQ";
|
||||
case A_SHRQ: return "SHRQ";
|
||||
case A_CMPQ: return "CMPQ";
|
||||
case A_PUSHQ: return "PUSHQ";
|
||||
case A_POPQ: return "POPQ";
|
||||
case A_LEAQ: return "LEAQ";
|
||||
case A_CALL: return "CALL";
|
||||
case A_RET: return "RET";
|
||||
case A_JMP: return "JMP";
|
||||
case A_JE: return "JE";
|
||||
case A_JNE: return "JNE";
|
||||
case A_JL: return "JL";
|
||||
case A_JLE: return "JLE";
|
||||
case A_JG: return "JG";
|
||||
case A_JGE: return "JGE";
|
||||
case A_JB: return "JB";
|
||||
case A_JBE: return "JBE";
|
||||
case A_JA: return "JA";
|
||||
case A_JAE: return "JAE";
|
||||
case A_JZ: return "JZ";
|
||||
case A_JNZ: return "JNZ";
|
||||
case A_SYSCALL: return "SYSCALL";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
const char *
|
||||
rnames(int r)
|
||||
{
|
||||
switch (r) {
|
||||
case D_AX: return "AX"; case D_CX: return "CX";
|
||||
case D_DX: return "DX"; case D_BX: return "BX";
|
||||
case D_SP: return "SP"; case D_BP: return "BP";
|
||||
case D_SI: return "SI"; case D_DI: return "DI";
|
||||
case D_R8: return "R8"; case D_R9: return "R9";
|
||||
case D_R10: return "R10"; case D_R11: return "R11";
|
||||
case D_R12: return "R12"; case D_R13: return "R13";
|
||||
case D_R14: return "R14"; case D_R15: return "R15";
|
||||
case D_X0: return "X0"; case D_X1: return "X1";
|
||||
case D_X2: return "X2"; case D_X3: return "X3";
|
||||
case D_X4: return "X4"; case D_X5: return "X5";
|
||||
case D_X6: return "X6"; case D_X7: return "X7";
|
||||
case D_X8: return "X8"; case D_X9: return "X9";
|
||||
case D_X10: return "X10"; case D_X11: return "X11";
|
||||
case D_X12: return "X12"; case D_X13: return "X13";
|
||||
case D_X14: return "X14"; case D_X15: return "X15";
|
||||
case D_PSP: return "SP"; case D_PFP: return "FP"; case D_PSB: return "SB";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void
|
||||
prAdr(FILE *f, Adr a)
|
||||
{
|
||||
switch (a.type) {
|
||||
case D_NONE: fputs("?", f); break;
|
||||
case D_CONST:
|
||||
fprintf(f, "$%lld", a.offset);
|
||||
break;
|
||||
case D_INDIR:
|
||||
if (a.offset)
|
||||
fprintf(f, "%lld(%s)", a.offset, rnames(a.reg));
|
||||
else
|
||||
fprintf(f, "(%s)", rnames(a.reg));
|
||||
break;
|
||||
case D_BRANCH:
|
||||
fputs(a.sym ? a.sym : "?", f);
|
||||
break;
|
||||
case D_EXTERN:
|
||||
fprintf(f, "%s(SB)", a.sym ? a.sym : "?");
|
||||
break;
|
||||
default:
|
||||
fputs(rnames(a.type), f);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
txt_emit(FILE *f, Prog *head)
|
||||
{
|
||||
for (Prog *p = head; p; p = p->link) {
|
||||
if (p->label) {
|
||||
fprintf(f, "%s:\n", p->label);
|
||||
if (p->as == A_NOP) continue;
|
||||
}
|
||||
switch (p->as) {
|
||||
case A_NOP:
|
||||
break;
|
||||
case A_TEXT:
|
||||
fprintf(f, "TEXT %s,$%lld\n",
|
||||
p->to.sym ? p->to.sym : "?",
|
||||
p->from.offset);
|
||||
break;
|
||||
case A_RET:
|
||||
fputs("\tRET\n", f);
|
||||
break;
|
||||
case A_SYSCALL:
|
||||
fputs("\tSYSCALL\n", f);
|
||||
break;
|
||||
case A_NEGQ:
|
||||
case A_NOTQ:
|
||||
case A_PUSHQ:
|
||||
case A_POPQ:
|
||||
case A_IDIVQ:
|
||||
case A_DIVQ:
|
||||
fprintf(f, "\t%s\t", anames(p->as));
|
||||
prAdr(f, p->to);
|
||||
fputc('\n', f);
|
||||
break;
|
||||
case A_CALL:
|
||||
case A_JMP:
|
||||
case A_JE: case A_JNE:
|
||||
case A_JL: case A_JLE: case A_JG: case A_JGE:
|
||||
case A_JB: case A_JBE: case A_JA: case A_JAE:
|
||||
case A_JZ: case A_JNZ:
|
||||
fprintf(f, "\t%s\t", anames(p->as));
|
||||
prAdr(f, p->to);
|
||||
fputc('\n', f);
|
||||
break;
|
||||
default:
|
||||
fprintf(f, "\t%s\t", anames(p->as));
|
||||
prAdr(f, p->from);
|
||||
fputs(", ", f);
|
||||
prAdr(f, p->to);
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user