pub struct Invariant { /* private fields */ }Expand description
A value that does not change inside the loop, read as on + scale * value + offset.
The value is a value defined outside the loop, or None when the linear part is a plain
number. Keeping the shape rather than a bare Value is what lets j = 2 * i + 3 come out
as {3, +, 2} instead of unknown: the base and the step of that chrec are expressions nothing
in the function computes, so a representation that could only name existing values would have
to give up.
The on is a second symbol, and it is there for one shape: a pointer plus an index the loop
did not start at zero. a[i] with i starting at a parameter has a first address of
a + start * 4, which is two symbols, and a representation with room for one has to answer
unknown to it. Nothing scales on and nothing negates it, because the thing it was added for
is a pointer and a pointer is not something a loop multiplies. It is an Anchor rather than
a value so that the address of a global can be one of them.
The read is the other half of the same shape, since in C that index is an int and what
reaches the address is sext(start). It is described rather than named, for the reason on
Widening. The two together are what let a[start + i] be followed, and on the SQLite
amalgamation they take 158 checks and 12 sites off the largest row of loop splitting’s census.
See tamnd/rucc#810.
Arithmetic on two of these is refused once the sum would need a third symbol, because
x + y + z is not of this shape. That is the boundary of the subset and it is where the answer
becomes unknown rather than wrong.
The fields are private on purpose. Every reader has to go through Invariant::plain, which
hands back the one symbol reading and refuses when there is a pointer in it, or through
Invariant::on, which hands back both halves. A reader that helped itself to value and
scale would quietly drop the on and build an address off the wrong object.
Implementations§
Source§impl Invariant
impl Invariant
Sourcepub fn scaled(value: Value, scale: i128, offset: i128) -> Self
pub fn scaled(value: Value, scale: i128, offset: i128) -> Self
So many of a value, plus a number.
Sourcepub fn address(symbol: Symbol) -> Self
pub fn address(symbol: Symbol) -> Self
The address of a global.
It goes straight into the on slot rather than into value, because that slot is the one
for the thing an address is measured from and an address is the only thing this ever is.
Nothing scales it and nothing negates it, which the rest of the arithmetic here already
refuses for whatever is in that slot.
Sourcepub fn plain(self) -> Option<Plain>
pub fn plain(self) -> Option<Plain>
The one symbol reading, and None when there is a second symbol in it.
Sourcepub fn on(self) -> Option<(Anchor, Plain)>
pub fn on(self) -> Option<(Anchor, Plain)>
What it is measured from and how far past that, when there is a second symbol in it.
Exactly one of this and Invariant::plain answers, so a reader that handles both has
handled every invariant there is.
Sourcepub fn alike(self, other: Self) -> bool
pub fn alike(self, other: Self) -> bool
Whether the two are the same expression apart from the number added to them.
Sourcepub fn minus(self, other: Self) -> Option<Self>
pub fn minus(self, other: Self) -> Option<Self>
The second subtracted from the first, when the difference is of this shape.