2012-07-31 20:34:36 -05:00
|
|
|
import common::*;
|
|
|
|
import syntax::{ast, ast_util, codemap, ast_map};
|
|
|
|
import base::get_insn_ctxt;
|
|
|
|
|
|
|
|
fn const_lit(cx: @crate_ctxt, e: @ast::expr, lit: ast::lit)
|
|
|
|
-> ValueRef {
|
|
|
|
let _icx = cx.insn_ctxt(~"trans_lit");
|
|
|
|
alt lit.node {
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::lit_int(i, t) => C_integral(T_int_ty(cx, t), i as u64, True),
|
|
|
|
ast::lit_uint(u, t) => C_integral(T_uint_ty(cx, t), u, False),
|
|
|
|
ast::lit_int_unsuffixed(i) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
let lit_int_ty = ty::node_id_to_type(cx.tcx, e.id);
|
|
|
|
alt ty::get(lit_int_ty).struct {
|
2012-08-03 21:59:04 -05:00
|
|
|
ty::ty_int(t) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
C_integral(T_int_ty(cx, t), i as u64, True)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ty::ty_uint(t) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
C_integral(T_uint_ty(cx, t), i as u64, False)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.sess.span_bug(lit.span,
|
|
|
|
~"integer literal doesn't have a type")
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::lit_float(fs, t) => C_floating(*fs, T_float_ty(cx, t)),
|
|
|
|
ast::lit_bool(b) => C_bool(b),
|
|
|
|
ast::lit_nil => C_nil(),
|
|
|
|
ast::lit_str(s) => C_estr_slice(cx, *s)
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME (#2530): this should do some structural hash-consing to avoid
|
|
|
|
// duplicate constants. I think. Maybe LLVM has a magical mode that does so
|
|
|
|
// later on?
|
2012-08-03 23:44:42 -05:00
|
|
|
|
|
|
|
fn const_vec_and_sz(cx: @crate_ctxt, e: @ast::expr, es: &[@ast::expr])
|
|
|
|
-> (ValueRef, ValueRef) {
|
|
|
|
let vec_ty = ty::expr_ty(cx.tcx, e);
|
|
|
|
let unit_ty = ty::sequence_element_type(cx.tcx, vec_ty);
|
|
|
|
let llunitty = type_of::type_of(cx, unit_ty);
|
|
|
|
let v = C_array(llunitty, es.map(|e| const_expr(cx, e)));
|
|
|
|
let unit_sz = shape::llsize_of(cx, llunitty);
|
|
|
|
let sz = llvm::LLVMConstMul(C_uint(cx, es.len()), unit_sz);
|
|
|
|
return (v, sz);
|
|
|
|
}
|
|
|
|
|
2012-07-31 20:34:36 -05:00
|
|
|
fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
|
|
|
|
let _icx = cx.insn_ctxt(~"const_expr");
|
|
|
|
alt e.node {
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_lit(lit) => consts::const_lit(cx, e, *lit),
|
|
|
|
ast::expr_binary(b, e1, e2) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
let te1 = const_expr(cx, e1);
|
|
|
|
let te2 = const_expr(cx, e2);
|
|
|
|
|
|
|
|
let te2 = base::cast_shift_const_rhs(b, te1, te2);
|
|
|
|
|
|
|
|
/* Neither type is bottom, and we expect them to be unified already,
|
|
|
|
* so the following is safe. */
|
|
|
|
let ty = ty::expr_ty(cx.tcx, e1);
|
|
|
|
let is_float = ty::type_is_fp(ty);
|
|
|
|
let signed = ty::type_is_signed(ty);
|
2012-08-01 19:30:05 -05:00
|
|
|
return alt b {
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::add => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFAdd(te1, te2) }
|
|
|
|
else { llvm::LLVMConstAdd(te1, te2) }
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::subtract => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFSub(te1, te2) }
|
|
|
|
else { llvm::LLVMConstSub(te1, te2) }
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::mul => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFMul(te1, te2) }
|
|
|
|
else { llvm::LLVMConstMul(te1, te2) }
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::div => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFDiv(te1, te2) }
|
|
|
|
else if signed { llvm::LLVMConstSDiv(te1, te2) }
|
|
|
|
else { llvm::LLVMConstUDiv(te1, te2) }
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::rem => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFRem(te1, te2) }
|
|
|
|
else if signed { llvm::LLVMConstSRem(te1, te2) }
|
|
|
|
else { llvm::LLVMConstURem(te1, te2) }
|
|
|
|
}
|
|
|
|
ast::and |
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::or => cx.sess.span_unimpl(e.span, ~"binop logic"),
|
|
|
|
ast::bitxor => llvm::LLVMConstXor(te1, te2),
|
|
|
|
ast::bitand => llvm::LLVMConstAnd(te1, te2),
|
|
|
|
ast::bitor => llvm::LLVMConstOr(te1, te2),
|
|
|
|
ast::shl => llvm::LLVMConstShl(te1, te2),
|
|
|
|
ast::shr => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if signed { llvm::LLVMConstAShr(te1, te2) }
|
|
|
|
else { llvm::LLVMConstLShr(te1, te2) }
|
|
|
|
}
|
|
|
|
ast::eq |
|
|
|
|
ast::lt |
|
|
|
|
ast::le |
|
|
|
|
ast::ne |
|
|
|
|
ast::ge |
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::gt => cx.sess.span_unimpl(e.span, ~"binop comparator")
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_unary(u, e) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
let te = const_expr(cx, e);
|
|
|
|
let ty = ty::expr_ty(cx.tcx, e);
|
|
|
|
let is_float = ty::type_is_fp(ty);
|
2012-08-01 19:30:05 -05:00
|
|
|
return alt u {
|
2012-07-31 20:34:36 -05:00
|
|
|
ast::box(_) |
|
|
|
|
ast::uniq(_) |
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::deref => cx.sess.span_bug(e.span,
|
|
|
|
~"bad unop type in const_expr"),
|
|
|
|
ast::not => llvm::LLVMConstNot(te),
|
|
|
|
ast::neg => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if is_float { llvm::LLVMConstFNeg(te) }
|
|
|
|
else { llvm::LLVMConstNeg(te) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_cast(base, tp) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
let ety = ty::expr_ty(cx.tcx, e), llty = type_of::type_of(cx, ety);
|
|
|
|
let basety = ty::expr_ty(cx.tcx, base);
|
|
|
|
let v = const_expr(cx, base);
|
|
|
|
alt check (base::cast_type_kind(basety), base::cast_type_kind(ety)) {
|
2012-08-03 21:59:04 -05:00
|
|
|
(base::cast_integral, base::cast_integral) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
let s = if ty::type_is_signed(basety) { True } else { False };
|
|
|
|
llvm::LLVMConstIntCast(v, llty, s)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
(base::cast_integral, base::cast_float) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) }
|
|
|
|
else { llvm::LLVMConstUIToFP(v, llty) }
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
(base::cast_float, base::cast_float) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
llvm::LLVMConstFPCast(v, llty)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
(base::cast_float, base::cast_integral) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) }
|
|
|
|
else { llvm::LLVMConstFPToUI(v, llty) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_addr_of(ast::m_imm, sub) => {
|
2012-08-03 20:07:45 -05:00
|
|
|
let cv = const_expr(cx, sub);
|
|
|
|
let subty = ty::expr_ty(cx.tcx, sub),
|
|
|
|
llty = type_of::type_of(cx, subty);
|
|
|
|
let gv = do str::as_c_str("const") |name| {
|
|
|
|
llvm::LLVMAddGlobal(cx.llmod, llty, name)
|
|
|
|
};
|
|
|
|
llvm::LLVMSetInitializer(gv, cv);
|
|
|
|
llvm::LLVMSetGlobalConstant(gv, True);
|
|
|
|
gv
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_tup(es) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
C_struct(es.map(|e| const_expr(cx, e)))
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_rec(fs, none) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
C_struct(fs.map(|f| const_expr(cx, f.node.expr)))
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_vec(es, m_imm) => {
|
2012-08-03 23:44:42 -05:00
|
|
|
let (v, _) = const_vec_and_sz(cx, e, es);
|
|
|
|
v
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_vstore(e, ast::vstore_fixed(_)) => {
|
2012-08-03 23:44:42 -05:00
|
|
|
const_expr(cx, e)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_vstore(sub, ast::vstore_slice(_)) => {
|
2012-08-03 23:44:42 -05:00
|
|
|
alt sub.node {
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_lit(lit) => {
|
2012-08-03 23:44:42 -05:00
|
|
|
alt lit.node {
|
|
|
|
ast::lit_str(*) => { const_expr(cx, sub) }
|
|
|
|
_ => { cx.sess.span_bug(e.span,
|
|
|
|
~"bad const-slice lit") }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::expr_vec(es, m_imm) => {
|
|
|
|
let (cv, sz) = const_vec_and_sz(cx, e, es);
|
|
|
|
let subty = ty::expr_ty(cx.tcx, sub),
|
|
|
|
llty = type_of::type_of(cx, subty);
|
|
|
|
let gv = do str::as_c_str("const") |name| {
|
|
|
|
llvm::LLVMAddGlobal(cx.llmod, llty, name)
|
|
|
|
};
|
|
|
|
llvm::LLVMSetInitializer(gv, cv);
|
|
|
|
llvm::LLVMSetGlobalConstant(gv, True);
|
|
|
|
C_struct(~[gv, sz])
|
|
|
|
}
|
|
|
|
_ => cx.sess.span_bug(e.span,
|
|
|
|
~"bad const-slice expr")
|
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_path(path) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
alt cx.tcx.def_map.find(e.id) {
|
2012-08-03 21:59:04 -05:00
|
|
|
some(ast::def_const(def_id)) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
// Don't know how to handle external consts
|
|
|
|
assert ast_util::is_local(def_id);
|
|
|
|
alt cx.tcx.items.get(def_id.node) {
|
|
|
|
ast_map::node_item(@{
|
|
|
|
node: ast::item_const(_, subexpr), _
|
2012-08-03 21:59:04 -05:00
|
|
|
}, _) => {
|
2012-07-31 20:34:36 -05:00
|
|
|
// FIXME (#2530): Instead of recursing here to regenerate
|
|
|
|
// the values for other constants, we should just look up
|
|
|
|
// the already-defined value.
|
|
|
|
const_expr(cx, subexpr)
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.sess.span_bug(e.span, ~"expected item")
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.sess.span_bug(e.span, ~"expected to find a const def")
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.sess.span_bug(e.span,
|
|
|
|
~"bad constant expression type in consts::const_expr")
|
2012-07-31 20:34:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) {
|
|
|
|
let _icx = ccx.insn_ctxt(~"trans_const");
|
|
|
|
let v = const_expr(ccx, e);
|
|
|
|
|
|
|
|
// The scalars come back as 1st class LLVM vals
|
|
|
|
// which we have to stick into global constants.
|
|
|
|
let g = base::get_item_val(ccx, id);
|
|
|
|
llvm::LLVMSetInitializer(g, v);
|
|
|
|
llvm::LLVMSetGlobalConstant(g, True);
|
|
|
|
}
|