import "cffc.hff"; import "util.hff"; import "common.hff"; import "set.hff"; struct TypeTraits { fn hash(ty *const Type) u32 { let h = FNV1A_INI; h = fnv1a_i(h, as(int)ty.u.#tag); h = fnv1a_i(h, as(int)ty.konst); switch ty.u { case Void; case Bool; h = fnv1a_i(h, ty.size); case Flo; h = fnv1a_i(h, ty.size); case Int i; h = fnv1a_i(h, i.sgn ? 1 : 0); h = fnv1a_i(h, ty.size); case Ptr child; h = fnv1a_i(h, child.id); case Arr arr; h = fnv1a_i(h, arr.child.id); h = fnv1a_i(h, arr.length); case Slice child; h = fnv1a_i(h, child.id); case Fn f; h = fnv1a_i(h, f.ret.id); foreach(p, _, f.params) { h = fnv1a_i(h, p.id); } case Agg agg; h = fnv1a_i(h, agg.id); case Enum enu; h = fnv1a_i(h, enu.id); case BitF bitf; h = fnv1a_i(h, bitf.id); } return h; } fn eq(a *const Type, b *const Type) bool { if a.u.#tag != b.u.#tag or a.konst != b.konst { return #f; } switch a.u { case Void; return #t; case Bool; return a.size == b.size; case Flo; return a.size == b.size; case Int i; return a.size == b.size and i.sgn == b.u.Int.sgn; case Ptr child; return child == b.u.Ptr; case Arr arr; let brr = b.u.Arr; return arr.length == brr.length and arr.child == brr.child; case Slice child; return child == b.u.Slice; case Fn f0; let f1 = b.u.Fn; if f0.variadic != f1.variadic { return #f; } if f0.ret != f1.ret { return #f; } if f0.params.#len != f1.params.#len { return #f; } foreach(p, i, f0.params) { if p != f1.params[i] { return #f; } } return #t; case Agg agg; let bgg = b.u.Agg; return agg.id == bgg.id; case Enum enu; return enu.id == b.u.Enum.id; case BitF bitf; return bitf.id == b.u.BitF.id; } return #f; } fn dup(ty *const Type) *const Type { let p *Type = xmalloc(sizeof Type); static id int = 0; *p = *ty; p.id = id++; switch p.u { case Fn *f; let params = f.params; f.params = (as(**const Type)xmalloc(params.#len * sizeof *Type))[0::params.#len]; memcpy(f.params.#ptr, params.#ptr, params.#len * sizeof *Type); } return p; } } static types_set Set<*const Type, TypeTraits> = {}; extern fn interntype(ty0 Type) *const Type { if ty0.align == 0 { ty0.align = ty0.size == 0 ? 1 : ty0.size; } return *types_set->intern(&ty0); } extern fn completetype(ty *const Type) bool { if ty.konst { return completetype(unconstify(ty)); } switch ty.u { case Void; return #f; case Fn; return #f; case Arr a; return a.length >= 0 and completetype(a.child); case Agg agg; return !agg.fwd; } return #t; } extern static ty_void *const Type = {}, ty_bool *const Type = {}, ty_intbool *const Type = {}, ty_i8 *const Type = {}, ty_u8 *const Type = {}, ty_i16 *const Type = {}, ty_u16 *const Type = {}, ty_i32 *const Type = {}, ty_u32 *const Type = {}, ty_int *const Type = {}, ty_uint *const Type = {}, ty_i64 *const Type = {}, ty_u64 *const Type = {}, ty_isize *const Type = {}, ty_usize *const Type = {}, ty_iptrint *const Type = {}, ty_uptrint *const Type = {}, ty_f32 *const Type = {}, ty_f64 *const Type = {}, ty_voidptr *const Type = {}, ty_c_char *const Type = {}, ty_c_long *const Type = {}, ty_c_ulong *const Type = {}, ty_c_llong *const Type = {}, ty_c_ullong *const Type = {}, ty_c_valist *const Type = {}; extern fn putprimtypes(env *Env) void { static types []struct { name *const u8, gty **const Type, ty Type } = { { "void", &ty_void, { .size: 0, .align: 1, .u: :Void }}, { "bool", &ty_bool, { .size: 1, .align: 1, .u: :Bool }}, { "f32", &ty_f32, { .size: 4, .u: :Flo }}, { "f64", &ty_f64, { .size: 8, .u: :Flo }}, { "i8", &ty_i8, { .size: 1, .u: :Int { .sgn: #t }}}, { "u8", &ty_u8, { .size: 1, .u: :Int { .sgn: #f }}}, { "i16", &ty_i16, { .size: 2, .u: :Int { .sgn: #t }}}, { "u16", &ty_u16, { .size: 2, .u: :Int { .sgn: #f }}}, { "i32", &ty_i32, { .size: 4, .u: :Int { .sgn: #t }}}, { "u32", &ty_u32, { .size: 4, .u: :Int { .sgn: #f }}}, { "int", &ty_int, { .u: :Int { .sgn: #t }}}, { "uint", &ty_uint, { .u: :Int { .sgn: #f }}}, { "intbool",&ty_intbool,{ .u: :Bool }}, { "isize", &ty_isize,{ .u: :Int { .sgn: #t }}}, { "usize", &ty_usize,{ .u: :Int { .sgn: #f }}}, { "i64", &ty_i64, { .size: 8, .u: :Int { .sgn: #t }}}, { "u64", &ty_u64, { .size: 8, .u: :Int { .sgn: #f }}}, { "iptrint",&ty_iptrint, { .u: :Int { .sgn: #t }}}, { "uptrint",&ty_uptrint, { .u: :Int { .sgn: #f }}}, { "c_char", &ty_c_char, { .size: 1 }}, { "c_long", &ty_c_long, { .u: :Int { .sgn : #t }}}, { "c_ulong",&ty_c_ulong, { .u: :Int { .sgn : #f }}}, { "c_llong",&ty_c_llong, { .u: :Int { .sgn : #t }}}, { "c_ullong",&ty_c_ullong,{.u: :Int { .sgn : #f }}}, { "va_list",&ty_c_valist,{ .u: :VaList }}, }; foreach(type, _, types[0::]) { let ty = &type.ty; let gty = type.gty; switch { case gty == &ty_f64; ty.align = g_targ.f64align; case gty == &ty_int or gty == &ty_uint or gty == &ty_intbool; ty.size = g_targ.intsize; case gty == &ty_isize or gty == &ty_usize; ty.size = g_targ.sizesize; case gty == &ty_i64 or gty == &ty_u64; ty.align = g_targ.i64align; case gty == &ty_iptrint or gty == &ty_uptrint; ty.size = g_targ.ptrsize; case gty == &ty_c_char; ty.u.Int.sgn = g_targ.charsigned; case gty == &ty_c_long or gty == &ty_c_ulong; ty.size = g_targ.longsize; ty.align = g_targ.longalign; case gty == &ty_c_llong or gty == &ty_c_ullong; ty.size = g_targ.longsize; ty.align = g_targ.longalign; case gty == &ty_c_valist; ty.size = g_targ.valistsize; ty.align = g_targ.valistalign; } envput(env, { type.name, .u: :Ty(*gty = interntype(*ty)) }, #null); } ty_voidptr = interntype({ .size: g_targ.ptrsize, .u: :Ptr(ty_void) }); } extern fn mkslicetype(child *const Type) *const Type { let align = MAX(g_targ.ptrsize, g_targ.sizesize); return interntype({ ALIGNUP(g_targ.ptrsize + g_targ.sizesize, align), align, .u: :Slice(child) }); } fn numtype2rank(ty *const Type) int { ty = unconstify(ty); switch { case ty->is(:Enum); assert(ty.u.Enum.lax, "lax"); return numtype2rank(ty.u.Enum.intty); case ty->is(:Int) and (ty == ty_int or ty.size < ty_int.size); return 0; case ty == ty_uint; return 1; case ty == ty_i32; return 2; case ty == ty_u32; return 3; case ty == ty_i64; return 4; case ty == ty_u64; return 5; case ty == ty_f32; return 6; case ty == ty_f64; return 7; } return -1; } fn rank2numtype(r int) *const Type { static const types []**const Type = { &ty_int, &ty_uint, &ty_i32, &ty_u32, &ty_i64, &ty_u64, &ty_f32, &ty_f64, }; assert(r >= 0 and r < types.#len, "rank"); return *types[r]; } fn arraydecay(ty *const Type) *const Type { switch ty.u { case Arr arr; return mkptrtype(arr.child); case else; return ty; } } fn constifychild(ty *const Type) *const Type { switch ty.u { case Ptr child; return mkptrtype(constify(child)); case Slice child; return mkslicetype(constify(child)); case else; assert(#f, "constifychild: not ptr or slice"); } } extern fn typeof2(a *const Type, b *const Type) *const Type { if a == b and !a->is(:Int) { return a; } if isnumtype(a) and isnumtype(b) { let ra = numtype2rank(a), rb = numtype2rank(b); return rank2numtype(MAX(ra, rb)); } if unconstify(a) == b { return a; } if a == unconstify(b) { return b; } if a->is(:Bool) and b->is(:Bool) { return a.size > b.size ? a : b; } if a->is(:Arr) and b->is(:Arr) { a = arraydecay(a); } if a->is(:Ptr) and b->is(:Arr) { b = arraydecay(b); } if a->is(:Arr) and b->is(:Ptr) { a = arraydecay(a); } if a.u.#tag == b.u.#tag and (a->is(:Ptr) or a->is(:Slice)) { let akonst = a.u.Ptr.konst, bkonst = b.u.Ptr.konst, uac = unconstify(a.u.Ptr), ubc = unconstify(b.u.Ptr); if uac == ubc { return akonst ? a : b; } if uac == ty_void { if !bkonst and akonst { return constifychild(b); } return b; } if ubc == ty_void { if !akonst and bkonst { return constifychild(a); } return a; } } return #null; } extern fn visittypes(proc *fn(*const Type, *void) void, arg *void) void { set_each(ty, types_set) { proc(ty, arg); } }