diff options
| -rw-r--r-- | examples/hello-world.cff | 36 | ||||
| -rw-r--r-- | src/cffc.hff | 7 | ||||
| -rw-r--r-- | src/llvm.cff | 514 | ||||
| -rw-r--r-- | src/main.cff | 6 | ||||
| -rw-r--r-- | src/map.hff | 12 | ||||
| -rw-r--r-- | src/parse.cff | 20 | ||||
| -rw-r--r-- | src/set.hff | 12 | ||||
| -rw-r--r-- | src/type.cff | 11 |
8 files changed, 570 insertions, 48 deletions
diff --git a/examples/hello-world.cff b/examples/hello-world.cff index a867cd9..fca5817 100644 --- a/examples/hello-world.cff +++ b/examples/hello-world.cff @@ -1,40 +1,6 @@ import "libc.hff"; -fn zero() int { - return 1 + 2; -} - -#{ -struct Vec2f { x f32, y f32 } - -fn x(v *Vec2f) f32 { - return (*v).y; -} - -fn len(x [#]int) int { - return x.#len; -} - -fn ptr(x [#]int) int { - return *x.#ptr; -} -} - -fn add(a int, b int) int { - let x *void = {}; - return a + b; -} - extern fn main(argc int, argv **u8) int { - let it int = 0; - argv[42] - 3; - while argc > 0 { - argc = argc - 1; - } - - printf("hello %d", argc + 42, - argc < 1 ? argv : #null, - *argv - ); + printf("hello %d", argc + 42); } diff --git a/src/cffc.hff b/src/cffc.hff index 2504fba..f78c61c 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -286,6 +286,7 @@ struct Decl { name *const u8, loc Loc, externp bool, + toplevel bool, u enum union { Let Var, Static Var, @@ -436,6 +437,7 @@ fn isnumtype(ty *const Type) bool { return ty->is(:Int) or ty->is(:Flo) or (ty->is(:Enum) and ty.u.Enum.lax); } extern fn typeof2(a *const Type, b *const Type) *const Type; +extern fn types_to_llvm() void; // env.cff extern fn mkenv(parent *Env, alloc *Allocator) *Env; @@ -459,4 +461,7 @@ extern static g_asmbackend Backend; // llvm.cff extern fn llvm_genfn(externp bool, name *const u8, f *Fn) void; -extern fn llvm_init() void; +extern fn llvm_addfn(*Decl) void; +extern fn llvm_addtype(*const Type) void; +extern fn llvm_init(*FILE) void; +extern fn llvm_fini() void; diff --git a/src/llvm.cff b/src/llvm.cff new file mode 100644 index 0000000..b839f09 --- /dev/null +++ b/src/llvm.cff @@ -0,0 +1,514 @@ +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; +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 Slice p; gen("{ %t*, %t }", p, ty_usize); + case Arr arr; gen("[%z x %t]", arr.length, arr.child); + case Agg agg; gen("%%%s.%d", agg.name ?? as(*const u8)"_", agg.id); + 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(") "); + } + } + + 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("%s", b ? "true" : "false"); + 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 else + assert(#f, "bad fmt '%c'", c); + } + } + ap->end(); +} + +static tmpid int = 1; + +fn genexpr(f *Fn, ex *Expr) Value; +static arena Arena = {}; +static alloc Allocator = {}; +static strs Vec<[#]const u8> = {}; + +fn genaddr(f *Fn, e *Expr) Value { + switch e.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(e.ty), :StrConstRef(strs.len - 1)}; + case else + assert(#f, "genaddr"); + } +} + +fn convert(f *Fn, to *const Type, ex *Expr) Value { + let from = ex.ty; + + defmacro cvt(inst) [ + (do let t = Value{to, :Tmp(tmpid++)}, + 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(:Ptr) and from->is(:Arr); + let addr = genaddr(f, ex); + let t = Value{ex.ty, :Tmp(tmpid++)}; + 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 else + assert(#f, "convert"); + } +} + +fn genblock(f *Fn, block [#]Stmt) void; + +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 StrLit s; return {ex.ty, :StrLit(s)}; + case BinOp b; + defmacro genbinop(inst) [ { + let lhs = convert(f, ex.ty, b.lhs), + rhs = convert(f, ex.ty, b.rhs), + t = Value{ex.ty, :Tmp(tmpid++)}; + gen("\t%v = %s %t %v, %v\n", t, inst, ex.ty, lhs, rhs); + return 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 = Value{ex.ty, :Tmp(tmpid++)}, + t1 = Value{ex.ty, :Tmp(tmpid++)}; + 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; + } ] + switch b.op { + case '+'; + // TODO ptr arithmetic + genbinop(!ex.ty->is(:Flo) ? "add" : "fadd"); + case '-'; + genbinop(!ex.ty->is(:Flo) ? "sub" : "fsub"); + case '*'; genbinop(!ex.ty->is(:Flo) ? "mul" : "fmul"); + case '/'; genbinop(ex.ty->is(:Flo) ? "fdiv" : ex.ty.u.Int.sgn ? "sdiv" : "udiv"); + case '%'; genbinop(ex.ty.u.Int.sgn ? "srem" : "urem"); + case '&'; genbinop("and"); + case '|'; genbinop("or"); + case '^'; genbinop("or"); + case '<<'; genbinop("shl"); + case '>>'; genbinop(ex.ty.u.Int.sgn ? "ashr" : "lshr"); + case '=='; gencmp("eq"); + 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 else + assert(#f, "binop? %d", b.op); + } + case UnOp op; + + case Symbol decl; + switch decl.u { + case Let *var; + let t = Value{ex.ty, :Tmp(tmpid++)}; + gen("\t%v = load %t, %t* %%%s.%d\n", t, ex.ty, ex.ty, decl.name, var.id); + return t; + case Static *var; + let t = Value{ex.ty, :Tmp(tmpid++)}; + if decl.externp { + gen("\t%v = load %t, %t* @%s\n", t, ex.ty, ex.ty, decl.name); + } else { + gen("\t%v = load %t, %t* @%s.%d\n", t, ex.ty, ex.ty, decl.name, var.id); + } + return t; + case Fn *f; + return {ex.ty, :Fn(f)}; + case else + assert(#f, "decl"); + } + 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{ex.ty, :Tmp(tmpid++)}; + 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 else + assert(#f, "expr? %d", ex.u.#tag); + } +} + +fn llvmbool(f *Fn, ex *Expr) Value { + let v = genexpr(f, ex); + let t = Value{ty_i1, :Tmp(tmpid++)}; + 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, st *Stmt) void { + switch st.u { + 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), 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.#ptr { + gen("\tbr label %%IfSk%d\n", id); + } + gen("IfF%d:", id); + nop(); + genblock(f, cnd.f); + if cnd.f.#ptr { + gen("IfSk%d:", id); + nop(); + } + case While loop; + 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 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 Return *e; + switch *e { + 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 else + assert(#f, "stmt? %d", st.u.#tag); + } +} + +fn genblock(f *Fn, block [#]Stmt) void { + foreach_ptr (st, _, block) { + genstmt(f, st); + } +} + +struct FuncKey { + name *const u8, + externp bool, +} +struct FuncEntry { + t enum { + Idk, Def, Decl + }, + ty *const Type, + id int, +} +static funcs Map<FuncKey, FuncEntry, struct { + fn hash(e FuncKey) u32 { + let h = fnv1a_s(FNV1A_INI, e.name); + h = fnv1a_i(h, as(int)e.externp); + return h; + } + fn eq(a FuncKey, b FuncKey) bool { + return a.name == b.name and a.externp == b.externp; + } +}> = {}; + +extern fn llvm_addfn(decl *Decl) void { + assert(decl.toplevel, "llvm-addfn"); + let slot = funcs->get_slot({decl.name, decl.externp}); + if slot.t != :Def { + slot.t = :Decl; + slot.ty = decl.u.Fn.ty; + slot.id = decl.u.Fn.id; + } +} + +extern fn llvm_genfn(externp bool, name *const u8, f *Fn) void { + if externp { + funcs->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, 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"); +} + +extern fn llvm_addtype(ty *const Type) void { + if ty.konst { return; } + switch ty.u { + case Agg agg; + assert(agg.kind == :Struct, "addtype"); + if agg.fwd { + gen("%%%s.%d = type opaque\n", agg.name, agg.id); + } else { + } + + } +} + +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(f, k, funcs) { + if f.t == :Decl { + let ty = f.ty; + gen("declare%s %t ", k.externp ? "" : " internal", ty.u.Fn.ret); + if !k.externp { + gen("@%s.%d(", k.name, f.id); + } else { + gen("@%s(", k.name); + } + foreach(tyy, i, ty.u.Fn.params) { + gen("%t", tyy); + if i < ty.u.Fn.params.#len - 1 { + gen(", "); + } else if ty.u.Fn.variadic { + gen(", ..."); + } + } + gen(");\n"); + } + } + strs->clear(); + funcs->clear(); + arena->destroy(); +} + +extern fn llvm_init(out *FILE) void { + alloc = {&arena, &Arena:allocf, #null}; + outfp = out; +} diff --git a/src/main.cff b/src/main.cff index 3327e48..d2330ed 100644 --- a/src/main.cff +++ b/src/main.cff @@ -8,8 +8,10 @@ extern fn main(argc int, argv **u8) int { let p = Parser {}; parser_init(&p, argv[1]); - llvm_init(); + llvm_init(stdout); let decls = parse(&p); - free(decls.#ptr); + defer free(decls.#ptr); + types_to_llvm(); + llvm_fini(); } diff --git a/src/map.hff b/src/map.hff index 700c553..c756d88 100644 --- a/src/map.hff +++ b/src/map.hff @@ -112,3 +112,15 @@ struct Map<K, V, KTraits> { } } + +defmacro map_each(v, k, map, &body) [ + let $m = (map); + for let $i = 0; $i < $m.N; ++$i { + if $m->_iempty($i) { + continue; + } + let k = $m.keys[$i]; + let v = $m.vals[$i]; + body; + } +] diff --git a/src/parse.cff b/src/parse.cff index 84ed1fe..699762c 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -966,7 +966,7 @@ fn xident2type(P *Parser, eloc Loc, name *const u8) *const Type { fn parsefnparams(P *Parser, variadic *bool, paramnames *Vec<*const u8>, paramtys *Vec<*const Type>) void { lexexpect(P, '('); let tok Tok #?; - *variadic = #t; + *variadic = #f; while !lexmatch(P, &tok, ')') { if lexmatch(P, #null, '...') { *variadic = #t; @@ -1513,7 +1513,7 @@ fn pexpostfix(P *Parser) Expr { P.targty = args.len + 1 <= params.#len ? *param : #null; let ex = parseexpr(P); args->push(ex); - if args.len >= params.#len and !Fn.variadic { + if args.len > params.#len and !Fn.variadic { fatal(P, ex.loc, "too many args (%z, expected %z)", args.len, params.#len); } if args.len <= params.#len and !typematchestarg(*param, ex.ty) { @@ -2397,7 +2397,8 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie ty = constify(ty); } - let decl Decl = { name, tok.loc, externp, :Let { ty, ini, fwd, P.curfn ? P.curfn.id : 0, } }; + let decl Decl = { name, tok.loc, externp, toplevel, + :Let { ty, ini, fwd, P.curfn ? P.curfn.id : 0, } }; if letp { decl.u.Let.id = P.varid++; } else { @@ -2414,7 +2415,7 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie } while !lexmatch(P, &tok, ';'); } -fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl { +fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Decl { let decl Decl = { name, loc, @@ -2435,9 +2436,13 @@ fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl { static id int = 0; Fn.id = ++id; let decl = putdecl(P, loc, decl); - + decl.toplevel = toplevel; + if !lexmatch(P, #null, '{') { lexexpects(P, ';', "';' or '{'"); + if toplevel { + llvm_addfn(decl); + } return decl; } @@ -2463,6 +2468,9 @@ fn parsefn(P *Parser, loc Loc, externp bool, name *const u8) *Decl { (as(*Arena)P.alloc.a)->destroy(); } } + if toplevel { + llvm_addfn(decl); + } P.varid = varid; P.loopid = loopid; return decl; @@ -2700,7 +2708,7 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) switch { case lexmatch(P, &tok, :kw_fn); let name = lexmatch(P, &tok, :ident) ? tok.u.ident : #null; - decl = parsefn(P, tok.loc, externp, name); + decl = parsefn(P, tok.loc, toplevel, externp, name); case lexmatch(P, &tok, :kw_import); let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr; diff --git a/src/set.hff b/src/set.hff index 431ef93..6a03e52 100644 --- a/src/set.hff +++ b/src/set.hff @@ -11,7 +11,6 @@ struct Set<T, Traits> { N int, count int, - fn _nexthash(h *u32) u32 { // return *h = (*h * 1664525) + 1013904223u; // double hashing return ++*h; // linear probing @@ -76,3 +75,14 @@ struct Set<T, Traits> { self->intern(x); } } + +defmacro set_each(v, Set, &body) [ + let $s = (Set); + for let $i = 0; $i < $s.N; ++$i { + let $idx = $s.set[$i]; + if $idx > 0 { + let v = $s.buf.dat[$idx - 1]; + body; + } + } +] diff --git a/src/type.cff b/src/type.cff index ddb4cd8..194ff01 100644 --- a/src/type.cff +++ b/src/type.cff @@ -115,14 +115,13 @@ struct TypeTraits { } } +static types_set Set<*const Type, TypeTraits> = {}; extern fn interntype(ty0 Type) *const Type { - static set Set<*const Type, TypeTraits> = {}; - if ty0.align == 0 { ty0.align = ty0.size; } - return *set->intern(&ty0); + return *types_set->intern(&ty0); } extern fn completetype(ty *const Type) bool { @@ -307,3 +306,9 @@ extern fn typeof2(a *const Type, b *const Type) *const Type { } return #null; } + +extern fn types_to_llvm() void { + set_each(ty, types_set) { + llvm_addtype(ty); + } +} |