diff options
| author | 2022-08-20 11:06:38 +0200 | |
|---|---|---|
| committer | 2022-08-20 11:06:38 +0200 | |
| commit | 46e1f128fd310bd29a2b4335b36c60d6cc0aa3a7 (patch) | |
| tree | feb93a69cf93773d0542435ae09c76de04e509c8 | |
| parent | df41a4512932f1312e4725d0409757a683b091ed (diff) | |
initial work on IR
| -rw-r--r-- | bootstrap/cgen.c | 7 | ||||
| -rw-r--r-- | bootstrap/parse.c | 5 | ||||
| -rw-r--r-- | examples/hello-world.cff | 2 | ||||
| -rw-r--r-- | src/cffc.hff | 10 | ||||
| -rw-r--r-- | src/fmt.cff | 6 | ||||
| -rw-r--r-- | src/ir.cff | 10 | ||||
| -rw-r--r-- | src/ir.hff | 62 | ||||
| -rw-r--r-- | src/irdump.cff | 119 | ||||
| -rw-r--r-- | src/irgen.cff | 124 | ||||
| -rw-r--r-- | src/parse.cff | 7 |
10 files changed, 345 insertions, 7 deletions
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 5fae699..e4ba1fd 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -246,7 +246,10 @@ genexpr(struct expr *ex) { pri(")"); break; case Eas: - pri("((%t)%e)", ex->ty, ex->child); + if (ex->ty->t != TYarr) + pri("((%t)%e)", ex->ty, ex->child); + else + pri("(%e)", ex->child); break; case Eenumval: pri("/*%s:%s*/", ex->ty->enu.name, ex->enu.vname); @@ -341,7 +344,7 @@ genstmt(struct blockstmt *block, struct stmt *stmt) { && (decl.var.ini->t == Ezeroini || decl.var.ini->t == Eini)) { geniniex(decl.var.ini); - } else if (decl.var.ty->t == TYeunion) { + } else if (decl.var.ini->t == Eeuini) { geneuiniex(decl.var.ini); } else { pri("%e", decl.var.ini); diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 371289f..91c7567 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -1644,7 +1644,10 @@ pexprefix(struct parser *P) { ex = pexprefix(P); from = ex.ty; - if (typeof2(to, from)) ; + if (to->t == TYarr && to->length < 0 && ex.t == Eini) { + ex.ty = to = mkarraytype(to->child, ex.ini.maxn + 1); + } + else if (typeof2(to, from)) ; else if (to->t == TYint && from->t == TYptr && to->size == from->size) ; else if (from->t == TYint && to->t == TYptr && to->size == from->size) ; else if (from->t == TYptr && to->t == TYptr) ; diff --git a/examples/hello-world.cff b/examples/hello-world.cff index 3eb198c..42258ab 100644 --- a/examples/hello-world.cff +++ b/examples/hello-world.cff @@ -1,6 +1,6 @@ import "libc.hff"; extern fn main(argc int, argv *const *const u8) int { - printf("hello world\n"); + printf("hello %d", 42); } diff --git a/src/cffc.hff b/src/cffc.hff index abc00ae..e10602d 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -20,7 +20,7 @@ struct Loc { enum TokT : i32 { // !sorted kw_alignof, kw_and, kw_as, kw_break, kw_case, kw_const, - kw_continue, kw_def, kw_defmacro, kw_do, + kw_continue, kw_def, kw_defer, kw_defmacro, kw_do, kw_else, kw_enum, kw_extern, kw_fn, kw_for, kw_if, kw_import, kw_let, kw_offsetof, kw_or, kw_return, kw_sizeof, kw_static, @@ -118,8 +118,10 @@ struct Type { struct Fn; struct Expan; +struct IRCtx; struct Parser { fp *FILE, + irctx *IRCtx, alloc *Allocator, tlalloc *Allocator, curfile *const u8, @@ -433,3 +435,9 @@ extern fn envfree(*Env) void; // fold.cff extern fn fold(*Expr) bool; + +// ir*.cff +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; diff --git a/src/fmt.cff b/src/fmt.cff index 91f13d8..8c0f6a4 100644 --- a/src/fmt.cff +++ b/src/fmt.cff @@ -211,6 +211,8 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) let quote = #f; for ;; { switch (c = *++fmt) { + case '%'; + fprintf(stderr, "%%"); case 'q'; quote = #t; continue; @@ -223,6 +225,10 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) case 'z'; sprintf(buf, "%zu", ap->arg(usize)); ps(buf); + case 'f'; + sprintf(buf, "%.17f", ap->arg(f64)); + ps(buf); + case 'p'; sprintf(buf, "%p", ap->arg(*void)); ps(buf); diff --git a/src/ir.cff b/src/ir.cff new file mode 100644 index 0000000..8b28746 --- /dev/null +++ b/src/ir.cff @@ -0,0 +1,10 @@ +import "ir.hff"; + +extern fn mkirctx(alloc *Allocator) *IRCtx { + let ctx *IRCtx = anew(alloc, IRCtx); + ctx.alloc = alloc; + return ctx; +} + +extern fn ir_free(IR *IRCtx) void { +} diff --git a/src/ir.hff b/src/ir.hff new file mode 100644 index 0000000..987db3b --- /dev/null +++ b/src/ir.hff @@ -0,0 +1,62 @@ +import "cffc.hff"; + +struct IRCtx { + alloc *Allocator, +} + +struct IRInst; +struct IRFn { + body *IRInst +} + +struct IRValue { + ty *const Type, + u enum union { + IImm i64, + FImm f64, + SImm [#]const u8, + BImm bool, + Null, + Tmp int, + } +} + +enum union IRArg { + Val IRValue, + Ty *const Type, + Fn *Decl, +} + +enum IRInstT { + neg, + compl, + fneg, + add, + sub, + mul, + div, + mod, + band, + bor, + xor, + lsl, + lsr, + asr, + fadd, + fsub, + fmul, + fdiv, + copy, + call, + ret0, +} + +struct IRInst { + t IRInstT, + next *IRInst, + cond *IRInst, + call_nargs int, + args [0]IRArg, +} + +extern fn irdump(*IRInst) void; diff --git a/src/irdump.cff b/src/irdump.cff new file mode 100644 index 0000000..4147c8a --- /dev/null +++ b/src/irdump.cff @@ -0,0 +1,119 @@ +import "ir.hff"; +import "common.hff"; + +fn dump(fmt *const u8, ...) void { + let ap va_list #?; + ap->start(fmt); + for let c = *fmt; c != 0; c = *++fmt { + if c != '%' { + fputc(c, stderr); + continue; + } + switch (c = *++fmt) { + case '%'; + fputc('%', stderr); + case 'd'; + efmt("%d", ap->arg(int)); + case 'a'; + let arg = ap->arg(IRArg); + switch arg { + case Val val; + switch val.u { + case IImm i; + efmt("%I", i); + case FImm f; + efmt("%f", f); + case SImm s; + efmt("%S", s); + case BImm b; + efmt("%s", b ? "#t" : "#f"); + case Null; + efmt("#null"); + case Tmp t; + efmt("%%%d", t); + } + case Ty ty; efmt("%t", ty); + case Fn decl; efmt("%s", decl.name); + case else assert(#f, "arg? %d", arg.#tag); + } + case 't'; + efmt("%t", ap->arg(*Type)); + case else + assert(#f, "bad fmt %c", c); + } + } + ap->end(); +} + +extern fn irdump(a *IRInst) void { + if a == #null { return; } + + dump(" "); + #'dump do { + defmacro exprinst(tag, nam, n) [ + if a.t == (tag) { + dump("%%%d(%t) = " ## nam ## " ", a.args[0].Val.u.Tmp, a.args[0].Val.ty); + for let $i = 1; $i < (n); ++$i { + dump("%a", a.args[$i]); + if $i < (n) - 1 { + dump(", "); + } + } + dump("\n"); + break #'dump; + } + ] + defmacro stmtinst(tag, nam, n) [ + if a.t == (tag) { + dump(nam##" "); + for let $i = 0; $i < (n); ++$i { + dump("%a", a.args[$i]); + if $i < (n) - 1 { + dump(", "); + } + } + dump("\n"); + break #'dump; + } + ] + + exprinst(:neg, "neg", 2) + exprinst(:compl, "compl", 2) + exprinst(:fneg, "fneg", 2) + exprinst(:add, "add", 3); + exprinst(:sub, "sub", 3); + exprinst(:mul, "mul", 3); + exprinst(:div, "div", 3); + exprinst(:mod, "mod", 3); + exprinst(:band, "band", 3); + exprinst(:bor, "bor", 3); + exprinst(:xor, "xor", 3); + exprinst(:lsl, "lsl", 3); + exprinst(:lsr, "lsr", 3); + exprinst(:asr, "asr", 3); + exprinst(:fadd, "fadd", 3); + exprinst(:fsub, "fsub", 3); + exprinst(:fmul, "fmul", 3); + exprinst(:fdiv, "fdiv", 3); + if a.t == :call { + let fnty = a.args[1].Fn.u.Fn.ty.u.Fn; + dump("%%%d(%t) = call %a, ", a.args[0].Val.u.Tmp, a.args[0].Val.ty, a.args[1]); + let n = a.call_nargs; + for let i = 0; i < n; ++i { + dump("%a(%t)", a.args[i + 2], + i < fnty.params.#len ? fnty.params[i] : a.args[i + 2].Val.ty); + if i < n - 1 { + dump(", "); + } + } + dump("\n"); + break #'dump; + } + + stmtinst(:ret0, "ret", 0); + + assert(#f, "inst? %d\n", a.t); + } while #f; + + irdump(a.next); +} diff --git a/src/irgen.cff b/src/irgen.cff new file mode 100644 index 0000000..1ba080d --- /dev/null +++ b/src/irgen.cff @@ -0,0 +1,124 @@ +import "ir.hff"; +import "common.hff"; + +extern fn ir_genstatic(IR *IRCtx, decl *Decl) void { +} + +struct InstStream { + IR *IRCtx, + head *IRInst, + tail *IRInst, + fn push(S *InstStream, inst *IRInst) void { + if S.tail { + S.tail.next = inst; + } else { + S.head = inst; + } + S.tail = inst; + } +} + + +fn mkinstr(S *InstStream, t IRInstT, args [#]IRArg) *IRInst { + let inst *IRInst = S.IR.alloc->alloc(sizeof IRInst + (args.#len * sizeof IRArg), alignof IRInst); + *inst = { .t: t }; + for let i = 0; i < args.#len; ++i { + inst.args[i] = args[i]; + } + return inst; +} + +defmacro mkslice(Ty, ...args) [ (as([]Ty){args})[0::] ] + +fn genblock(S *InstStream, block [#]Stmt) void; +fn genexpr(S *InstStream, ex *Expr) IRValue { + static tmpid int = 0; + + // fold(ex); + + defmacro genunop(u,t) [ { + let child = genexpr(S, (u).ex); + let res = IRValue{ex.ty, .u: :Tmp(tmpid++)}; + S->push(mkinstr(S, (t), mkslice(IRArg, :Val(res), :Val(child)))); + return res; + } ] + + defmacro genbinop(b,t) [ { + let lhs = genexpr(S, (b).lhs); + let rhs = genexpr(S, (b).rhs); + let res = IRValue{ex.ty, .u: :Tmp(tmpid++)}; + S->push(mkinstr(S, (t), mkslice(IRArg, :Val(res), :Val(lhs), :Val(rhs)))); + return res; + } ] + + switch ex.u { + case IntLit i; return {ex.ty, :IImm(i.i)}; + case FloLit f; return {ex.ty, :FImm(f)}; + case StrLit s; return {ex.ty, :SImm(s)}; + case BoolLit b; return {ex.ty, :BImm(b)}; + case NullLit; return {ex.ty, :Null}; + case UnOp u; + switch u.op { + case :neg; genunop(u, ex.ty->is(:Int) ? :neg : :fneg); + case :compl; genunop(u, :compl); + } + case BinOp b; + switch b.op { + case '+'; genbinop(b, ex.ty->is(:Int) ? :add : :fadd); + case '-'; genbinop(b, ex.ty->is(:Int) ? :sub : :fsub); + case '*'; genbinop(b, ex.ty->is(:Int) ? :mul : :fmul); + case '/'; genbinop(b, ex.ty->is(:Int) ? :div : :fdiv); + case '%'; genbinop(b, :mod); + case '&'; genbinop(b, :band); + case '|'; genbinop(b, :bor); + case '^'; genbinop(b, :xor); + case '<<'; genbinop(b, :lsl); + case '>>'; genbinop(b, ex.ty.u.Int.sgn ? :asr : :lsr); + } + case Call c; + let n = c.args.#len; + let args [#]IRArg = (as(*IRArg)malloc((n + 2) * sizeof IRArg))[0::n + 2]; + defer free(args.#ptr); + let res = IRValue{ex.ty, .u: :Tmp(tmpid++)}; + args[0] = :Val(res); + if c.lhs.u.#tag == :Symbol { + args[1] = :Fn(c.lhs.u.Symbol); + for let i = 0; i < n; ++i { + args[i + 2] = :Val(genexpr(S, &c.args[i])); + } + let inst = mkinstr(S, :call, args); + inst.call_nargs = n; + S->push(inst); + return res; + } else { + } + + } + assert(#f, "NYI ex"); +} + +fn genstmt(S *InstStream, stmt *Stmt) void { + switch stmt.u { + case Expr *ex; + genexpr(S, ex); + case else + assert(#f, "NYI st"); + } +} + +fn genblock(S *InstStream, block [#]Stmt) void { + foreach_ptr(st, _, block) { + genstmt(S, st); + } +} + +extern fn ir_genfn(IR *IRCtx, f *Fn) void { + let stream InstStream = {.IR: IR}; + + genblock(&stream, f.body.Some); + stream->push(mkinstr(&stream, :ret0, {})); + + efmt("----------------------\n"); + efmt("function %s.%d:\n", container_of(f, Decl, u.Fn).name, f.id); + irdump(stream.head); +} diff --git a/src/parse.cff b/src/parse.cff index f2bbb41..e32b758 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -142,7 +142,7 @@ fn eatspaces(P *Parser) void { // !sorted extern static keyword2str [NUM_KEYWORDS]*const u8 = { "alignof", "and", "as", "break", "case", "const", - "continue", "def", "defmacro", "do", + "continue", "def", "defer", "defmacro", "do", "else", "enum", "extern", "fn", "for", "if", "import", "let", "offsetof", "or", "return", "sizeof", "static", @@ -1510,7 +1510,7 @@ fn pexpostfix(P *Parser) Expr { if args.len >= params.#len and !Fn.variadic { fatal(P, ex.loc, "too many args (%z, expected %z)", args.len, params.#len); } - if args.len < params.#len and !typematchestarg(*param, ex.ty) { + if args.len <= params.#len and !typematchestarg(*param, ex.ty) { err(P, ex.loc, "function call argument type mismatch (%t, expected %t)", ex.ty, *param); } @@ -2444,6 +2444,7 @@ fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl { } Fn.body = :Some(parseblock(P)); popenv(P); + ir_genfn(P.irctx, Fn); (as(*Arena)P.alloc.a)->destroy(); } } @@ -2752,6 +2753,7 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) if a.yield { a.yield(decl, a.yarg); } + ir_genstatic(a.P.irctx, decl); } parsevardecl(P, toplevel, externp, #{let?} #t, &varyield, &Arg { P, externp, toplevel, yield, yarg }); return; @@ -2835,6 +2837,7 @@ extern fn parse(P *Parser) [#]Decl { let alloc = Allocator { &aralloc, &Arena:allocf, #null }; let decls Vec<Decl> = {}; P.alloc = (P.tlalloc = &alloc); + P.irctx = mkirctx(P.alloc); P.curenv = mkenv(#null, P.alloc); if primenv == #null { primenv = mkenv(#null, P.alloc); |