From 166cb1b28bc23303d15e8c1c4a71d0cdff0556a2 Mon Sep 17 00:00:00 2001 From: Elliott Slaughter Date: Mon, 6 Aug 2012 14:14:17 -0700 Subject: [PATCH] rustc: Strict enforcement of glue function types. Make all glue functions take values by alias to remove the need for bitcasts at the top of every glue function. Use static type information to produce the correct type for glue functions so that LLVM can enforce the type system at call sites. --- src/rustc/middle/trans/base.rs | 155 +++++++++++++++++++----------- src/rustc/middle/trans/closure.rs | 6 +- src/rustc/middle/trans/common.rs | 2 +- src/rustc/middle/trans/tvec.rs | 5 +- src/rustc/middle/trans/type_of.rs | 7 ++ src/rustc/middle/trans/uniq.rs | 3 +- 6 files changed, 118 insertions(+), 60 deletions(-) diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index fd13d8fa1efbf..ce6669f979992 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -546,18 +546,18 @@ fn make_generic_glue_inner(ccx: @crate_ctxt, t: ty::t, let fcx = new_fn_ctxt(ccx, ~[], llfn, none); lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); ccx.stats.n_glues_created += 1u; - // Any nontrivial glue is with values passed *by alias*; this is a + // All glue functions take values passed *by alias*; this is a // requirement since in many contexts glue is invoked indirectly and // the caller has no idea if it's dealing with something that can be // passed by value. - - let llty = T_ptr(type_of(ccx, t)); + // + // llfn is expected be declared to take a parameter of the appropriate + // type, so we don't need to explicitly cast the function parameter. let bcx = top_scope_block(fcx, none); let lltop = bcx.llbb; let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint); - let llval0 = BitCast(bcx, llrawptr0, llty); - helper(bcx, llval0, t); + helper(bcx, llrawptr0, t); finish_fn(fcx, lltop); return llfn; } @@ -581,28 +581,44 @@ fn make_generic_glue(ccx: @crate_ctxt, t: ty::t, llfn: ValueRef, fn emit_tydescs(ccx: @crate_ctxt) { let _icx = ccx.insn_ctxt(~"emit_tydescs"); for ccx.tydescs.each |key, val| { - let glue_fn_ty = T_ptr(T_glue_fn(ccx)); + let glue_fn_ty = T_ptr(T_generic_glue_fn(ccx)); let ti = val; + // Each of the glue functions needs to be cast to a generic type + // before being put into the tydesc because we only have a singleton + // tydesc type. Then we'll recast each function to its real type when + // calling it. let take_glue = match copy ti.take_glue { none => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - some(v) => { ccx.stats.n_real_glues += 1u; v } + some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } }; let drop_glue = match copy ti.drop_glue { none => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - some(v) => { ccx.stats.n_real_glues += 1u; v } + some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } }; let free_glue = match copy ti.free_glue { none => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - some(v) => { ccx.stats.n_real_glues += 1u; v } + some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } }; let visit_glue = match copy ti.visit_glue { none => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - some(v) => { ccx.stats.n_real_glues += 1u; v } + some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } }; let shape = shape_of(ccx, key); @@ -692,20 +708,20 @@ fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { - // v is a pointer to the actual box component of the type here. The - // ValueRef will have the wrong type here (make_generic_glue is casting - // everything to a pointer to the type that the glue acts on). + // NB: v0 is an *alias* of type t here, not a direct value. let _icx = bcx.insn_ctxt(~"make_free_glue"); let ccx = bcx.ccx(); let bcx = match ty::get(t).struct { ty::ty_box(body_mt) => { - let v = PointerCast(bcx, v, type_of(ccx, t)); + let v = Load(bcx, v); let body = GEPi(bcx, v, ~[0u, abi::box_field_body]); + // Cast away the addrspace of the box pointer. + let body = PointerCast(bcx, body, T_ptr(type_of(ccx, body_mt.ty))); let bcx = drop_ty(bcx, body, body_mt.ty); trans_free(bcx, v) } ty::ty_opaque_box => { - let v = PointerCast(bcx, v, type_of(ccx, t)); + let v = Load(bcx, v); let td = Load(bcx, GEPi(bcx, v, ~[0u, abi::box_field_tydesc])); let valptr = GEPi(bcx, v, ~[0u, abi::box_field_body]); // Generate code that, dynamically, indexes into the @@ -715,7 +731,6 @@ fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { trans_free(bcx, v) } ty::ty_uniq(content_mt) => { - let v = PointerCast(bcx, v, type_of(ccx, t)); uniq::make_free_glue(bcx, v, t) } ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) | @@ -785,7 +800,7 @@ fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { } ty::ty_uniq(_) | ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { - free_ty(bcx, Load(bcx, v0), t) + free_ty(bcx, v0, t) } ty::ty_unboxed_vec(_) => { tvec::make_drop_glue_unboxed(bcx, v0, t) @@ -861,14 +876,12 @@ fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block { let ccx = bcx.ccx(); maybe_validate_box(bcx, box_ptr); - let llbox_ty = T_opaque_box_ptr(ccx); - let box_ptr = PointerCast(bcx, box_ptr, llbox_ty); do with_cond(bcx, IsNotNull(bcx, box_ptr)) |bcx| { let rc_ptr = GEPi(bcx, box_ptr, ~[0u, abi::box_field_refcnt]); let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1)); Store(bcx, rc, rc_ptr); let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc); - with_cond(bcx, zero_test, |bcx| free_ty(bcx, box_ptr, t)) + with_cond(bcx, zero_test, |bcx| free_ty_immediate(bcx, box_ptr, t)) } } @@ -1097,17 +1110,16 @@ fn lazily_emit_all_tydesc_glue(ccx: @crate_ctxt, fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, ti: @tydesc_info) { let _icx = ccx.insn_ctxt(~"lazily_emit_tydesc_glue"); + let llfnty = type_of_glue_fn(ccx, ti.ty); if field == abi::tydesc_field_take_glue { match ti.take_glue { some(_) => (), none => { debug!{"+++ lazily_emit_tydesc_glue TAKE %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; - let glue_fn = declare_generic_glue - (ccx, ti.ty, T_glue_fn(ccx), ~"take"); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"take"); ti.take_glue = some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, - make_take_glue, ~"take"); + make_generic_glue(ccx, ti.ty, glue_fn, make_take_glue, ~"take"); debug!{"--- lazily_emit_tydesc_glue TAKE %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; } @@ -1118,11 +1130,9 @@ fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, none => { debug!{"+++ lazily_emit_tydesc_glue DROP %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; - let glue_fn = - declare_generic_glue(ccx, ti.ty, T_glue_fn(ccx), ~"drop"); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"drop"); ti.drop_glue = some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, - make_drop_glue, ~"drop"); + make_generic_glue(ccx, ti.ty, glue_fn, make_drop_glue, ~"drop"); debug!{"--- lazily_emit_tydesc_glue DROP %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; } @@ -1133,11 +1143,9 @@ fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, none => { debug!{"+++ lazily_emit_tydesc_glue FREE %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; - let glue_fn = - declare_generic_glue(ccx, ti.ty, T_glue_fn(ccx), ~"free"); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"free"); ti.free_glue = some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, - make_free_glue, ~"free"); + make_generic_glue(ccx, ti.ty, glue_fn, make_free_glue, ~"free"); debug!{"--- lazily_emit_tydesc_glue FREE %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; } @@ -1148,11 +1156,9 @@ fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, none => { debug!{"+++ lazily_emit_tydesc_glue VISIT %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; - let glue_fn = - declare_generic_glue(ccx, ti.ty, T_glue_fn(ccx), ~"visit"); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"visit"); ti.visit_glue = some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, - make_visit_glue, ~"visit"); + make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, ~"visit"); debug!{"--- lazily_emit_tydesc_glue VISIT %s", ppaux::ty_to_str(ccx.tcx, ti.ty)}; } @@ -1161,43 +1167,63 @@ fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, } // See [Note-arg-mode] -fn call_tydesc_glue_full(++cx: block, v: ValueRef, tydesc: ValueRef, +fn call_tydesc_glue_full(++bcx: block, v: ValueRef, tydesc: ValueRef, field: uint, static_ti: option<@tydesc_info>) { - let _icx = cx.insn_ctxt(~"call_tydesc_glue_full"); - if cx.unreachable { return; } + let _icx = bcx.insn_ctxt(~"call_tydesc_glue_full"); + if bcx.unreachable { return; } + let ccx = bcx.ccx(); - let mut static_glue_fn = none; - match static_ti { - none => {/* no-op */ } + let static_glue_fn = match static_ti { + none => none, some(sti) => { - lazily_emit_tydesc_glue(cx.ccx(), field, sti); + lazily_emit_tydesc_glue(ccx, field, sti); if field == abi::tydesc_field_take_glue { - static_glue_fn = sti.take_glue; + sti.take_glue } else if field == abi::tydesc_field_drop_glue { - static_glue_fn = sti.drop_glue; + sti.drop_glue } else if field == abi::tydesc_field_free_glue { - static_glue_fn = sti.free_glue; + sti.free_glue } else if field == abi::tydesc_field_visit_glue { - static_glue_fn = sti.visit_glue; + sti.visit_glue + } else { + none } } - } + }; + + // When available, use static type info to give glue the right type. + let static_glue_fn = match static_ti { + none => none, + some(sti) => { + match static_glue_fn { + none => none, + some(sgf) => some( + PointerCast(bcx, sgf, T_ptr(type_of_glue_fn(ccx, sti.ty)))) + } + } + }; - let llrawptr = PointerCast(cx, v, T_ptr(T_i8())); + // When static type info is available, avoid casting parameter because the + // function already has the right type. Otherwise cast to generic pointer. + let llrawptr = if is_none(static_ti) || is_none(static_glue_fn) { + PointerCast(bcx, v, T_ptr(T_i8())) + } else { + v + }; let llfn = { match static_glue_fn { none => { // Select out the glue function to call from the tydesc - let llfnptr = GEPi(cx, tydesc, ~[0u, field]); - Load(cx, llfnptr) + let llfnptr = GEPi(bcx, tydesc, ~[0u, field]); + Load(bcx, llfnptr) } some(sgf) => sgf } }; - Call(cx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())), - C_null(T_ptr(T_ptr(cx.ccx().tydesc_type))), llrawptr]); + Call(bcx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())), + C_null(T_ptr(T_ptr(bcx.ccx().tydesc_type))), llrawptr]); } // See [Note-arg-mode] @@ -1231,6 +1257,7 @@ fn call_cmp_glue(bcx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t, } fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. let _icx = cx.insn_ctxt(~"take_ty"); if ty::type_needs_drop(cx.tcx(), t) { return call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue); @@ -1239,6 +1266,7 @@ fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block { } fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. let _icx = cx.insn_ctxt(~"drop_ty"); if ty::type_needs_drop(cx.tcx(), t) { return call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue); @@ -1252,7 +1280,7 @@ fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { ty::ty_uniq(_) | ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { - free_ty(bcx, v, t) + free_ty_immediate(bcx, v, t) } ty::ty_box(_) | ty::ty_opaque_box | ty::ty_evec(_, ty::vstore_box) | @@ -1284,6 +1312,7 @@ fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> result { } fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. let _icx = cx.insn_ctxt(~"free_ty"); if ty::type_needs_drop(cx.tcx(), t) { return call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue); @@ -1291,6 +1320,24 @@ fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block { return cx; } +fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { + let _icx = bcx.insn_ctxt(~"free_ty_immediate"); + match ty::get(t).struct { + ty::ty_uniq(_) | + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_estr(ty::vstore_uniq) | + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) | + ty::ty_opaque_closure_ptr(_) => { + let vp = alloca_zeroed(bcx, type_of(bcx.ccx(), t)); + Store(bcx, v, vp); + free_ty(bcx, vp, t) + } + _ => bcx.tcx().sess.bug(~"free_ty_immediate: non-box ty") + } +} + fn call_memmove(cx: block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef) { // FIXME (Related to #1645, I think?): Provide LLVM with better diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs index 752ce97427ca5..d5af4e181a756 100644 --- a/src/rustc/middle/trans/closure.rs +++ b/src/rustc/middle/trans/closure.rs @@ -492,7 +492,7 @@ fn make_opaque_cbox_drop_glue( ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) } ty::ck_uniq => { - free_ty(bcx, Load(bcx, cboxptr), + free_ty(bcx, cboxptr, ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) } } @@ -501,7 +501,7 @@ fn make_opaque_cbox_drop_glue( fn make_opaque_cbox_free_glue( bcx: block, ck: ty::closure_kind, - cbox: ValueRef) // ptr to the opaque closure + cbox: ValueRef) // ptr to ptr to the opaque closure -> block { let _icx = bcx.insn_ctxt(~"closure::make_opaque_cbox_free_glue"); match ck { @@ -513,7 +513,7 @@ fn make_opaque_cbox_free_glue( do with_cond(bcx, IsNotNull(bcx, cbox)) |bcx| { // Load the type descr found in the cbox let lltydescty = T_ptr(ccx.tydesc_type); - let cbox = PointerCast(bcx, cbox, T_opaque_cbox_ptr(ccx)); + let cbox = Load(bcx, cbox); let tydescptr = GEPi(bcx, cbox, ~[0u, abi::box_field_tydesc]); let tydesc = Load(bcx, tydescptr); let tydesc = PointerCast(bcx, tydesc, lltydescty); diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index 7e48297f12ec6..20573eff4cce0 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -655,7 +655,7 @@ fn T_tydesc_field(cx: @crate_ctxt, field: uint) -> TypeRef unsafe { return t; } -fn T_glue_fn(cx: @crate_ctxt) -> TypeRef { +fn T_generic_glue_fn(cx: @crate_ctxt) -> TypeRef { let s = ~"glue_fn"; match name_has_type(cx.tn, s) { some(t) => return t, diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs index aa92c96a68681..94c48ace7497e 100644 --- a/src/rustc/middle/trans/tvec.rs +++ b/src/rustc/middle/trans/tvec.rs @@ -175,10 +175,13 @@ fn trans_evec(bcx: block, elements: evec_elements, let ty = ty::mk_evec(bcx.tcx(), {ty: unit_ty, mutbl: ast::m_mutbl}, ty::vstore_fixed(count)); + let llty = T_ptr(type_of::type_of(bcx.ccx(), ty)); let n = C_uint(ccx, count); let vp = base::arrayalloca(bcx, llunitty, n); - add_clean(bcx, vp, ty); + // Cast to the fake type we told cleanup to expect. + let vp0 = BitCast(bcx, vp, llty); + add_clean(bcx, vp0, ty); let len = Mul(bcx, n, unit_sz); diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs index 86c9cb0ff97b5..06f3b179536af 100644 --- a/src/rustc/middle/trans/type_of.rs +++ b/src/rustc/middle/trans/type_of.rs @@ -10,6 +10,7 @@ export type_of_dtor; export type_of_explicit_args; export type_of_fn_from_ty; export type_of_fn; +export type_of_glue_fn; export type_of_non_gc_box; fn type_of_explicit_args(cx: @crate_ctxt, @@ -244,3 +245,9 @@ fn type_of_dtor(ccx: @crate_ctxt, self_ty: ty::t) -> TypeRef { llvm::LLVMVoidType()) } +fn type_of_glue_fn(ccx: @crate_ctxt, t: ty::t) -> TypeRef { + let tydescpp = T_ptr(T_ptr(ccx.tydesc_type)); + let llty = T_ptr(type_of(ccx, t)); + return T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, llty], + T_void()); +} diff --git a/src/rustc/middle/trans/uniq.rs b/src/rustc/middle/trans/uniq.rs index e66ae41e5c644..e9374dbb05e82 100644 --- a/src/rustc/middle/trans/uniq.rs +++ b/src/rustc/middle/trans/uniq.rs @@ -7,9 +7,10 @@ import shape::llsize_of; export make_free_glue, autoderef, duplicate; -fn make_free_glue(bcx: block, vptr: ValueRef, t: ty::t) +fn make_free_glue(bcx: block, vptrptr: ValueRef, t: ty::t) -> block { let _icx = bcx.insn_ctxt(~"uniq::make_free_glue"); + let vptr = Load(bcx, vptrptr); do with_cond(bcx, IsNotNull(bcx, vptr)) |bcx| { let content_ty = content_ty(t); let body_ptr = opaque_box_body(bcx, content_ty, vptr);