TMI: More fun with boxed classes
May. 9th, 2012 10:17 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
(From Wednesday, May 9)
Continuing the work on heap-allocated classes (which I ended up abandoning), I ran into more bugs with how I was invoking GetElementPtr:
and the assertion failure was saying that lhs has the wrong type for me to
index into it with ix (I wasn't sure what the problematic relevant value of ix is). Time to add another print statement. I also looked at "The Often Misunderstood GEP Instruction", but all I learned just then was that I still wasn't sure I understood how classes were laid out in memory.
LLVMDumpValue prints out the code for the relevant field dereference as:
where %5 is the memory location that the self pointer gets stored in. That's for the @class case. But what does it look like for the class case? Well, if I run a stack-allocated-class example with debugging on, I get
Since the class in both of these cases has two 64-bit int fields, the { i64, i64 } part -- the actual data in the class -- looks the same in both cases. The alloca instruction returns a pointer to a struct with two 64-bit int fields (in this case, anyway). I guess I see what's going on here. (And also, is alloca allocating on the stack?) The class case seems to box twice, which is weird, but I just want to make it work for now, and then I'll go through and check that it's actually doing what we expect.
I found that the code was double-boxing because type_of maps @class types onto boxes, but lval_static_fn_inner creates an additional level of indirection, which is not necessary.
Then, the code I had translated the ctor; but I got an "Invalid GEP pointer type" when translating a reference to a class field. So I guess I also have to translate references to class fields so as to dereference them. Or else, in the context, self could be set to a pointer to the body, not the box itself? In that case, actually, the context would have to contain both a pointer to the body and a pointer to the box, since self can escape (that's the whole point!)
So there were two ways to structure the trans code (both ways generate the same code)
* pass around both the box and the body -- the box would be an option, and would be some(...) if and only if we're translating an @class
* keep the self pointer represented as it is, but add a flag to the context that's true if and only if we're on an @class
These are almost equivalent. I also wondered whether clever use of autoderef (Rust's type system allows some values to be dereferenced automatically: for example, you can write x.y if x is a pointer to a record with field y) would do the trick.
I realized the inconsistency arose from the predicate type_is_boxed being true for an @class. Because of that, load_is_immediate was dereferencing the self value one too many
times. Other than that, trans_path already does the right thing and pulls the box
field out of the self pointer. But then I got confused thinking about how @class should be categorized. It should be the same as a box that points to a class -- but having to insert automatic dereferences confused me. And that was it for Wednesday.
Continuing the work on heap-allocated classes (which I ended up abandoning), I ran into more bugs with how I was invoking GetElementPtr:
Assertion failed: (Ty && "Invalid GetElementPtrInst indices for type!"), function checkGEPType, file /Users/TimChevalier/rust/src/llvm/include /llvm/Instructions.h, line 703.
In other words, I was indexing into something (like a struct) out of bounds or that didn't. have a field of the right type. trans_class_ctor had two calls to GEPi, but the backtrace showed me that the crashing one was being called from an anonymous function so I could see that the problem was with this code:
for fields.each {|field| let ix = field_idx_strict(bcx.tcx(), sp, field.ident, fields); bcx = zero_alloca(bcx, GEPi(bcx, lhs, [0u, ix]), field.mt.ty); }
and the assertion failure was saying that lhs has the wrong type for me to
index into it with ix (I wasn't sure what the problematic relevant value of ix is). Time to add another print statement. I also looked at "The Often Misunderstood GEP Instruction", but all I learned just then was that I still wasn't sure I understood how classes were laid out in memory.
LLVMDumpValue prints out the code for the relevant field dereference as:
%5 = getelementptr inbounds { i64, %tydesc*, i8*, i8*, { i64, %tydesc*, i8*, i8*, { i64, i64 } }* }* %17, i32 0, i32 4
where %5 is the memory location that the self pointer gets stored in. That's for the @class case. But what does it look like for the class case? Well, if I run a stack-allocated-class example with debugging on, I get
%4 = alloca { i64, i64 }
Since the class in both of these cases has two 64-bit int fields, the { i64, i64 } part -- the actual data in the class -- looks the same in both cases. The alloca instruction returns a pointer to a struct with two 64-bit int fields (in this case, anyway). I guess I see what's going on here. (And also, is alloca allocating on the stack?) The class case seems to box twice, which is weird, but I just want to make it work for now, and then I'll go through and check that it's actually doing what we expect.
I found that the code was double-boxing because type_of maps @class types onto boxes, but lval_static_fn_inner creates an additional level of indirection, which is not necessary.
Then, the code I had translated the ctor; but I got an "Invalid GEP pointer type" when translating a reference to a class field. So I guess I also have to translate references to class fields so as to dereference them. Or else, in the context, self could be set to a pointer to the body, not the box itself? In that case, actually, the context would have to contain both a pointer to the body and a pointer to the box, since self can escape (that's the whole point!)
So there were two ways to structure the trans code (both ways generate the same code)
* pass around both the box and the body -- the box would be an option, and would be some(...) if and only if we're translating an @class
* keep the self pointer represented as it is, but add a flag to the context that's true if and only if we're on an @class
These are almost equivalent. I also wondered whether clever use of autoderef (Rust's type system allows some values to be dereferenced automatically: for example, you can write x.y if x is a pointer to a record with field y) would do the trick.
I realized the inconsistency arose from the predicate type_is_boxed being true for an @class. Because of that, load_is_immediate was dereferencing the self value one too many
times. Other than that, trans_path already does the right thing and pulls the box
field out of the self pointer. But then I got confused thinking about how @class should be categorized. It should be the same as a box that points to a class -- but having to insert automatic dereferences confused me. And that was it for Wednesday.