aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/hello-world.cff3
-rw-r--r--src/asm.cff109
-rw-r--r--src/cffc.hff15
-rw-r--r--src/irdump.cff10
-rw-r--r--src/irgen.cff100
-rw-r--r--src/main.cff1
-rw-r--r--src/parse.cff20
-rw-r--r--src/targ.cff6
8 files changed, 227 insertions, 37 deletions
diff --git a/examples/hello-world.cff b/examples/hello-world.cff
index 0ffca9f..ba16752 100644
--- a/examples/hello-world.cff
+++ b/examples/hello-world.cff
@@ -1,7 +1,8 @@
import "libc.hff";
extern fn main(argc int, argv **u8) int {
- *argv = "hi";
+ let it = *argv = "hi";
+ argv[42] - 3;
while argc > 0 {
argc = argc - 1;
}
diff --git a/src/asm.cff b/src/asm.cff
new file mode 100644
index 0000000..a92e14d
--- /dev/null
+++ b/src/asm.cff
@@ -0,0 +1,109 @@
+import "cffc.hff";
+import "common.hff";
+
+struct ASM {
+ outf *FILE,
+ lastsection enum { none, data, text },
+
+ fn gen(A *ASM, fmt *const u8, ...) void {
+ let ap va_list #?;
+ ap->start(fmt);
+ fn fmtproc(c u8, fp *void) void {
+ fputc(c, fp);
+ }
+ for let c = *fmt; c != 0; c = *++fmt {
+ if c != '%' {
+ fputc(c, A.outf);
+ continue;
+ }
+ switch (c = *++fmt) {
+ case 's';
+ fprintf(A.outf, "%s", ap->arg(*const u8));
+ case 'd';
+ fprintf(A.outf, "%d", ap->arg(int));
+ case 'I';
+ fprintf(A.outf, "%lld", as(c_llong) ap->arg(i64));
+ case 'f';
+ fprintf(A.outf, "%.17f", ap->arg(f64));
+ case 'z';
+ fprintf(A.outf, "%llu", as(c_ullong) ap->arg(usize));
+ case 'S';
+ pfmt(&fmtproc, A.outf, "%S", ap->arg([#]const u8));
+ case else
+ assert(#f, "bad fmt '%c'", c);
+ }
+ }
+ ap->end();
+ }
+}
+
+fn init(B *Backend, outf *FILE) void {
+ B.impl = calloc(1, sizeof ASM);
+ let impl *ASM = B.impl;
+ impl.outf = outf;
+}
+
+static directive4size []*const u8 = {
+ [1] = "byte",
+ [2] = "short",
+ [4] = "int",
+ [8] = "quad"
+};
+
+fn gendata(A *ASM, ty *const Type, ex *Expr) void {
+ switch ex.u {
+ case ZeroIni;
+ A->gen(".fill %z", ty.size);
+ case IntLit i;
+ A->gen(".%s %I", directive4size[ty.size], i.i);
+ case FloLit f;
+ A->gen(".%s %f", ty.size == 4 ? "single" : "double", f);
+ case StrLit s;
+ if ty->is(:Arr) {
+ A->gen(".ascii %S", s);
+ } else {
+ static id int = 0;
+ A->gen(".%s strlit%d.", directive4size[g_targ.ptrsize], id);
+ A->gen("\nstrlit%d.:\n\t.ascii %S", id++, s);
+ }
+ case else
+ assert(#f, "NYI data");
+ }
+}
+
+fn genstatic(A *ASM, decl *Decl) void {
+ if decl.u.Static.fwd {
+ return;
+ }
+ if A.lastsection != :data {
+ A->gen(".section data\n");
+ A.lastsection = :data;
+ }
+ if decl.externp {
+ A->gen(".globl %s\n%s:\n", decl.name, decl.name);
+ } else {
+ A->gen("%s.s%d:\n", decl.name, decl.u.Static.id);
+ }
+ A->gen("\t.align %z\n", decl.u.Static.ty.align);
+ switch decl.u.Static.ini {
+ case None;
+ A->gen("\t.fill %z\n", decl.u.Static.ty.size);
+ case Some *ex;
+ A->gen("\t");
+ gendata(A, decl.u.Static.ty, ex);
+ A->gen("\n");
+ }
+}
+
+fn bgenstatic(B *Backend, decl *Decl) void {
+ genstatic(B.impl, decl);
+}
+
+fn bgenfn(B *Backend, Fn *Fn) void {
+}
+
+extern static g_asmbackend Backend = {
+ .initf: &init,
+ .genstaticf: &bgenstatic,
+ .genfnf: &bgenfn,
+};
diff --git a/src/cffc.hff b/src/cffc.hff
index 31af5fb..c8e3f11 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -321,6 +321,14 @@ struct DeclList {
decl Decl,
}
+
+struct Backend {
+ impl *void,
+ initf *fn(*Backend, *FILE) void,
+ genstaticf *fn(*Backend, *Decl) void,
+ genfnf *fn(*Backend, *Fn) void,
+}
+
struct Targ {
name *const u8,
ptrsize u8,
@@ -333,6 +341,8 @@ struct Targ {
valistsize u8, valistalign u8,
charsigned bool,
shortenum bool,
+
+ B *Backend,
}
// parse.cff
@@ -351,7 +361,7 @@ extern fn err(P *Parser, Loc, fmt *const u8, ...) void;
extern fn fatal(*Parser, Loc, fmt *const u8, ...) void;
// targ.cff
-extern static g_targ *const Targ;
+extern static g_targ Targ;
extern fn targ_ini(name *const u8) bool;
// type.cff
@@ -443,3 +453,6 @@ extern fn mkirctx(*Allocator) *IRCtx;
extern fn ir_genstatic(*IRCtx, *Decl) void;
extern fn ir_genfn(*IRCtx, *Fn) void;
extern fn ir_free(*IRCtx) void;
+
+// asm.cff
+extern static g_asmbackend Backend;
diff --git a/src/irdump.cff b/src/irdump.cff
index 0942f87..ee68f91 100644
--- a/src/irdump.cff
+++ b/src/irdump.cff
@@ -144,8 +144,14 @@ extern fn irdump(inst *IRInst) void {
dump("set %a, %a\n", inst.args[0], inst.args[1]);
continue #'dump;
}
- exprinst(:load, "load", 2)
- stmtinst(:store, "store", 2)
+ if inst.t == :load {
+ dump("%a(%t) = load %a[%a]\n", inst.args[0], inst.args[0].Val.ty, inst.args[1], inst.args[2]);
+ continue #'dump;
+ }
+ if inst.t == :store {
+ dump("store %a[%a], %a\n", inst.args[0], inst.args[1], inst.args[2]);
+ continue #'dump;
+ }
stmtinst(:ret0, "ret", 0);
stmtinst(:ret, "ret", 1);
if inst.t == :beqz {
diff --git a/src/irgen.cff b/src/irgen.cff
index 1cd5172..9d32bfd 100644
--- a/src/irgen.cff
+++ b/src/irgen.cff
@@ -3,6 +3,7 @@ import "common.hff";
import "map.hff";
extern fn ir_genstatic(IR *IRCtx, decl *Decl) void {
+ g_targ.B.genstaticf(g_targ.B, decl);
}
struct IntTraits {
@@ -64,7 +65,7 @@ fn genblock(S *InstStream, block [#]Stmt) void;
fn genexpr(S *InstStream, ex *Expr) IRValue {
static tmpid int = 0;
- // fold(ex);
+ fold(ex);
defmacro genunop(u,t) [ {
let child = genexpr(S, (u).ex);
@@ -94,7 +95,12 @@ fn genexpr(S *InstStream, ex *Expr) IRValue {
case :neg; genunop(u, ex.ty->is(:Int) ? :neg : :fneg);
case :not; genunop(u, :not);
case :compl; genunop(u, :compl);
- case :deref; genunop(u, :load);
+ case :deref;
+ let child = genexpr(S, (u).ex);
+ let res = IRValue{ex.ty, .u: :Tmp(tmpid++)};
+ let inst = S->mkinst3({:load}, :Val(res), :Val(child), :Val{ty_int,:IImm(0)});
+ S->push(inst);
+ return res;
case :preinc, :predec;
let one = u.op == :preinc ? 1 : -1;
switch u.ex.u {
@@ -106,14 +112,8 @@ fn genexpr(S *InstStream, ex *Expr) IRValue {
S->push(rhs);
let inst = S->mkinst2({:setvar}, :Val(ex), :Val(inced));
S->push(inst);
- let ex = genexpr(S, u.ex);
- if decl.u.#tag == :Let {
- return {ex.ty, :Local(decl)};
- } else {
- return {ex.ty, :Global(decl)};
- }
+ return inced;
}
-
}
case BinOp b;
switch b.op {
@@ -146,27 +146,58 @@ fn genexpr(S *InstStream, ex *Expr) IRValue {
S->push(inst);
return res;
case '=';
- switch b.lhs.u {
+ switch b.lhs.u { // var = ex
case Symbol decl;
- let inst = S->mkinst2({:setvar}, :Val(genexpr(S, b.lhs)), :Val(genexpr(S, b.rhs)));
+ let r = genexpr(S, b.rhs);
+ let inst = S->mkinst2({:setvar}, :Val(genexpr(S, b.lhs)), :Val(r));
S->push(inst);
- if decl.u.#tag == :Let {
- return {ex.ty, :Local(decl)};
- } else {
- return {ex.ty, :Global(decl)};
- }
- case UnOp u;
+ return r;
+ case UnOp u; // *a = ex
assert(u.op == :deref, "deref?? unop=");
let child = genexpr(S, u.ex);
- let inst = S->mkinst2({:store}, :Val(child), :Val(genexpr(S, b.rhs)));
+ let r = genexpr(S, b.rhs);
+ let inst = S->mkinst3({:store}, :Val(child), :Val{ty_int,:IImm(0)}, :Val(r));
S->push(inst);
- let it = genexpr(S, b.lhs);
- return it;
-
+ return r;
+ case Index index; // a[b] = ex
+ let indexee = genexpr(S, index.lhs);
+ let index = genexpr(S, index.rhs);
+ let newval = genexpr(S, b.rhs);
+ S->push(S->mkinst3({:store}, :Val(indexee), :Val(index), :Val(newval)));
+ return newval;
case else
assert(#f, "nyi =");
}
+ case '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=';
+ let o IRInstT = b.op == '+=' ? (ex.ty->is(:Flo) ? :fadd : :add)
+ : b.op == '-=' ? (ex.ty->is(:Flo) ? :fsub : :sub)
+ : b.op == '*=' ? (ex.ty->is(:Flo) ? :fmul : :mul)
+ : b.op == '/=' ? (ex.ty->is(:Flo) ? :fdiv : :div)
+ : b.op == '%=' ? :mod
+ : b.op == '&=' ? :band
+ : b.op == '|=' ? :bor
+ : b.op == '<<=' ? :lsl
+ : b.op == '>>=' ? (ex.ty.u.Int.sgn ? :asr : :lsr) : :nop;
+ switch b.lhs.u {
+ case Symbol decl; // var += ex
+ let lhs = genexpr(S, b.lhs);
+ let rhs = genexpr(S, b.rhs);
+ let new = IRValue{ex.ty, .u: :Tmp(tmpid++)};
+ S->push(S->mkinst3({o}, :Val(new), :Val(lhs), :Val(rhs)));
+ let inst = S->mkinst2({:setvar}, :Val(lhs), :Val(new));
+ S->push(inst);
+ return new;
+ }
+
}
+ case Index idx;
+ let i = genexpr(S, idx.lhs);
+ let off = genexpr(S, idx.rhs);
+ let res = IRValue{ex.ty, .u: :Tmp(tmpid++)};
+ let inst = S->mkinst3({:load}, :Val(res), :Val(i), :Val(off));
+ S->push(inst);
+ return res;
+
case Cond c;
let test = genexpr(S, c.test);
let beqz = S->mkinst1({:beqz}, :Val(test));
@@ -226,6 +257,15 @@ fn genstmt(S *InstStream, stmt *Stmt) void {
}
case If If;
+ #{
+ %t = <test>
+ beqz %t, .L0
+ <t>
+ b .L1
+ .L0:
+ <f>
+ .L1:
+ }
let t = genexpr(S, &If.test);
let beqz = S->mkinst1({:beqz}, :Val(t));
S->push(beqz);
@@ -241,6 +281,14 @@ fn genstmt(S *InstStream, stmt *Stmt) void {
}
case While loop;
+ #{
+ .cont:
+ %t = <test>
+ beqz %t, .brk
+ <body>
+ b .cont
+ .brk:
+ }
let begin = S->pushnop();
let t = genexpr(S, &loop.test);
let beqz = S->mkinst1({:beqz}, :Val(t));
@@ -277,12 +325,14 @@ fn genstmt(S *InstStream, stmt *Stmt) void {
beqz.branch = end;
case Break id;
- let jumpto = *S.loopbrks->get(id);
- S->push(S->mkinst({:b, .branch: jumpto},0));
+ let jumpto = S.loopbrks->get(id);
+ assert(jumpto != #null, "no brk label %d", id);
+ S->push(S->mkinst({:b, .branch: *jumpto},0));
case Continue id;
- let jumpto = *S.loopconts->get(id);
- S->push(S->mkinst({:b, .branch: jumpto},0));
+ let jumpto = S.loopconts->get(id);
+ assert(jumpto != #null, "no cont label %d", id);
+ S->push(S->mkinst({:b, .branch: *jumpto},0));
case Return retex;
if retex->empty() {
diff --git a/src/main.cff b/src/main.cff
index 5246c60..ac4ca70 100644
--- a/src/main.cff
+++ b/src/main.cff
@@ -5,7 +5,6 @@ extern fn main(argc int, argv **u8) int {
assert(argc > 1, "args?");
targ_ini("amd64-sysv");
- assert(g_targ != #null, "target?");
let p = Parser {};
parser_init(&p, argv[1]);
diff --git a/src/parse.cff b/src/parse.cff
index 43c4e7d..11c17cc 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -2092,17 +2092,18 @@ fn pstfor(P *Parser, loc Loc, label *const u8) Stmt {
lexexpect(P, '{');
}
let body [#]Stmt #?;
- with_tmpchange(P.curloop, ++P.loopid) {
+ let id = ++P.loopid;
+ with_tmpchange(P.curloop, id) {
let env = mkenv(P.curenv, P.alloc);
defer envfree(env);
pushenv(P, env);
if label {
- putdecl(P, loc, { label, loc, .u: :Label(P.loopid) });
+ putdecl(P, loc, { label, loc, .u: :Label(id) });
}
body = parseblock(P);
popenv(P);
}
- return { loc, :For { ini, test, next, body, P.loopid }};
+ return { loc, :For { ini, test, next, body, id }};
}
fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt {
@@ -2396,8 +2397,12 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
ty = constify(ty);
}
- let decl Decl = { name, tok.loc, externp, :Let { ty, ini, fwd, P.curfn ? P.curfn.id : 0, P.varid++ } };
- if !letp {
+ let decl Decl = { name, tok.loc, externp, :Let { ty, ini, fwd, P.curfn ? P.curfn.id : 0, } };
+ if letp {
+ decl.u.Let.id = P.varid++;
+ } else {
+ static id int = 0;
+ decl.u.Static.id = id++;
decl.u.#tag = :Static;
}
yield(putdecl(P, tok.loc, decl), yarg);
@@ -2436,6 +2441,9 @@ fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl {
return decl;
}
+ let varid = P.varid, loopid = P.loopid;
+ P.varid = 0;
+ P.loopid = 0;
with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }) {
with_tmpchange(P.curfn, Fn) {
let env = mkenv(P.curenv, P.alloc);
@@ -2454,6 +2462,8 @@ fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl {
(as(*Arena)P.alloc.a)->destroy();
}
}
+ P.varid = varid;
+ P.loopid = loopid;
return decl;
}
diff --git a/src/targ.cff b/src/targ.cff
index 895a20a..38fcb48 100644
--- a/src/targ.cff
+++ b/src/targ.cff
@@ -17,12 +17,14 @@ static const targs []const Targ = {
}
};
-extern static g_targ *const Targ = {};
+extern static g_targ Targ = {};
extern fn targ_ini(name *const u8) bool {
foreach(targ, i, targs[0::]) {
if streq(name, targ.name) {
- g_targ = &targs[i];
+ g_targ = targs[i];
+ g_targ.B = &g_asmbackend;
+ g_targ.B.initf(g_targ.B, stdout);
return #t;
}
}