import "cffc.hff"; import "common.hff"; import "vec.hff"; import "map.hff"; static outfp *FILE = {}; struct Value { ty *const Type, u enum union { IImm i64, FImm f64, BImm bool, Null, ZeroIni, StrLit [#]const u8, StrConstRef int, Tmp int, LocalRef *Var, GlobalRef *Var, Fn *Fn, } } static ty_i1 *Type = as(*void)&outfp; // dummy fn gen(fmt *const u8, ...) void; extern fn genagg(ty *const Type) void { let agg = &ty.u.Agg; if agg.fwd { gen("%%.type.opaque"); } 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(" }"); } 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("}"); } 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("}"); } } fn gendata(ty *const Type, ex *Expr) void; fn gen(fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); fn pritype(ty *const Type) void { if ty == ty_i1 { gen("i1"); return; } switch ty.u { case Void; gen("void"); case Bool; gen("i%z", ty.size*8); case Int; gen("i%z", ty.size*8); case Flo; gen(ty.size == 4 ? "float" : "double"); // case Ptr p; gen("%t*", p->is(:Void) ? ty_i8 : p); case Ptr p; gen("ptr"); case Slice p; gen("{ %t, %t }", mkptrtype(p), ty_usize); case Arr arr; gen("[%z x %t]", arr.length, arr.child); case Agg; genagg(ty); case Enum enu; pritype(enu.intty); case Fn f; gen("%t(", f.ret); foreach(ty, i, f.params) { gen("%t", ty); if i < f.params.#len - 1 { gen(", "); } else if f.variadic { gen(", ..."); } } gen(") "); case VaList; gen("%s", g_targ.valistllvmty); } } for let c = *fmt; c != 0; c = *++fmt { if c != '%' { fputc(c, outfp); continue; } switch c = *++fmt { case '%'; fprintf(outfp, "%%"); case 'c'; fprintf(outfp, "%c", ap->arg(int)); case 's'; fprintf(outfp, "%s", ap->arg(*const u8)); case 'S'; let str = ap->arg([#]const u8); fputc('"', outfp); foreach(c, _, str) { extern fn isprint(int) int; if isprint(c) == 0 { fprintf(outfp, "\\%.2X", c); } else { fputc(c, outfp); } } fprintf(outfp, "\\00\""); case 'd'; fprintf(outfp, "%d", ap->arg(int)); case 'I'; fprintf(outfp, "%lld", as(c_llong)ap->arg(i64)); case 'f'; fprintf(outfp, "%.17f", ap->arg(f64)); case 'z'; fprintf(outfp, "%zu", ap->arg(usize)); case 'v'; switch ap->arg(Value).u { case IImm i; gen("%I", i); case FImm f; gen("%f", f); case BImm b; gen("%d", as(int)b); case Null; gen("null"); case ZeroIni; gen("zeroinitializer"); case Tmp t; fprintf(outfp, "%%t%d", t); case LocalRef var; gen("%%%s.%d", container_of(var, Decl, u.Let).name, var.id); case Fn f; let decl = container_of(f, Decl, u.Fn); if !decl.externp { gen("@%s.%d", decl.name, f.id); } else { gen("@%s", decl.name); } case GlobalRef v; let decl = container_of(v, Decl, u.Static); if !decl.externp { gen("@%s.%d", decl.name, v.id); } else { gen("@%s", decl.name); } case StrConstRef id; gen("@.str.%d", id); } case 't'; pritype(ap->arg(*Type)); case 'C'; let ty = ap->arg(*const Type); let ex = ap->arg(*Expr); gendata(ty, ex); case else assert(#f, "bad fmt '%c'", c); } } ap->end(); } static tmpid int = 1; fn mktmp(ty *const Type) Value { return Value{ty, :Tmp(tmpid++)}; } fn convert(f *Fn, to *const Type, ex *Expr) Value; fn genexpr(f *Fn, ex *Expr) Value; static arena Arena = {}; static alloc Allocator = {}; static strs Vec<[#]const u8> = {}; fn genaddr(f *Fn, ex *Expr) Value { switch ex.u { case StrLit s; let p *u8 = alloc->alloc(s.#len + 1, 1); memcpy(p, s.#ptr, s.#len + 1); let s = p[0::s.#len]; strs->push(s); return {mkptrtype(ex.ty), :StrConstRef(strs.len - 1)}; case Symbol decl; switch decl.u { case Let *var; return {mkptrtype(var.ty), :LocalRef(var)}; case Static *var; return {mkptrtype(var.ty), :GlobalRef(var)}; } case UnOp un; switch un.op { case :deref; return genexpr(f, un.ex); case :addrof; return genaddr(f, un.ex); } case Index idx; let lhs Value #?; if idx.lhs.ty->is(:Arr) { let arr = genaddr(f, idx.lhs); lhs = mktmp(mkptrtype(ex.ty)); gen("\t%v = getelementptr %t, %t %v, i32 0, i32 0\n", lhs, arr.ty.u.Arr.child, arr.ty, arr); } else { lhs = genexpr(f, idx.lhs); } let rhs = genexpr(f, idx.rhs), addr = mktmp(mkptrtype(ex.ty)); gen("\t%v = getelementptr %t, %t %v, %t %v\n", addr, ex.ty, lhs.ty, lhs, rhs.ty, rhs); return addr; case Dot dot; let lhs = dot.lhs.ty->is(:Ptr) ? genexpr(f, dot.lhs) : genaddr(f, dot.lhs); assert(dot.fld.off % dot.fld.ty.align == 0, "field align"); let idx int #?; if dot.lhs.ty->is(:Ptr) { idx = dot.fld - dot.lhs.ty.u.Ptr.u.Agg.flds.#ptr; } else { idx = dot.fld - dot.lhs.ty.u.Agg.flds.#ptr; } let addr = mktmp(mkptrtype(ex.ty)); switch lhs.ty.u.Ptr.u.Agg.kind { case :Struct; gen("\t%v = getelementptr %t, %t %v, i32 0, i32 %d\n", addr, lhs.ty.u.Ptr, lhs.ty, lhs, idx); case :Union; gen("\t%v = bitcast %t %v to %t", addr, lhs.ty, lhs, addr.ty); case else let off int = dot.fld.off; gen("\t%v = getelementptr i8, %t %v, i32 %d\n", addr, lhs.ty, lhs, off); } return addr; case EUTag eex; let lhs = eex.ty->is(:Ptr) ? genexpr(f, eex) : genaddr(f, eex); let addr = mktmp(mkptrtype(ex.ty)); gen("\t%v = bitcast %t %v to %t", addr, lhs.ty, lhs, addr.ty); return addr; case AggIni ini; let tmp = mktmp(mkptrtype(ex.ty)); gen("\t%v = alloca %t\n", tmp, ex.ty); gen("\tstore %t zeroinitializer, %t %v\n", ex.ty, tmp.ty, tmp); foreach(fld, i, ini.flds) { let fex = &ini.exs[i]; let ptr = mktmp(mkptrtype(fex.ty)); let idx int = fld - ex.ty.u.Agg.flds.#ptr; switch ex.ty.u.Agg.kind { case :Struct; gen("\t%v = getelementptr %t, %t %v, i32 0, i32 %d\n", ptr, ex.ty, tmp.ty, tmp, idx); case :Union; gen("\t%v = bitcast %t %v to %t\n", ptr, tmp.ty, tmp, ptr.ty); } gen("\tstore %t %v, %t %v\n", fex.ty, convert(f, fld.ty, fex), ptr.ty, ptr); } return tmp; } assert(#f, "genaddr %d", ex.u.#tag); } fn convert(f *Fn, to *const Type, ex *Expr) Value { to = unconstify(to); let from = unconstify(ex.ty); defmacro cvt(inst) [ (do let t = mktmp(to), val = genexpr(f, ex); gen("\t%v = %s %t %v to %t\n", t, inst, from, val, to); t; ) ] switch { case from == to; return genexpr(f, ex); case to->is(:Int) and from->is(:Int) and to.size == from.size and to.u.Int.sgn != from.u.Int.sgn; return genexpr(f, ex); case to->is(:Int) and from->is(:Int) and to.size == from.size; return cvt("bitcast"); case to->is(:Int) and from->is(:Int) and to.size < from.size; return cvt("trunc"); case to->is(:Int) and from->is(:Int) and to.size > from.size; return cvt(from.u.Int.sgn ? "sext" : "zext"); case to->is(:Int) and from->is(:Int) and to.size < from.size; return cvt("trunc"); case to->is(:Int) and from->is(:Int) and to.size > from.size; return cvt(from.u.Int.sgn ? "sext" : "zext"); case to->is(:Flo) and from->is(:Flo) and to.size == from.size; return genexpr(f, ex); case to->is(:Flo) and from->is(:Flo) and to.size > from.size; return cvt("fpext"); case to->is(:Flo) and from->is(:Flo) and to.size < from.size; return cvt("fptrunc"); case to->is(:Flo) and from->is(:Int); return cvt(from.u.Int.sgn ? "sitofp" : "uitofp"); case to->is(:Int) and from->is(:Flo) and to.size < ty_int.size; let t0 = mktmp(ty_int); let val = genexpr(f, ex); gen("\t%v = fptosi %t %v to %t\n", t0, from, val, ty_int); let t1 = mktmp(to); gen("\t%v =%s %t %v to %t\n", t1, to.u.Int.sgn ? "sext" : "zext", t0.ty, t0, to); return t1; case to->is(:Int) and from->is(:Flo); return cvt(to.u.Int.sgn ? "fptosi" : "fptoui"); case to->is(:Int) and from->is(:Bool); let t0 = mktmp(ty_i1); gen("\t%v = icmp ne %t %v, 0\n", t0, ex.ty, genexpr(f, ex)); let t1 = mktmp(to); gen("\t%v = zext %t %v to %t\n", t1, t0.ty, t0, t1.ty); return t1; case to->is(:Bool) and from->is(:Int); let t0 = mktmp(ty_i1); gen("\t%v = icmp ne %t %v, 0\n", t0, ex.ty, genexpr(f, ex)); let t1 = mktmp(to); gen("\t%v = zext %t %v to %t\n", t1, t0.ty, t0, t1.ty); return t1; case to->is(:Ptr) and from->is(:Ptr); let t = mktmp(to), val = genexpr(f, ex); gen("\t%v = bitcast %t %v to %t\n", t, from, val, to); return t; case to->is(:Ptr) and from->is(:Arr); let addr = genaddr(f, ex); let t = mktmp(ex.ty); gen("\t%v = getelementptr %t, %t %v, %t 0, %t 0\n", t, addr.ty.u.Ptr, addr.ty, addr, ty_usize, ty_usize); return t; case to->is(:Arr) and from->is(:Arr); return genexpr(f, ex); case else assert(#f, "convert"); } } fn genblock(f *Fn, block Block) Option; fn llvmbool(f *Fn, ex *Expr) Value; fn genexpr(f *Fn, ex *Expr) Value { fold(ex); switch ex.u { case IntLit i; return {ex.ty, :IImm(i.i)}; case FloLit f; return {ex.ty, :FImm(f)}; case BoolLit b; return {ex.ty, :BImm(b)}; case NullLit; return {ex.ty, :Null}; case ZeroIni; return {ex.ty, :ZeroIni}; case EnumIni i; return {ex.ty, :IImm(i)}; case StrLit s; return {ex.ty, :StrLit(s)}; case BinOp b; defmacro genbinop(a, b, inst) [ (do let lhs = convert(f, ex.ty, a), rhs = convert(f, ex.ty, b), t = mktmp(ex.ty); gen("\t%v = %s %t %v, %v\n", t, inst, ex.ty, lhs, rhs); t; ) ] let ty2 = typeof2(b.lhs.ty, b.rhs.ty); defmacro gencmp(op) [ { let lhs = convert(f, ty2, b.lhs), rhs = convert(f, ty2, b.rhs), t0 = mktmp(ex.ty), t1 = mktmp(ex.ty); gen("\t%v = %s %s %t %v, %v\n", t0, ty2->is(:Flo) ? "fcmp" : "icmp", op, ty2, lhs, rhs); gen("\t%v = zext i1 %v to %t\n", t1, t0, ex.ty); return t1; } ] defmacro genadd(type, lhs, rhs) [ (do let res Value #?; if isnumtype(type) { let $t = mktmp(type); gen("\t%v = %s %t %v, %v\n", $t, ex.ty->is(:Flo) ? "fadd" : "add", ex.ty, lhs, rhs); res = $t; } else if lhs.ty->is(:Ptr) { let $t = mktmp(type); gen("\t%v = getelementptr %t, %t %v, %t %v\n", $t, lhs.ty.u.Ptr, lhs.ty, lhs, rhs.ty, rhs); res = $t; } else if rhs.ty->is(:Ptr) { } else { assert(#f, "bad sub"); } res; ) ] defmacro gensub(type, lhs, rhs) [ (do let res Value #?; if isnumtype(type) { let $t = mktmp(type); gen("\t%v = %s %t %v, %v\n", $t, ex.ty->is(:Flo) ? "fsub" : "sub", ex.ty, lhs, rhs); res = $t; } else if lhs.ty->is(:Ptr) and rhs.ty->is(:Int) { let $off = mktmp(rhs.ty); gen("\t%v = sub %t 0, %v\n", $off, rhs.ty, rhs); let $t = mktmp(type); gen("\t%v = getelementptr %t, %t %v, %t %v\n", $t, lhs.ty.u.Ptr, lhs.ty, lhs, $off.ty, $off); res = $t; } else { assert(#f, "bad add"); } res; ) ] defmacro genassignop(inst, type, a, b) [ (do let addr = genaddr(f, a); let rhs = convert(f, a.ty, b); let lhs0 = mktmp(type); gen("\t%v = load %t, %t %v\n", lhs0, type, addr.ty, addr); let tmp = (do let t = mktmp(ex.ty); gen("\t%v = %s %t %v, %v\n", t, inst, ex.ty, lhs0, rhs); t; ); gen("\tstore %t %v, %t %v\n", tmp.ty, tmp, addr.ty, addr); rhs; ) ] switch b.op { case '+'; let ty2 = typeof2(b.lhs.ty, b.rhs.ty); let lhs = b.lhs.ty->is(:Ptr) ? genexpr(f, b.lhs) : convert(f, ty2 ?? b.lhs.ty, b.lhs), rhs = b.rhs.ty->is(:Ptr) ? genexpr(f, b.rhs) : convert(f, ty2 ?? b.rhs.ty, b.rhs); return genadd(ex.ty, lhs, rhs); case '-'; let ty2 = typeof2(b.lhs.ty, b.rhs.ty); let lhs = b.lhs.ty->is(:Ptr) ? genexpr(f, b.lhs) : convert(f, ty2 ?? b.lhs.ty, b.lhs), 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.u.Int.sgn ? "srem" : "urem"); case '&'; return genbinop(b.lhs, b.rhs, "and"); case '|'; return genbinop(b.lhs, b.rhs, "or"); case '^'; return genbinop(b.lhs, b.rhs, "xor"); case '<<'; return genbinop(b.lhs, b.rhs, "shl"); case '>>'; return genbinop(b.lhs, b.rhs, ex.ty.u.Int.sgn ? "ashr" : "lshr"); case '=='; gencmp("eq"); case '!='; gencmp("ne"); case '<'; gencmp(ty2->is(:Flo) ? "olt" : ty2.u.Int.sgn ? "slt" : "ult"); case '<='; gencmp(ty2->is(:Flo) ? "ole" : ty2.u.Int.sgn ? "sle" : "ule"); case '>'; gencmp(ty2->is(:Flo) ? "ogt" : ty2.u.Int.sgn ? "sgt" : "ugt"); case '>='; gencmp(ty2->is(:Flo) ? "oge" : ty2.u.Int.sgn ? "sge" : "uge"); case '='; let addr = genaddr(f, b.lhs); let rhs = convert(f, b.lhs.ty, b.rhs); gen("\tstore %t %v, %t %v\n", ex.ty, rhs, addr.ty, addr); return rhs; case '+='; let addr = genaddr(f, b.lhs); let rhs = convert(f, b.lhs.ty, b.rhs); let lhs0 = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", lhs0, ex.ty, addr.ty, addr); let tmp = genadd(ex.ty, lhs0, rhs); gen("\tstore %t %v, %t %v\n", tmp.ty, tmp, addr.ty, addr); return rhs; case '-='; let addr = genaddr(f, b.lhs); let rhs = convert(f, b.lhs.ty, b.rhs); let lhs0 = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", lhs0, ex.ty, addr.ty, addr); let tmp = gensub(ex.ty, lhs0, rhs); gen("\tstore %t %v, %t %v\n", tmp.ty, tmp, addr.ty, addr); return rhs; case '*='; return genassignop(ex.ty->is(:Flo) ? "fdiv" : "mul", ex.ty, b.lhs, b.rhs); case '/='; return genassignop(ex.ty->is(:Flo) ? "fdiv" : ex.ty.u.Int.sgn ? "sdiv" : "udiv", 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 'or'; static orid int = {}; let id = orid++; let lhs = genexpr(f, b.lhs); let cnd = mktmp(ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); gen("\tstore %t %v, %t %v\n", ex.ty, lhs, tmpvar.ty, tmpvar); gen("\t%v = icmp ne %t %v, 0\n", cnd, lhs.ty, lhs); gen("\tbr i1 %v, label %%OrT%d, label %%OrF%d\n", cnd, id, id); gen("OrF%d: ", id); let rhs = genexpr(f, b.rhs); gen("\tstore %t %v, %t %v\n", ex.ty, rhs, tmpvar.ty, tmpvar); gen("\tbr label %%OrT%d\n", cnd, id, id); gen("OrT%d: ", id); gen("\t%v = load %t, %t %v\n", res, ex.ty, tmpvar.ty, tmpvar); return res; case 'and'; static andid int = {}; let id = andid++; let lhs = genexpr(f, b.lhs); let cnd = mktmp(ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); gen("\tstore %t %v, %t %v\n", ex.ty, lhs, tmpvar.ty, tmpvar); gen("\t%v = icmp ne %t %v, 0\n", cnd, lhs.ty, lhs); gen("\tbr i1 %v, label %%AndT%d, label %%AndF%d\n", cnd, id, id); gen("AndT%d: ", id); let rhs = genexpr(f, b.rhs); gen("\tstore %t %v, %t %v\n", ex.ty, rhs, tmpvar.ty, tmpvar); gen("\tbr label %%AndF%d\n", cnd, id, id); gen("AndF%d: ", id); gen("\t%v = load %t, %t %v\n", res, ex.ty, tmpvar.ty, tmpvar); return res; case else assert(#f, "binop? %c", b.op); } case Cond cond; static condid int = {}; let id = condid++; let lhs = genexpr(f, cond.test); let cnd = mktmp(ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); gen("\t%v = icmp ne %t %v, 0\n", cnd, lhs.ty, lhs); gen("\tbr i1 %v, label %%CondT%d, label %%CondF%d\n", cnd, id, id); gen("CondT%d: ", id); let t = convert(f, ex.ty, cond.t); gen("\tstore %t %v, %t %v\n", ex.ty, t, tmpvar.ty, tmpvar); gen("\tbr label %%CondFin%d\n", cnd, id, id); gen("CondF%d: ", id); let f = convert(f, ex.ty, cond.f); gen("\tstore %t %v, %t %v\n", ex.ty, f, tmpvar.ty, tmpvar); gen("\tbr label %%CondFin%d\n", cnd, id, id); gen("CondFin%d: ", id); gen("\t%v = load %t, %t %v\n", res, ex.ty, tmpvar.ty, tmpvar); return res; case UnOp un; switch un.op { case :neg; let tmp = mktmp(ex.ty); if ex.ty->is(:Int) { gen("\t%v = sub %t 0, %v\n", tmp, ex.ty, convert(f, ex.ty, un.ex)); } else { gen("\t%v = fsub %t 0.0, %v\n", tmp, ex.ty, convert(f, ex.ty, un.ex)); } return tmp; case :compl; let tmp = mktmp(ex.ty); gen("\t%v = xor %t -1, %v\n", tmp, ex.ty, convert(f, ex.ty, un.ex)); return tmp; case :not; let tmp = mktmp(ty_i1); gen("\t%v = xor i1 1, %v\n", tmp, ex.ty, llvmbool(f, un.ex)); let res = mktmp(ex.ty); gen("\t%v = zext i1 %v to %t\n", res, tmp, res.ty); return res; case :preinc, :predec; let one = Value{ex.ty, :IImm(un.op == :preinc ? 1 : -1)}; let addr = genaddr(f, un.ex); let var = mktmp(ex.ty); let val = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", var, ex.ty, addr.ty, addr); 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, addr.ty, addr); return val; case :postinc, :postdec; let one = Value{ex.ty, :IImm(un.op == :postinc ? 1 : -1)}; let addr = genaddr(f, un.ex); let var = mktmp(ex.ty); let val = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", var, ex.ty, addr.ty, addr); 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, addr.ty, addr); return var; case :deref; let rhs = genexpr(f, un.ex); let val = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", val, ex.ty, rhs.ty, rhs); return val; case :addrof; return genaddr(f, ex); case else assert(#f, "unop?"); } case Index; let tmp = mktmp(ex.ty); let addr = genaddr(f, ex); gen("\t%v = load %t, %t %v\n", tmp, ex.ty, addr.ty, addr); return tmp; case Dot; let tmp = mktmp(ex.ty); let addr = genaddr(f, ex); gen("\t%v = load %t, %t %v\n", tmp, ex.ty, addr.ty, addr); return tmp; case EUTag; let tmp = mktmp(ex.ty); let addr = genaddr(f, ex); gen("\t%v = load %t, %t %v\n", tmp, ex.ty, addr.ty, addr); return tmp; case Cast it; return convert(f, ex.ty, it); case Symbol decl; switch decl.u { case Fn *f; return {ex.ty, :Fn(f)}; case else let tmp = mktmp(ex.ty); let addr = genaddr(f, ex); gen("\t%v = load %t, %t %v\n", tmp, ex.ty, addr.ty, addr); return tmp; } case Slice sl; if sl.lhs.ty.u.#tag == :Arr { assert(#f,""); } else if sl.lhs.ty.u.#tag == :Ptr { let addr = genexpr(f, sl.lhs); let begin = convert(f, ty_usize, sl.begin); let end = convert(f, ty_usize, sl.end); let len = mktmp(ty_usize); gen("\t%v = sub %t %v, %v\n", len, ty_usize, end, begin); let tmp = mktmp(ex.ty); gen("\t%v = insertvalue %t undef, %t %v, 0\n", tmp, ex.ty, addr.ty, addr); let res = mktmp(ex.ty); gen("\t%v = insertvalue %t %v, %t %v, 1\n", res, ex.ty, tmp, len.ty, len); return res; } else if sl.lhs.ty.u.#tag == :Slice { let slice = genexpr(f, sl.lhs); let begin = convert(f, ty_usize, sl.begin); let end = convert(f, ty_usize, sl.end); let addr = mktmp(mkptrtype(slice.ty.u.Slice)); gen("\t%v = extractvalue %t %v, 0", addr, ex.ty, slice); let len = mktmp(ty_usize); gen("\t%v = sub %t %v, %v\n", len, ty_usize, end, begin); let tmp = mktmp(ex.ty); gen("\t%v = insertvalue %t undef, %t %v, 0\n", tmp, ex.ty, addr.ty, addr); let res = mktmp(ex.ty); gen("\t%v = insertvalue %t %v, %t %v, 1\n", res, ex.ty, tmp, len.ty, len); return res; } case Call call; let lhs = genexpr(f, call.lhs); let fnty = &(lhs.ty->is(:Ptr) ? lhs.ty.u.Ptr : lhs.ty).u.Fn; let args *Value = xcalloc(call.args.#len, sizeof Value); defer free(args); foreach_ptr(arg, i, call.args) { let ty = i < fnty.params.#len ? fnty.params[i] : typeof2(arg.ty, arg.ty); args[i] = convert(f, ty, arg); } let t Value #?; if fnty.ret->is(:Void) { t = {ty_void}; gen("\tcall %t %v(", call.lhs.ty, lhs); } else { t = mktmp(ex.ty); gen("\t%v = call %t %v(", t, call.lhs.ty, lhs); } foreach_ptr(arg, i, call.args) { let ty = i < fnty.params.#len ? fnty.params[i] : typeof2(arg.ty, arg.ty); gen("%t %v", ty, args[i]); if i < call.args.#len - 1 { gen(", "); } } gen(")\n"); return t; case AggIni ini; let tmp = genaddr(f, ex); let val = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", val, ex.ty, tmp.ty, tmp); return val; case EUnionIni ini; let t0 = mktmp(mkptrtype(ex.ty)); let vidx = ini.var - ex.ty.u.Agg.flds.#ptr; gen("\t%v = alloca %t\n", t0, ex.ty); gen("\tstore %t zeroinitializer, %t %v\n", ex.ty, t0.ty, t0); gen("\tstore %t %d, %t %v\n", ex.ty.u.Agg.enumty.u.Enum.intty, vidx, t0.ty, t0); if ini.var.ty != #null { let tini = convert(f, ini.var.ty, ini.ex); let t1 = mktmp(mkptrtype(ini.var.ty)); gen("\t%v = bitcast %t %v to %t\n", t1, t0.ty, t0, t1.ty); let t2 = mktmp(t1.ty); gen("\t%v = getelementptr %t, %t %v, i32 1\n", t2, ini.var.ty, t1.ty, t1); gen("\tstore %t %v, %t %v\n", tini.ty, tini, t2.ty, t2); } let res = mktmp(ex.ty); gen("\t%v = load %t, %t %v\n", res, res.ty, t0.ty, t0); return res; 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); } } fn llvmbool(f *Fn, ex *Expr) Value { let v = genexpr(f, ex); let t = mktmp(ty_i1); if v.ty->is(:Bool) or v.ty->is(:Int) { gen("\t%v = icmp ne %t %v, 0\n", t, v.ty, v); } else { gen("\t%v = icmp ne %t %v, null\n", t, v.ty, v); } return t; } fn nop() void { gen("\t%%t%d = bitcast i8 0 to i8 ; NOP\n", tmpid++); } fn genstmt(f *Fn, block *Block, st *Stmt) void { switch st.u { case Block block; genblock(f, block); case Expr *e; genexpr(f, e); case Decl decl; switch decl.u { case Let var; let nam = decl.name, ty = var.ty; gen("\t%%%s.%d = alloca %t\n", nam, var.id, ty); if !var.ini->empty() { gen("\tstore %t %v, %t %%%s.%d\n", ty, convert(f, ty, &var.ini.Some), mkptrtype(ty), nam, var.id); } } case If *cnd; static ifid int = {}; let id = ifid++; gen("\tbr i1 %v, label %%IfT%d, label %%IfF%d\n", llvmbool(f, &cnd.test), id, id); gen("IfT%d:", id); nop(); genblock(f, cnd.t); 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.sts.#ptr { gen("\tbr label %%IfSk%d\n", id); gen("IfSk%d:", id); nop(); } case While loop; gen("\tbr label %%Cont%d\n", loop.id); gen("Cont%d:", loop.id); nop(); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", llvmbool(f, &loop.test), loop.id, loop.id); gen("Next%d:", loop.id); nop(); genblock(f, loop.body); gen("\tbr label %%Cont%d\n", loop.id); gen("Brk%d:",loop.id); nop(); case DoWhile loop; gen("\tbr label %%Next%d\n", loop.id); gen("Next%d:", loop.id); nop(); genblock(f, loop.body); gen("\tbr label %%Cont%d\n", loop.id); gen("Cont%d:", loop.id); nop(); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", llvmbool(f, &loop.test), loop.id, loop.id); gen("Brk%d:",loop.id); nop(); case For loop; genblock(f, loop.ini); gen("\tbr label %%Cont%d\n", loop.id); gen("Cont%d:", loop.id); nop(); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", llvmbool(f, &loop.test), loop.id, loop.id); gen("Next%d:", loop.id); nop(); genblock(f, loop.body); if !loop.next->empty() { genexpr(f, &loop.next.Some); } gen("\tbr label %%Cont%d\n", loop.id); gen("Brk%d:",loop.id); nop(); case Break loopid; gen("\tbr label %%Brk%d\n", loopid); case Continue loopid; gen("\tbr label %%Cont%d\n", loopid); 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; let retty = f.ty.u.Fn.ret; gen("\tret %t %v\n", retty, convert(f, retty, e)); } case ISwitch *sw; static swid int = {}; let id = swid++; let test = genexpr(f, &sw.ex); gen("\tswitch %t %v, label %%ISx%d [", test.ty, test); foreach (c, i, sw.cs) { foreach_ptr (ex, _, c.es) { let t = convert(f, test.ty, ex); gen(" %t %v, label %%IS%d.%d ", t.ty, t, id, i); } } gen(" ]\n"); foreach (c, i, sw.cs) { gen("IS%d.%d: ", id, i); genblock(f, c.t); gen("\tbr label %%ISe%d\n", id); } gen("ISx%d: ", id); genblock(f, sw.f); gen("\tbr label %%ISe%d\n", id); gen("ISe%d: ", id); nop(); case EUSwitch *sw; static swid int = {}; let id = swid++; let exaddr Value #?; if sw.lvalue { exaddr = genaddr(f, &sw.ex); } else { exaddr = mktmp(mkptrtype(sw.ex.ty)); gen("\t%v = alloca %t\n", exaddr, exaddr.ty); let test = genexpr(f, &sw.ex); gen("\tstore %t %v, %t %v\n", test.ty, test, exaddr.ty, exaddr); } let tag = (do let addr = mktmp(mkptrtype(sw.ex.ty.u.Agg.enumty)); gen("\t%v = bitcast %t %v to %t\n", addr, exaddr.ty, exaddr, addr.ty); let tmp = mktmp(sw.ex.ty.u.Agg.enumty); gen("\t%v = load %t, %t %v\n", tmp, tmp.ty, addr.ty, addr); tmp; ); gen("\tswitch %t %v, label %%ESx%d [ ", tag.ty, tag, id); foreach (c, i, sw.cs) { let t = Value{tag.ty, :IImm(c.variant)}; gen(" %t %v, label %%ES%d.%d ", t.ty, t, id, i); } gen(" ]\n"); foreach (c, i, sw.cs) { gen("ES%d.%d: ", id, i); if c.capt { gen("\t%%%s.%d = alloca %t\n", c.capt, c.captid, c.captty); let valaddr = mktmp(mkptrtype(c.fld.ty)); gen("\t%v = getelementptr i8, %t %v, i32 %d\n", valaddr, exaddr.ty, exaddr, as(int)c.fld.off); if c.captptr { gen("\tstore %t %v, %t %%%s.%d\n", valaddr.ty, valaddr, mkptrtype(c.captty), c.capt, c.captid); } else { let val = mktmp(c.captty); gen("\t%v = load %t, %t %v\n", val, val.ty, valaddr.ty, valaddr); gen("\tstore %t %v, %t %%%s.%d\n", val.ty, val, mkptrtype(c.captty), c.capt, c.captid); } } genblock(f, c.t); gen("\tbr label %%ESe%d\n", id); } gen("ESx%d: ", id); genblock(f, sw.f); gen("\tbr label %%ESe%d\n", id); gen("ESe%d: ", id); nop(); case CSwitch *sw; static swid int = {}; let id = swid++; if sw.cs.#len == 0 { gen("\tbr label %%CSx%d\n", id); } foreach (c, i, sw.cs) { let t = llvmbool(f, &c.test); gen("\tbr i1 %v, label %%CST%d.%d, label %%CSF%d.%d\n", t, id, i, id, i); gen("CST%d.%d: ", id, i); genblock(f, c.t); gen("\tbr label %%CSe%d\n", id); gen("CSF%d.%d: ", id, i); if i == sw.cs.#len - 1 { gen("\tbr label %%CSx%d\n", id); } } gen("CSx%d: ", id); genblock(f, sw.f); gen("\tbr label %%CSe%d\n", id); gen("CSe%d: ", id); nop(); case else assert(#f, "stmt? %d", st.u.#tag); } } fn genblock(f *Fn, block Block) Option { let res Option = :None; foreach_ptr (st, i, block.sts) { if i == block.sts.#len - 1 and st.u.#tag == :Expr { res = :Some(genexpr(f, &st.u.Expr)); } else { genstmt(f, &block, st); } } for let defers = block.defers; defers; defers = defers.next { if defers.blockid == block.id { genexpr(f, &defers.ex); } } return res; } struct GloblKey { name *const u8, externp bool, } struct GloblEntry { t enum { Idk, Def, Decl }, ty *const Type, id int, } struct GloblKeyTraits { fn hash(e GloblKey) u32 { let h = fnv1a_s(FNV1A_INI, e.name); h = fnv1a_i(h, as(int)e.externp); return h; } fn eq(a GloblKey, b GloblKey) bool { return streq(a.name, b.name) and a.externp == b.externp; } } static globls Map = {}; extern fn llvm_addglobl(decl *Decl, staticp bool) void { assert(decl.toplevel, "llvm-addfn"); let slot = globls->get_slot({decl.name, decl.externp}); if slot.t != :Def { slot.t = :Decl; if staticp { slot.ty = decl.u.Static.ty; slot.id = decl.u.Static.id; } else { slot.ty = decl.u.Fn.ty; slot.id = decl.u.Fn.id; } } } extern fn llvm_genfn(externp bool, name *const u8, f *Fn) void { globls->put({name, externp}, {:Def, f.ty, f.id}); tmpid = 0; gen("define%s %t ", externp ? "" : " internal", f.ty.u.Fn.ret); if !externp { gen("@%s.%d(", name, f.id); } else { gen("@%s(", name); } foreach(ty, i, f.ty.u.Fn.params) { gen("%t", ty); if f.paramnames[i] { gen(" %%%s", f.paramnames[i]); } if i < f.paramnames.#len - 1 { gen(", "); } } gen(") { \n"); let id = 0; foreach(nam, i, f.paramnames) { if nam { let ty = f.ty.u.Fn.params[i]; gen("%%%s.%d = alloca %t ", nam, id, ty); gen("store %t %%%s, %t %%%s.%d\n", ty, nam, mkptrtype(ty), nam, id); ++id; } } genblock(f, f.body.Some); if f.ty.u.Fn.ret->is(:Void) { gen("\tret void\n"); } else { let retty = f.ty.u.Fn.ret; gen("\tret %t undef\n", retty); } gen("}\n"); } fn gendata(ty *const Type, ex *Expr) void { fold(ex); switch ex.u { case IntLit i; return gen("%I", i.i); case FloLit f; return gen("%f", f); case BoolLit b; return gen("%d", b); case NullLit; return gen("null"); case ZeroIni; return gen("zeroinitializer"); } assert(#f, "bad static"); } extern fn llvm_genstatic(externp bool, name *const u8, var *Var) void { globls->put({name, externp}, {:Def, var.ty, var.id}); if !externp { gen("@%s.%d", name, var.id); } else { gen("@%s", name); } let ini = var.ini->some_or({.u: :ZeroIni}); gen(" = %sglobal %t %C\n", externp ? "" : "internal ", var.ty, var.ty, &ini); } extern fn llvm_fini() void { vec_each(s, i, strs) { gen("@.str.%z = internal constant [%z x i8] c%S;\n", i, s.#len + 1, s); } map_each(g, k, globls) { if g.t != :Decl { continue; } let ty = g.ty; switch ty.u { case Fn f; gen("declare %t ", f.ret); if !k.externp { gen("@%s.%d(", k.name, g.id); } else { gen("@%s(", k.name); } foreach(tyy, i, f.params) { gen("%t", tyy); if i < f.params.#len - 1 { gen(", "); } else if f.variadic { gen(", ..."); } } gen(");\n"); case else if !k.externp { gen("@%s.%d", k.name, g.id); } else { gen("@%s", k.name); } gen(" = external global %t\n", ty); } } strs->clear(); globls->clear(); arena->destroy(); } extern fn llvm_init(out *FILE) void { alloc = {&arena, &Arena:allocf, #null}; outfp = out; gen("target triple = \"%s\"\n", g_targ.triple); gen("%%.type.opaque = type opaque\n"); gen("\n"); }