// Routines useful for garbage collection. import lib::llvm::{True, False}; import lib::llvm::llvm::ValueRef; import middle::trans; import middle::trans::{get_tydesc, tps_normal}; import middle::trans_common::*; import middle::ty; import std::option::{some, none}; import std::{ptr, str, unsafe, vec}; import lll = lib::llvm::llvm; import bld = trans_build; type ctxt = @{mutable next_tydesc_num: uint}; fn mk_ctxt() -> ctxt { ret @{mutable next_tydesc_num: 0u}; } fn add_global(ccx: @crate_ctxt, llval: ValueRef, name: str) -> ValueRef { let llglobal = str::as_buf(name, {|buf| lll::LLVMAddGlobal(ccx.llmod, val_ty(llval), buf) }); lll::LLVMSetInitializer(llglobal, llval); lll::LLVMSetGlobalConstant(llglobal, True); ret llglobal; } fn add_gc_root(cx: @block_ctxt, llval: ValueRef, ty: ty::t) -> @block_ctxt { let bcx = cx; if !type_is_gc_relevant(bcx_tcx(cx), ty) || ty::type_has_dynamic_size(bcx_tcx(cx), ty) { ret bcx; } let gc_cx = bcx_ccx(cx).gc_cx; // FIXME (issue #839): For now, we are unconditionally zeroing out all // GC-relevant types. Eventually we should use typestate for this. bcx = trans::zero_alloca(bcx, llval, ty); let ti = none; let td_r = get_tydesc(bcx, ty, false, tps_normal, ti); bcx = td_r.result.bcx; let lltydesc = td_r.result.val; let gcroot = bcx_ccx(bcx).intrinsics.get("llvm.gcroot"); let llvalptr = bld::PointerCast(bcx, llval, T_ptr(T_ptr(T_i8()))); alt td_r.kind { tk_derived. { // It's a derived type descriptor. First, spill it. let lltydescptr = trans::alloca(bcx, val_ty(lltydesc)); let llderivedtydescs = trans::llderivedtydescs_block_ctxt(bcx_fcx(bcx)); bld::Store(llderivedtydescs, lltydesc, lltydescptr); let number = gc_cx.next_tydesc_num; gc_cx.next_tydesc_num += 1u; let lldestindex = add_global(bcx_ccx(bcx), C_struct([C_int(0), C_uint(number)]), "rust_gc_tydesc_dest_index"); let llsrcindex = add_global(bcx_ccx(bcx), C_struct([C_int(1), C_uint(number)]), "rust_gc_tydesc_src_index"); lldestindex = lll::LLVMConstPointerCast(lldestindex, T_ptr(T_i8())); llsrcindex = lll::LLVMConstPointerCast(llsrcindex, T_ptr(T_i8())); lltydescptr = bld::PointerCast(llderivedtydescs, lltydescptr, T_ptr(T_ptr(T_i8()))); bld::Call(llderivedtydescs, gcroot, [lltydescptr, lldestindex]); bld::Call(bcx, gcroot, [llvalptr, llsrcindex]); } tk_param. { bcx_tcx(cx).sess.bug("we should never be trying to root values " + "of a type parameter"); } tk_static. { // Static type descriptor. let llstaticgcmeta = add_global(bcx_ccx(bcx), C_struct([C_int(2), lltydesc]), "rust_gc_tydesc_static_gc_meta"); let llstaticgcmetaptr = lll::LLVMConstPointerCast(llstaticgcmeta, T_ptr(T_i8())); bld::Call(bcx, gcroot, [llvalptr, llstaticgcmetaptr]); } } ret bcx; } fn type_is_gc_relevant(cx: ty::ctxt, ty: ty::t) -> bool { alt ty::struct(cx, ty) { ty::ty_nil. | ty::ty_bot. | ty::ty_bool. | ty::ty_int. | ty::ty_float. | ty::ty_uint. | ty::ty_machine(_) | ty::ty_char. | ty::ty_str. | ty::ty_type. | ty::ty_native(_) | ty::ty_ptr(_) | ty::ty_type. | ty::ty_native(_) { ret false; } ty::ty_rec(fields) { for f in fields { if type_is_gc_relevant(cx, f.mt.ty) { ret true; } } ret false; } ty::ty_tup(elts) { for elt in elts { if type_is_gc_relevant(cx, elt) { ret true; } } ret false; } ty::ty_tag(did, tps) { let variants = ty::tag_variants(cx, did); for variant in variants { for aty in variant.args { let arg_ty = ty::substitute_type_params(cx, tps, aty); if type_is_gc_relevant(cx, arg_ty) { ret true; } } } ret false; } ty::ty_vec(tm) { ret type_is_gc_relevant(cx, tm.ty); } ty::ty_constr(sub, _) { ret type_is_gc_relevant(cx, sub); } ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_fn(_, _, _, _, _) | ty::ty_native_fn(_, _, _) | ty::ty_obj(_) | ty::ty_param(_, _) | ty::ty_res(_, _, _) { ret true; } ty::ty_var(_) { fail "ty_var in type_is_gc_relevant"; } } }