diff options
| author | 2022-08-24 06:46:23 +0200 | |
|---|---|---|
| committer | 2022-08-24 06:46:23 +0200 | |
| commit | e5ed3b20351a2715fe88d9a5dbcc8e6757fe96aa (patch) | |
| tree | 7333f357bc38ed389e8845d59196594377dc46d6 /src | |
| parent | 039ab20bbf6b68c423f420be2481b447d85c606b (diff) | |
llvm defer and more
Diffstat (limited to 'src')
| -rw-r--r-- | src/cffc.hff | 48 | ||||
| -rw-r--r-- | src/env.cff | 8 | ||||
| -rw-r--r-- | src/fold.cff | 1 | ||||
| -rw-r--r-- | src/irgen.cff | 14 | ||||
| -rw-r--r-- | src/llvm.cff | 119 | ||||
| -rw-r--r-- | src/parse.cff | 226 | ||||
| -rw-r--r-- | src/targ.cff | 1 | ||||
| -rw-r--r-- | src/type.cff | 12 |
8 files changed, 304 insertions, 125 deletions
diff --git a/src/cffc.hff b/src/cffc.hff index 1c80b3a..d327766 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -119,6 +119,7 @@ struct Type { struct Fn; struct Expan; struct IRCtx; +struct Block; struct Parser { fp *FILE, irctx *IRCtx, @@ -128,6 +129,7 @@ struct Parser { curfn *Fn, curenv *Env, curloop int, + curblock *Block, curexpan *Expan, expanno int, loopid int, @@ -154,6 +156,13 @@ enum UnOp { } struct Stmt; +struct Defers; +struct Block { + sts [#]Stmt, + defers *Defers, + id int, +} + struct Expr { loc Loc, ty *const Type, @@ -180,13 +189,21 @@ struct Expr { EUnionIni struct { var *const AggField, ex *Expr }, AggIni struct { flds [#]*const AggField, exs [#]Expr }, ArrIni struct { idxs [#]u32, exs [#]Expr, maxn i64 }, - Stmt [#]Stmt, + Stmt Block, } } + +struct Defers { + next *Defers, + blockid int, + age int, + ex Expr +} + struct ISwitchCase { es [#]Expr, - t [#]Stmt + t Block } struct EUSwitchCase { @@ -196,35 +213,36 @@ struct EUSwitchCase { variant int, captty *const Type, fld *AggField, - t [#]Stmt + t Block } struct CSwitchCase { test Expr, - t [#]Stmt, + t Block, } struct Stmt { loc Loc, u enum union { - Block [#]Stmt, - If struct { test Expr, t [#]Stmt, f [#]Stmt }, - While struct { test Expr, body [#]Stmt, id int }, + Block Block, + If struct { test Expr, t Block, f Block }, + While struct { test Expr, body Block, id int }, + DoWhile struct { test Expr, body Block, id int }, For struct { - ini [#]Stmt, + ini Block, test Expr, next Option<Expr>, - body [#]Stmt, + body Block, id int, }, Break #{loopid} int, Continue #{loopid} int, - Return Option<Expr>, + Return struct { ex Option<Expr>, deferage int }, Expr Expr, Decl *Decl, - ISwitch struct { ex Expr, cs [#]ISwitchCase, f [#]Stmt }, - EUSwitch struct { ex Expr, cs [#]EUSwitchCase, f [#]Stmt }, - CSwitch struct { cs [#]CSwitchCase, f [#]Stmt } + ISwitch struct { ex Expr, cs [#]ISwitchCase, f Block }, + EUSwitch struct { ex Expr, cs [#]EUSwitchCase, f Block }, + CSwitch struct { cs [#]CSwitchCase, f Block } } } @@ -233,7 +251,7 @@ struct Fn { paramnames [#]*const u8, variadic bool, id int, - body Option<[#]Stmt> + body Option<Block> } struct Var { @@ -340,6 +358,7 @@ struct Targ { sizesize u8, f64align u8, valistsize u8, valistalign u8, + valistllvmty *const u8, charsigned bool, shortenum bool, @@ -443,6 +462,7 @@ extern fn types_to_llvm() void; extern fn mkenv(parent *Env, alloc *Allocator) *Env; extern fn envparent(*Env) *Env; extern fn envput(*Env, Decl, **const Decl) *Decl; +extern fn envput_alloc(*Env, *Allocator, Decl, **const Decl) *Decl; extern fn envfind(*Env, *const u8) *Decl; extern fn envfind_noparent(*Env, *const u8) *Decl; extern fn envfree(*Env) void; diff --git a/src/env.cff b/src/env.cff index 816053e..6bc24fe 100644 --- a/src/env.cff +++ b/src/env.cff @@ -24,9 +24,9 @@ extern fn envparent(env *Env) *Env { return env.parent; } -extern fn envput(env *Env, decl Decl, old **const Decl) *Decl { +extern fn envput_alloc(env *Env, alloc *Allocator, decl Decl, old **const Decl) *Decl { let l **DeclList = env.decls->get_slot(decl.name); - let n *DeclList = anew(env.alloc, DeclList); + let n *DeclList = anew(alloc, DeclList); if *l { // decl with this name exists let old = (*old = &(*l).decl); switch { @@ -51,6 +51,10 @@ extern fn envput(env *Env, decl Decl, old **const Decl) *Decl { return &n.decl; } +extern fn envput(env *Env, decl Decl, old **const Decl) *Decl { + return envput_alloc(env, env.alloc, decl, old); +} + extern fn envfind(env *Env, name *const u8) *Decl { let l **DeclList = env.decls->get(name); if l == #null { diff --git a/src/fold.cff b/src/fold.cff index c9991b7..00e4702 100644 --- a/src/fold.cff +++ b/src/fold.cff @@ -129,6 +129,7 @@ fn fbinary(ex *Expr) void { case op == '<<' and ty->is(:Int); ei.i = li.i << ri.i; case op == '>>' and ty->is(:Int) and !ty.u.Int.sgn; ei.u = li.u >> ri.u; case op == '>>' and ty->is(:Int); ei.i = li.i >> ri.i; + case else return; } if !ex.ty->is(:Bool) { diff --git a/src/irgen.cff b/src/irgen.cff index e157406..c0ea1e1 100644 --- a/src/irgen.cff +++ b/src/irgen.cff @@ -70,7 +70,7 @@ struct InstStream { } -fn genblock(S *InstStream, block [#]Stmt) void; +fn genblock(S *InstStream, block Block) void; fn genexpr(S *InstStream, ex *Expr) IRValue { static tmpid int = 0; @@ -343,7 +343,7 @@ fn genstmt(S *InstStream, stmt *Stmt) void { let beqz = S->mkinst1({:beqz}, :Val(t)); S->push(beqz); genblock(S, If.t); - if If.f.#ptr { + if If.f.sts.#ptr { let b = S->mkinst({:b}, 0); S->push(b); beqz.branch = S->pushnop(); @@ -407,11 +407,11 @@ fn genstmt(S *InstStream, stmt *Stmt) void { assert(jumpto != #null, "no cont label %d", id); S->push(S->mkinst({:b, .branch: *jumpto},0)); - case Return retex; - if retex->empty() { + case Return *ret; + if ret.ex->empty() { S->push(S->mkinst({:ret0}, 0)); } else { - let inst = S->mkinst1({:ret}, :Val(genexpr(S, &retex.Some))); + let inst = S->mkinst1({:ret}, :Val(genexpr(S, &ret.ex.Some))); S->push(inst); } @@ -420,8 +420,8 @@ fn genstmt(S *InstStream, stmt *Stmt) void { } } -fn genblock(S *InstStream, block [#]Stmt) void { - foreach_ptr(st, _, block) { +fn genblock(S *InstStream, block Block) void { + foreach_ptr(st, _, block.sts) { genstmt(S, st); } } diff --git a/src/llvm.cff b/src/llvm.cff index 4ea469d..053d44d 100644 --- a/src/llvm.cff +++ b/src/llvm.cff @@ -55,6 +55,7 @@ fn gen(fmt *const u8, ...) void { } } gen(") "); + case VaList; gen("%s", g_targ.valistllvmty); } } @@ -94,7 +95,7 @@ fn gen(fmt *const u8, ...) void { switch ap->arg(Value).u { case IImm i; gen("%I", i); case FImm f; gen("%f", f); - case BImm b; gen("%s", b ? "true" : "false"); + case BImm b; gen("%d", as(int)b); case Null; gen("null"); case ZeroIni; gen("zeroinitializer"); case Tmp t; fprintf(outfp, "%%t%d", t); @@ -235,7 +236,7 @@ fn convert(f *Fn, to *const Type, ex *Expr) Value { } } -fn genblock(f *Fn, block [#]Stmt) void; +fn genblock(f *Fn, block Block) Option<Value>; fn genexpr(f *Fn, ex *Expr) Value { fold(ex); @@ -333,8 +334,8 @@ fn genexpr(f *Fn, ex *Expr) Value { rhs = b.rhs.ty->is(:Ptr) ? genexpr(f, b.rhs) : convert(f, ty2 ?? b.rhs.ty, b.rhs); return gensub(ex.ty, lhs, rhs); case '*'; return genbinop(b.lhs, b.rhs, !ex.ty->is(:Flo) ? "mul" : "fmul"); - case '/'; return genbinop(b.lhs, b.rhs, ex.ty->is(:Flo) ? "fdiv" - : ex.ty.u.Int.sgn ? "sdiv" : "udiv"); + case '/'; return genbinop(b.lhs, b.rhs, + ex.ty->is(:Flo) ? "fdiv" : ex.ty.u.Int.sgn ? "sdiv" : "udiv"); case '%'; return genbinop(b.lhs, b.rhs, ex.ty.u.Int.sgn ? "srem" : "urem"); case '&'; return genbinop(b.lhs, b.rhs, "and"); case '|'; return genbinop(b.lhs, b.rhs, "or"); @@ -376,12 +377,12 @@ fn genexpr(f *Fn, ex *Expr) Value { ex.ty, b.lhs, b.rhs); case '%='; return genassignop(ex.ty.u.Int.sgn ? "srem" : "urem", ex.ty, b.lhs, b.rhs); - case '&='; return genassignop("and", ex.ty, b.lhs, b.rhs); - case '|='; return genassignop("or", ex.ty, b.lhs, b.rhs); - case '^='; return genassignop("xor", ex.ty, b.lhs, b.rhs); - case '<<='; return genassignop("shl", ex.ty, b.lhs, b.rhs); - case '>>='; return genassignop(ex.ty.u.Int.sgn ? "ashr" : "lshr", ex.ty, b.lhs, b.rhs); - + case '&='; return genassignop("and", ex.ty, b.lhs, b.rhs); + case '|='; return genassignop("or", ex.ty, b.lhs, b.rhs); + case '^='; return genassignop("xor", ex.ty, b.lhs, b.rhs); + case'<<='; return genassignop("shl", ex.ty, b.lhs, b.rhs); + case'>>='; return genassignop(ex.ty.u.Int.sgn ? "ashr" : "lshr", ex.ty, b.lhs, b.rhs); + case else assert(#f, "binop? %c", b.op); } @@ -393,7 +394,11 @@ fn genexpr(f *Fn, ex *Expr) Value { let var = mktmp(ex.ty); let val = mktmp(ex.ty); gen("\t%v = load %t, %t* %v\n", var, ex.ty, ex.ty, addr); - gen("\t%v = add %t %v, %v\n", val, ex.ty, var, one); + if ex.ty->is(:Ptr) { + gen("\t%v = getelementptr %t, %t %v, i32 %v\n", val, ex.ty.u.Ptr, ex.ty, var, one); + } else { + gen("\t%v = add %t %v, %v\n", val, ex.ty, var, one); + } gen("\tstore %t %v, %t* %v\n", ex.ty, val, ex.ty, addr); return val; @@ -403,7 +408,11 @@ fn genexpr(f *Fn, ex *Expr) Value { let var = mktmp(ex.ty); let val = mktmp(ex.ty); gen("\t%v = load %t, %t* %v\n", var, ex.ty, ex.ty, addr); - gen("\t%v = add %t %v, %v\n", val, ex.ty, var, one); + if ex.ty->is(:Ptr) { + gen("\t%v = getelementptr %t, %t %v, i32 %v\n", val, ex.ty.u.Ptr, ex.ty, var, one); + } else { + gen("\t%v = add %t %v, %v\n", val, ex.ty, var, one); + } gen("\tstore %t %v, %t* %v\n", ex.ty, val, ex.ty, addr); return var; @@ -466,7 +475,7 @@ fn genexpr(f *Fn, ex *Expr) Value { let t Value #?; if fnty.ret->is(:Void) { t = {ty_void}; - gen("call %t %v(", call.lhs.ty, lhs); + gen("\tcall %t %v(", call.lhs.ty, lhs); } else { t = mktmp(ex.ty); gen("\t%v = call %t %v(", t, call.lhs.ty, lhs); @@ -480,6 +489,12 @@ fn genexpr(f *Fn, ex *Expr) Value { } gen(")\n"); return t; + + case Stmt block; + switch genblock(f, block) { + case Some val; return val; + case None; return {ty_void}; + } case else assert(#f, "expr? %d", ex.u.#tag); @@ -501,8 +516,11 @@ fn nop() void { gen("\t%%t%d = bitcast i8 0 to i8 ; NOP\n", tmpid++); } -fn genstmt(f *Fn, st *Stmt) void { +fn genstmt(f *Fn, block *Block, st *Stmt) void { switch st.u { + case Block block; + genblock(f, block); + case Expr *e; genexpr(f, e); @@ -525,14 +543,14 @@ fn genstmt(f *Fn, st *Stmt) void { gen("IfT%d:", id); nop(); genblock(f, cnd.t); - if cnd.f.#ptr { + if cnd.f.sts.#ptr { gen("\tbr label %%IfSk%d\n", id); } gen("\tbr label %%IfF%d\n", id); gen("IfF%d:", id); nop(); genblock(f, cnd.f); - if cnd.f.#ptr { + if cnd.f.sts.#ptr { gen("\tbr label %%IfSk%d\n", id); gen("IfSk%d:", id); nop(); @@ -573,8 +591,14 @@ fn genstmt(f *Fn, st *Stmt) void { case Continue loopid; gen("\tbr label %%Cont%d\n", loopid); - case Return *e; - switch *e { + case Return *ret; + for let defers = block.defers; defers; defers = defers.next { + if defers.age < ret.deferage { + genexpr(f, &defers.ex); + } + + } + switch ret.ex { case None; gen("\tret void\n"); case Some *e; @@ -586,10 +610,19 @@ fn genstmt(f *Fn, st *Stmt) void { } } -fn genblock(f *Fn, block [#]Stmt) void { - foreach_ptr (st, _, block) { - genstmt(f, st); +fn genblock(f *Fn, block Block) Option<Value> { + foreach_ptr (st, _, block.sts) { + genstmt(f, &block, st); } + for let defers = block.defers; defers; defers = defers.next { + if defers.blockid == block.id { + let ex = genexpr(f, &defers.ex); + if defers.next == #null { + return :Some(ex); + } + } + } + return :None; } struct FuncKey { @@ -666,10 +699,48 @@ extern fn llvm_addtype(ty *const Type) void { if ty.konst { return; } switch ty.u { case Agg agg; - assert(agg.kind == :Struct, "addtype"); + gen("%%%s.%d = type ", agg.name ?? as(*const u8)"_", agg.id); if agg.fwd { - gen("%%%s.%d = type opaque\n", agg.name, agg.id); - } else { + gen("opaque\n"); + } else if agg.flds.#len == 0 { + gen("{ i8 }"); + } else if agg.kind == :Struct { + gen("{ "); + foreach (fld, i, agg.flds) { + gen("%t", fld.ty); + if i < agg.flds.#len - 1 { + gen(", "); + } + } + gen(" }\n"); + } else if agg.kind == :Union { + let size = ty.size; + let ty *const Type = #null; + foreach (fld, i, agg.flds) { + if ty == #null or fld.ty.align > ty.align { + ty = fld.ty; + } + } + gen("{ %t ", ty); + if ty.size < size { + gen(", [%z x i8] ", size - ty.size); + } + gen("}\n"); + } else if agg.kind == :EUnion { + gen("{ %t, ", agg.enumty); + let size = ty.size; + let ty *const Type = #null; + foreach (fld, i, agg.flds) { + if ty == #null or (fld.ty != #null and fld.ty.align > ty.align) { + ty = fld.ty; + } + } + ty = ty ?? ty_void; + gen(" %t ", ty); + if ty.size < size { + gen(", [%z x i8] ", size - ty.size); + } + gen("}\n"); } } diff --git a/src/parse.cff b/src/parse.cff index 760c0f9..e731d47 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -81,7 +81,7 @@ fn issep(c u8) bool { case '(', ')', '[', ']', '{', '}', '.', ',', ';', '?', '+', '-', '*', '/', '&', '|', '^', '~', '=', '\'', '"', '<', '>', ':', - '@', '#', '\\', '`'; + '@', '#', '\\', '`', '%', '!'; return #t; } return #f; @@ -100,7 +100,7 @@ fn ishsep(c u8) bool { } fn readtilsep(P *Parser, buf [#]u8, dot bool) int { - let i = 0, + let i = 0z, c u8 #?; while (!issep(c = chrpeek(P))) or (dot and c == '.') { chr(P); @@ -114,7 +114,7 @@ fn readtilsep(P *Parser, buf [#]u8, dot bool) int { } fn readtilhsep(P *Parser, buf [#]u8, dot bool) int { - let i = 0, + let i = 0z, c u8 #?, pred = &ishsep; while (!pred(c = chrpeek(P))) or (dot and c == '.') { @@ -151,8 +151,8 @@ extern static keyword2str [NUM_KEYWORDS]*const u8 = { }; fn str2keyword(s *const u8) int { - let i = 0zs, - j = keyword2str.#len - 1; + let i isize = 0, + j isize = keyword2str.#len - 1; while i <= j { let k = (j + i) / 2; let cmp = strcmp(keyword2str[k], s); @@ -452,7 +452,9 @@ fn lex(P *Parser) Tok { if delim == '"' { tok.t = :str; + str->push('\0'); tok.u.str = str->move(P.alloc); + tok.u.str = tok.u.str[0::tok.u.str.#len - 1]; } else { tok.t = :chr; if str.len == 0 { @@ -627,6 +629,18 @@ fn putdecl(P *Parser, eloc Loc, decl Decl) *Decl { return d; } +fn putdecl_alloc(P *Parser, alloc *Allocator, eloc Loc, decl Decl) *Decl { + if envfind(primenv, decl.name) { + fatal(P, eloc, "cannot shadow primitive `%s'", decl.name); + } + let d0 *const Decl; + let d = envput_alloc(P.curenv, alloc, decl, &d0); + if d == #null { + fatal(P, eloc, "attempt to redefine `%s' (previously defined at %l)", decl.name, d0.loc); + } + return d; +} + /////////////////// // Types Parsing // /////////////////// @@ -669,8 +683,8 @@ fn parseenum(P *Parser, name *const u8, lax bool) *const Type { } } let intty *const Type = intty ?? ( - max < 1u64 << ((g_targ.intsize * 8) - 1) ? ty_int - : max < 1u64 << 31 ? ty_i32 + max < 1i64 << ((g_targ.intsize * 8) - 1) ? ty_int + : max < 1i64 << 31 ? ty_i32 : ty_i64); static id int = 0; return interntype({ .u: :Enum { intty, name, lax, id++, vals->move(P.alloc) }}); @@ -716,10 +730,10 @@ fn parseagg(P *Parser, loc Loc, kind AggKind, name *const u8, retdecl **Decl) *c if !lexmatch(P, &tok, ';') { agg.fwd = #f; lexexpect(P, '{'); - let size = 0, align = 1; + let size = 0z, align = 1z; let flds Vec<AggField> = {}; let havedecls = #f; - let f0align = -1i64; + let f0align = -1zs; while !lexmatch(P, &tok, '}') { if isdecltokt(lexpeek(P).t) { havedecls = #t; @@ -895,7 +909,7 @@ fn parseexpandtepl(P *Parser, tepl *Tepl) *const Type { #'search for cache = tepl.cache; cache; cache = cache.next { - for let i = 0; i < nparam; ++i { + for let i = 0z; i < nparam; ++i { let arg = &tpargs[i]; switch cache.args[i] { case Ty ty; @@ -1102,7 +1116,7 @@ fn parseaggini(P *Parser, loc Loc, ty *const Type) Expr { let tok Tok #?; let flds Vec<*const AggField> = {}; let exs Vec<Expr> = {}; - let iota = 0; + let iota = 0u64; while !lexmatch(P, &tok, '}') { let fld *AggField #?; let ex Expr #?; @@ -1299,49 +1313,32 @@ fn parseexpandmacro(P *Parser, loc Loc, mac *Macro) void { } -fn parseblock0(P *Parser) [#]Stmt; +fn parseblock0(P *Parser) Block; fn pexprimary(P *Parser) Expr { let tok Tok = lex(P); let ex Expr #?; - switch tok.t { - case :int, :chr; - ex = { tok.loc, tok.ty, .u: :IntLit { tok.u.int }}; - - case :flo; - ex = { tok.loc, tok.ty, .u: :FloLit(tok.u.flo) }; - case :bool; - ex = { tok.loc, ty_bool, .u: :BoolLit(tok.u.bool) }; - - case :null; - ex = { tok.loc, ty_voidptr, .u: :NullLit }; - - case :str; - let ty = mkarrtype(tok.u.str.#len + 1, #t, ty_u8); - ex = { tok.loc, ty, .u: :StrLit(tok.u.str) }; - - case :ident; - let ident = tok.u.ident; - let decl = finddecl(P, ident); - let fakedecl Decl = {}; + let fakedecl Decl #?; + fn resolvedecl(P *Parser, tok Tok, decl *Decl, ex *Expr) void { + let fakedecl Decl #?; if decl == #null { fatal(P, tok.loc, "%qT is not defined", tok); } while #t { switch decl.u { case Fn f; - ex = { tok.loc, f.ty, .u: :Symbol(decl) }; + *ex = { tok.loc, f.ty, .u: :Symbol(decl) }; case Let var; - ex = { tok.loc, var.ty, .u: :Symbol(decl) }; + *ex = { tok.loc, var.ty, .u: :Symbol(decl) }; case Static var; - ex = { tok.loc, var.ty, .u: :Symbol(decl) }; + *ex = { tok.loc, var.ty, .u: :Symbol(decl) }; case Def dex; dex.loc = tok.loc; - ex = dex; + *ex = dex; case Macro *mac; parseexpandmacro(P, tok.loc, mac); - ex = parseexpr(P); + *ex = parseexpr(P); case Ty ty; if (tok = lexpeek(P)).t != ':' and tok.t != '{' { lexexpects(P, ':', "`:' or `{' after type name"); @@ -1356,9 +1353,10 @@ fn pexprimary(P *Parser) Expr { continue; } else if !ty->is(:Agg) and !ty->is(:Enum) { fatal(P, tok.loc, "type is not aggregate or enum"); - } else { + } else {; P.targty = ty; - return parseexpr(P); + *ex = parseexpr(P); + return; } case Tepl *tepl; fakedecl = { .u: :Ty(parseexpandtepl(P, tepl)) }; @@ -1367,6 +1365,34 @@ fn pexprimary(P *Parser) Expr { } break; } + } + + switch tok.t { + case :int, :chr; + ex = { tok.loc, tok.ty, .u: :IntLit { tok.u.int }}; + + case :flo; + ex = { tok.loc, tok.ty, .u: :FloLit(tok.u.flo) }; + + case :bool; + ex = { tok.loc, ty_bool, .u: :BoolLit(tok.u.bool) }; + + case :null; + ex = { tok.loc, ty_voidptr, .u: :NullLit }; + + case :str; + let ty = mkarrtype(tok.u.str.#len + 1, #t, ty_u8); + ex = { tok.loc, ty, .u: :StrLit(tok.u.str) }; + + case :ident; + let ident = tok.u.ident; + let decl = finddecl(P, ident); + resolvedecl(P, tok, decl, &ex); + + case :typearg; + fakedecl = { .u: :Ty(tok.ty) }; + resolvedecl(P, tok, &fakedecl, &ex); + case :kw_sizeof; let ty *const Type #?; if lexmatch(P, &tok, '(') { @@ -1418,7 +1444,8 @@ fn pexprimary(P *Parser) Expr { case '('; if lexmatch(P, &tok, :kw_do) { - let st = parseblock0(P); + let block = parseblock0(P); + let st = block.sts; lexexpect(P, ')'); if st.#len == 0 or st[st.#len - 1].u.#tag != :Expr { ex = { tok.loc, ty_void, :Stmt(st) }; @@ -1840,7 +1867,11 @@ fn pexcmp(P *Parser) Expr { fatal(P, tok.loc, "invalid non-numeric operands %t and %t to relational operator %qT", ex.ty, rhs.ty, tok); } - if ty->is(:Int) and ty != ty_int and ex.ty.u.Int.sgn != rhs.ty.u.Int.sgn { + fn ispositiveintlit(ex Expr) bool { + return ex.u.#tag == :IntLit and ex.u.IntLit.i >- 0; + } + if ty->is(:Int) and ty != ty_int and ex.ty.u.Int.sgn != rhs.ty.u.Int.sgn + and !(ispositiveintlit(ex) or ispositiveintlit(rhs)) { warn(P, tok.loc, "comparing integers of different signedness (%t and %t)", ex.ty, rhs.ty); } } @@ -1889,13 +1920,16 @@ fn pexlog(P *Parser) Expr { fn pexcond(P *Parser) Expr { let tok Tok = {}; + let targty = P.targty; let ex = pexlog(P); if (lexmatch(P, &tok, '?')) { + P.targty = targty; let ex2 = parseexpr(P); if !ex.ty->is(:Bool) and !ex.ty->is(:Ptr) { fatal(P, ex.loc, "invalid test operand to conditional operator (%t)", ex.ty); } lexexpect(P, ':'); + P.targty = targty; let ex3 = pexcond(P); let ty = typeof2(ex2.ty, ex3.ty); if ty == #null { @@ -1971,7 +2005,7 @@ fn stmtdup(alloc *Allocator, st Stmt) *Stmt { } typedef StmtYielder *fn(Stmt, *void) void; -fn parseblock(P *Parser) [#]Stmt; +fn parseblock(P *Parser) Block; fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, DeclYielder, *void) void; fn pstlet(P *Parser, yield StmtYielder, yarg *void) void { @@ -1998,7 +2032,7 @@ fn pstif(P *Parser, loc Loc) Stmt { if lexmatch(P, &tok, :kw_else) { if lexmatch(P, &tok, :kw_if) { let f = stmtdup(P.alloc, pstif(P, tok.loc)); - return { loc, :If { test, t, f[0::1] }}; + return { loc, :If { test, t, { f[0::1] } }}; } else { lexexpect(P, '{'); let f = parseblock(P); @@ -2008,7 +2042,7 @@ fn pstif(P *Parser, loc Loc) Stmt { return { loc, :If { test, t }}; } -fn pstreturn(P *Parser, loc Loc) Stmt { +fn pstreturn(P *Parser, loc Loc, deferage int) Stmt { if P.curfn == #null { fatal(P, loc, "return outside function"); } @@ -2017,7 +2051,7 @@ fn pstreturn(P *Parser, loc Loc) Stmt { if retty != ty_void { err(P, loc, "return with no value in function returning %t", retty); } - return { loc, :Return{} }; + return { loc, :Return{ :None, deferage} }; } else { P.targty = retty; let ex = parseexpr(P); @@ -2026,7 +2060,7 @@ fn pstreturn(P *Parser, loc Loc) Stmt { err(P, ex.loc, "incompatible type in return statement (%t, expected %t)", ex.ty, retty); } - return { loc, :Return(:Some(ex)) }; + return { loc, :Return{:Some(ex), deferage} }; } } @@ -2036,7 +2070,7 @@ fn pstwhile(P *Parser, loc Loc, label *const u8) Stmt { err(P, test.loc, "while condition must be bool or pointer (%t)", test.ty); } lexexpect(P, '{'); - let body [#]Stmt #?; + let body Block #?; with_tmpchange(P.curloop, ++P.loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); @@ -2050,8 +2084,29 @@ fn pstwhile(P *Parser, loc Loc, label *const u8) Stmt { return { loc, :While { test, body, P.loopid }}; } +fn pstdowhile(P *Parser, loc Loc, label *const u8) Stmt { + lexexpect(P, '{'); + let body Block #?; + with_tmpchange(P.curloop, ++P.loopid) { + let env = mkenv(P.curenv, P.alloc); + defer envfree(env); + pushenv(P, env); + if label { + putdecl(P, loc, { label, loc, .u: :Label(P.loopid) }); + } + body = parseblock(P); + popenv(P); + } + lexexpect(P, :kw_while); + let test = parseexpr(P); + if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { + err(P, test.loc, "do-while condition must be bool or pointer (%t)", test.ty); + } + return { loc, :DoWhile { test, body, P.loopid }}; +} + fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { - let ini [#]Stmt = {}; + let ini Block = {}; let test Expr #?; let next = Option<Expr>:None; let tok Tok #?; @@ -2062,12 +2117,12 @@ fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { sts->push(st); } pstlet(P, &yieldlet, &sts); - ini = sts->move(P.alloc); + ini = { sts->move(P.alloc) }; } else if lexmatch(P, #null, ';') { // pass } else { let ex = parseexpr(P); - ini = stmtdup(P.alloc, { ex.loc, :Expr(ex) })[0::1]; + ini = { stmtdup(P.alloc, { ex.loc, :Expr(ex) })[0::1] }; lexexpect(P, ';'); } @@ -2085,7 +2140,7 @@ fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { next = :Some(parseexpr(P)); lexexpect(P, '{'); } - let body [#]Stmt #?; + let body Block #?; let id = ++P.loopid; with_tmpchange(P.curloop, id) { let env = mkenv(P.curenv, P.alloc); @@ -2103,7 +2158,7 @@ fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt { let tok Tok #?; let cs Vec<ISwitchCase> = {}; - let f [#]Stmt = {}; + let f Block = {}; let elsep = #f; while !lexmatch(P, &tok, '}') { lexexpect(P, :kw_case); @@ -2140,7 +2195,7 @@ fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt { fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt { let cs Vec<EUSwitchCase> = {}; let tok Tok #?; - let f [#]Stmt = {}; + let f Block = {}; let elsep = #f; while !lexmatch(P, &tok, '}') { @@ -2196,7 +2251,7 @@ fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt { fn pstcswitch(P *Parser, loc Loc) Stmt { let cs Vec<CSwitchCase> = {}; let tok Tok #?; - let f [#]Stmt = {}; + let f Block = {}; let elsep = #f; while !lexmatch(P, &tok, '}') { @@ -2237,6 +2292,7 @@ fn pstswitch(P *Parser, loc Loc) Stmt { } fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { + static deferage int = {}; let tok Tok = {}; switch { case lexmatch(P, &tok, '{'); @@ -2251,7 +2307,7 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { return yield(pstif(P, tok.loc), yarg); case lexmatch(P, &tok, :kw_return); - return yield(pstreturn(P, tok.loc), yarg); + return yield(pstreturn(P, tok.loc, deferage), yarg); case lexmatch(P, &tok, :label); let label = tok.u.ident; @@ -2260,6 +2316,8 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { return yield(pstwhile(P, tok.loc, label), yarg); case lexmatch(P, &tok, :kw_for); return yield(pstfor(P, tok.loc, label), yarg); + case lexmatch(P, &tok, :kw_do); + return yield(pstdowhile(P, tok.loc, label), yarg); case else fatal(P, tok.loc, "label can only precede looping statement"); } @@ -2270,6 +2328,10 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { case lexmatch(P, &tok, :kw_for); return yield(pstfor(P, tok.loc, #null), yarg); + case lexmatch(P, &tok, :kw_do); + return yield(pstdowhile(P, tok.loc, #null), yarg); + + case lexmatch(P, &tok, :kw_break) or lexmatch(P, &tok, :kw_continue); if P.curloop == 0 { err(P, tok.loc, "%s outside loop", tok.t == :kw_break ? "break" : "continue"); @@ -2287,6 +2349,20 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { lexexpect(P, ';'); return yield({ tok.loc, tok.t == :kw_break ? :Break(loop) : :Continue(loop) }, yarg); + case lexmatch(P, &tok, :kw_defer); + if P.curblock == #null { + fatal(P, tok.loc, "defer outside function"); + } + let ex = parseexpr(P); + lexexpect(P, ';'); + let defr *Defers = anew(P.alloc, Defers); + defr.age = deferage++; + defr.ex = ex; + defr.blockid = P.curblock.id; + defr.next = P.curblock.defers; + P.curblock.defers = defr; + return; + case lexmatch(P, &tok, :kw_switch); return yield(pstswitch(P, tok.loc), yarg); @@ -2317,25 +2393,30 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { } } -fn parseblock0(P *Parser) [#]Stmt { +fn parseblock0(P *Parser) Block { + static id int = 1; + let block Block = { .id: id++, .defers: P.curblock ? P.curblock.defers : #null }; let sts Vec<Stmt> = {}; let tok Tok = {}; let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); - while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != ')' { - if lexmatch(P, #null, ';') { continue; } - fn pushstmt(st Stmt, arg *void) void { - let sts *Vec<Stmt> = arg; - sts->push(st); + with_tmpchange(P.curblock, &block) { + while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != ')' { + if lexmatch(P, #null, ';') { continue; } + fn pushstmt(st Stmt, arg *void) void { + let sts *Vec<Stmt> = arg; + sts->push(st); + } + parsestmts(P, &pushstmt, &sts); } - parsestmts(P, &pushstmt, &sts); } popenv(P); - return sts->move(P.alloc); + block.sts = sts->move(P.alloc); + return block; } -fn parseblock(P *Parser) [#]Stmt { +fn parseblock(P *Parser) Block { let b = parseblock0(P); lexexpect(P, '}'); return b; @@ -2429,7 +2510,8 @@ fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Dec }}); static id int = 0; Fn.id = ++id; - let decl = putdecl(P, loc, decl); + // TODO tlalloc necessary for templates. but it leaks some memory + let decl = putdecl_alloc(P, P.tlalloc, loc, decl); decl.toplevel = toplevel; if !lexmatch(P, #null, '{') { @@ -2443,7 +2525,6 @@ fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Dec 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); defer envfree(env); @@ -2455,12 +2536,13 @@ fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Dec }}); } } - Fn.body = :Some(parseblock(P)); - popenv(P); - // ir_genfn(P.irctx, Fn); - llvm_genfn(externp, name, Fn); - (as(*Arena)P.alloc.a)->destroy(); - } + with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }) { + Fn.body = :Some(parseblock(P)); + popenv(P); + // ir_genfn(P.irctx, Fn); + llvm_genfn(externp, name, Fn); + (as(*Arena)P.alloc.a)->destroy(); + } } if toplevel { llvm_addfn(decl); diff --git a/src/targ.cff b/src/targ.cff index 38fcb48..c5fc093 100644 --- a/src/targ.cff +++ b/src/targ.cff @@ -12,6 +12,7 @@ static const targs []const Targ = { .sizesize: 8, .f64align: 8, .valistsize: 24, .valistalign: 8, + .valistllvmty: "{ i32, i32, i8*, i8* }", .charsigned: #t, .shortenum: #f, } diff --git a/src/type.cff b/src/type.cff index 194ff01..3feb14c 100644 --- a/src/type.cff +++ b/src/type.cff @@ -295,14 +295,14 @@ extern fn typeof2(a *const Type, b *const Type) *const Type { if uac == ubc { return akonst ? a : b; } - if uac == ty_void { - if bkonst and !akonst { return constifychild(a); } - return a; - } - if ubc == ty_void { - if akonst and !bkonst { return constifychild(b); } + if uac->is(:Void) { + if !bkonst and akonst { return constifychild(b); } return b; } + if ubc->is(:Void) { + if !akonst and bkonst { return constifychild(a); } + return a; + } } return #null; } |