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, FnRef *Fn, } } // XXX if gen isn't extern llvm hates it and wooshes it away ? extern 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; do { foreach (fld, i, agg.flds) { if ty == #null or fld.ty.align > ty.align or fld.ty.size > ty.size { ty = fld.ty; } } } while ty->is(:Agg) and ty.u.Agg.kind != :Struct; if ty->is(:Agg) { // struct gen("{ i%z, [%z x i8] }", ty.align * 8, size - ty.align); } else { gen("{ %t, [%z x i8] }", ty, size - ty.size); } } else if agg.kind == :EUnion { let size = ty.size; gen("{ %t, [%z x i8],", agg.enumty, ty.align - agg.enumty.size); let ty *const Type = #null; let off isize #?; do { foreach (fld, i, agg.flds) { if ty == #null or (fld.ty != #null and (fld.ty.align > ty.align or fld.ty.size > ty.size)) { ty = fld.ty; off = fld.off; } } } while ty->is(:Agg) and ty.u.Agg.kind != :Struct; ty = ty ?? ty_void; if ty->is(:Agg) { // struct gen(" i%z, [%z x i8] }", ty.align * 8, size - off - ty.align); } else { gen(" %t, [%z x i8] }", ty, size - ty.size - off); } } } extern fn llvm_addtype(ty *const Type) void { if ty.u.#tag == :Agg and !ty.u.Agg.fwd { gen("%%type.%s.%d = type ", ty.u.Agg.name ?? as(*u8)".", ty.u.Agg.id); genagg(ty); gen("\n"); } } fn gendata(ty *const Type, ex *Expr) void; extern fn gen(fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); fn pritype(ty *const Type) void { if ty == #null { 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("ptr"); case Slice p; gen("{ %t, %t }", mkptrtype(p), ty_usize); case Arr arr; assert(arr.length >= 0, "arr length"); gen("[%z x %t]", arr.length, arr.child); case Agg agg; if agg.fwd { genagg(ty); } else { gen("%%type.%s.%d", ty.u.Agg.name ?? as(*u8)".", ty.u.Agg.id); } case Enum enu; pritype(enu.intty); case BitF bitf; pritype(bitf.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(", "); } } if f.variadic { gen("..."); } gen(") "); case VaList; gen("%%.type.valist"); case else assert(#f, "type?"); } } 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 or c == '"' or c == '\\' { 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'; let f f64 = as(f32)ap->arg(f64); fprintf(outfp, "0x%.16llx", *as(*u64)&f); case 'F'; fprintf(outfp, "%e", ap->arg(f64)); case 'z'; fprintf(outfp, "%zu", ap->arg(usize)); case 'v'; let val = ap->arg(*const Value); switch val.u { case IImm i; gen("%I", i); case FImm f; gen(val.ty.size == 4 ? "%f" : "%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 FnRef 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 StrConstRef id; gen("@.str.%d", id); case StrLit *s; gen("c%S", s); case else assert(#f, "value? %d", val.u.#tag); } 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++)}; } defmacro gentmp(type, ...rest) [ (do let $tmp = mktmp(type); gen("\t%v = ", &$tmp); gen(rest); gen("\n"); $tmp; ) ] 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> = {}; enum union Ref { Addr Value, BitDot struct { lhs Value, fld *const BitFField } } fn gencopy(f *Fn, ty *const Type, ref Ref, src *Expr) void; fn genref(f *Fn, ex *Expr) Ref { 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 :Addr{mkptrtype(ex.ty), :StrConstRef(strs.len - 1)}; case Symbol decl; switch decl.u { case Let *var; return :Addr{mkptrtype(var.ty), :LocalRef(var)}; case Static *var; return :Addr{mkptrtype(var.ty), :GlobalRef(var)}; case Fn *f; return :Addr{mkptrtype(f.ty), :FnRef(f)}; } case UnOp un; switch un.op { case :deref; return :Addr(genexpr(f, un.ex)); case :addrof; return genref(f, un.ex); } case Index idx; let lhs Value #?; switch idx.lhs.ty.u { case Arr arrty; let arr = genref(f, idx.lhs).Addr; lhs = gentmp(mkptrtype(ex.ty), "getelementptr %t, %t %v, i32 0, i32 0", idx.lhs.ty, arr.ty, &arr); case Ptr; lhs = genexpr(f, idx.lhs); case Slice child; let tmp = genexpr(f, idx.lhs); lhs = gentmp(mkptrtype(child), "extractvalue %t %v, 0", tmp.ty, &tmp); case else; assert(#f, "index"); } let rhs = genexpr(f, idx.rhs); let addr = gentmp(mkptrtype(ex.ty), "getelementptr %t, %t %v, %t %v", ex.ty, lhs.ty, &lhs, rhs.ty, &rhs); return :Addr(addr); case Dot dot; let lhs = dot.lhs.ty->is(:Ptr) ? genexpr(f, dot.lhs) : genref(f, dot.lhs).Addr; assert(dot.fld.ty.align > 0, "%s align %zu", dot.fld.name, dot.fld.ty.align); assert(dot.fld.off % dot.fld.ty.align == 0, "field align %s %zu %zu", dot.fld.name, dot.fld.off, dot.fld.ty.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 Value #?; let ptrty = mkptrtype(ex.ty); switch lhs.ty.u.Ptr.u.Agg.kind { case :Struct; addr = gentmp(ptrty, "getelementptr %t, %t %v, i32 0, i32 %d", lhs.ty.u.Ptr, lhs.ty, &lhs, idx); case :Union; addr = gentmp(ptrty, "bitcast %t %v to %t", lhs.ty, &lhs, ptrty); case :EUnion; let off int = dot.fld.off; addr = gentmp(ptrty, "getelementptr i8, ptr %v, i32 %d", &lhs, off); } return :Addr(addr); case BitDot dot; return :BitDot{genref(f, dot.lhs).Addr, dot.fld}; case EUTag eex; let lhs = eex.ty->is(:Ptr) ? genexpr(f, eex) : genref(f, eex).Addr; let addr = gentmp(mkptrtype(ex.ty), "bitcast %t %v to %t", lhs.ty, &lhs, mkptrtype(ex.ty)); return :Addr(addr); case BitRaw ex; return genref(f, ex); case AggIni ini; let tmp = gentmp(mkptrtype(ex.ty), "alloca %t", ex.ty); gen("\tcall void @llvm.memset.p0.%t(ptr %v, i8 0, %t %z, i1 false)\n", ty_usize, &tmp, ty_usize, ex.ty.size); 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); case else assert(#f, "?"); } gencopy(f, fld.ty, :Addr(ptr), fex); } return :Addr(tmp); case ArrIni ini; let ty = mkarrtype(ini.maxn, #f, ex.ty.u.Arr.child); let tmp = gentmp(mkptrtype(ty), "alloca %t", ty); gen("\tcall void @llvm.memset.p0.%t(ptr %v, i8 0, %t %z, i1 false)\n", ty_usize, &tmp, ty_usize, ty.size); foreach(idx, i, ini.idxs) { let fex = &ini.exs[i]; let ptr = mktmp(mkptrtype(fex.ty)); gen("\t%v = getelementptr %t, %t %v, i32 0, i32 %d\n", &ptr, ty, tmp.ty, &tmp, idx); gencopy(f, ty.u.Arr.child, :Addr(ptr), fex); } return :Addr(tmp); case else let it = genexpr(f, ex); let tmp = gentmp(mkptrtype(ex.ty), "alloca %t", ex.ty); gen("store %t %v, %t %v", it.ty, &it, tmp.ty, &tmp); return :Addr(tmp); } assert(#f, "genref %d", ex.u.#tag); } fn genload(f *Fn, ref Ref) Value { switch ref { case Addr addr; return gentmp(addr.ty.u.Ptr, "load %t, %t %v", addr.ty.u.Ptr, addr.ty, &addr); case BitDot dot; let intty = dot.lhs.ty.u.BitF.intty; let fld = dot.fld; let lhs = genload(f, :Addr(dot.lhs)); let shifted = fld.off == 0 ? lhs : gentmp(lhs.ty, "lshr %t %v, %d", lhs.ty, &lhs, fld.off); let truncd = gentmp(#null, "trunc %t %v to i%d", shifted.ty, &shifted, fld.size); let res = mktmp(fld.ty); switch { case dot.fld.ty->is(:Bool); let tmp = gentmp(#null, "icmp ne i%d %v, 0", fld.size, &truncd); gen("\t%v = zext i1 %v to %t\n", &res, &tmp, res.ty); case dot.fld.ty->is(:Int); gen("\t%v = %s i%d %v to %t\n", &res, dot.fld.ty.u.Int.sgn ? "sext" : "zext", fld.size, &truncd, lhs.ty); case else assert(#f, ""); } return res; } } fn genstore(f *Fn, ref Ref, v Value) void { switch ref { case Addr *addr; gen("\tstore %t %v, %t %v\n", v.ty, &v, addr.ty, addr); case BitDot dot; let intty = dot.lhs.ty.u.BitF.intty; let fld = dot.fld; let srcmask = fld.size == 64 ? ~0u64 : ((1u64 << fld.size) - 1); let addr = dot.lhs; let src0 = v; let src = gentmp(intty, "and %t %v, %I", intty, &src0, srcmask); let src = gentmp(intty, "shl %t %v, %d", src.ty, &src, fld.off); let dstmask = ~(srcmask << fld.off); let lhs = gentmp(intty, "load %t, %t %v", intty, addr.ty, &addr); let lhs = genload(f, :Addr(addr)); let lhs = gentmp(lhs.ty, "and %t %v, %I", lhs.ty, &lhs, dstmask); let raw = gentmp(lhs.ty, "or %t %v, %v", lhs.ty, &lhs, &src); gen("\tstore %t %v, %t %v\n", raw.ty, &raw, addr.ty, &addr); } } fn gencopy(f *Fn, ty *const Type, ref Ref, src *Expr) void { if (ty.u.#tag == :Agg or ty.u.#tag == :Arr) and src.u.#tag == :ZeroIni { gen("\tcall void @llvm.memset.p0.%t(ptr %v, i8 0, %t %z, i1 false)\n", ty_usize, &ref.Addr, ty_usize, ty.size); // XXX: LLVM optimizations mess this up ? // } else if (ty.u.#tag == :Agg or ty.u.#tag == :Arr) and ref.#tag == :Addr { // let rhs = genref(f, src); // gen("\tcall void @llvm.memmove.p0.p0.%t(ptr %v, ptr %v, %t %z, i1 false)\n", // ty_usize, &ref.Addr, &rhs.Addr, ty_usize, ty.size); } else { genstore(f, ref, convert(f, ty, src)); } } fn convert(f *Fn, to *const Type, ex *Expr) Value { to = unconstify(to); let from = unconstify(ex.ty); if to->is(:Enum) { to = to.u.Enum.intty; } if from->is(:Enum) { from = from.u.Enum.intty; } 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; return genexpr(f, ex); 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(:Ptr) and from->is(:Int); return cvt("inttoptr"); case to->is(:Int) and from->is(:Ptr); return cvt("ptrtoint"); 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 = trunc %t %v to %t\n", &t1, 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(#null); let b = genexpr(f, ex); gen("\t%v = icmp ne %t %v, 0\n", &t0, ex.ty, &b); 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(#null); let b = genexpr(f, ex); gen("\t%v = icmp ne %t %v, 0\n", &t0, ex.ty, &b); 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 r = genexpr(f, ex); let tmp = gentmp(to, "bitcast ptr %v to ptr", &r); return tmp; case to->is(:Slice) and from->is(:Slice); return genexpr(f, ex); case to->is(:Ptr) and from->is(:Arr); let addr = genref(f, ex).Addr; let t = mktmp(to); gen("\t%v = getelementptr %t, %t %v, i32 0, i32 0\n", &t, addr.ty.u.Ptr, addr.ty, &addr); return t; case to->is(:Arr) and from->is(:Arr); return genexpr(f, ex); } efmt("%t %t\n",from,to); assert(#f, "convert"); } fn genblock(f *Fn, block Block) Option; fn llvmbool(f *Fn, ex *Expr) Value; fn nop() Value { return gentmp(ty_i8, "bitcast i8 0 to i8 ; NOP"); } 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 = gentmp(type, "%s %t %v, %v", 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->is(:Void) ? ty_i8 : lhs.ty.u.Ptr, lhs.ty, &lhs, rhs.ty, &rhs); res = $t; } else if rhs.ty->is(:Ptr) { let $t = mktmp(type); gen("\t%v = getelementptr %t, %t %v, %t %v\n", &$t, rhs.ty.u.Ptr->is(:Void) ? ty_i8 : rhs.ty.u.Ptr, rhs.ty, &rhs, rhs.ty, &lhs); res = $t; } else { assert(#f, "bad ad"); } res; ) ] defmacro gensub(type, lhs, rhs) [ (do let res Value #?; if isnumtype(type) and !lhs.ty->is(:Ptr) { 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 == ty_void ? ty_i8 : lhs.ty.u.Ptr, lhs.ty, &lhs, $off.ty, &$off); res = $t; } else if lhs.ty->is(:Ptr) and rhs.ty->is(:Ptr) { let $l = gentmp(ty_isize, "ptrtoint ptr %v to %t", &lhs, ty_isize); let $r = gentmp(ty_isize, "ptrtoint ptr %v to %t", &rhs, ty_isize); let $diff = gentmp(ty_isize, "sub %t %v, %v", ty_isize, &$l, &$r); let $res = gentmp(ty_isize, "sdiv %t %v, %z", ty_isize, &$diff, lhs.ty.u.Ptr == ty_void ? 1 : lhs.ty.u.Ptr.size); return $res; } else { assert(#f, "bad sub"); } res; ) ] defmacro genassignop(inst, type, a, b) [ (do let ref = genref(f, a); let rhs = convert(f, a.ty, b); let lhs0 = genload(f, ref); let tmp = gentmp(ex.ty, "%s %t %v, %v", inst, ex.ty, &lhs0, &rhs); genstore(f, ref, tmp); genload(f, ref); // load again because value could have overflowed when storing it ) ] 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(ty2->is(:Flo) ? "oeq" : "eq"); case '!='; gencmp(ty2->is(:Flo) ? "une" : "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 ref = genref(f, b.lhs); gencopy(f, b.lhs.ty, ref, b.rhs); return genload(f, ref); // load again because value could have overflowed when storing it case '+='; let ref = genref(f, b.lhs); let rhs = b.lhs.ty->is(:Ptr) ? genexpr(f, b.rhs) : convert(f, b.lhs.ty, b.rhs); let lhs0 = genload(f, ref); let tmp = genadd(ex.ty, lhs0, rhs); genstore(f, ref, tmp); return genload(f, ref); // load again because value could have overflowed when storing it case '-='; let ref = genref(f, b.lhs); let rhs = convert(f, b.lhs.ty, b.rhs); let lhs0 = genload(f, ref); let tmp = gensub(ex.ty, lhs0, rhs); genstore(f, ref, tmp); return genload(f, ref); // load again because value could have overflowed when storing it 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(#null); 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", 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(#null); 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", id); gen("AndF%d: ", id); gen("\t%v = load %t, %t %v\n", &res, ex.ty, tmpvar.ty, &tmpvar); return res; case '??'; static orid int = {}; let id = orid++; let lhs = genexpr(f, b.lhs); let cnd = mktmp(#null); 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, null\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", id); gen("OrT%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(#null); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); let voidp = ex.ty->is(:Void); if !voidp { gen("\t%v = alloca %t\n", &tmpvar, ex.ty); } gen("\t%v = icmp ne %t %v, %s\n", &cnd, lhs.ty, &lhs, lhs.ty->is(:Ptr) ? "null" : "0"); 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); if !voidp { gen("\tstore %t %v, %t %v\n", ex.ty, &t, tmpvar.ty, &tmpvar); } gen("\tbr label %%CondFin%d\n", id); gen("CondF%d: ", id); let f = convert(f, ex.ty, cond.f); if !voidp { gen("\tstore %t %v, %t %v\n", ex.ty, &f, tmpvar.ty, &tmpvar); } gen("\tbr label %%CondFin%d\n", id); gen("CondFin%d: ", id); if !voidp { 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); let rhs = convert(f, ex.ty, un.ex); if ex.ty->is(:Int) { gen("\t%v = sub %t 0, %v\n", &tmp, ex.ty, &rhs); } else { gen("\t%v = fsub %t 0.0, %v\n", &tmp, ex.ty, &rhs); } return tmp; case :compl; let tmp = mktmp(ex.ty); let rhs = convert(f, ex.ty, un.ex); gen("\t%v = xor %t -1, %v\n", &tmp, ex.ty, &rhs); return tmp; case :not; let tmp = mktmp(#null); let rhs = llvmbool(f, un.ex); gen("\t%v = xor i1 1, %v\n", &tmp, &rhs); 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 ref = genref(f, un.ex); let val = mktmp(ex.ty); let var = genload(f, ref); if ex.ty->is(:Ptr) { gen("\t%v = getelementptr %t, %t %v, i32 %v\n", &val, ex.ty.u.Ptr->is(:Void) ? ty_i8 : ex.ty.u.Ptr, ex.ty, &var, &one); } else { gen("\t%v = add %t %v, %v\n", &val, ex.ty, &var, &one); } genstore(f, ref, val); return genload(f, ref); // load again because value could have overflowed when storing it case :postinc, :postdec; let one = Value{ex.ty, :IImm(un.op == :postinc ? 1 : -1)}; let ref = genref(f, un.ex); let val = mktmp(ex.ty); let var = genload(f, ref); if ex.ty->is(:Ptr) { gen("\t%v = getelementptr %t, %t %v, i32 %v\n", &val, ex.ty.u.Ptr->is(:Void) ? ty_i8 : ex.ty.u.Ptr, ex.ty, &var, &one); } else { gen("\t%v = add %t %v, %v\n", &val, ex.ty, &var, &one); } genstore(f, ref, val); 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 genref(f, ex).Addr; case else assert(#f, "unop?"); } case Index; let ref = genref(f, ex); return genload(f, ref); case Dot; let ref = genref(f, ex); return genload(f, ref); case BitRaw ex; return genexpr(f, ex); case BitDot dot; let ref = genref(f, ex); return genload(f, ref); case SPtr sl; let sl = genexpr(f, sl); let ptr = mktmp(mkptrtype(sl.ty.u.Slice)); gen("\t%v = extractvalue %t %v, 0\n", &ptr, sl.ty, &sl); return ptr; case SLen sl; let sl = genexpr(f, sl); let len = mktmp(ty_usize); gen("\t%v = extractvalue %t %v, 1\n", &len, sl.ty, &sl); return len; case EUTag; let ref = genref(f, ex); return genload(f, ref); 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 ref = genref(f, ex); return genload(f, ref); } case Slice sl; if sl.lhs.ty.u.#tag == :Arr { let addr = genref(f, sl.lhs).Addr; 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 addr2 = mktmp(addr.ty); gen("\t%v = getelementptr %t, %t %v, %t %v\n", &addr2, ex.ty.u.Slice, addr.ty, &addr, ty_usize, &begin); let tmp = mktmp(ex.ty); gen("\t%v = insertvalue %t undef, %t %v, 0\n", &tmp, ex.ty, addr2.ty, &addr2); 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 == :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 addr2 = mktmp(addr.ty); gen("\t%v = getelementptr %t, %t %v, %t %v\n", &addr2, ex.ty.u.Slice, addr.ty, &addr, ty_usize, &begin); let tmp = mktmp(ex.ty); gen("\t%v = insertvalue %t undef, %t %v, 0\n", &tmp, ex.ty, addr2.ty, &addr2); 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 addr2 = mktmp(addr.ty); gen("\t%v = getelementptr %t, %t %v, %t %v\n", &addr2, ex.ty.u.Slice, addr.ty, &addr, ty_usize, &begin); let tmp = mktmp(ex.ty); gen("\t%v = insertvalue %t undef, %t %v, 0\n", &tmp, ex.ty, addr2.ty, &addr2); 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); fn varargpromote(ty *const Type) *const Type { if ty->is(:Bool) { return ty_int; }; if ty->is(:Int) { return typeof2(ty, ty); } if ty->is(:Flo) and ty.size < 8 { return ty_f64; } if ty->is(:Arr) { return mkptrtype(ty.u.Arr.child); } return ty; } foreach_ptr(arg, i, call.args) { let ty = i < fnty.params.#len ? fnty.params[i] : varargpromote(arg.ty); args[i] = convert(f, ty, arg); } let t Value = mktmp(ex.ty); if fnty.ret->is(:Void) { gen("\tcall void %v(", &lhs); } else { t = mktmp(ex.ty); gen("\t%v = call %t %v(", &t, call.lhs.ty->is(:Ptr) ? call.lhs.ty.u.Ptr : call.lhs.ty, &lhs); } foreach_ptr(arg, i, call.args) { let ty = i < fnty.params.#len ? fnty.params[i] : varargpromote(arg.ty); gen("%t %v", ty, &args[i]); if i < call.args.#len - 1 { gen(", "); } } gen(")\n"); return t; case AggIni ini; let ref = genref(f, ex); return genload(f, ref); case ArrIni ini; let ty = mkarrtype(ini.maxn, #f, ex.ty.u.Arr.child); let tmp = genref(f, ex).Addr; let val = mktmp(ty); gen("\t%v = load %t, %t %v\n", &val, ty, tmp.ty, &tmp); return val; case BitFIni ini; assert(ex.ty->is(:BitF), "not bitf bitf ini"); let intty = ex.ty.u.BitF.intty; let tmp = gentmp(ex.ty, "bitcast %t 0 to %t", ex.ty, ex.ty); foreach(fld, i, ini.flds) { let ex0 = convert(f, intty, &ini.exs[i]); let fmask = fld.size == 64 ? ~0u64 : ((1u64 << fld.size) - 1); let masked = gentmp(intty, "and %t %v, %I", intty, &ex0, fmask); let shifted = gentmp(intty, "shl %t %v, %d", intty, &masked, fld.off); tmp = gentmp(ex.ty, "or %t %v, %v", intty, &tmp, &shifted); } return tmp; 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("\tcall void @llvm.memset.p0.%t(ptr %v, i8 0, %t %z, i1 false)\n", ty_usize, &t0, ty_usize, ex.ty.size); gen("\tstore %t %d, ptr %v\n", ex.ty.u.Agg.enumty, vidx, &t0); if ini.var.ty != #null { let t2 = gentmp(t0.ty, "getelementptr i8, ptr %v, i32 %z", &t0, ini.var.off); gencopy(f, ini.var.ty, :Addr(t2), ini.ex); } return gentmp(ex.ty, "load %t, %t %v", ex.ty, t0.ty, &t0); case VaStart ap; let ap = genref(f, ap).Addr; gen("\tcall void @llvm.va_start(ptr %v)\n", &ap); case VaArg ap; let ap = genref(f, ap).Addr; return gentmp(ex.ty, "va_arg ptr %v, %t", &ap, ex.ty); case VaEnd ap; let ap = genref(f, ap).Addr; gen("\tcall void @llvm.va_end(ptr %v)\n", &ap); 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(#null); 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 genstmt(f *Fn, block *Block, st *Stmt) void { gen(";; line %d\n", st.loc.line); 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); switch var.ini { case Some *ex; let it = convert(f, ty, ex); gen("\tstore %t %v, %t %%%s.%d\n", ty, &it, mkptrtype(ty), nam, var.id); } } case If *cnd; static ifid int = {}; let id = ifid++; let test = llvmbool(f, &cnd.test); gen("\tbr i1 %v, label %%IfT%d, label %%IfF%d\n", &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(); let test = llvmbool(f, &loop.test); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", &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(); let test = llvmbool(f, &loop.test); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", &test, loop.id, loop.id); gen("Brk%d:",loop.id); nop(); case For loop; genblock(f, loop.ini); gen("\tbr label %%Test%d\n", loop.id); gen("Test%d:", loop.id); nop(); let test = llvmbool(f, &loop.test); gen("\tbr i1 %v, label %%Next%d, label %%Brk%d\n", &test, loop.id, 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(); if !loop.next->empty() { genexpr(f, &loop.next.Some); } gen("\tbr label %%Test%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; let it = convert(f, retty, e); if retty->is(:Void) { gen("\tret void\n"); } else { gen("\tret %t %v\n", retty, &it); } } 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, id); foreach (c, i, sw.cs) { foreach_ptr (ex, _, c.es) { assert(ex.u.#tag == :IntLit or ex.u.#tag == :EnumIni, "case"); gen(" %t %I, label %%IS%d.%d ", test.ty, ex.u.IntLit.i, 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 = genref(f, &sw.ex).Addr; } else { exaddr = mktmp(mkptrtype(sw.ex.ty)); gen("\t%v = alloca %t\n", &exaddr, sw.ex.ty); gencopy(f, sw.ex.ty, :Addr(exaddr), &sw.ex); } 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, ptr %v, i32 %z\n", &valaddr, &exaddr, 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 a.name == b.name and a.externp == b.externp; } } static globls Map = {}; extern fn llvm_addglobl(decl *Decl, staticp bool) void { assert(decl.toplevel or decl.externp, "llvm-addfn %d", decl.u.#tag); 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 or f.variadic { gen(", "); } } if f.variadic { 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"); } struct IntTraits { fn hash(x T) u32 { return x ^ 7; } fn eq(a T, b T) bool { return a == b; } } fn gendata(ty *const Type, ex *Expr) void { fn addr(ex *Expr) void { switch ex.u { case Symbol decl; switch decl.u { case Fn f; if !decl.externp { gen("@%s.%d", decl.name, f.id); } else { gen("@%s", decl.name); } return; case Static v; if !decl.externp { gen("@%s.%d", decl.name, v.id); } else { gen("@%s", decl.name); } return; } } assert(#f, "bad static addr"); } enum Action { type, value } fn data(act Action, ty *const Type, ex *Expr) void { fold(ex); switch ex.u { case IntLit i; return act == :type ? gen("%t", ty) : gen("%I", i.i); case FloLit f; return act == :type ? gen("%t", ty) : gen("%f", f); case BoolLit b; return act == :type ? gen("%t", ty) : gen("%d", b); case NullLit; return act == :type ? gen("%t", ty) : gen("null"); case ZeroIni; return act == :type ? gen("%t", ty) : gen("zeroinitializer"); case EnumIni i; return act == :type ? gen("%t", ty) : gen("%I", i); case StrLit *s; if act == :type { return gen("%t", ty); } if ty->is(:Arr) { return gen("c%S", s); } else { strs->push(*s); return gen("@.str.%z", strs.len - 1); } case UnOp un; switch un.op { case :addrof; if act == :type { return gen("%t", ty); } return addr(un.ex); } case ArrIni ini; assert(ty->is(:Arr), "not arr arr ini"); let map Map,> = {}; defer map->clear(); foreach (idx, i, ini.idxs) { map->put(idx, &ini.exs[i]); } gen("{ "); for let i = 0i64; i < ty.u.Arr.length; ++i { let ex **Expr = map->get(i); ex ? data(:type, ty.u.Arr.child, *ex) : gen("%t", ty.u.Arr.child); if act != :type { gen(" "); if ex { data(:value, ty.u.Arr.child, *ex); } else { gen("zeroinitializer"); } } if i < ty.u.Arr.length - 1 { gen(", "); } } gen(" }"); return; case AggIni ini; assert(ty->is(:Agg), "not agg agg ini"); let map Map,> = {}; defer map->clear(); if ty.u.Agg.kind == :Struct { foreach (fld, i, ini.flds) { let idx = fld - ty.u.Agg.flds.#ptr; map->put(idx, &ini.exs[i]); } gen("{ "); for let i = 0z; i < ty.u.Agg.flds.#len; ++i { let ex **Expr = map->get(i); ex ? data(:type, ty.u.Agg.flds[i].ty, *ex) : gen("%t", ty.u.Agg.flds[i].ty); gen(" ", ty.u.Agg.flds[i].ty); if act != :type { if ex { data(:value, ty.u.Agg.flds[i].ty, *ex); } else { gen("zeroinitializer"); } } if i < ty.u.Agg.flds.#len - 1 { gen(", "); } } gen(" }"); return; } else if ty.u.Agg.kind == :Union { assert(ini.exs.#len == 1, "bad static union init"); let fty = ini.flds[0].ty; let ex = &ini.exs[0]; if act == :type { gen("{ "); data(:type, fty, ex); gen(", [%z x i8] }", ty.size - fty.size); } else { gen("{ "); gendata(fty, ex); gen(", [%z x i8] zeroinitializer }", ty.size - fty.size); } return; } assert(#f, "bad static %d", ex.u.#tag); case EUnionIni ini; let fld = ini.var; let fty = fld.ty; let ex = ini.ex; if act == :type { if ex { gen("{ %t, [%z x i8], ", ty.u.Agg.enumty, fld.off - ty.u.Agg.enumty.size); data(:type, fty, ex); gen(", [%z x i8] }", ty.size - (fld.off + fty.size)); } else { gen("{ %t, [%z x i8] }", ty.u.Agg.enumty, ty.size - ty.u.Agg.enumty.size); } } else { let idx isize = fld - ty.u.Agg.flds.#ptr; if ex { gen("{ %t %z, [%z x i8] zeroinitializer, ", ty.u.Agg.enumty, idx, fld.off - ty.u.Agg.enumty.size); gendata(fty, ex); gen(", [%z x i8] zeroinitializer }", ty.size - (fld.off + fty.size)); } else { gen("{ %t %z, [%z x i8] zeroinitializer }", ty.u.Agg.enumty, idx, ty.size - ty.u.Agg.enumty.size); } } return; case BitFIni ini; if act == :type { gen("%t", ex.ty); return; } let acc u64 = 0; foreach (fld, i, ini.flds) { let ex = &ini.exs[i]; if !fold(ex) { assert(#f, "er"); } assert(ex.u.#tag == :BoolLit or ex.u.#tag == :IntLit, "bit field tag"); let n u64 = ex.u.#tag == :BoolLit ? as(u64)ex.u.BoolLit : ex.u.IntLit.u; let mask = fld.size == 64 ? ~0u64 : ((1u64 << fld.size) - 1); acc &= ~(mask << fld.off); acc |= (n & mask) << fld.off; } gen("%I", acc); return; case else assert(#f, "nyi static %d", ex.u.#tag); } } data(:type, ty, ex); gen(" "); data(:value, ty, ex); } 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 %C , align %z\n", externp ? "" : "internal ", var.ty, &ini, var.ty.align); } extern fn llvm_fini() void { gen("declare void @llvm.va_start(ptr)\n"); gen("declare void @llvm.va_copy(ptr, ptr)\n"); gen("declare void @llvm.va_end(ptr)\n"); gen("declare void @llvm.memset.p0.%t(ptr, i8, %t, i1)\n", ty_usize, ty_usize); gen("declare void @llvm.memmove.p0.p0.%t(ptr, ptr, %t, i1)\n", ty_usize, ty_usize); vec_each(s, i, strs) { gen("@.str.%z = private unnamed_addr 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(", "); } } 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("%%.type.valist = type %s\n", g_targ.valistllvmty); gen("\n"); }