Skip to content

Commit 626ad48

Browse files
committed
Unified algebraic datatype representation implementation, initial version.
Later changes on this branch adapt the rest of rustc::middle::trans to use this module instead of scattered hard-coded knowledge of representations; a few of them also have improvements or cleanup for adt.rs (and many added comments) that weren't drastic enough to justify changing history to move them into this commit.
1 parent b269ce2 commit 626ad48

File tree

4 files changed

+366
-15
lines changed

4 files changed

+366
-15
lines changed

src/librustc/middle/trans/adt.rs

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use core::option::{Option, Some, None};
12+
use core::vec;
13+
use lib::llvm::{ValueRef, TypeRef};
14+
use middle::trans::_match;
15+
use middle::trans::build::*;
16+
use middle::trans::common::*;
17+
use middle::trans::machine;
18+
use middle::trans::type_of;
19+
use middle::ty;
20+
use syntax::ast;
21+
use util::ppaux::ty_to_str;
22+
23+
24+
// XXX: should this be done with boxed traits instead of ML-style?
25+
pub enum Repr {
26+
CEnum,
27+
Univariant(Struct, Destructor),
28+
General(~[Struct])
29+
}
30+
31+
enum Destructor {
32+
DtorPresent,
33+
DtorAbsent,
34+
NoDtor
35+
}
36+
37+
struct Struct {
38+
size: u64,
39+
align: u64,
40+
fields: ~[ty::t]
41+
}
42+
43+
44+
pub fn represent_node(bcx: block, node: ast::node_id)
45+
-> Repr {
46+
represent_type(bcx.ccx(), node_id_type(bcx, node))
47+
}
48+
49+
pub fn represent_type(cx: @CrateContext, t: ty::t) -> Repr {
50+
debug!("Representing: %s", ty_to_str(cx.tcx, t));
51+
// XXX: cache this
52+
match ty::get(t).sty {
53+
ty::ty_tup(ref elems) => {
54+
Univariant(mk_struct(cx, *elems), NoDtor)
55+
}
56+
ty::ty_rec(ref fields) => {
57+
// XXX: Are these in the right order?
58+
Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)), DtorAbsent)
59+
}
60+
ty::ty_struct(def_id, ref substs) => {
61+
let fields = ty::lookup_struct_fields(cx.tcx, def_id);
62+
let dt = ty::ty_dtor(cx.tcx, def_id).is_present();
63+
Univariant(mk_struct(cx, fields.map(|field| {
64+
ty::lookup_field_type(cx.tcx, def_id, field.id, substs)
65+
})), if dt { DtorPresent } else { DtorAbsent })
66+
}
67+
ty::ty_enum(def_id, ref substs) => {
68+
struct Case { discr: i64, tys: ~[ty::t] };
69+
70+
let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
71+
let arg_tys = do vi.args.map |&raw_ty| {
72+
ty::subst(cx.tcx, substs, raw_ty)
73+
};
74+
Case { discr: vi.disr_val /*bad*/as i64, tys: arg_tys }
75+
};
76+
if cases.len() == 0 {
77+
// Uninhabitable; represent as unit
78+
Univariant(mk_struct(cx, ~[]), NoDtor)
79+
} else if cases.len() == 1 && cases[0].discr == 0 {
80+
// struct, tuple, newtype, etc.
81+
Univariant(mk_struct(cx, cases[0].tys), NoDtor)
82+
} else if cases.all(|c| c.tys.len() == 0) {
83+
CEnum
84+
} else {
85+
if !cases.alli(|i,c| c.discr == (i as i64)) {
86+
cx.sess.bug(fmt!("non-C-like enum %s with specified \
87+
discriminants",
88+
ty::item_path_str(cx.tcx, def_id)))
89+
}
90+
General(cases.map(|c| mk_struct(cx, c.tys)))
91+
}
92+
}
93+
_ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
94+
}
95+
}
96+
97+
fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct {
98+
let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
99+
let llty_rec = T_struct(lltys);
100+
Struct {
101+
size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64,
102+
align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64,
103+
fields: vec::from_slice(tys)
104+
}
105+
}
106+
107+
108+
pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
109+
generic_fields_of(cx, r, true)
110+
}
111+
pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
112+
generic_fields_of(cx, r, false)
113+
}
114+
fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
115+
-> ~[TypeRef] {
116+
match *r {
117+
CEnum => ~[T_enum_discrim(cx)],
118+
Univariant(ref st, dt) => {
119+
let f = if sizing {
120+
st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
121+
} else {
122+
st.fields.map(|&ty| type_of::type_of(cx, ty))
123+
};
124+
match dt {
125+
NoDtor => f,
126+
DtorAbsent => ~[T_struct(f)],
127+
DtorPresent => ~[T_struct(f), T_i8()]
128+
}
129+
}
130+
General(ref sts) => {
131+
~[T_enum_discrim(cx),
132+
T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
133+
}
134+
}
135+
}
136+
137+
pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef) ->
138+
(_match::branch_kind, Option<ValueRef>) {
139+
// XXX: LoadRangeAssert
140+
match *r {
141+
CEnum => {
142+
(_match::switch, Some(Load(bcx, GEPi(bcx, scrutinee, [0, 0]))))
143+
}
144+
Univariant(*) => {
145+
(_match::single, None)
146+
}
147+
General(*) => {
148+
(_match::switch, Some(Load(bcx, GEPi(bcx, scrutinee, [0, 0]))))
149+
}
150+
}
151+
}
152+
153+
pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
154+
match *r {
155+
CEnum => {
156+
_match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
157+
}
158+
Univariant(*) => {
159+
bcx.ccx().sess.bug(~"no cases for univariants or structs")
160+
}
161+
General(*) => {
162+
_match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
163+
}
164+
}
165+
}
166+
167+
pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
168+
match *r {
169+
CEnum => {
170+
Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
171+
}
172+
Univariant(_, DtorPresent) => {
173+
assert discr == 0;
174+
Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1]))
175+
}
176+
Univariant(*) => {
177+
assert discr == 0;
178+
}
179+
General(*) => {
180+
Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
181+
}
182+
}
183+
}
184+
185+
pub fn num_args(r: &Repr, discr: int) -> uint {
186+
match *r {
187+
CEnum => 0,
188+
Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() }
189+
General(ref cases) => cases[discr as uint].fields.len()
190+
}
191+
}
192+
193+
pub fn trans_GEP(bcx: block, r: &Repr, val: ValueRef, discr: int, ix: uint)
194+
-> ValueRef {
195+
// Note: if this ever needs to generate conditionals (e.g., if we
196+
// decide to do some kind of cdr-coding-like non-unique repr
197+
// someday), it'll need to return a possibly-new bcx as well.
198+
match *r {
199+
CEnum => {
200+
bcx.ccx().sess.bug(~"element access in C-like enum")
201+
}
202+
Univariant(ref st, dt) => {
203+
assert discr == 0;
204+
let val = match dt {
205+
NoDtor => val,
206+
DtorPresent | DtorAbsent => GEPi(bcx, val, [0, 0])
207+
};
208+
struct_GEP(bcx, st, val, ix)
209+
}
210+
General(ref cases) => {
211+
struct_GEP(bcx, &cases[discr as uint],
212+
GEPi(bcx, val, [0, 1]), ix)
213+
}
214+
}
215+
}
216+
217+
fn struct_GEP(bcx: block, st: &Struct, val: ValueRef, ix: uint)
218+
-> ValueRef {
219+
let ccx = bcx.ccx();
220+
221+
let real_llty = T_struct(st.fields.map(
222+
|&ty| type_of::type_of(ccx, ty)));
223+
let cast_val = PointerCast(bcx, val, T_ptr(real_llty));
224+
225+
GEPi(bcx, cast_val, [0, ix])
226+
}
227+
228+
pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
229+
vals: &[ValueRef]) -> ValueRef {
230+
match *r {
231+
CEnum => {
232+
assert vals.len() == 0;
233+
C_int(ccx, discr)
234+
}
235+
Univariant(ref st, _dt) => {
236+
assert discr == 0;
237+
// consts are never destroyed, so the dtor flag is not needed
238+
C_struct(build_const_struct(ccx, st, vals))
239+
}
240+
General(ref cases) => {
241+
let case = &cases[discr as uint];
242+
let max_sz = cases.map(|s| s.size).max();
243+
let body = build_const_struct(ccx, case, vals);
244+
245+
C_struct([C_int(ccx, discr),
246+
C_packed_struct([C_struct(body)]),
247+
padding(max_sz - case.size)])
248+
}
249+
}
250+
}
251+
252+
fn padding(size: u64) -> ValueRef {
253+
C_undef(T_array(T_i8(), size /*bad*/as uint))
254+
}
255+
256+
fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
257+
-> ~[ValueRef] {
258+
assert vals.len() == st.fields.len();
259+
260+
let mut offset = 0;
261+
let mut cfields = ~[];
262+
for st.fields.eachi |i, &ty| {
263+
let llty = type_of::sizing_type_of(ccx, ty);
264+
let type_align = machine::llalign_of_min(ccx, llty)
265+
/*bad*/as u64;
266+
let val_align = machine::llalign_of_min(ccx, val_ty(vals[i]))
267+
/*bad*/as u64;
268+
let target_offset = roundup(offset, type_align);
269+
offset = roundup(offset, val_align);
270+
if (offset != target_offset) {
271+
cfields.push(padding(target_offset - offset));
272+
offset = target_offset;
273+
}
274+
assert !is_undef(vals[i]);
275+
// If that assert fails, could change it to wrap in a struct?
276+
cfields.push(vals[i]);
277+
}
278+
279+
return cfields;
280+
}
281+
282+
#[always_inline]
283+
fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }
284+
285+
286+
pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
287+
-> int {
288+
match *r {
289+
CEnum(*) => const_to_int(val) as int,
290+
Univariant(*) => 0,
291+
General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int,
292+
}
293+
}
294+
295+
pub fn const_get_element(ccx: @CrateContext, r: &Repr, val: ValueRef,
296+
_discr: int, ix: uint) -> ValueRef {
297+
// Not to be confused with common::const_get_elt.
298+
match *r {
299+
CEnum(*) => ccx.sess.bug(~"element access in C-like enum const"),
300+
Univariant(*) => const_struct_field(ccx, val, ix),
301+
General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
302+
[1, 0]), ix)
303+
}
304+
}
305+
306+
fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint)
307+
-> ValueRef {
308+
// Get the ix-th non-undef element of the struct.
309+
let mut real_ix = 0; // actual position in the struct
310+
let mut ix = ix; // logical index relative to real_ix
311+
let mut field;
312+
loop {
313+
loop {
314+
field = const_get_elt(ccx, val, [real_ix]);
315+
if !is_undef(field) {
316+
break;
317+
}
318+
real_ix = real_ix + 1;
319+
}
320+
if ix == 0 {
321+
return field;
322+
}
323+
ix = ix - 1;
324+
real_ix = real_ix + 1;
325+
}
326+
}

src/librustc/middle/trans/common.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use util::ppaux::{expr_repr, ty_to_str};
4444

4545
use core::cast;
4646
use core::hash;
47-
use core::libc::c_uint;
47+
use core::libc::{c_uint, c_longlong, c_ulonglong};
4848
use core::ptr;
4949
use core::str;
5050
use core::to_bytes;
@@ -1087,6 +1087,12 @@ pub fn C_null(t: TypeRef) -> ValueRef {
10871087
}
10881088
}
10891089

1090+
pub fn C_undef(t: TypeRef) -> ValueRef {
1091+
unsafe {
1092+
return llvm::LLVMGetUndef(t);
1093+
}
1094+
}
1095+
10901096
pub fn C_integral(t: TypeRef, u: u64, sign_extend: Bool) -> ValueRef {
10911097
unsafe {
10921098
return llvm::LLVMConstInt(t, u, sign_extend);
@@ -1254,6 +1260,38 @@ pub fn get_param(fndecl: ValueRef, param: uint) -> ValueRef {
12541260
}
12551261
}
12561262
1263+
pub fn const_get_elt(cx: @CrateContext, v: ValueRef, us: &[c_uint])
1264+
-> ValueRef {
1265+
unsafe {
1266+
let r = do vec::as_imm_buf(us) |p, len| {
1267+
llvm::LLVMConstExtractValue(v, p, len as c_uint)
1268+
};
1269+
1270+
debug!("const_get_elt(v=%s, us=%?, r=%s)",
1271+
val_str(cx.tn, v), us, val_str(cx.tn, r));
1272+
1273+
return r;
1274+
}
1275+
}
1276+
1277+
pub fn const_to_int(v: ValueRef) -> c_longlong {
1278+
unsafe {
1279+
llvm::LLVMConstIntGetSExtValue(v)
1280+
}
1281+
}
1282+
1283+
pub fn const_to_uint(v: ValueRef) -> c_ulonglong {
1284+
unsafe {
1285+
llvm::LLVMConstIntGetZExtValue(v)
1286+
}
1287+
}
1288+
1289+
pub fn is_undef(val: ValueRef) -> bool {
1290+
unsafe {
1291+
llvm::LLVMIsUndef(val) != False
1292+
}
1293+
}
1294+
12571295
// Used to identify cached monomorphized functions and vtables
12581296
#[deriving_eq]
12591297
pub enum mono_param_id {

0 commit comments

Comments
 (0)