This takes the `AllocId` out of PrimValKind, replacing it with a
`relocation` field on `PrimVal`, which is closer to an earlier design
for `PrimVal` I discussed with @eddyb.
This commit prepares the code for removing the `PrimValKind` from
`PrimVal` and making them more pure bitbags. The only code dealing with
`PrimValKind` will be code making decisions like "what kind of operation
do I need to do on these bits", like operators and casting. Transmutes
of `PrimVal`s will become true no-ops, not even adjusting a `kind`
field.
This commit also removes my horrible `value_to_primval` hack that made
an allocation for every `ByVal` passed in, so it could use `read_value`
to get a `PrimVal` with the right kind. Now I just compute the
`PrimValKind` from the `Ty` and re-tag the `PrimVal`.
The code got slightly messier in some areas here, but I think a _lot_ of
code will simplify in obvious ways once I remove the `kind` field from
`PrimVal`.
Gosh, if my commit messages aren't turning into essays these days.
Now instead of holding a native type based on the tag, all PrimVals
store a u64 (the `bits`), along with a `kind` corresponding to the
variant as it would be in the old PrimVal representation.
This commit makes no major optimizations and attempts to not change any
behaviour. There will be commits to follow that make use of this
representation to eliminate unnecessary allocation hacks like in
`value_to_primval`.
A number of places could be even more cleaned up after this commit,
particularly in `cast.rs`.
Previously, you could perform the following, if you assume we could make
`Cell<i32>` into a primitive. (Alternately, you could achieve this with
unsafe code):
x = Cell::new(12);
y = &x;
// Miri locals array:
// x = ByRef(alloc123);
// y = ByVal(Ptr(alloc123));
//
// Miri allocations:
// alloc123: [12, 0, 0, 0]
x.set(42);
// Miri locals array:
// x = ByVal(I32(42));
// y = ByVal(Ptr(alloc123));
//
// Miri allocations:
// alloc123: [12, 0, 0, 0]
Notice how `y` still refers to the allocation that used to represent
`x`. But now `x` was changed to `42` and `y` is still looking at memory
containing `12`.
Now, instead, we keep `x` as a `ByRef` and write the `42` constant into
it.
Unit test to follow in the next commit.
This helps in the case of field projections of the results of checked
binary operations. E.g.:
_1 = CheckedAdd(const 1i32, const 2i32);
assert(!(_1.1: bool), "attempt to add with overflow" -> bb1
Previously, the `_1.1` field projection lvalue would force_allocate `_1`
so it could read the memory in the old-style way. Now checked math with
its assertions will not allocate at all.
The oom2.rs compile-fail test had to be re-written, because the old
version of it no longer allocates _at all_ (yay!), so it would hit the
stack depth limit instead, from recursion.
Thanks to the `Value` locals refactoring, now primitive locals (ints,
floats, chars, bools, and the like) will not require `Allocation`s at
all, and locals that are never initialized at all because of conditional
control flow won't be wasting memory.
Previously ReturnPointer was just the first slot in the locals array,
which had type `Vec<Pointer>`. But after my recent refactoring, locals
is `Vec<Value>` and it became increasingly hacky to pull a pointer out
of the first slot to be the value. Besides, that hack wouldn't allow
ReturnPointer to ever be an `Lvalue::Local`, referring directly to a
local on a higher stack frame.
Now ReturnPointer has no presence in the locals array, instead being
upgraded to its own field on `Frame`.
This introduces a couple of new hacks, detailed by some of my FIXME
comments, so that I could get the tests passing again and commit. More
commits coming soon should clean up these hacks without much trouble,
and overall I feel that the code is converging on a cleaner, more
efficient design.
The new `Lvalue` has an additional form, `Lvalue::Local`, with the old
form being `Lvalue::Ptr`. In an upcoming commit, we will start producing
the new form for locals, and enable reading and writing of primitive
locals without ever touching `Memory`.
Statics should be able to get a similar treatment, where primitive
statics can be stored and accessed without touching `Memory`.
Turning locals into `Vec<Value>` will allow writing `PrimVal` results
directly into the locals array without creating `memory::Allocation`s
for every local.
This will entail passing around a generalized kind of `Lvalue` instead
of `Pointer`s for the destinations of operations. Replacing `Pointer`
with `Lvalue` is mostly done with this commit, but expanding `Lvalue`
will come later.
This commit turns every local from `Pointer` into `Value::ByRef(ptr)`.
Locals which are `Value::ByVal(prim_val)` will come in a later commit.