548c0982ca
`simplify_type` was bogus, as there was no way for it to handle enums properly. It was also slow, because it created many Rust types at runtime. In general creating Rust types during trans is a source of slowness, and I'd like to avoid doing it as much as possible. (It is probably not possible to eliminate it entirely, due to `subst`, but we should get rid of as much of it as we can.) So this patch replaces `simplify_type` with `sizing_type_of`, which creates a size-equivalent LLVM type directly without going through a Rust type first. Because this is causing an ICE in Servo, I'm rubber stamping it.
1461 lines
44 KiB
Rust
1461 lines
44 KiB
Rust
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
|
|
/**
|
|
Code that is useful in various trans modules.
|
|
|
|
*/
|
|
|
|
use core::prelude::*;
|
|
|
|
use back::{link, abi, upcall};
|
|
use driver::session;
|
|
use driver::session::Session;
|
|
use lib::llvm::{ModuleRef, ValueRef, TypeRef, BasicBlockRef, BuilderRef};
|
|
use lib::llvm::{True, False, Bool};
|
|
use lib::llvm::{llvm, target_data, type_names, associate_type, name_has_type};
|
|
use lib;
|
|
use metadata::common::link_meta;
|
|
use metadata::{csearch};
|
|
use middle::astencode;
|
|
use middle::resolve;
|
|
use middle::trans::base;
|
|
use middle::trans::build;
|
|
use middle::trans::callee;
|
|
use middle::trans::datum;
|
|
use middle::trans::debuginfo;
|
|
use middle::trans::expr;
|
|
use middle::trans::glue;
|
|
use middle::trans::meth;
|
|
use middle::trans::reachable;
|
|
use middle::trans::shape;
|
|
use middle::trans::type_of;
|
|
use middle::trans::type_use;
|
|
use middle::ty::substs;
|
|
use middle::ty;
|
|
use middle::typeck;
|
|
use util::ppaux::{expr_repr, ty_to_str};
|
|
|
|
use core::cast;
|
|
use core::cmp;
|
|
use core::hash;
|
|
use core::libc::c_uint;
|
|
use core::ptr;
|
|
use core::str;
|
|
use core::to_bytes;
|
|
use core::vec::raw::to_ptr;
|
|
use core::vec;
|
|
use std::oldmap::{HashMap, Set};
|
|
use syntax::ast::ident;
|
|
use syntax::ast_map::path;
|
|
use syntax::codemap::span;
|
|
use syntax::parse::token::ident_interner;
|
|
use syntax::print::pprust::expr_to_str;
|
|
use syntax::{ast, ast_map};
|
|
|
|
pub type namegen = fn@(~str) -> ident;
|
|
pub fn new_namegen(intr: @ident_interner) -> namegen {
|
|
return fn@(prefix: ~str) -> ident {
|
|
// XXX: Bad copies.
|
|
return intr.gensym(@fmt!("%s_%u",
|
|
prefix,
|
|
intr.gensym(@copy prefix).repr))
|
|
};
|
|
}
|
|
|
|
pub type addrspace = c_uint;
|
|
|
|
// Address spaces communicate to LLVM which destructors need to run for
|
|
// specific types.
|
|
// 0 is ignored by the GC, and is used for all non-GC'd pointers.
|
|
// 1 is for opaque GC'd boxes.
|
|
// >= 2 are for specific types (e.g. resources).
|
|
pub const default_addrspace: addrspace = 0;
|
|
pub const gc_box_addrspace: addrspace = 1;
|
|
|
|
pub type addrspace_gen = fn@() -> addrspace;
|
|
pub fn new_addrspace_gen() -> addrspace_gen {
|
|
let i = @mut 1;
|
|
return fn@() -> addrspace { *i += 1; *i };
|
|
}
|
|
|
|
pub struct tydesc_info {
|
|
ty: ty::t,
|
|
tydesc: ValueRef,
|
|
size: ValueRef,
|
|
align: ValueRef,
|
|
addrspace: addrspace,
|
|
take_glue: Option<ValueRef>,
|
|
drop_glue: Option<ValueRef>,
|
|
free_glue: Option<ValueRef>,
|
|
visit_glue: Option<ValueRef>
|
|
}
|
|
|
|
/*
|
|
* A note on nomenclature of linking: "extern", "foreign", and "upcall".
|
|
*
|
|
* An "extern" is an LLVM symbol we wind up emitting an undefined external
|
|
* reference to. This means "we don't have the thing in this compilation unit,
|
|
* please make sure you link it in at runtime". This could be a reference to
|
|
* C code found in a C library, or rust code found in a rust crate.
|
|
*
|
|
* Most "externs" are implicitly declared (automatically) as a result of a
|
|
* user declaring an extern _module_ dependency; this causes the rust driver
|
|
* to locate an extern crate, scan its compilation metadata, and emit extern
|
|
* declarations for any symbols used by the declaring crate.
|
|
*
|
|
* A "foreign" is an extern that references C (or other non-rust ABI) code.
|
|
* There is no metadata to scan for extern references so in these cases either
|
|
* a header-digester like bindgen, or manual function prototypes, have to
|
|
* serve as declarators. So these are usually given explicitly as prototype
|
|
* declarations, in rust code, with ABI attributes on them noting which ABI to
|
|
* link via.
|
|
*
|
|
* An "upcall" is a foreign call generated by the compiler (not corresponding
|
|
* to any user-written call in the code) into the runtime library, to perform
|
|
* some helper task such as bringing a task to life, allocating memory, etc.
|
|
*
|
|
*/
|
|
|
|
pub struct Stats {
|
|
n_static_tydescs: uint,
|
|
n_glues_created: uint,
|
|
n_null_glues: uint,
|
|
n_real_glues: uint,
|
|
n_fns: uint,
|
|
n_monos: uint,
|
|
n_inlines: uint,
|
|
n_closures: uint,
|
|
llvm_insn_ctxt: @mut ~[~str],
|
|
llvm_insns: HashMap<~str, uint>,
|
|
fn_times: @mut ~[{ident: ~str, time: int}]
|
|
}
|
|
|
|
pub struct BuilderRef_res {
|
|
B: BuilderRef,
|
|
drop {
|
|
unsafe {
|
|
llvm::LLVMDisposeBuilder(self.B);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn BuilderRef_res(B: BuilderRef) -> BuilderRef_res {
|
|
BuilderRef_res {
|
|
B: B
|
|
}
|
|
}
|
|
|
|
type ExternMap = HashMap<@str, ValueRef>;
|
|
|
|
// Crate context. Every crate we compile has one of these.
|
|
pub struct crate_ctxt {
|
|
sess: session::Session,
|
|
llmod: ModuleRef,
|
|
td: target_data,
|
|
tn: type_names,
|
|
externs: ExternMap,
|
|
intrinsics: HashMap<~str, ValueRef>,
|
|
item_vals: HashMap<ast::node_id, ValueRef>,
|
|
exp_map2: resolve::ExportMap2,
|
|
reachable: reachable::map,
|
|
item_symbols: HashMap<ast::node_id, ~str>,
|
|
link_meta: link_meta,
|
|
enum_sizes: HashMap<ty::t, uint>,
|
|
discrims: HashMap<ast::def_id, ValueRef>,
|
|
discrim_symbols: HashMap<ast::node_id, ~str>,
|
|
tydescs: HashMap<ty::t, @mut tydesc_info>,
|
|
// Set when running emit_tydescs to enforce that no more tydescs are
|
|
// created.
|
|
mut finished_tydescs: bool,
|
|
// Track mapping of external ids to local items imported for inlining
|
|
external: HashMap<ast::def_id, Option<ast::node_id>>,
|
|
// Cache instances of monomorphized functions
|
|
monomorphized: HashMap<mono_id, ValueRef>,
|
|
monomorphizing: HashMap<ast::def_id, uint>,
|
|
// Cache computed type parameter uses (see type_use.rs)
|
|
type_use_cache: HashMap<ast::def_id, ~[type_use::type_uses]>,
|
|
// Cache generated vtables
|
|
vtables: HashMap<mono_id, ValueRef>,
|
|
// Cache of constant strings,
|
|
const_cstr_cache: HashMap<~str, ValueRef>,
|
|
|
|
// Reverse-direction for const ptrs cast from globals.
|
|
// Key is an int, cast from a ValueRef holding a *T,
|
|
// Val is a ValueRef holding a *[T].
|
|
//
|
|
// Needed because LLVM loses pointer->pointee association
|
|
// when we ptrcast, and we have to ptrcast during translation
|
|
// of a [T] const because we form a slice, a [*T,int] pair, not
|
|
// a pointer to an LLVM array type.
|
|
const_globals: HashMap<int, ValueRef>,
|
|
|
|
// Cache of emitted const values
|
|
const_values: HashMap<ast::node_id, ValueRef>,
|
|
module_data: HashMap<~str, ValueRef>,
|
|
lltypes: HashMap<ty::t, TypeRef>,
|
|
llsizingtypes: HashMap<ty::t, TypeRef>,
|
|
names: namegen,
|
|
next_addrspace: addrspace_gen,
|
|
symbol_hasher: @hash::State,
|
|
type_hashcodes: HashMap<ty::t, @str>,
|
|
type_short_names: HashMap<ty::t, ~str>,
|
|
all_llvm_symbols: Set<~str>,
|
|
tcx: ty::ctxt,
|
|
maps: astencode::Maps,
|
|
stats: @mut Stats,
|
|
upcalls: @upcall::upcalls,
|
|
tydesc_type: TypeRef,
|
|
int_type: TypeRef,
|
|
float_type: TypeRef,
|
|
task_type: TypeRef,
|
|
opaque_vec_type: TypeRef,
|
|
builder: BuilderRef_res,
|
|
shape_cx: shape::Ctxt,
|
|
crate_map: ValueRef,
|
|
// Set when at least one function uses GC. Needed so that
|
|
// decl_gc_metadata knows whether to link to the module metadata, which
|
|
// is not emitted by LLVM's GC pass when no functions use GC.
|
|
mut uses_gc: bool,
|
|
dbg_cx: Option<debuginfo::debug_ctxt>,
|
|
mut do_not_commit_warning_issued: bool
|
|
}
|
|
|
|
// Types used for llself.
|
|
pub struct ValSelfData {
|
|
v: ValueRef,
|
|
t: ty::t,
|
|
is_owned: bool
|
|
}
|
|
|
|
pub enum local_val { local_mem(ValueRef), local_imm(ValueRef), }
|
|
|
|
// Here `self_ty` is the real type of the self parameter to this method. It
|
|
// will only be set in the case of default methods.
|
|
pub struct param_substs {
|
|
tys: ~[ty::t],
|
|
vtables: Option<typeck::vtable_res>,
|
|
bounds: @~[ty::param_bounds],
|
|
self_ty: Option<ty::t>
|
|
}
|
|
|
|
pub fn param_substs_to_str(tcx: ty::ctxt, substs: ¶m_substs) -> ~str {
|
|
fmt!("param_substs {tys:%?, vtables:%?, bounds:%?}",
|
|
substs.tys.map(|t| ty_to_str(tcx, *t)),
|
|
substs.vtables.map(|vs| vs.map(|v| v.to_str(tcx))),
|
|
substs.bounds.map(|b| ty::param_bounds_to_str(tcx, *b)))
|
|
}
|
|
|
|
// Function context. Every LLVM function we create will have one of
|
|
// these.
|
|
pub struct fn_ctxt_ {
|
|
// The ValueRef returned from a call to llvm::LLVMAddFunction; the
|
|
// address of the first instruction in the sequence of
|
|
// instructions for this function that will go in the .text
|
|
// section of the executable we're generating.
|
|
llfn: ValueRef,
|
|
|
|
// The two implicit arguments that arrive in the function we're creating.
|
|
// For instance, foo(int, int) is really foo(ret*, env*, int, int).
|
|
llenv: ValueRef,
|
|
llretptr: ValueRef,
|
|
|
|
// These elements: "hoisted basic blocks" containing
|
|
// administrative activities that have to happen in only one place in
|
|
// the function, due to LLVM's quirks.
|
|
// A block for all the function's static allocas, so that LLVM
|
|
// will coalesce them into a single alloca call.
|
|
mut llstaticallocas: BasicBlockRef,
|
|
// A block containing code that copies incoming arguments to space
|
|
// already allocated by code in one of the llallocas blocks.
|
|
// (LLVM requires that arguments be copied to local allocas before
|
|
// allowing most any operation to be performed on them.)
|
|
mut llloadenv: Option<BasicBlockRef>,
|
|
mut llreturn: BasicBlockRef,
|
|
// The 'self' value currently in use in this function, if there
|
|
// is one.
|
|
//
|
|
// NB: This is the type of the self *variable*, not the self *type*. The
|
|
// self type is set only for default methods, while the self variable is
|
|
// set for all methods.
|
|
mut llself: Option<ValSelfData>,
|
|
// The a value alloca'd for calls to upcalls.rust_personality. Used when
|
|
// outputting the resume instruction.
|
|
mut personality: Option<ValueRef>,
|
|
// If this is a for-loop body that returns, this holds the pointers needed
|
|
// for that
|
|
mut loop_ret: Option<{flagptr: ValueRef, retptr: ValueRef}>,
|
|
|
|
// Maps arguments to allocas created for them in llallocas.
|
|
llargs: HashMap<ast::node_id, local_val>,
|
|
// Maps the def_ids for local variables to the allocas created for
|
|
// them in llallocas.
|
|
lllocals: HashMap<ast::node_id, local_val>,
|
|
// Same as above, but for closure upvars
|
|
llupvars: HashMap<ast::node_id, ValueRef>,
|
|
|
|
// The node_id of the function, or -1 if it doesn't correspond to
|
|
// a user-defined function.
|
|
id: ast::node_id,
|
|
|
|
// The def_id of the impl we're inside, or None if we aren't inside one.
|
|
impl_id: Option<ast::def_id>,
|
|
|
|
// If this function is being monomorphized, this contains the type
|
|
// substitutions used.
|
|
param_substs: Option<param_substs>,
|
|
|
|
// The source span and nesting context where this function comes from, for
|
|
// error reporting and symbol generation.
|
|
span: Option<span>,
|
|
path: path,
|
|
|
|
// This function's enclosing crate context.
|
|
ccx: @crate_ctxt
|
|
}
|
|
|
|
pub type fn_ctxt = @fn_ctxt_;
|
|
|
|
pub fn warn_not_to_commit(ccx: @crate_ctxt, msg: ~str) {
|
|
if !ccx.do_not_commit_warning_issued {
|
|
ccx.do_not_commit_warning_issued = true;
|
|
ccx.sess.warn(msg + ~" -- do not commit like this!");
|
|
}
|
|
}
|
|
|
|
// Heap selectors. Indicate which heap something should go on.
|
|
pub enum heap {
|
|
heap_shared,
|
|
heap_exchange,
|
|
}
|
|
|
|
#[deriving_eq]
|
|
pub enum cleantype {
|
|
normal_exit_only,
|
|
normal_exit_and_unwind
|
|
}
|
|
|
|
pub enum cleanup {
|
|
clean(fn@(block) -> block, cleantype),
|
|
clean_temp(ValueRef, fn@(block) -> block, cleantype),
|
|
}
|
|
|
|
// Used to remember and reuse existing cleanup paths
|
|
// target: none means the path ends in an resume instruction
|
|
pub type cleanup_path = {target: Option<BasicBlockRef>, dest: BasicBlockRef};
|
|
|
|
pub fn scope_clean_changed(scope_info: scope_info) {
|
|
if scope_info.cleanup_paths.len() > 0u { scope_info.cleanup_paths = ~[]; }
|
|
scope_info.landing_pad = None;
|
|
}
|
|
|
|
pub fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype {
|
|
if ty::type_needs_unwind_cleanup(cx, ty) {
|
|
normal_exit_and_unwind
|
|
} else {
|
|
normal_exit_only
|
|
}
|
|
}
|
|
|
|
// This is not the same as datum::Datum::root(), which is used to keep copies
|
|
// of @ values live for as long as a borrowed pointer to the interior exists.
|
|
// In the new GC, we can identify immediates on the stack without difficulty,
|
|
// but have trouble knowing where non-immediates are on the stack. For
|
|
// non-immediates, we must add an additional level of indirection, which
|
|
// allows us to alloca a pointer with the right addrspace.
|
|
pub fn root_for_cleanup(bcx: block, v: ValueRef, t: ty::t)
|
|
-> {root: ValueRef, rooted: bool} {
|
|
let ccx = bcx.ccx();
|
|
|
|
let addrspace = base::get_tydesc(ccx, t).addrspace;
|
|
if addrspace > gc_box_addrspace {
|
|
let llty = type_of::type_of_rooted(ccx, t);
|
|
let root = base::alloca(bcx, llty);
|
|
build::Store(bcx, build::PointerCast(bcx, v, llty), root);
|
|
{root: root, rooted: true}
|
|
} else {
|
|
{root: v, rooted: false}
|
|
}
|
|
}
|
|
|
|
pub fn add_clean(bcx: block, val: ValueRef, t: ty::t) {
|
|
if !ty::type_needs_drop(bcx.tcx(), t) { return; }
|
|
debug!("add_clean(%s, %s, %s)",
|
|
bcx.to_str(), val_str(bcx.ccx().tn, val),
|
|
ty_to_str(bcx.ccx().tcx, t));
|
|
let {root, rooted} = root_for_cleanup(bcx, val, t);
|
|
let cleanup_type = cleanup_type(bcx.tcx(), t);
|
|
do in_scope_cx(bcx) |scope_info| {
|
|
scope_info.cleanups.push(
|
|
clean(|a| glue::drop_ty_root(a, root, rooted, t),
|
|
cleanup_type));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
|
|
pub fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) {
|
|
if !ty::type_needs_drop(cx.tcx(), ty) { return; }
|
|
debug!("add_clean_temp_immediate(%s, %s, %s)",
|
|
cx.to_str(), val_str(cx.ccx().tn, val),
|
|
ty_to_str(cx.ccx().tcx, ty));
|
|
let cleanup_type = cleanup_type(cx.tcx(), ty);
|
|
do in_scope_cx(cx) |scope_info| {
|
|
scope_info.cleanups.push(
|
|
clean_temp(val, |a| glue::drop_ty_immediate(a, val, ty),
|
|
cleanup_type));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
|
|
if !ty::type_needs_drop(bcx.tcx(), t) { return; }
|
|
debug!("add_clean_temp_mem(%s, %s, %s)",
|
|
bcx.to_str(), val_str(bcx.ccx().tn, val),
|
|
ty_to_str(bcx.ccx().tcx, t));
|
|
let {root, rooted} = root_for_cleanup(bcx, val, t);
|
|
let cleanup_type = cleanup_type(bcx.tcx(), t);
|
|
do in_scope_cx(bcx) |scope_info| {
|
|
scope_info.cleanups.push(
|
|
clean_temp(val, |a| glue::drop_ty_root(a, root, rooted, t),
|
|
cleanup_type));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
pub fn add_clean_frozen_root(bcx: block, val: ValueRef, t: ty::t) {
|
|
debug!("add_clean_frozen_root(%s, %s, %s)",
|
|
bcx.to_str(), val_str(bcx.ccx().tn, val),
|
|
ty_to_str(bcx.ccx().tcx, t));
|
|
let {root, rooted} = root_for_cleanup(bcx, val, t);
|
|
let cleanup_type = cleanup_type(bcx.tcx(), t);
|
|
do in_scope_cx(bcx) |scope_info| {
|
|
scope_info.cleanups.push(
|
|
clean_temp(val, |bcx| {
|
|
let bcx = callee::trans_rtcall_or_lang_call(
|
|
bcx,
|
|
bcx.tcx().lang_items.return_to_mut_fn(),
|
|
~[
|
|
build::Load(bcx,
|
|
build::PointerCast(bcx,
|
|
root,
|
|
T_ptr(T_ptr(T_i8()))))
|
|
],
|
|
expr::Ignore
|
|
);
|
|
glue::drop_ty_root(bcx, root, rooted, t)
|
|
}, cleanup_type));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
pub fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
|
|
let free_fn = match heap {
|
|
heap_shared => {
|
|
let f: @fn(block) -> block = |a| glue::trans_free(a, ptr);
|
|
f
|
|
}
|
|
heap_exchange => {
|
|
let f: @fn(block) -> block = |a| glue::trans_unique_free(a, ptr);
|
|
f
|
|
}
|
|
};
|
|
do in_scope_cx(cx) |scope_info| {
|
|
scope_info.cleanups.push(clean_temp(ptr, free_fn,
|
|
normal_exit_and_unwind));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
|
|
// Note that this only works for temporaries. We should, at some point, move
|
|
// to a system where we can also cancel the cleanup on local variables, but
|
|
// this will be more involved. For now, we simply zero out the local, and the
|
|
// drop glue checks whether it is zero.
|
|
pub fn revoke_clean(cx: block, val: ValueRef) {
|
|
do in_scope_cx(cx) |scope_info| {
|
|
let cleanup_pos = vec::position(
|
|
scope_info.cleanups,
|
|
|cu| match *cu {
|
|
clean_temp(v, _, _) if v == val => true,
|
|
_ => false
|
|
});
|
|
for cleanup_pos.each |i| {
|
|
scope_info.cleanups =
|
|
vec::append(vec::slice(scope_info.cleanups, 0u, *i).to_vec(),
|
|
vec::slice(scope_info.cleanups,
|
|
*i + 1u,
|
|
scope_info.cleanups.len()));
|
|
scope_clean_changed(scope_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn block_cleanups(bcx: block) -> ~[cleanup] {
|
|
match bcx.kind {
|
|
block_non_scope => ~[],
|
|
block_scope(ref inf) => /*bad*/copy inf.cleanups
|
|
}
|
|
}
|
|
|
|
pub enum block_kind {
|
|
// A scope at the end of which temporary values created inside of it are
|
|
// cleaned up. May correspond to an actual block in the language, but also
|
|
// to an implicit scope, for example, calls introduce an implicit scope in
|
|
// which the arguments are evaluated and cleaned up.
|
|
block_scope(scope_info),
|
|
|
|
// A non-scope block is a basic block created as a translation artifact
|
|
// from translating code that expresses conditional logic rather than by
|
|
// explicit { ... } block structure in the source language. It's called a
|
|
// non-scope block because it doesn't introduce a new variable scope.
|
|
block_non_scope,
|
|
}
|
|
|
|
pub struct scope_info {
|
|
loop_break: Option<block>,
|
|
loop_label: Option<ident>,
|
|
// A list of functions that must be run at when leaving this
|
|
// block, cleaning up any variables that were introduced in the
|
|
// block.
|
|
mut cleanups: ~[cleanup],
|
|
// Existing cleanup paths that may be reused, indexed by destination and
|
|
// cleared when the set of cleanups changes.
|
|
mut cleanup_paths: ~[cleanup_path],
|
|
// Unwinding landing pad. Also cleared when cleanups change.
|
|
mut landing_pad: Option<BasicBlockRef>,
|
|
}
|
|
|
|
pub trait get_node_info {
|
|
fn info() -> Option<node_info>;
|
|
}
|
|
|
|
pub impl get_node_info for @ast::expr {
|
|
fn info() -> Option<node_info> {
|
|
Some({id: self.id, span: self.span})
|
|
}
|
|
}
|
|
|
|
pub impl get_node_info for ast::blk {
|
|
fn info() -> Option<node_info> {
|
|
Some({id: self.node.id, span: self.span})
|
|
}
|
|
}
|
|
|
|
// XXX: Work around a trait parsing bug. remove after snapshot
|
|
pub type optional_boxed_ast_expr = Option<@ast::expr>;
|
|
|
|
pub impl get_node_info for optional_boxed_ast_expr {
|
|
fn info() -> Option<node_info> {
|
|
self.chain_ref(|s| s.info())
|
|
}
|
|
}
|
|
|
|
pub type node_info = {
|
|
id: ast::node_id,
|
|
span: span
|
|
};
|
|
|
|
// Basic block context. We create a block context for each basic block
|
|
// (single-entry, single-exit sequence of instructions) we generate from Rust
|
|
// code. Each basic block we generate is attached to a function, typically
|
|
// with many basic blocks per function. All the basic blocks attached to a
|
|
// function are organized as a directed graph.
|
|
pub struct block_ {
|
|
// The BasicBlockRef returned from a call to
|
|
// llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic
|
|
// block to the function pointed to by llfn. We insert
|
|
// instructions into that block by way of this block context.
|
|
// The block pointing to this one in the function's digraph.
|
|
llbb: BasicBlockRef,
|
|
mut terminated: bool,
|
|
mut unreachable: bool,
|
|
parent: Option<block>,
|
|
// The 'kind' of basic block this is.
|
|
kind: block_kind,
|
|
// Is this block part of a landing pad?
|
|
is_lpad: bool,
|
|
// info about the AST node this block originated from, if any
|
|
node_info: Option<node_info>,
|
|
// The function context for the function to which this block is
|
|
// attached.
|
|
fcx: fn_ctxt
|
|
}
|
|
|
|
pub fn block_(llbb: BasicBlockRef, parent: Option<block>, -kind: block_kind,
|
|
is_lpad: bool, node_info: Option<node_info>, fcx: fn_ctxt)
|
|
-> block_ {
|
|
|
|
block_ {
|
|
llbb: llbb,
|
|
terminated: false,
|
|
unreachable: false,
|
|
parent: parent,
|
|
kind: kind,
|
|
is_lpad: is_lpad,
|
|
node_info: node_info,
|
|
fcx: fcx
|
|
}
|
|
}
|
|
|
|
/* This must be enum and not type, or trans goes into an infinite loop (#2572)
|
|
*/
|
|
pub enum block = @block_;
|
|
|
|
pub fn mk_block(llbb: BasicBlockRef, parent: Option<block>, -kind: block_kind,
|
|
is_lpad: bool, node_info: Option<node_info>, fcx: fn_ctxt)
|
|
-> block {
|
|
block(@block_(llbb, parent, kind, is_lpad, node_info, fcx))
|
|
}
|
|
|
|
// First two args are retptr, env
|
|
pub const first_real_arg: uint = 2u;
|
|
|
|
pub struct Result {
|
|
bcx: block,
|
|
val: ValueRef
|
|
}
|
|
|
|
pub fn rslt(bcx: block, val: ValueRef) -> Result {
|
|
Result {bcx: bcx, val: val}
|
|
}
|
|
|
|
pub impl Result {
|
|
fn unpack(bcx: &mut block) -> ValueRef {
|
|
*bcx = self.bcx;
|
|
return self.val;
|
|
}
|
|
}
|
|
|
|
pub fn ty_str(tn: type_names, t: TypeRef) -> @str {
|
|
return lib::llvm::type_to_str(tn, t);
|
|
}
|
|
|
|
pub fn val_ty(v: ValueRef) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMTypeOf(v);
|
|
}
|
|
}
|
|
|
|
pub fn val_str(tn: type_names, v: ValueRef) -> @str {
|
|
return ty_str(tn, val_ty(v));
|
|
}
|
|
|
|
// Returns the nth element of the given LLVM structure type.
|
|
pub fn struct_elt(llstructty: TypeRef, n: uint) -> TypeRef {
|
|
unsafe {
|
|
let elt_count = llvm::LLVMCountStructElementTypes(llstructty) as uint;
|
|
assert (n < elt_count);
|
|
let mut elt_tys = vec::from_elem(elt_count, T_nil());
|
|
llvm::LLVMGetStructElementTypes(
|
|
llstructty,
|
|
ptr::to_mut_unsafe_ptr(&mut elt_tys[0]));
|
|
return llvm::LLVMGetElementType(elt_tys[n]);
|
|
}
|
|
}
|
|
|
|
pub fn in_scope_cx(cx: block, f: fn(scope_info)) {
|
|
let mut cur = cx;
|
|
loop {
|
|
match cur.kind {
|
|
block_scope(ref inf) => {
|
|
debug!("in_scope_cx: selected cur=%s (cx=%s)",
|
|
cur.to_str(), cx.to_str());
|
|
f((*inf));
|
|
return;
|
|
}
|
|
_ => ()
|
|
}
|
|
cur = block_parent(cur);
|
|
}
|
|
}
|
|
|
|
pub fn block_parent(cx: block) -> block {
|
|
match cx.parent {
|
|
Some(b) => b,
|
|
None => cx.sess().bug(fmt!("block_parent called on root block %?",
|
|
cx))
|
|
}
|
|
}
|
|
|
|
// Accessors
|
|
|
|
pub impl block {
|
|
pure fn ccx() -> @crate_ctxt { self.fcx.ccx }
|
|
pure fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
|
|
pure fn sess() -> Session { self.fcx.ccx.sess }
|
|
|
|
fn node_id_to_str(id: ast::node_id) -> ~str {
|
|
ast_map::node_id_to_str(self.tcx().items, id, self.sess().intr())
|
|
}
|
|
|
|
fn expr_to_str(e: @ast::expr) -> ~str {
|
|
expr_repr(self.tcx(), e)
|
|
}
|
|
|
|
fn expr_is_lval(e: @ast::expr) -> bool {
|
|
ty::expr_is_lval(self.tcx(), self.ccx().maps.method_map, e)
|
|
}
|
|
|
|
fn expr_kind(e: @ast::expr) -> ty::ExprKind {
|
|
ty::expr_kind(self.tcx(), self.ccx().maps.method_map, e)
|
|
}
|
|
|
|
fn def(nid: ast::node_id) -> ast::def {
|
|
match self.tcx().def_map.find(&nid) {
|
|
Some(v) => v,
|
|
None => {
|
|
self.tcx().sess.bug(fmt!(
|
|
"No def associated with node id %?", nid));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn val_str(val: ValueRef) -> @str {
|
|
val_str(self.ccx().tn, val)
|
|
}
|
|
|
|
fn llty_str(llty: TypeRef) -> @str {
|
|
ty_str(self.ccx().tn, llty)
|
|
}
|
|
|
|
fn ty_to_str(t: ty::t) -> ~str {
|
|
ty_to_str(self.tcx(), t)
|
|
}
|
|
fn to_str(&self) -> ~str {
|
|
match self.node_info {
|
|
Some(node_info) => {
|
|
fmt!("[block %d]", node_info.id)
|
|
}
|
|
None => {
|
|
fmt!("[block %x]", ptr::addr_of(&(*self)) as uint)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// LLVM type constructors.
|
|
pub fn T_void() -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMVoidType();
|
|
}
|
|
}
|
|
|
|
pub fn T_nil() -> TypeRef {
|
|
return T_struct(~[])
|
|
}
|
|
|
|
pub fn T_metadata() -> TypeRef { unsafe { return llvm::LLVMMetadataType(); } }
|
|
|
|
pub fn T_i1() -> TypeRef { unsafe { return llvm::LLVMInt1Type(); } }
|
|
|
|
pub fn T_i8() -> TypeRef { unsafe { return llvm::LLVMInt8Type(); } }
|
|
|
|
pub fn T_i16() -> TypeRef { unsafe { return llvm::LLVMInt16Type(); } }
|
|
|
|
pub fn T_i32() -> TypeRef { unsafe { return llvm::LLVMInt32Type(); } }
|
|
|
|
pub fn T_i64() -> TypeRef { unsafe { return llvm::LLVMInt64Type(); } }
|
|
|
|
pub fn T_f32() -> TypeRef { unsafe { return llvm::LLVMFloatType(); } }
|
|
|
|
pub fn T_f64() -> TypeRef { unsafe { return llvm::LLVMDoubleType(); } }
|
|
|
|
pub fn T_bool() -> TypeRef { return T_i8(); }
|
|
|
|
pub fn T_int(targ_cfg: @session::config) -> TypeRef {
|
|
return match targ_cfg.arch {
|
|
session::arch_x86 => T_i32(),
|
|
session::arch_x86_64 => T_i64(),
|
|
session::arch_arm => T_i32()
|
|
};
|
|
}
|
|
|
|
pub fn T_int_ty(cx: @crate_ctxt, t: ast::int_ty) -> TypeRef {
|
|
match t {
|
|
ast::ty_i => cx.int_type,
|
|
ast::ty_char => T_char(),
|
|
ast::ty_i8 => T_i8(),
|
|
ast::ty_i16 => T_i16(),
|
|
ast::ty_i32 => T_i32(),
|
|
ast::ty_i64 => T_i64()
|
|
}
|
|
}
|
|
|
|
pub fn T_uint_ty(cx: @crate_ctxt, t: ast::uint_ty) -> TypeRef {
|
|
match t {
|
|
ast::ty_u => cx.int_type,
|
|
ast::ty_u8 => T_i8(),
|
|
ast::ty_u16 => T_i16(),
|
|
ast::ty_u32 => T_i32(),
|
|
ast::ty_u64 => T_i64()
|
|
}
|
|
}
|
|
|
|
pub fn T_float_ty(cx: @crate_ctxt, t: ast::float_ty) -> TypeRef {
|
|
match t {
|
|
ast::ty_f => cx.float_type,
|
|
ast::ty_f32 => T_f32(),
|
|
ast::ty_f64 => T_f64()
|
|
}
|
|
}
|
|
|
|
pub fn T_float(targ_cfg: @session::config) -> TypeRef {
|
|
return match targ_cfg.arch {
|
|
session::arch_x86 => T_f64(),
|
|
session::arch_x86_64 => T_f64(),
|
|
session::arch_arm => T_f64()
|
|
};
|
|
}
|
|
|
|
pub fn T_char() -> TypeRef { return T_i32(); }
|
|
|
|
pub fn T_size_t(targ_cfg: @session::config) -> TypeRef {
|
|
return T_int(targ_cfg);
|
|
}
|
|
|
|
pub fn T_fn(inputs: ~[TypeRef], output: TypeRef) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMFunctionType(output, to_ptr(inputs),
|
|
inputs.len() as c_uint,
|
|
False);
|
|
}
|
|
}
|
|
|
|
pub fn T_fn_pair(cx: @crate_ctxt, tfn: TypeRef) -> TypeRef {
|
|
return T_struct(~[T_ptr(tfn), T_opaque_cbox_ptr(cx)]);
|
|
}
|
|
|
|
pub fn T_ptr(t: TypeRef) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMPointerType(t, default_addrspace);
|
|
}
|
|
}
|
|
|
|
pub fn T_root(t: TypeRef, addrspace: addrspace) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMPointerType(t, addrspace);
|
|
}
|
|
}
|
|
|
|
pub fn T_struct(elts: ~[TypeRef]) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMStructType(to_ptr(elts),
|
|
elts.len() as c_uint,
|
|
False);
|
|
}
|
|
}
|
|
|
|
pub fn T_named_struct(name: ~str) -> TypeRef {
|
|
unsafe {
|
|
let c = llvm::LLVMGetGlobalContext();
|
|
return str::as_c_str(name, |buf| llvm::LLVMStructCreateNamed(c, buf));
|
|
}
|
|
}
|
|
|
|
pub fn set_struct_body(t: TypeRef, elts: ~[TypeRef]) {
|
|
unsafe {
|
|
llvm::LLVMStructSetBody(t,
|
|
to_ptr(elts),
|
|
elts.len() as c_uint,
|
|
False);
|
|
}
|
|
}
|
|
|
|
pub fn T_empty_struct() -> TypeRef { return T_struct(~[]); }
|
|
|
|
// A vtable is, in reality, a vtable pointer followed by zero or more pointers
|
|
// to tydescs and other vtables that it closes over. But the types and number
|
|
// of those are rarely known to the code that needs to manipulate them, so
|
|
// they are described by this opaque type.
|
|
pub fn T_vtable() -> TypeRef { T_array(T_ptr(T_i8()), 1u) }
|
|
|
|
pub fn T_task(targ_cfg: @session::config) -> TypeRef {
|
|
let t = T_named_struct(~"task");
|
|
|
|
// Refcount
|
|
// Delegate pointer
|
|
// Stack segment pointer
|
|
// Runtime SP
|
|
// Rust SP
|
|
// GC chain
|
|
|
|
|
|
// Domain pointer
|
|
// Crate cache pointer
|
|
|
|
let t_int = T_int(targ_cfg);
|
|
let elems =
|
|
~[t_int, t_int, t_int, t_int,
|
|
t_int, t_int, t_int, t_int];
|
|
set_struct_body(t, elems);
|
|
return t;
|
|
}
|
|
|
|
pub fn T_tydesc_field(cx: @crate_ctxt, field: uint) -> TypeRef {
|
|
// Bit of a kludge: pick the fn typeref out of the tydesc..
|
|
|
|
unsafe {
|
|
let mut tydesc_elts: ~[TypeRef] =
|
|
vec::from_elem::<TypeRef>(abi::n_tydesc_fields,
|
|
T_nil());
|
|
llvm::LLVMGetStructElementTypes(
|
|
cx.tydesc_type,
|
|
ptr::to_mut_unsafe_ptr(&mut tydesc_elts[0]));
|
|
let t = llvm::LLVMGetElementType(tydesc_elts[field]);
|
|
return t;
|
|
}
|
|
}
|
|
|
|
pub fn T_generic_glue_fn(cx: @crate_ctxt) -> TypeRef {
|
|
let s = @"glue_fn";
|
|
match name_has_type(cx.tn, s) {
|
|
Some(t) => return t,
|
|
_ => ()
|
|
}
|
|
let t = T_tydesc_field(cx, abi::tydesc_field_drop_glue);
|
|
associate_type(cx.tn, s, t);
|
|
return t;
|
|
}
|
|
|
|
pub fn T_tydesc(targ_cfg: @session::config) -> TypeRef {
|
|
let tydesc = T_named_struct(~"tydesc");
|
|
let tydescpp = T_ptr(T_ptr(tydesc));
|
|
let pvoid = T_ptr(T_i8());
|
|
let glue_fn_ty =
|
|
T_ptr(T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp,
|
|
pvoid], T_void()));
|
|
|
|
let int_type = T_int(targ_cfg);
|
|
let elems =
|
|
~[int_type, int_type,
|
|
glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty,
|
|
T_ptr(T_i8()), T_ptr(T_i8())];
|
|
set_struct_body(tydesc, elems);
|
|
return tydesc;
|
|
}
|
|
|
|
pub fn T_array(t: TypeRef, n: uint) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMArrayType(t, n as c_uint);
|
|
}
|
|
}
|
|
|
|
// Interior vector.
|
|
pub fn T_vec2(targ_cfg: @session::config, t: TypeRef) -> TypeRef {
|
|
return T_struct(~[T_int(targ_cfg), // fill
|
|
T_int(targ_cfg), // alloc
|
|
T_array(t, 0u)]); // elements
|
|
}
|
|
|
|
pub fn T_vec(ccx: @crate_ctxt, t: TypeRef) -> TypeRef {
|
|
return T_vec2(ccx.sess.targ_cfg, t);
|
|
}
|
|
|
|
// Note that the size of this one is in bytes.
|
|
pub fn T_opaque_vec(targ_cfg: @session::config) -> TypeRef {
|
|
return T_vec2(targ_cfg, T_i8());
|
|
}
|
|
|
|
// Let T be the content of a box @T. tuplify_box_ty(t) returns the
|
|
// representation of @T as a tuple (i.e., the ty::t version of what T_box()
|
|
// returns).
|
|
pub fn tuplify_box_ty(tcx: ty::ctxt, t: ty::t) -> ty::t {
|
|
let ptr = ty::mk_ptr(
|
|
tcx,
|
|
ty::mt {ty: ty::mk_nil(tcx), mutbl: ast::m_imm}
|
|
);
|
|
return ty::mk_tup(tcx, ~[ty::mk_uint(tcx), ty::mk_type(tcx),
|
|
ptr, ptr,
|
|
t]);
|
|
}
|
|
|
|
pub fn T_box_header_fields(cx: @crate_ctxt) -> ~[TypeRef] {
|
|
let ptr = T_ptr(T_i8());
|
|
return ~[cx.int_type, T_ptr(cx.tydesc_type), ptr, ptr];
|
|
}
|
|
|
|
pub fn T_box_header(cx: @crate_ctxt) -> TypeRef {
|
|
return T_struct(T_box_header_fields(cx));
|
|
}
|
|
|
|
pub fn T_box(cx: @crate_ctxt, t: TypeRef) -> TypeRef {
|
|
return T_struct(vec::append(T_box_header_fields(cx), ~[t]));
|
|
}
|
|
|
|
pub fn T_box_ptr(t: TypeRef) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMPointerType(t, gc_box_addrspace);
|
|
}
|
|
}
|
|
|
|
pub fn T_opaque_box(cx: @crate_ctxt) -> TypeRef {
|
|
return T_box(cx, T_i8());
|
|
}
|
|
|
|
pub fn T_opaque_box_ptr(cx: @crate_ctxt) -> TypeRef {
|
|
return T_box_ptr(T_opaque_box(cx));
|
|
}
|
|
|
|
pub fn T_unique(cx: @crate_ctxt, t: TypeRef) -> TypeRef {
|
|
return T_struct(vec::append(T_box_header_fields(cx), ~[t]));
|
|
}
|
|
|
|
pub fn T_unique_ptr(t: TypeRef) -> TypeRef {
|
|
unsafe {
|
|
return llvm::LLVMPointerType(t, gc_box_addrspace);
|
|
}
|
|
}
|
|
|
|
pub fn T_port(cx: @crate_ctxt, _t: TypeRef) -> TypeRef {
|
|
return T_struct(~[cx.int_type]); // Refcount
|
|
|
|
}
|
|
|
|
pub fn T_chan(cx: @crate_ctxt, _t: TypeRef) -> TypeRef {
|
|
return T_struct(~[cx.int_type]); // Refcount
|
|
|
|
}
|
|
|
|
pub fn T_taskptr(cx: @crate_ctxt) -> TypeRef { return T_ptr(cx.task_type); }
|
|
|
|
|
|
// This type must never be used directly; it must always be cast away.
|
|
pub fn T_typaram(tn: type_names) -> TypeRef {
|
|
let s = @"typaram";
|
|
match name_has_type(tn, s) {
|
|
Some(t) => return t,
|
|
_ => ()
|
|
}
|
|
let t = T_i8();
|
|
associate_type(tn, s, t);
|
|
return t;
|
|
}
|
|
|
|
pub fn T_typaram_ptr(tn: type_names) -> TypeRef {
|
|
return T_ptr(T_typaram(tn));
|
|
}
|
|
|
|
pub fn T_opaque_cbox_ptr(cx: @crate_ctxt) -> TypeRef {
|
|
// closures look like boxes (even when they are fn~ or fn&)
|
|
// see trans_closure.rs
|
|
return T_opaque_box_ptr(cx);
|
|
}
|
|
|
|
pub fn T_enum_discrim(cx: @crate_ctxt) -> TypeRef {
|
|
return cx.int_type;
|
|
}
|
|
|
|
pub fn T_opaque_enum(cx: @crate_ctxt) -> TypeRef {
|
|
let s = @"opaque_enum";
|
|
match name_has_type(cx.tn, s) {
|
|
Some(t) => return t,
|
|
_ => ()
|
|
}
|
|
let t = T_struct(~[T_enum_discrim(cx), T_i8()]);
|
|
associate_type(cx.tn, s, t);
|
|
return t;
|
|
}
|
|
|
|
pub fn T_opaque_enum_ptr(cx: @crate_ctxt) -> TypeRef {
|
|
return T_ptr(T_opaque_enum(cx));
|
|
}
|
|
|
|
pub fn T_captured_tydescs(cx: @crate_ctxt, n: uint) -> TypeRef {
|
|
return T_struct(vec::from_elem::<TypeRef>(n, T_ptr(cx.tydesc_type)));
|
|
}
|
|
|
|
pub fn T_opaque_trait(cx: @crate_ctxt, vstore: ty::vstore) -> TypeRef {
|
|
match vstore {
|
|
ty::vstore_box => {
|
|
T_struct(~[T_ptr(cx.tydesc_type), T_opaque_box_ptr(cx)])
|
|
}
|
|
ty::vstore_uniq => {
|
|
T_struct(~[T_ptr(cx.tydesc_type),
|
|
T_unique_ptr(T_unique(cx, T_i8())),
|
|
T_ptr(cx.tydesc_type)])
|
|
}
|
|
_ => T_struct(~[T_ptr(cx.tydesc_type), T_ptr(T_i8())])
|
|
}
|
|
}
|
|
|
|
pub fn T_opaque_port_ptr() -> TypeRef { return T_ptr(T_i8()); }
|
|
|
|
pub fn T_opaque_chan_ptr() -> TypeRef { return T_ptr(T_i8()); }
|
|
|
|
|
|
// LLVM constant constructors.
|
|
pub fn C_null(t: TypeRef) -> ValueRef {
|
|
unsafe {
|
|
return llvm::LLVMConstNull(t);
|
|
}
|
|
}
|
|
|
|
pub fn C_integral(t: TypeRef, u: u64, sign_extend: Bool) -> ValueRef {
|
|
unsafe {
|
|
return llvm::LLVMConstInt(t, u, sign_extend);
|
|
}
|
|
}
|
|
|
|
pub fn C_floating(s: ~str, t: TypeRef) -> ValueRef {
|
|
unsafe {
|
|
return str::as_c_str(s, |buf| llvm::LLVMConstRealOfString(t, buf));
|
|
}
|
|
}
|
|
|
|
pub fn C_nil() -> ValueRef {
|
|
return C_struct(~[]);
|
|
}
|
|
|
|
pub fn C_bool(b: bool) -> ValueRef {
|
|
C_integral(T_bool(), if b { 1u64 } else { 0u64 }, False)
|
|
}
|
|
|
|
pub fn C_i1(b: bool) -> ValueRef {
|
|
return C_integral(T_i1(), if b { 1 } else { 0 }, False);
|
|
}
|
|
|
|
pub fn C_i32(i: i32) -> ValueRef {
|
|
return C_integral(T_i32(), i as u64, True);
|
|
}
|
|
|
|
pub fn C_i64(i: i64) -> ValueRef {
|
|
return C_integral(T_i64(), i as u64, True);
|
|
}
|
|
|
|
pub fn C_int(cx: @crate_ctxt, i: int) -> ValueRef {
|
|
return C_integral(cx.int_type, i as u64, True);
|
|
}
|
|
|
|
pub fn C_uint(cx: @crate_ctxt, i: uint) -> ValueRef {
|
|
return C_integral(cx.int_type, i as u64, False);
|
|
}
|
|
|
|
pub fn C_u8(i: uint) -> ValueRef {
|
|
return C_integral(T_i8(), i as u64, False);
|
|
}
|
|
|
|
|
|
// This is a 'c-like' raw string, which differs from
|
|
// our boxed-and-length-annotated strings.
|
|
pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef {
|
|
unsafe {
|
|
match cx.const_cstr_cache.find(&s) {
|
|
Some(llval) => return llval,
|
|
None => ()
|
|
}
|
|
|
|
let sc = do str::as_c_str(s) |buf| {
|
|
llvm::LLVMConstString(buf, str::len(s) as c_uint, False)
|
|
};
|
|
let g =
|
|
str::as_c_str(fmt!("str%u", (cx.names)(~"str").repr),
|
|
|buf| llvm::LLVMAddGlobal(cx.llmod, val_ty(sc), buf));
|
|
llvm::LLVMSetInitializer(g, sc);
|
|
llvm::LLVMSetGlobalConstant(g, True);
|
|
lib::llvm::SetLinkage(g, lib::llvm::InternalLinkage);
|
|
|
|
cx.const_cstr_cache.insert(s, g);
|
|
|
|
return g;
|
|
}
|
|
}
|
|
|
|
// NB: Do not use `do_spill_noroot` to make this into a constant string, or
|
|
// you will be kicked off fast isel. See issue #4352 for an example of this.
|
|
pub fn C_estr_slice(cx: @crate_ctxt, +s: ~str) -> ValueRef {
|
|
unsafe {
|
|
let len = str::len(s);
|
|
let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s), T_ptr(T_i8()));
|
|
C_struct(~[cs, C_uint(cx, len + 1u /* +1 for null */)])
|
|
}
|
|
}
|
|
|
|
// Returns a Plain Old LLVM String:
|
|
pub fn C_postr(s: ~str) -> ValueRef {
|
|
unsafe {
|
|
return do str::as_c_str(s) |buf| {
|
|
llvm::LLVMConstString(buf, str::len(s) as c_uint, False)
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn C_zero_byte_arr(size: uint) -> ValueRef {
|
|
unsafe {
|
|
let mut i = 0u;
|
|
let mut elts: ~[ValueRef] = ~[];
|
|
while i < size { elts.push(C_u8(0u)); i += 1u; }
|
|
return llvm::LLVMConstArray(T_i8(),
|
|
vec::raw::to_ptr(elts),
|
|
elts.len() as c_uint);
|
|
}
|
|
}
|
|
|
|
pub fn C_struct(elts: &[ValueRef]) -> ValueRef {
|
|
unsafe {
|
|
do vec::as_imm_buf(elts) |ptr, len| {
|
|
llvm::LLVMConstStruct(ptr, len as c_uint, False)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn C_packed_struct(elts: &[ValueRef]) -> ValueRef {
|
|
unsafe {
|
|
do vec::as_imm_buf(elts) |ptr, len| {
|
|
llvm::LLVMConstStruct(ptr, len as c_uint, True)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn C_named_struct(T: TypeRef, elts: &[ValueRef]) -> ValueRef {
|
|
unsafe {
|
|
do vec::as_imm_buf(elts) |ptr, len| {
|
|
llvm::LLVMConstNamedStruct(T, ptr, len as c_uint)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn C_array(ty: TypeRef, elts: ~[ValueRef]) -> ValueRef {
|
|
unsafe {
|
|
return llvm::LLVMConstArray(ty, vec::raw::to_ptr(elts),
|
|
elts.len() as c_uint);
|
|
}
|
|
}
|
|
|
|
pub fn C_bytes(bytes: ~[u8]) -> ValueRef {
|
|
unsafe {
|
|
return llvm::LLVMConstString(
|
|
cast::reinterpret_cast(&vec::raw::to_ptr(bytes)),
|
|
bytes.len() as c_uint, True);
|
|
}
|
|
}
|
|
|
|
pub fn C_bytes_plus_null(bytes: ~[u8]) -> ValueRef {
|
|
unsafe {
|
|
return llvm::LLVMConstString(
|
|
cast::reinterpret_cast(&vec::raw::to_ptr(bytes)),
|
|
bytes.len() as c_uint, False);
|
|
}
|
|
}
|
|
|
|
pub fn C_shape(ccx: @crate_ctxt, +bytes: ~[u8]) -> ValueRef {
|
|
unsafe {
|
|
let llshape = C_bytes_plus_null(bytes);
|
|
let name = fmt!("shape%u", (ccx.names)(~"shape").repr);
|
|
let llglobal = str::as_c_str(name, |buf| {
|
|
llvm::LLVMAddGlobal(ccx.llmod, val_ty(llshape), buf)
|
|
});
|
|
llvm::LLVMSetInitializer(llglobal, llshape);
|
|
llvm::LLVMSetGlobalConstant(llglobal, True);
|
|
lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage);
|
|
return llvm::LLVMConstPointerCast(llglobal, T_ptr(T_i8()));
|
|
}
|
|
}
|
|
|
|
pub fn get_param(fndecl: ValueRef, param: uint) -> ValueRef {
|
|
unsafe {
|
|
llvm::LLVMGetParam(fndecl, param as c_uint)
|
|
}
|
|
}
|
|
|
|
// Used to identify cached monomorphized functions and vtables
|
|
#[deriving_eq]
|
|
pub enum mono_param_id {
|
|
mono_precise(ty::t, Option<~[mono_id]>),
|
|
mono_any,
|
|
mono_repr(uint /* size */,
|
|
uint /* align */,
|
|
bool /* is_float */,
|
|
datum::DatumMode),
|
|
}
|
|
|
|
#[deriving_eq]
|
|
pub struct mono_id_ {
|
|
def: ast::def_id,
|
|
params: ~[mono_param_id],
|
|
impl_did_opt: Option<ast::def_id>
|
|
}
|
|
|
|
pub type mono_id = @mono_id_;
|
|
|
|
pub impl to_bytes::IterBytes for mono_param_id {
|
|
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
|
match /*bad*/copy *self {
|
|
mono_precise(t, mids) =>
|
|
to_bytes::iter_bytes_3(&0u8, &ty::type_id(t), &mids, lsb0, f),
|
|
|
|
mono_any => 1u8.iter_bytes(lsb0, f),
|
|
|
|
mono_repr(ref a, ref b, ref c, ref d) =>
|
|
to_bytes::iter_bytes_5(&2u8, a, b, c, d, lsb0, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub impl to_bytes::IterBytes for mono_id_ {
|
|
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
|
to_bytes::iter_bytes_2(&self.def, &self.params, lsb0, f);
|
|
}
|
|
}
|
|
|
|
pub fn umax(cx: block, a: ValueRef, b: ValueRef) -> ValueRef {
|
|
let cond = build::ICmp(cx, lib::llvm::IntULT, a, b);
|
|
return build::Select(cx, cond, b, a);
|
|
}
|
|
|
|
pub fn umin(cx: block, a: ValueRef, b: ValueRef) -> ValueRef {
|
|
let cond = build::ICmp(cx, lib::llvm::IntULT, a, b);
|
|
return build::Select(cx, cond, a, b);
|
|
}
|
|
|
|
pub fn align_to(cx: block, off: ValueRef, align: ValueRef) -> ValueRef {
|
|
let mask = build::Sub(cx, align, C_int(cx.ccx(), 1));
|
|
let bumped = build::Add(cx, off, mask);
|
|
return build::And(cx, bumped, build::Not(cx, mask));
|
|
}
|
|
|
|
pub fn path_str(sess: session::Session, p: path) -> ~str {
|
|
let mut r = ~"", first = true;
|
|
for vec::each(p) |e| {
|
|
match *e {
|
|
ast_map::path_name(s) | ast_map::path_mod(s) => {
|
|
if first { first = false; }
|
|
else { r += ~"::"; }
|
|
r += sess.str_of(s);
|
|
}
|
|
}
|
|
}
|
|
r
|
|
}
|
|
|
|
pub fn monomorphize_type(bcx: block, t: ty::t) -> ty::t {
|
|
match /*bad*/copy bcx.fcx.param_substs {
|
|
Some(substs) => {
|
|
ty::subst_tps(bcx.tcx(), substs.tys, substs.self_ty, t)
|
|
}
|
|
_ => { assert !ty::type_has_params(t); t }
|
|
}
|
|
}
|
|
|
|
pub fn node_id_type(bcx: block, id: ast::node_id) -> ty::t {
|
|
let tcx = bcx.tcx();
|
|
let t = ty::node_id_to_type(tcx, id);
|
|
monomorphize_type(bcx, t)
|
|
}
|
|
|
|
pub fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {
|
|
node_id_type(bcx, ex.id)
|
|
}
|
|
|
|
pub fn node_id_type_params(bcx: block, id: ast::node_id) -> ~[ty::t] {
|
|
let tcx = bcx.tcx();
|
|
let params = ty::node_id_to_type_params(tcx, id);
|
|
match /*bad*/copy bcx.fcx.param_substs {
|
|
Some(substs) => {
|
|
do vec::map(params) |t| {
|
|
ty::subst_tps(tcx, substs.tys, substs.self_ty, *t)
|
|
}
|
|
}
|
|
_ => params
|
|
}
|
|
}
|
|
|
|
pub fn node_vtables(bcx: block, id: ast::node_id)
|
|
-> Option<typeck::vtable_res> {
|
|
let raw_vtables = bcx.ccx().maps.vtable_map.find(&id);
|
|
raw_vtables.map(
|
|
|vts| resolve_vtables_in_fn_ctxt(bcx.fcx, *vts))
|
|
}
|
|
|
|
pub fn resolve_vtables_in_fn_ctxt(fcx: fn_ctxt, vts: typeck::vtable_res)
|
|
-> typeck::vtable_res {
|
|
@vec::map(*vts, |d| resolve_vtable_in_fn_ctxt(fcx, copy *d))
|
|
}
|
|
|
|
// Apply the typaram substitutions in the fn_ctxt to a vtable. This should
|
|
// eliminate any vtable_params.
|
|
pub fn resolve_vtable_in_fn_ctxt(fcx: fn_ctxt, +vt: typeck::vtable_origin)
|
|
-> typeck::vtable_origin {
|
|
let tcx = fcx.ccx.tcx;
|
|
match vt {
|
|
typeck::vtable_static(trait_id, tys, sub) => {
|
|
let tys = match /*bad*/copy fcx.param_substs {
|
|
Some(substs) => {
|
|
do vec::map(tys) |t| {
|
|
ty::subst_tps(tcx, substs.tys, substs.self_ty, *t)
|
|
}
|
|
}
|
|
_ => tys
|
|
};
|
|
typeck::vtable_static(trait_id, tys,
|
|
resolve_vtables_in_fn_ctxt(fcx, sub))
|
|
}
|
|
typeck::vtable_param(n_param, n_bound) => {
|
|
match fcx.param_substs {
|
|
Some(ref substs) => {
|
|
find_vtable(tcx, substs, n_param, n_bound)
|
|
}
|
|
_ => {
|
|
tcx.sess.bug(fmt!(
|
|
"resolve_vtable_in_fn_ctxt: asked to lookup but \
|
|
no vtables in the fn_ctxt!"))
|
|
}
|
|
}
|
|
}
|
|
vt => vt
|
|
}
|
|
}
|
|
|
|
pub fn find_vtable(tcx: ty::ctxt, ps: ¶m_substs,
|
|
n_param: uint, n_bound: uint)
|
|
-> typeck::vtable_origin {
|
|
debug!("find_vtable_in_fn_ctxt(n_param=%u, n_bound=%u, ps=%?)",
|
|
n_param, n_bound, param_substs_to_str(tcx, ps));
|
|
|
|
// Vtables are stored in a flat array, finding the right one is
|
|
// somewhat awkward
|
|
let first_n_bounds = ps.bounds.view(0, n_param);
|
|
let vtables_to_skip =
|
|
ty::count_traits_and_supertraits(tcx, first_n_bounds);
|
|
let vtable_off = vtables_to_skip + n_bound;
|
|
/*bad*/ copy ps.vtables.get()[vtable_off]
|
|
}
|
|
|
|
pub fn dummy_substs(+tps: ~[ty::t]) -> ty::substs {
|
|
substs {
|
|
self_r: Some(ty::re_bound(ty::br_self)),
|
|
self_ty: None,
|
|
tps: tps
|
|
}
|
|
}
|
|
|
|
pub fn struct_field(index: uint) -> [uint * 3] {
|
|
//! The GEPi sequence to access a field of a record/struct.
|
|
|
|
[0, 0, index]
|
|
}
|
|
|
|
pub fn struct_dtor() -> [uint * 2] {
|
|
//! The GEPi sequence to access the dtor of a struct.
|
|
|
|
[0, 1]
|
|
}
|
|
|
|
// Casts a Rust bool value to an i1.
|
|
pub fn bool_to_i1(bcx: block, llval: ValueRef) -> ValueRef {
|
|
build::ICmp(bcx, lib::llvm::IntNE, llval, C_bool(false))
|
|
}
|
|
|
|
//
|
|
// Local Variables:
|
|
// mode: rust
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// End:
|
|
//
|