From 9d3ebd6a57b51f02788331f56681e8f593d82e60 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Wed, 19 Jan 2011 16:29:14 -0800 Subject: [PATCH] Implement dynamic GEP enough to permit expr_field to work on tup(T,T,T). --- src/Makefile | 2 + src/comp/middle/trans.rs | 106 ++++++++++++++++++++++++++++++- src/comp/middle/ty.rs | 23 +++++++ src/test/run-pass/generic-tup.rs | 10 +++ 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/generic-tup.rs diff --git a/src/Makefile b/src/Makefile index 628b00cb3b7..0e039d657e1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -392,6 +392,7 @@ TEST_XFAILS_BOOT := $(TASK_XFAILS) \ test/run-pass/vec-slice.rs \ test/run-pass/fn-lval.rs \ test/run-pass/generic-recursive-tag.rs \ + test/run-pass/generic-tup.rs \ test/run-pass/iter-ret.rs \ test/run-pass/lib-io.rs \ test/run-pass/mlist-cycle.rs \ @@ -440,6 +441,7 @@ TEST_XFAILS_RUSTC := $(filter-out \ fact.rs \ generic-fn-infer.rs \ generic-drop-glue.rs \ + generic-tup.rs \ hello.rs \ int.rs \ i32-sub.rs \ diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index c3c8ee11131..a96c92cf9b6 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -732,6 +732,108 @@ fn dynamic_align_of(@block_ctxt cx, @ty.t t) -> ValueRef { } } +// Replacement for the LLVM 'GEP' instruction when field-indexing into a +// tuple-like structure (tup, rec, tag) with a static index. This one is +// driven off ty.struct and knows what to do when it runs into a ty_param +// stuck in the middle of the thing it's GEP'ing into. Much like size_of and +// align_of, above. + +fn GEP_tup_like(@block_ctxt cx, @ty.t t, + ValueRef base, vec[int] ixs) -> ValueRef { + + check (ty.type_is_tup_like(t)); + + // It might be a static-known type. Handle this. + + if (! ty.type_has_dynamic_size(t)) { + let vec[ValueRef] v = vec(); + for (int i in ixs) { + v += C_int(i); + } + ret cx.build.GEP(base, v); + } + + // It is a dynamic-containing type that, if we convert directly to an LLVM + // TypeRef, will be all wrong; there's no proper LLVM type to represent + // it, and the lowering function will stick in i8* values for each + // ty_param, which is not right; the ty_params are all of some dynamic + // size. + // + // What we must do instead is sadder. We must look through the indices + // manually and split the input type into a prefix and a target. We then + // measure the prefix size, bump the input pointer by that amount, and + // cast to a pointer-to-target type. + + + // Given a type, an index vector and an element number N in that vector, + // calculate index X and the type that results by taking the first X-1 + // elements of the type and splitting the Xth off. Return the prefix as + // well as the innermost Xth type. + + fn split_type(@ty.t t, vec[int] ixs, uint n) + -> rec(vec[@ty.t] prefix, @ty.t target) { + + let uint len = _vec.len[int](ixs); + + // We don't support 0-index or 1-index GEPs. The former is nonsense + // and the latter would only be meaningful if we supported non-0 + // values for the 0th index (we don't). + + check (len > 1u); + + if (n == 0u) { + // Since we're starting from a value that's a pointer to a + // *single* structure, the first index (in GEP-ese) should just be + // 0, to yield the pointee. + check (ixs.(n) == 0); + ret split_type(t, ixs, n+1u); + } + + check (n < len); + + let int ix = ixs.(n); + let vec[@ty.t] prefix = vec(); + let int i = 0; + while (i < ix) { + append[@ty.t](prefix, ty.get_element_type(t, i as uint)); + i +=1 ; + } + + auto selected = ty.get_element_type(t, i as uint); + + if (n == len-1u) { + // We are at the innermost index. + ret rec(prefix=prefix, target=selected); + + } else { + // Not the innermost index; call self recursively to dig deeper. + // Once we get an inner result, append it current prefix and + // return to caller. + auto inner = split_type(selected, ixs, n+1u); + prefix += inner.prefix; + ret rec(prefix=prefix with inner); + } + } + + // We make a fake prefix tuple-type here; luckily for measuring sizes + // the tuple parens are associative so it doesn't matter that we've + // flattened the incoming structure. + + auto s = split_type(t, ixs, 0u); + auto prefix_ty = ty.plain_ty(ty.ty_tup(s.prefix)); + auto sz = size_of(cx, prefix_ty); + auto raw = cx.build.PointerCast(base, T_ptr(T_i8())); + auto bumped = cx.build.GEP(raw, vec(sz)); + alt (s.target.struct) { + case (ty.ty_param(_)) { ret bumped; } + case (_) { + auto ty = T_ptr(type_of(cx.fcx.ccx, s.target)); + ret cx.build.PointerCast(bumped, ty); + } + } +} + + fn trans_malloc_inner(@block_ctxt cx, TypeRef llptr_ty) -> result { auto llbody_ty = lib.llvm.llvm.LLVMGetElementType(llptr_ty); // FIXME: need a table to collect tydesc globals. @@ -1969,12 +2071,12 @@ impure fn trans_field(@block_ctxt cx, &ast.span sp, @ast.expr base, alt (t.struct) { case (ty.ty_tup(?fields)) { let uint ix = ty.field_num(cx.fcx.ccx.sess, sp, field); - auto v = r.bcx.build.GEP(r.val, vec(C_int(0), C_int(ix as int))); + auto v = GEP_tup_like(r.bcx, t, r.val, vec(0, ix as int)); ret lval_mem(r.bcx, v); } case (ty.ty_rec(?fields)) { let uint ix = ty.field_idx(cx.fcx.ccx.sess, sp, field, fields); - auto v = r.bcx.build.GEP(r.val, vec(C_int(0), C_int(ix as int))); + auto v = GEP_tup_like(r.bcx, t, r.val, vec(0, ix as int)); ret lval_mem(r.bcx, v); } case (ty.ty_obj(?methods)) { diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 531e3f855fc..182e25219c3 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -361,6 +361,29 @@ fn type_is_structural(@t ty) -> bool { fail; } +fn type_is_tup_like(@t ty) -> bool { + alt (ty.struct) { + case (ty_tup(_)) { ret true; } + case (ty_rec(_)) { ret true; } + case (ty_tag(_)) { ret true; } + case (_) { ret false; } + } + fail; +} + +fn get_element_type(@t ty, uint i) -> @t { + check (type_is_tup_like(ty)); + alt (ty.struct) { + case (ty_tup(?tys)) { + ret tys.(i); + } + case (ty_rec(?flds)) { + ret flds.(i).ty; + } + } + fail; +} + fn type_is_boxed(@t ty) -> bool { alt (ty.struct) { case (ty_str) { ret true; } diff --git a/src/test/run-pass/generic-tup.rs b/src/test/run-pass/generic-tup.rs new file mode 100644 index 00000000000..29a66371f11 --- /dev/null +++ b/src/test/run-pass/generic-tup.rs @@ -0,0 +1,10 @@ + +fn get_third[T](&tup(T,T,T) t) -> T { + ret t._2; +} + +fn main() { + log get_third(tup(1,2,3)); + check (get_third(tup(1,2,3)) == 3); + check (get_third(tup(5u8,6u8,7u8)) == 7u8); +} \ No newline at end of file