rust/src/comp/middle/trans_dps.rs

522 lines
17 KiB
Rust
Raw Normal View History

// Translates individual functions in the completed AST to the LLVM IR, using
// destination-passing style.
import back::abi;
2011-07-15 17:59:50 -07:00
import back::link;
import lib::llvm::llvm;
import llvm::TypeRef;
import llvm::ValueRef;
import middle::trans;
import middle::ty;
import syntax::ast;
2011-07-15 17:59:50 -07:00
import syntax::codemap::span;
import trans::block_ctxt;
import trans::crate_ctxt;
import trans::fn_ctxt;
import trans::local_ctxt;
2011-07-15 17:59:50 -07:00
import util::ppaux;
import std::ivec;
import std::option::none;
import std::option::some;
import std::str;
import std::uint;
import LLFalse = lib::llvm::False;
import LLTrue = lib::llvm::True;
2011-07-15 17:59:50 -07:00
import ll = lib::llvm;
import lltype_of = trans::val_ty;
import option = std::option::t;
import tc = trans_common;
import type_of_node = trans::node_id_type;
// LLVM utilities
fn llelement_type(TypeRef llty) -> TypeRef {
lib::llvm::llvm::LLVMGetElementType(llty)
}
fn llalign_of(&@crate_ctxt ccx, TypeRef llty) -> uint {
ret llvm::LLVMPreferredAlignmentOfType(ccx.td.lltd, llty);
}
fn llsize_of(&@crate_ctxt ccx, TypeRef llty) -> uint {
ret llvm::LLVMStoreSizeOfType(ccx.td.lltd, llty);
}
fn mk_const(&@crate_ctxt ccx, &str name, bool exported, ValueRef llval)
-> ValueRef {
auto llglobal = llvm::LLVMAddGlobal(ccx.llmod, trans::val_ty(llval),
str::buf(name));
llvm::LLVMSetInitializer(llglobal, llval);
llvm::LLVMSetGlobalConstant(llglobal, LLTrue);
if !exported {
llvm::LLVMSetLinkage(llglobal,
lib::llvm::LLVMInternalLinkage as llvm::Linkage);
}
ret llglobal;
}
// Destination utilities
tag dest_slot {
dst_nil;
dst_imm(@mutable option[ValueRef]);
dst_ptr(ValueRef);
}
2011-07-15 17:59:50 -07:00
tag dest_mode { dm_copy; dm_move; dm_alias; }
type dest = rec(dest_slot slot, dest_mode mode);
fn dest_slot_for_ptr(&ty::ctxt tcx, ValueRef llptr, ty::t t) -> dest_slot {
if ty::type_is_nil(tcx, t) { dst_nil } else { dst_ptr(llptr) }
}
fn dest_copy(&ty::ctxt tcx, ValueRef llptr, ty::t t) -> dest {
2011-07-15 17:59:50 -07:00
ret rec(slot=dest_slot_for_ptr(tcx, llptr, t), mode=dm_copy);
}
fn dest_move(&ty::ctxt tcx, ValueRef llptr, ty::t t) -> dest {
2011-07-15 17:59:50 -07:00
ret rec(slot=dest_slot_for_ptr(tcx, llptr, t), mode=dm_move);
}
fn dest_alias(&ty::ctxt tcx, ValueRef llptr, ty::t t) -> dest {
ret rec(slot=dest_slot_for_ptr(tcx, llptr, t), mode=dm_alias);
}
2011-07-15 17:59:50 -07:00
fn dest_tmp(&@block_ctxt bcx, ty::t t, bool alias) -> tup(@block_ctxt, dest) {
auto mode = if alias { dm_alias } else { dm_move };
if ty::type_is_nil(bcx_tcx(bcx), t) {
2011-07-15 17:59:50 -07:00
ret tup(bcx, rec(slot=dst_nil, mode=mode));
}
if trans::type_is_immediate(bcx_ccx(bcx), t) {
ret tup(bcx, rec(slot=dst_imm(@mutable none), mode=mode));
}
auto r = trans::alloc_ty(bcx, t);
2011-07-15 17:59:50 -07:00
trans::add_clean(bcx, r.val, t);
ret tup(r.bcx, rec(slot=dest_slot_for_ptr(bcx_tcx(bcx), r.val, t),
mode=mode));
}
// Invariant: the type of the destination must be structural (non-immediate).
fn dest_ptr(&dest dest) -> ValueRef {
alt (dest.slot) {
dst_nil { fail "nil dest in dest_ptr" }
dst_imm(_) { fail "immediate dest in dest_ptr" }
dst_ptr(?llptr) { llptr }
}
}
fn dest_llval(&dest dest) -> ValueRef {
alt (dest.slot) {
dst_nil { ret tc::C_nil(); }
dst_imm(?box) {
alt (*box) {
none { fail "immediate wasn't filled in prior to dest_llval"; }
some(?llval) { ret llval; }
}
}
dst_ptr(?llval) { ret llval; }
}
}
// Accessors
// TODO: When we have overloading, simplify these names!
fn bcx_tcx(&@block_ctxt bcx) -> ty::ctxt { ret bcx.fcx.lcx.ccx.tcx; }
fn bcx_ccx(&@block_ctxt bcx) -> @crate_ctxt { ret bcx.fcx.lcx.ccx; }
fn bcx_lcx(&@block_ctxt bcx) -> @local_ctxt { ret bcx.fcx.lcx; }
fn bcx_fcx(&@block_ctxt bcx) -> @fn_ctxt { ret bcx.fcx; }
2011-07-15 17:59:50 -07:00
fn lcx_ccx(&@local_ctxt lcx) -> @crate_ctxt { ret lcx.ccx; }
// Common operations
// If "cast" is true, casts dest appropriately before the store.
fn store(&@block_ctxt bcx, &dest dest, ValueRef llsrc, bool cast)
-> @block_ctxt {
alt (dest.slot) {
dst_nil { /* no-op */ }
dst_imm(?box) {
if !std::option::is_none(*box) {
fail "attempt to store an immediate twice";
};
*box = some(llsrc);
}
dst_ptr(?lldestptr_orig) {
auto lldestptr = lldestptr_orig;
if cast {
lldestptr = bcx.build.PointerCast(lldestptr,
tc::T_ptr(lltype_of(llsrc)));
}
bcx.build.Store(llsrc, lldestptr);
}
}
ret bcx;
}
fn memmove(&@block_ctxt bcx, &dest dest, ValueRef llsrcptr) -> @block_ctxt {
alt (dest.slot) {
// TODO: We might want to support these; I can't think of any case in
// which we would want them off the top of my head, but feel free to add
// them if they aid orthogonality.
dst_nil { fail "dst_nil in memmove"; }
dst_imm(_) { fail "dst_imm in memmove"; }
dst_ptr(?lldestptr) {
auto lldestty = llelement_type(trans::val_ty(llsrcptr));
auto llsrcty = llelement_type(trans::val_ty(llsrcptr));
auto dest_align = llalign_of(bcx_ccx(bcx), lldestty);
auto src_align = llalign_of(bcx_ccx(bcx), llsrcty);
auto align = uint::min(dest_align, src_align);
auto llfn = bcx_ccx(bcx).intrinsics.get("llvm.memmove.p0i8.p0i8.i32");
auto lldestptr_i8 = bcx.build.PointerCast(lldestptr,
tc::T_ptr(tc::T_i8()));
auto llsrcptr_i8 = bcx.build.PointerCast(llsrcptr,
tc::T_ptr(tc::T_i8()));
bcx.build.Call(llfn,
~[lldestptr_i8,
llsrcptr_i8,
tc::C_uint(llsize_of(bcx_ccx(bcx), llsrcty)),
tc::C_uint(align),
tc::C_bool(false)]);
ret bcx;
}
}
}
// Allocates a value of the given LLVM size on either the task heap or the
// shared heap.
tag heap { hp_task; hp_shared; }
fn malloc(&@block_ctxt bcx, ValueRef lldest, heap heap,
option[ValueRef] llcustom_size_opt) -> @block_ctxt {
auto llptrty = llelement_type(lltype_of(lldest));
auto llty = llelement_type(llptrty);
auto lltydescptr = tc::C_null(tc::T_ptr(bcx_ccx(bcx).tydesc_type));
auto llsize;
alt (llcustom_size_opt) {
none { llsize = trans::llsize_of(llty); }
some(?llcustom_size) { llsize = llcustom_size; }
}
auto llupcall;
alt (heap) {
hp_task { llupcall = bcx_ccx(bcx).upcalls.malloc; }
hp_shared { llupcall = bcx_ccx(bcx).upcalls.shared_malloc; }
}
auto llresult = bcx.build.Call(llupcall, ~[bcx_fcx(bcx).lltaskptr, llsize,
lltydescptr]);
llresult = bcx.build.PointerCast(llresult, llptrty);
bcx.build.Store(llresult, lldest);
ret bcx;
}
// AST substructure translation, with destinations
fn trans_lit(&@block_ctxt cx, &dest dest, &ast::lit lit) -> @block_ctxt {
auto bcx = cx;
alt (lit.node) {
ast::lit_str(?s, ast::sk_unique) {
auto r = trans_lit_str_common(bcx_ccx(bcx), s);
auto llstackpart = r._0; auto llheappartopt = r._1;
bcx = memmove(bcx, dest, llstackpart);
alt (llheappartopt) {
none { /* no-op */ }
some(?llheappart) {
auto lldestptrptr =
bcx.build.InBoundsGEP(dest_ptr(dest),
~[tc::C_int(0),
tc::C_uint(abi::ivec_elt_elems)]);
auto llheappartty = lltype_of(llheappart);
lldestptrptr =
bcx.build.PointerCast(lldestptrptr,
tc::T_ptr(tc::T_ptr(llheappartty)));
malloc(bcx, lldestptrptr, hp_shared, none);
auto lldestptr = bcx.build.Load(lldestptrptr);
memmove(bcx, rec(slot=dst_ptr(lldestptr), mode=dm_copy),
llheappart);
}
}
}
_ {
bcx = store(bcx, dest, trans_lit_common(bcx_ccx(bcx), lit), false);
}
}
ret bcx;
}
2011-07-15 17:59:50 -07:00
fn trans_log(&@block_ctxt cx, &span sp, int level, &@ast::expr expr)
-> @block_ctxt {
fn trans_log_level(&@local_ctxt lcx) -> ValueRef {
auto modname = str::connect_ivec(lcx.module_path, "::");
if (lcx_ccx(lcx).module_data.contains_key(modname)) {
ret lcx_ccx(lcx).module_data.get(modname);
}
auto s =
link::mangle_internal_name_by_path_and_seq(lcx_ccx(lcx),
lcx.module_path,
"loglevel");
auto lllevelptr = llvm::LLVMAddGlobal(lcx.ccx.llmod, tc::T_int(),
str::buf(s));
llvm::LLVMSetGlobalConstant(lllevelptr, LLFalse);
llvm::LLVMSetInitializer(lllevelptr, tc::C_int(0));
llvm::LLVMSetLinkage(lllevelptr, lib::llvm::LLVMInternalLinkage as
llvm::Linkage);
lcx_ccx(lcx).module_data.insert(modname, lllevelptr);
ret lllevelptr;
}
fn trans_log_upcall(&@block_ctxt cx, &span sp, ValueRef in_llval,
int level, ty::t t) -> @block_ctxt {
auto bcx = cx;
2011-07-15 17:59:50 -07:00
auto llval = in_llval;
auto llupcall;
2011-07-15 17:59:50 -07:00
alt (ty::struct(bcx_tcx(bcx), t)) {
ty::ty_machine(ast::ty_f32) {
llupcall = bcx_ccx(bcx).upcalls.log_float;
2011-07-15 17:59:50 -07:00
}
ty::ty_machine(ast::ty_f64) | ty::ty_float {
llupcall = bcx_ccx(bcx).upcalls.log_double;
// TODO: Here we have to spill due to legacy calling conventions.
// This is no longer necessary.
auto r = trans::alloc_ty(bcx, t);
bcx = r.bcx; auto llptr = r.val;
bcx.build.Store(llval, llptr);
llval = llptr;
2011-07-15 17:59:50 -07:00
}
ty::ty_bool | ty::ty_machine(ast::ty_i8) |
ty::ty_machine(ast::ty_i16) | ty::ty_machine(ast::ty_u8) |
ty::ty_machine(ast::ty_u16) {
llupcall = bcx_ccx(bcx).upcalls.log_int;
2011-07-15 17:59:50 -07:00
llval = bcx.build.ZExt(llval, tc::T_i32());
}
ty::ty_int | ty::ty_machine(ast::ty_i32) |
ty::ty_machine(ast::ty_u32) {
llupcall = bcx_ccx(bcx).upcalls.log_int;
2011-07-15 17:59:50 -07:00
}
ty::ty_istr {
llupcall = bcx_ccx(bcx).upcalls.log_istr;
}
2011-07-15 17:59:50 -07:00
_ {
bcx_ccx(bcx).sess.span_unimpl(sp, "logging for values of type " +
ppaux::ty_to_str(bcx_tcx(bcx), t));
}
}
bcx.build.Call(llupcall,
~[bcx_fcx(bcx).lltaskptr, tc::C_int(level), llval]);
ret bcx;
2011-07-15 17:59:50 -07:00
}
auto bcx = cx;
auto lllevelptr = trans_log_level(bcx_lcx(bcx));
auto log_bcx = trans::new_scope_block_ctxt(bcx, "log");
auto next_bcx = trans::new_scope_block_ctxt(bcx, "next_log");
auto should_log = bcx.build.ICmp(ll::LLVMIntSGE,
bcx.build.Load(lllevelptr),
tc::C_int(level));
bcx.build.CondBr(should_log, log_bcx.llbb, next_bcx.llbb);
auto expr_t = ty::expr_ty(bcx_tcx(log_bcx), expr);
auto r = dest_tmp(log_bcx, expr_t, true);
log_bcx = r._0; auto tmp = r._1;
log_bcx = trans_expr(log_bcx, tmp, expr);
log_bcx = trans_log_upcall(log_bcx, sp, dest_llval(tmp), level, expr_t);
2011-07-15 17:59:50 -07:00
log_bcx = trans::trans_block_cleanups(log_bcx,
trans::find_scope_cx(log_bcx));
log_bcx.build.Br(next_bcx.llbb);
ret next_bcx;
}
fn trans_expr(&@block_ctxt bcx, &dest dest, &@ast::expr expr) -> @block_ctxt {
alt (expr.node) {
ast::expr_lit(?lit) { trans_lit(bcx, dest, *lit); ret bcx; }
2011-07-15 17:59:50 -07:00
ast::expr_log(?level, ?operand) {
ret trans_log(bcx, expr.span, level, operand);
}
_ { fail "unhandled expr type in trans_expr"; }
}
}
fn trans_recv(&@block_ctxt bcx, &dest dest, &@ast::expr expr) -> @block_ctxt {
ret bcx; // TODO
}
fn trans_block(&@block_ctxt cx, &dest dest, &ast::block block)
-> @block_ctxt {
auto bcx = cx;
for each (@ast::local local in trans::block_locals(block)) {
bcx = trans::alloc_local(bcx, local).bcx;
}
for (@ast::stmt stmt in block.node.stmts) {
bcx = trans_stmt(bcx, stmt);
// If we hit a terminator, control won't go any further so
// we're in dead-code land. Stop here.
if trans::is_terminated(bcx) { ret bcx; }
}
alt (block.node.expr) {
2011-07-15 17:59:50 -07:00
some(?e) { bcx = trans_expr(bcx, dest, e); }
none { /* no-op */ }
}
2011-07-15 17:59:50 -07:00
bcx = trans::trans_block_cleanups(bcx, trans::find_scope_cx(bcx));
ret bcx;
}
// AST substructure translation, without destinations
// Common setup code shared between the crate-constant literal string case and
// the block-local literal string case. We don't use destination-passing style
// since that doesn't work for crate constants.
fn trans_lit_str_common(&@crate_ctxt ccx, &str s)
-> tup(ValueRef, option[ValueRef]) {
auto llstackpart; auto llheappartopt;
auto len = str::byte_len(s);
auto array = ~[];
for (u8 ch in s) { array += ~[tc::C_u8(ch as uint)]; }
array += ~[tc::C_u8(0u)];
if len < abi::ivec_default_length - 1u { // minus 1 because of the \0
while (ivec::len(array) < abi::ivec_default_length) {
array += ~[tc::C_u8(0u)];
}
llstackpart = tc::C_struct(~[tc::C_uint(len + 1u),
tc::C_uint(abi::ivec_default_length),
tc::C_array(tc::T_i8(), array)]);
llheappartopt = none;
} else {
auto llheappart = tc::C_struct(~[tc::C_uint(len),
tc::C_array(tc::T_i8(), array)]);
llstackpart =
tc::C_struct(~[tc::C_uint(0u),
tc::C_uint(abi::ivec_default_length),
tc::C_null(tc::T_ptr(lltype_of(llheappart)))]);
llheappartopt = some(mk_const(ccx, "const_istr_heap", false,
llheappart));
}
ret tup(mk_const(ccx, "const_istr_stack", false, llstackpart),
llheappartopt);
}
// As above, we don't use destination-passing style here.
fn trans_lit_common(&@crate_ctxt ccx, &ast::lit lit) -> ValueRef {
alt (lit.node) {
ast::lit_int(?i) { ret tc::C_int(i); }
ast::lit_uint(?u) { ret tc::C_int(u as int); }
ast::lit_mach_int(?tm, ?i) {
// FIXME: the entire handling of mach types falls apart
// if target int width is larger than host, at the moment;
// re-do the mach-int types using 'big' when that works.
auto t = tc::T_int();
auto s = LLTrue;
alt (tm) {
ast::ty_u8 { t = tc::T_i8(); s = LLFalse; }
ast::ty_u16 { t = tc::T_i16(); s = LLFalse; }
ast::ty_u32 { t = tc::T_i32(); s = LLFalse; }
ast::ty_u64 { t = tc::T_i64(); s = LLFalse; }
ast::ty_i8 { t = tc::T_i8(); }
ast::ty_i16 { t = tc::T_i16(); }
ast::ty_i32 { t = tc::T_i32(); }
ast::ty_i64 { t = tc::T_i64(); }
}
ret tc::C_integral(t, i as uint, s);
}
ast::lit_float(?fs) { ret tc::C_float(fs); }
ast::lit_mach_float(?tm, ?s) {
auto t = tc::T_float();
alt (tm) {
ast::ty_f32 { t = tc::T_f32(); }
ast::ty_f64 { t = tc::T_f64(); }
}
ret tc::C_floating(s, t);
}
ast::lit_char(?c) {
ret tc::C_integral(tc::T_char(), c as uint, LLFalse);
}
ast::lit_bool(?b) { ret tc::C_bool(b); }
ast::lit_nil { ret tc::C_nil(); }
ast::lit_str(?s, ast::sk_rc) { ret tc::C_str(ccx, s); }
ast::lit_str(?s, ast::sk_unique) {
fail "unique str in trans_lit_common";
}
}
}
fn trans_init_local(&@block_ctxt bcx, &@ast::local local) -> @block_ctxt {
auto llptr = bcx_fcx(bcx).lllocals.get(local.node.id);
auto t = type_of_node(bcx_ccx(bcx), local.node.id);
trans::add_clean(bcx, llptr, t);
alt (local.node.init) {
some(?init) {
alt (init.op) {
ast::init_assign {
ret trans_expr(bcx, dest_copy(bcx_tcx(bcx), llptr, t), init.expr);
}
ast::init_move {
ret trans_expr(bcx, dest_move(bcx_tcx(bcx), llptr, t), init.expr);
}
ast::init_recv {
ret trans_recv(bcx, dest_copy(bcx_tcx(bcx), llptr, t), init.expr);
}
}
}
none {
ret store(bcx, dest_copy(bcx_tcx(bcx), llptr, t),
tc::C_null(llelement_type(trans::val_ty(llptr))), false);
}
}
}
fn trans_stmt(&@block_ctxt cx, &@ast::stmt stmt) -> @block_ctxt {
auto bcx = cx;
alt (stmt.node) {
ast::stmt_expr(?e, _) {
2011-07-15 17:59:50 -07:00
auto tmp_r = dest_tmp(bcx, ty::expr_ty(bcx_tcx(bcx), e), true);
bcx = tmp_r._0; auto tmp = tmp_r._1;
ret trans_expr(bcx, tmp, e);
}
ast::stmt_decl(?d, _) {
alt (d.node) {
ast::decl_local(?local) { ret trans_init_local(bcx, local); }
ast::decl_item(?item) {
trans::trans_item(bcx_lcx(bcx), *item);
ret bcx;
}
}
}
}
}