singrdk/base/Imported/Bartok/runtime/verified/GCs/VerifiedCopyingCollector.bpl

2804 lines
106 KiB
Plaintext

//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Verified copying garbage collector
//
// medium term goal: support more Bartok array-of-struct and vector-of-struct object layouts
// medium term goal: make generational
// long term goal: support various other features: threads, pinning, stack markers, etc.
// Imports:
// - Trusted.bpl
// - VerifiedBitVectors.bpl
// Includes:
// - VerifiedCommon.bpl
// \Spec#\bin\Boogie.exe /noinfer Trusted.bpl VerifiedBitVectors.bpl VerifiedCommon.bpl VerifiedCopyingCollector.bpl
/*****************************************************************************
******************************************************************************
**** VERIFIED
******************************************************************************
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// COPYING COLLECTOR
//////////////////////////////////////////////////////////////////////////////
function IsFwdPtr(i:int) returns(bool) { gcAddrEx(i) }
var $freshAbs:int;
// The allocation bitmap ranges from ?gcLo..HeapLo
// The spaces (from and to) range from HeapLo..?gcHi
var HeapLo:int;
// Fromspace ranges from Fi to Fl, where Fk..Fl is empty
// Tospace ranges from Ti to Tl, where Tk..Tl is empty
var Fi:int;
var Fk:int;
var Fl:int;
var Ti:int;
var Tj:int;
var Tk:int;
var Tl:int;
// Bitmaps for fromspace and tospace:
var BF:int;
var BT:int;
function ObjectSpc(i1:int, i2:int, r:[int]int) returns (bool)
{
(forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 && r[i] != NO_ABS ==>
i + 4 * numFields(r[i]) <= i2
&& (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==> r[ii] == NO_ABS))
}
function ObjectSeq(i1:int, i2:int, r:[int]int) returns (bool)
{
(i1 == i2 || r[i1] != NO_ABS)
&& (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 && r[i] != NO_ABS ==>
(forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==>
r[ii] == NO_ABS)
&& (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields(r[i]) ==> gcAddr(i + 4 * j)) // REVIEW: necessary?
&& ( (i + 4 * numFields(r[i]) == i2)
|| (i + 4 * numFields(r[i]) < i2 && r[i + 4 * numFields(r[i])] != NO_ABS)))
}
function EmptySeq(i1:int, i2:int, r:[int]int, $toAbs:[int]int) returns (bool)
{
(forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 ==> gcAddr(i) && r[i] == NO_ABS && $toAbs[i] == NO_ABS) // REVIEW: gcAddr necessary?
}
// invariant:
// note: typically, Tj = _tj and Tk = _tk
function CopyGcInv($freshAbs:int, BF:int, BT:int, HeapLo:int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, _tj:int, Tk:int, _tk:int, Tl:int, $Time:Time, $r1:[int]int, $r2:[int]int,
r1Live:bool, $toAbs:[int]int, $AbsMem:[int][int]int,
$GcMem:[int]int, $gcSlice:[int]int) returns (bool)
{
?gcLo <= HeapLo
&& HeapLo <= ?gcHi
&& ((Fi == HeapLo && Ti == Fl && BF == ?gcLo) || (Ti == HeapLo && Fi == Tl && BT == ?gcLo))
&& (Fi == HeapLo ==> ?gcLo + BitIndex(HeapLo, Fl) == BT && ?gcLo + BitIndex(HeapLo, Tl) == HeapLo)
&& (Ti == HeapLo ==> ?gcLo + BitIndex(HeapLo, Tl) == BF && ?gcLo + BitIndex(HeapLo, Fl) == HeapLo)
&& Fi <= Fk && Fk <= Fl && Fl <= ?gcHi
&& Ti <= Tj && Tj <= _tj && _tj <= Tk && Tk <= _tk && _tk <= Tl && Tl <= ?gcHi
&& Aligned(Fi) && Aligned(Fk) && Aligned(Ti) && Aligned(Tj) && Aligned(Tk)
&& Aligned(?gcLo + BitIndex(HeapLo, Fi)) && Aligned(?gcLo + BitIndex(HeapLo, Ti))
&& ?gcLo <= ?gcLo + BitIndex(HeapLo, Fi) && ?gcLo + BitIndex(HeapLo, Fl) <= HeapLo
&& ?gcLo <= ?gcLo + BitIndex(HeapLo, Ti) && ?gcLo + BitIndex(HeapLo, Tl) <= HeapLo
&& BF == ?gcLo + BitIndex(HeapLo, Fi) && BT == ?gcLo + BitIndex(HeapLo, Ti)
&& (Fl - Fi) == Mult(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))) // Mult is faster than "*" here
&& (Tl - Ti) == Mult(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))) // Mult is faster than "*" here
&& bbvec4($r1, NO_ABS, Fi, $GcMem, Fi, Fi, Fl, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl))
&& bbvec4($toAbs, NO_ABS, Ti, $GcMem, Ti, Ti, Tl, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl))
&& WellFormed($toAbs)
&& ObjectSpc(Fi, Fk, $r1)
&& EmptySeq( Fk, Fl, $r1, $toAbs)
&& ObjectSpc(Ti, Tj, $r2)
&& ObjectSeq(_tj, Tk, $r2)
&& EmptySeq( _tk, Tl, $r2, $toAbs)
&& (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $r1[i] != NO_ABS && r1Live ==> Fi <= i && i < Fk)
&& (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $r2[i] != NO_ABS ==> Ti <= i && i < _tk)
&& (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $r1[i] != NO_ABS ==>
( IsFwdPtr($GcMem[i + 4]) <==> $toAbs[i] == NO_ABS)
&& ( IsFwdPtr($GcMem[i + 4]) ==> Pointer($r2, $GcMem[i + 4] - 4, $r1[i]) && AlignedHeapAddr(i + 4) && word($GcMem[i + 4]))
&& (!IsFwdPtr($GcMem[i + 4]) ==> $toAbs[i] == $r1[i])
&& (!IsFwdPtr($GcMem[i + 4]) ==> r1Live ==> ObjInv(i, $r1, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice))
&& i + 4 < Fk // REVIEW: hack?
&& Aligned(i) // REVIEW: redundant?
)
&& (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fl && $toAbs[i] != NO_ABS ==> $r1[i] != NO_ABS && $r1[i] != NO_ABS)
&& (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tl && $toAbs[i] != NO_ABS ==> $r2[i] != NO_ABS && $r2[i] != NO_ABS)
&& (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tj && $r2[i] != NO_ABS ==>
$toAbs[i] != NO_ABS && $toAbs[i] == $r2[i]
&& reached($toAbs[i], $Time)
&& ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)
&& !IsFwdPtr($GcMem[i + 4])
)
&& (Tj != _tj ==> (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < _tj ==>
gcAddr(Tj + 4 * j) && $gcSlice[Tj + 4 * j] == Tj)) // REVIEW: gcAddr necessary?
&& (forall i:int::{TV(i)} TV(i) ==> Tj < i && i < _tj ==> $r2[i] == NO_ABS)
&& (forall i:int::{TV(i)} TV(i) ==> _tj <= i && i < Tk && $r2[i] != NO_ABS ==>
$toAbs[i] != NO_ABS && $toAbs[i] == $r2[i]
&& reached($toAbs[i], $Time)
&& ObjInv(i, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)
&& !IsFwdPtr($GcMem[i + 4])
)
&& (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs && $r2[i] != $freshAbs)
}
function MutatorInv(BF:int, BT:int, HeapLo:int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, Tk:int, Tl:int,
$GcMem:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, $gcSlice:[int]int) returns (bool)
{
?gcLo <= HeapLo
&& HeapLo <= ?gcHi
&& ((Fi == HeapLo && Ti == Fl && BF == ?gcLo) || (Ti == HeapLo && Fi == Tl && BT == ?gcLo))
&& (Fi == HeapLo ==> ?gcLo + BitIndex(HeapLo, Fl) == BT && ?gcLo + BitIndex(HeapLo, Tl) == HeapLo)
&& (Ti == HeapLo ==> ?gcLo + BitIndex(HeapLo, Tl) == BF && ?gcLo + BitIndex(HeapLo, Fl) == HeapLo)
&& Fi <= Fk && Fk <= Fl && Fl <= ?gcHi
&& Ti <= Tj && Tj <= Tk && Tk <= Tl && Tl <= ?gcHi
&& Aligned(Fi) && Aligned(Fk) && Aligned(Ti) && Aligned(Tj) && Aligned(Tk)
&& Aligned(?gcLo + BitIndex(HeapLo, Fi)) && Aligned(?gcLo + BitIndex(HeapLo, Ti))
&& ?gcLo <= ?gcLo + BitIndex(HeapLo, Fi) && ?gcLo + BitIndex(HeapLo, Fl) <= HeapLo
&& ?gcLo <= ?gcLo + BitIndex(HeapLo, Ti) && ?gcLo + BitIndex(HeapLo, Tl) <= HeapLo
&& BF == ?gcLo + BitIndex(HeapLo, Fi) && BT == ?gcLo + BitIndex(HeapLo, Ti)
&& (Fl - Fi) == Mult(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))) // Mult is faster than "*" here
&& (Tl - Ti) == Mult(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))) // Mult is faster than "*" here
&& bbvec4($toAbs, NO_ABS, Fi, $GcMem, Fi, Fi, Fl, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl))
&& WellFormed($toAbs)
&& ObjectSpc(Fi, Fk, $toAbs)
&& EmptySeq( Fk, Fl, $toAbs, $toAbs)
&& EmptySeq( Ti, Tl, $toAbs, $toAbs)
&& (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> ($toAbs[i] != NO_ABS ==> Fi <= i && i < Fk))
&& (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $toAbs[i] != NO_ABS ==>
$toAbs[i] != NO_ABS
&& ObjInv(i, $toAbs, $toAbs, $toAbs, $AbsMem, $GcMem, $gcSlice)
&& !IsFwdPtr($GcMem[i + 4])
)
&& (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fl && $toAbs[i] != NO_ABS ==> $toAbs[i] != NO_ABS)
&& (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tl && $toAbs[i] != NO_ABS ==> false)
}
procedure BB4Zero($a:[int]int, $on:int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int, $_i0:int, $_g1:int)
requires eax == $g1;
requires ebx == $g2;
requires (forall i:int::{TV(i)} TV(i) && $i1 <= i && i < $i2 ==> $a[$aBase + (i - $i0)] == $off);
requires Aligned($g1) && Aligned($g2);
requires $i2 - $i1 == 32 * ($g2 - $g1);
requires word($i1 - $i0) && word($i2 - $i0);
requires ?gcLo <= $g1 && $g1 <= $g2 && $g2 <= ?gcHi;
requires $i1 == $i0;
modifies $GcMem;
modifies eax, ebx, esi, edi, ebp, esp;
ensures bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2);
ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]);
ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i + 4) ==> $GcMem[i + 4] == old($GcMem)[i + 4]); // REVIEW: HACK?
ensures (forall i:int,j:int::{TV(i),TO(j)} TV(i) && TO(j) && !between($g1, $g2, i + 4 * j) ==> $GcMem[i + 4 * j] == old($GcMem)[i + 4 * j]); // REVIEW: HACK?
ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, $_g1 + BitIndex($_i0, i)) ==> $GcMem[$_g1 + BitIndex($_i0, i)] == old($GcMem)[$_g1 + BitIndex($_i0, i)]); // REVIEW: HACK?
{
var $iter:int;
esi := eax;
$iter := $i1;
//while (esi < $g2)
loop:
assert Aligned(esi) && TV(esi);
assert $g1 <= esi && esi <= $g2;
assert $iter - $i1 == 32 * (esi - $g1);
assert bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2);
assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]);
assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i + 4) ==> $GcMem[i + 4] == old($GcMem)[i + 4]); // REVIEW: HACK?
assert (forall i:int,j:int::{TV(i),TO(j)} TV(i) && TO(j) && !between($g1, $g2, i + 4 * j) ==> $GcMem[i + 4 * j] == old($GcMem)[i + 4 * j]); // REVIEW: HACK?
assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, $_g1 + BitIndex($_i0, i)) ==> $GcMem[$_g1 + BitIndex($_i0, i)] == old($GcMem)[$_g1 + BitIndex($_i0, i)]); // REVIEW: HACK?
if (esi >= ebx) { goto loopEnd; }
call __notAligned(esi);
call __bb4Zero($a, $off, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2, esi);
call GcStore(esi, 0);//$GcMem[esi] := 0;
$iter := $iter + 128;
call esi := Add(esi, 4);
assert TO(1);
goto loop;
loopEnd:
assert esi == $g2;
assert $iter == $i2;
esp := esp + 4; return;
}
procedure bb4GetBit($a:[int]int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int)
requires bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2);
requires word($k - $i0) && $i1 <= $k && $k < $i2;
requires word($k) && word($i0) && Aligned($k) && Aligned($i0);
requires word($i2 - $i0);
requires eax == $g1;
requires ebx == $k - $i0;
requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi;
requires word($i1 - $i0) && word($i2 - $i0);
modifies ebx, ecx, edx;
ensures ebx == 0 <==> $a[$aBase + ($k - $i0)] == $off;
{
var $idx:int;
var $bbb:int;
$idx := $g1 + 4 * shr($k - $i0, 7);
$bbb := and($GcMem[$idx], shl(1, and(shr($k - $i0, 2), 31)));
call __subAligned($k, $i0);
call __bb4GetBit($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $g1, $g2);
edx := ebx;
assert TV($g1);
assert TO(shr(ebx, 7));
call ebx := Shr(ebx, 7);
call edx := Shr(edx, 2);
call ebx := Add(ebx, ebx);
call ebx := Add(ebx, ebx);
call ebx := Add(ebx, eax);
call ebx := GcLoad(ebx);
call edx := And(edx, 31);
ecx := edx;
edx := 1;
call edx := Shl(edx, ecx);
call ebx := And(ebx, edx);
}
procedure bb4SetBit($a:[int]int, $on:int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int)
requires bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2);
requires word($k - $i0) && $i1 <= $k && $k < $i2;
requires word($k) && word($i0) && Aligned($k) && Aligned($i0);
requires $on != $off;
requires word($i2 - $i0);
requires esi == $k - $i0;
requires edi == $g1;
requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi;
requires word($i1 - $i0) && word($i2 - $i0);
modifies esi, edi, ecx, $GcMem;
ensures bbvec4($a[$aBase + ($k - $i0) := $on], $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2);
ensures $GcMem == old($GcMem)[$g1 + BitIndex($i0, $k) := edi];
ensures Aligned($k - $i0);
{
var $idx:int;
var $bbb:int;
$idx := $g1 + 4 * shr($k - $i0, 7);
$bbb := or($GcMem[$idx], shl(1, and(shr($k - $i0, 2), 31)));
call __subAligned($k, $i0);
call __bb4SetBit($a, $on, $off, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $GcMem[$idx := $bbb], $g1, $g2);
ecx := esi;
assert TV($g1);
assert TO(shr(esi, 7));
call esi := Shr(esi, 7);
call ecx := Shr(ecx, 2);
call esi := Add(esi, esi);
call esi := Add(esi, esi);
call esi := Add(esi, edi);
call ecx := And(ecx, 31);
edi := 1;
call edi := Shl(edi, ecx);
ecx := edi; // REVIEW: optimize this more
call edi := GcLoad(esi);
call edi := Or(edi, ecx);
call GcStore(esi, edi);
}
procedure copyWord($ptr:int, $_tj:int, $ret:int, $ind:int, $s:int)
requires ecx == $ptr && esi == $ret && edi == $ind;
requires Pointer($r1, $ptr, $r1[$ptr]) && TV($ptr);
requires !IsFwdPtr($GcMem[$ptr + 4]);
requires $_tj <= Tk;
requires $s == 4 * numFields($r1[$ptr]);
requires Tk == $ret + $s;
requires Tk <= Tl;
requires TO($ind) && 0 <= $ind && $ind < numFields($r1[$ptr]);
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, $ret, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j) && $gcSlice[$ret + 4 * j] == $ret); // REVIEW: gcAddr necessary?
requires EmptySeq($ret + 4 * $ind, Tk, $r2, $toAbs);
requires (forall j:int::{TO(j)} TO(j) ==>
0 <= j && j < $ind ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[$ret + 4 * j], $AbsMem[$toAbs[$ptr]][j]));
requires (forall i:int::{TV(i)} TV(i) ==> $ret < i && i < $ret + 4 * $ind ==> $r2[i] == NO_ABS);
requires $r2[$ret] == NO_ABS;
modifies $GcMem, $gcSlice;
modifies eax, ebx;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, $ret, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> gcAddr($ret + 4 * j) && $gcSlice[$ret + 4 * j] == $ret); // REVIEW: gcAddr necessary?
ensures (forall j:int::{TO(j)} TO(j) ==>
0 <= j && j < $ind + 1 ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[$ret + 4 * j], $AbsMem[$toAbs[$ptr]][j]));
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==>
$GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j]));
ensures RExtend(old($r2), $r2);
{
assert TO(numFields($r1[$ptr]));
assert TV($ret);
call eax := GcLoad(ecx + 4 * edi);
assert TV($ret + 4 * $ind);
$gcSlice[$ret + 4 * $ind] := $ret;
call GcStore(esi + 4 * edi, eax);
assert TO(1);
}
procedure CopyAndForward($ptr:int, $_tj:int)
requires ecx == $ptr;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Pointer($r1, $ptr, $r1[$ptr]) && TV($ptr);
requires !IsFwdPtr($GcMem[$ptr + 4]);
requires $_tj <= Tk;
requires reached($toAbs[$ptr], $Time);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
ensures Pointer($r2, eax, $r1[$ptr]);
ensures Tj == old(Tj);
ensures Tk >= old(Tk);
ensures old($toAbs)[Tj] != NO_ABS ==> $toAbs[Tj] != NO_ABS && $toAbs[Tj] == old($toAbs)[Tj];
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==>
$GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j]));
ensures Ti <= eax && eax < Tk && gcAddrEx(eax + 4);
{
var tmp:int;
call edx := GcLoad(ecx + 4);
esp := esp - 4; call GetSize($ptr, edx, $r1, $r1);
ebp := eax;
assert TO(numFields($r1[$ptr]));
esi := Tk;
call Tk := AddChecked(Tk, ebp);
assert TV(esi);
eax := Tl;
if (Tk <= eax) { goto skip1; }
// out of memory
call DebugBreak();
skip1:
edi := 0;
edx := 0;
// while (edx < ebp)
loop:
assert 4 * edi == edx;
assert TO(edi) && 0 <= edi && edi <= numFields($r1[$ptr]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, esi, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < edi ==> gcAddr(esi + 4 * j) && $gcSlice[esi + 4 * j] == esi); // REVIEW: gcAddr necessary?
assert EmptySeq(esi + 4 * edi, Tk, $r2, $toAbs);
assert (forall j:int::{TO(j)} TO(j) ==>
0 <= j && j < edi ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[esi + 4 * j], $AbsMem[$toAbs[$ptr]][j]));
assert (forall i:int::{TV(i)} TV(i) ==> esi < i && i < esi + 4 * edi ==> $r2[i] == NO_ABS);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==>
$GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j]));
assert $r2[esi] == NO_ABS;
if (edx >= ebp) { goto loopEnd; }
call copyWord($ptr, $_tj, esi, edi, ebp);
call edi := Add(edi, 1);
call edx := Add(edx, 4);
goto loop;
loopEnd:
call eax := Lea(esi + 4);
call GcStore(ecx + 4, eax);
eax := esi;
call esi := Sub(esi, Ti);
edi := BT;
call bb4SetBit($toAbs, $r1[$ptr], NO_ABS, Ti, Ti, Ti, Tl, eax, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl));
$r2[eax] := $r1[$ptr];
$toAbs[eax] := $toAbs[$ptr];
$toAbs[$ptr] := NO_ABS;
assert TO(1);
assert TV(eax - Ti);
esp := esp + 4; return;
}
procedure forwardFromspacePtr($ptr:int, $abs:int, $_tj:int)
requires ecx == $ptr;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires word($ptr);
requires Pointer($r1, $ptr - 4, $abs);
requires $_tj <= Tk;
requires IsFwdPtr($GcMem[$ptr]) || reached($toAbs[$ptr - 4], $Time);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
ensures Pointer($r2, eax - 4, $abs);
ensures Tj == old(Tj);
ensures Tk >= old(Tk);
ensures old($toAbs)[Tj] != NO_ABS ==> $toAbs[Tj] != NO_ABS && $toAbs[Tj] == old($toAbs)[Tj];
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==>
$GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j]));
ensures Ti <= eax - 4 && eax - 4 < Tk;
ensures gcAddrEx(eax);
{
assert TV($ptr - 4);
call eax := GcLoad(ecx);
if (?gcLo > eax) { goto skip; }
if (?gcHi >= eax) { goto done; }
skip:
call ecx := Sub(ecx, 4);
esp := esp - 4; call CopyAndForward($ptr - 4, $_tj);
call eax := Add(eax, 4);
done:
assert TV(eax - 4);
}
procedure scanObjectDense($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?DENSE_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var save1:int;
var save2:int;
var save3:int;
assert TO(numFields($r2[Tj]));
esi := 2;
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH);
call edx := RoLoad32(ecx + ?VT_MASK);
edi := Tj;
call edi := Add(edi, 8);
call ebp := Add(ebp, Tj);
//while (edi < ebp)
loop:
assert TO(esi);// && 0 < esi;
assert edi == Tj + 4 * esi && ebp == Tj + 4 * numFields($r2[Tj]) && edx == mask($vt);
assert 2 <= esi && esi <= numFields($r2[Tj]);
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvPartial(Tj, 0, esi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert ObjInvPartial(Tj, esi, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
if (edi >= ebp) { goto loopEnd; }
if (esi >= 30) { goto loopEnd; }
assert TO(0) && TO(1);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
//assert getBit(mask($vt), 2 + esi) == VFieldPtr($r2[Tj], esi);
//if (getBit(mask, 2 + esi))
call ecx := Lea(esi + 2);
ebx := edx;
call ebx := Shr(ebx, ecx);
call ebx := And(ebx, 1);
if (ebx != 1) { goto skip1; }
call ecx := GcLoad(edi);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip2; }
if (ecx > ?gcHi) { goto skip2; }
assert TV(ecx - 4);
call reach($toAbs[Tj], esi, $Time);
save1 := esi;
save2 := ebp;
save3 := edx;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][esi], Tj + 4 * numFields($r2[Tj]));
esi := save1;
ebp := save2;
edx := save3;
edi := Tj;
call edi := Lea(edi + 4 * esi);
call GcStore(edi, eax);
skip2:
skip1:
call esi := Add(esi, 1);
call edi := Add(edi, 4);
goto loop;
loopEnd:
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
Tj := ebp;
assert TO(1);
}
procedure scanObjectSparse($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?SPARSE_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var save1:int;
var save2:int;
var save3:int;
var save4:int;
assert TO(numFields($r2[Tj]));
esi := 1;
assert TO(numFields($r2[Tj]));
assert TVT($toAbs[Tj], $vt);
call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH);
call edx := RoLoad32(ecx + ?VT_MASK);
assert TVL($r2[Tj]);
esi := 1;
//while (esi < 8)
loop:
assert edx == mask($vt) && ebp == 4 * numFields($r2[Tj]);
assert TSlot(esi) && 0 < esi && esi <= 8;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvBase(Tj, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==>
between(1, esi, fieldToSlot($vt, j - 2)) ==>
ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice));
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==>
!between(1, esi, fieldToSlot($vt, j - 2)) ==>
ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice));
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
if (esi >= 8) { goto loopEnd; }
assert TO(0) && TO(1);
assert TVT($toAbs[Tj], $vt);
assert TO(getNib(edx, 4 * esi) + 1);
//if (getNib(edx, 4 * esi) != 0)
ecx := 0;
call ecx := Lea(ecx + 4 * esi);
ebx := edx;
call ebx := Shr(ebx, ecx);
call ebx := And(ebx, 15);
if (ebx == 0) { goto skip1; }
eax := Tj;
call ecx := GcLoad(eax + 4 * ebx + 4);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip2; }
if (ecx > ?gcHi) { goto skip2; }
assert TV(ecx - 4);
call reach($toAbs[Tj], 1 + ebx, $Time);
save1 := esi;
save2 := ebp;
save3 := edx;
save4 := ebx;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][1 + getNib(edx, 4 * esi)], Tj + ebp);
esi := save1;
ebp := save2;
edx := save3;
ebx := save4;
ecx := Tj;
call GcStore(ecx + 4 * ebx + 4, eax);
skip2:
skip1:
call esi := Add(esi, 1);
goto loop;
loopEnd:
assert TVT($toAbs[Tj], $vt);
assert TVL($r2[Tj]);
call Tj := Add(Tj, ebp);
assert TO(1);
}
procedure scanObjectOtherVectorNoPointers($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?OTHER_VECTOR_TAG;
requires arrayOf($vt) != ?TYPE_STRUCT || (arrayOf($vt) == ?TYPE_STRUCT && mask(arrayElementClass($vt)) == ?SPARSE_TAG);
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
edx := ecx;
ecx := Tj;
esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1);
assert TO(numFields($r2[Tj]));
assert TVT($r2[Tj], $vt);
call Tj := Add(Tj, eax);
}
procedure scanObjectOtherVectorPointersSparseFields($vt:int, $jj:int, $index:int)
requires edx == mask(arrayElementClass($vt));
requires edi == $jj;
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?OTHER_VECTOR_TAG;
requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG;
requires TO($jj) && $jj == baseWords($vt) + Mult(arrayElementWords($vt), $index);
requires $jj < numFields($r2[Tj]);
requires TO($index) && 0 <= $index; // && $index <= nElems;
requires Mult(arrayElementWords($vt), $index) >= 0;
requires reached($toAbs[Tj], $Time);
requires Pointer($r2, Tj, $r2[Tj]);
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires ObjInvPartial(Tj, $jj, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
requires (forall j:int::{TO(j)} TO(j) ==>
between(0, arrayElementWords($vt), j - $jj) ==>
ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice));
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures Pointer($r2, Tj, $r2[Tj]);
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
ensures ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures ObjInvPartial(Tj, $jj + arrayElementWords($vt), numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
ensures (forall j:int::{TO(j)} TO(j) ==>
between(0, arrayElementWords($vt), j - $jj) ==>
between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==>
ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice));
ensures (forall j:int::{TO(j)} TO(j) ==>
between(0, arrayElementWords($vt), j - $jj) ==>
!between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==>
ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice));
ensures edi == old(edi);
ensures edx == old(edx);
{
var save1:int;
var save2:int;
var save3:int;
var save4:int;
assert TO(numFields($r2[Tj]));
assert TO(2);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
esi := 1;
//while (esi < 8)
loop:
assert TSlot(esi) && 0 < esi && esi <= 8;
assert edx == mask(arrayElementClass($vt));
assert edi == $jj;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert ObjInvPartial(Tj, $jj + arrayElementWords($vt), numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
assert (forall j:int::{TO(j)} TO(j) ==>
between(0, arrayElementWords($vt), j - $jj) ==>
between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==>
ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice));
assert (forall j:int::{TO(j)} TO(j) ==>
between(0, arrayElementWords($vt), j - $jj) ==>
!between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==>
ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice));
if (esi >= 8) { goto loopEnd; }
// ebx := getNib(mask(arrayElementClass($vt)), 4 * esi);
ecx := 0;
call ecx := Lea(ecx + 4 * esi);
ebx := edx;
call ebx := Shr(ebx, ecx);
call ebx := And(ebx, 15);
assert ebx == getNib(mask(arrayElementClass($vt)), 4 * esi);
// if (ebx != 0)
if (ebx == 0) { goto skip1; }
call ebx := Sub(ebx, 1);
call ebx := Add(ebx, edi);
assert TO(ebx);
eax := Tj;
call ecx := GcLoad(eax + 4 * ebx);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip2; }
if (ecx > ?gcHi) { goto skip2; }
assert TV(ecx - 4);
call reach($toAbs[Tj], ebx, $Time);
assert TO(0);
assert TO(1);
save1 := esi;
save2 := edi;
save3 := edx;
save4 := ebx;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][ebx], Tj + 4 * numFields($r2[Tj]));
esi := save1;
edi := save2;
edx := save3;
ebx := save4;
ecx := Tj;
call GcStore(ecx + 4 * ebx, eax);
skip2:
skip1:
call esi := Add(esi, 1);
goto loop;
loopEnd:
}
procedure scanObjectOtherVectorPointers($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?OTHER_VECTOR_TAG;
requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var save1:int;
var save2:int;
var $index:int;
assert TO(numFields($r2[Tj]));
assert TO(2);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
$index := 0;
call edi := RoLoad32(ecx + ?VT_BASE_LENGTH);
call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE);
edx := Tj;
call edx := GcLoad(edx + 8);
eax := ebx;
call eax, edx := MulChecked(eax, edx);
call eax := AddChecked(eax, edi);
call eax := AddChecked(eax, 3);
edx := 3;
call edx := Not(edx);
call eax := And(eax, edx);
ebp := eax;
call edi := Shr(edi, 2);
call ebx := Shr(ebx, 2); // arrayElementWords($vt)
assert TVM(ebx, 0);
call edx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_CLASS);
call edx := RoLoad32(edx + ?VT_MASK);
//while (4 * edi < ebp)
loop:
assert TO($index) && 0 <= $index;
assert Mult(ebx, $index) >= 0;
assert TO(edi) && edi == baseWords($vt) + Mult(ebx, $index);
assert ebp == 4 * numFields($r2[Tj]);
assert edx == mask(arrayElementClass($vt));
assert ebx == arrayElementWords($vt);
assert 4 * edi <= ebp;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvPartial(Tj, 0, edi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert ObjInvPartial(Tj, edi, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
eax := 0;
call eax := Lea(eax + 4 * edi);
if (eax >= ebp) { goto loopEnd; }
save1 := ebx;
save2 := ebp;
call scanObjectOtherVectorPointersSparseFields($vt, edi, $index);
ebx := save1;
ebp := save2;
assert TVM3(ebx, $index, 1);
assert TVM(ebx, $index);
$index := $index + 1;
call edi := Add(edi, ebx);
goto loop;
loopEnd:
call Tj := Add(Tj, ebp);
assert TO(1);
}
procedure scanObjectPtrArray($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?PTR_ARRAY_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var $ind:int;
var save1:int;
var save2:int;
assert TO(numFields($r2[Tj]));
assert TO(3);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
ebx := Tj;
call ebp := GcLoad(ebx + 12);
call esi := RoLoad32(ecx + ?VT_BASE_LENGTH);
// size := pad(esi + 4 * ebp);
call ebp := AddChecked(ebp, ebp);
call ebp := AddChecked(ebp, ebp);
call ebp := AddChecked(ebp, esi);
call ebp := AddChecked(ebp, 3);
eax := 3;
call eax := Not(eax);
call ebp := And(ebp, eax);
call esi := Shr(esi, 2);
$ind := esi;
call edi := Lea(ebx + 4 * esi);
call ebp := Add(ebp, ebx);
//while (edi < ebp)
loop:
assert edi == Tj + 4 * $ind;
assert ebp == Tj + 4 * numFields($r2[Tj]);
assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvPartial(Tj, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert ObjInvPartial(Tj, $ind, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
if (edi >= ebp) { goto loopEnd; }
assert TO(0) && TO(1) && TO(3);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
call ecx := GcLoad(edi);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip1; }
if (ecx > ?gcHi) { goto skip1; }
assert TV(ecx - 4);
call reach($toAbs[Tj], $ind, $Time);
save1 := edi;
save2 := ebp;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][$ind], ebp);
edi := save1;
ebp := save2;
call GcStore(edi, eax);
skip1:
$ind := $ind + 1;
call edi := Add(edi, 4);
goto loop;
loopEnd:
Tj := ebp;
}
procedure scanObjectPtrVector($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?PTR_VECTOR_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var $ind:int;
var save1:int;
var save2:int;
assert TO(numFields($r2[Tj]));
assert TO(2);
assert TVT($r2[Tj], $vt);
assert TVL($r2[Tj]);
ebx := Tj;
call ebp := GcLoad(ebx + 8);
call esi := RoLoad32(ecx + ?VT_BASE_LENGTH);
// size := pad(esi + 4 * ebp);
call ebp := AddChecked(ebp, ebp);
call ebp := AddChecked(ebp, ebp);
call ebp := AddChecked(ebp, esi);
call ebp := AddChecked(ebp, 3);
eax := 3;
call eax := Not(eax);
call ebp := And(ebp, eax);
call esi := Shr(esi, 2);
$ind := esi;
call edi := Lea(ebx + 4 * esi);
call ebp := Add(ebp, ebx);
//while (edi < ebp)
loop:
assert edi == Tj + 4 * $ind;
assert ebp == Tj + 4 * numFields($r2[Tj]);
assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvPartial(Tj, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert ObjInvPartial(Tj, $ind, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
if (edi >= ebp) { goto loopEnd; }
assert TO(0) && TO(1) && TO(2);
assert TVT($r2[Tj], $vt);
call ecx := GcLoad(edi);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip1; }
if (ecx > ?gcHi) { goto skip1; }
assert TV(ecx - 4);
call reach($toAbs[Tj], $ind, $Time);
save1 := edi;
save2 := ebp;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][$ind], ebp);
edi := save1;
ebp := save2;
call GcStore(edi, eax);
skip1:
$ind := $ind + 1;
call edi := Add(edi, 4);
goto loop;
loopEnd:
Tj := ebp;
}
procedure scanObjectOtherArrayNoPointers($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?OTHER_ARRAY_TAG;
requires arrayOf($vt) != ?TYPE_STRUCT;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
edx := ecx;
ecx := Tj;
esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1);
assert TO(numFields($r2[Tj]));
assert TVT($r2[Tj], $vt);
call Tj := Add(Tj, eax);
}
procedure scanObjectString($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires tag($vt) == ?STRING_TAG;
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
edx := ecx;
ecx := Tj;
esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1);
assert TO(numFields($r2[Tj]));
assert TVT($r2[Tj], $vt);
call Tj := Add(Tj, eax);
}
procedure scanObjectOther($vt:int)
requires ecx == $vt;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
requires $vt == $AbsMem[$r2[Tj]][1];
requires isOtherTag(tag($vt));
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var save1:int;
var save2:int;
var save3:int;
var save4:int;
var save5:int;
assert TO(numFields($r2[Tj]));
assert TVT($toAbs[Tj], $vt);
assert TVL($r2[Tj]);
call edx := RoLoad32(ecx + ?VT_MASK);
call edi := RoLoad32(edx);
call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH);
esi := 1;
//while (esi < edi + 1)
loop:
assert ebp == 4 * numFields($r2[Tj]);
assert edx == mask($vt);
assert edi == ro32(mask($vt));
assert TSlot(esi) && 0 < esi && esi <= ro32(mask($vt)) + 1;
assert Pointer($r2, Tj, $r2[Tj]);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert ObjInvBase(Tj, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==>
between(1, esi, fieldToSlot($vt, j)) ==>
ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice));
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==>
!between(1, esi, fieldToSlot($vt, j)) ==>
ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice));
assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj];
if (esi > edi) { goto loopEnd; }
assert TO(0) && TO(1);
assert TVT($toAbs[Tj], $vt);
assert TVL($r2[Tj]);
assert TO(ro32(mask($vt) + 4 * esi) + 1);
call ebx := RoLoad32(edx + 4 * esi);
//if (ebx != 0)
if (ebx == 0) { goto skip1; }
eax := Tj;
call ecx := GcLoad(eax + 4 * ebx + 4);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip2; }
if (ecx > ?gcHi) { goto skip2; }
assert TV(ecx - 4);
call reach($toAbs[Tj], 1 + ro32(edx + 4 * esi), $Time);
save1 := ebx;
save2 := esi;
save3 := edi;
save4 := ebp;
save5 := edx;
call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][1 + ro32(edx + 4 * esi)], Tj + ebp);
ebx := save1;
esi := save2;
edi := save3;
ebp := save4;
edx := save5;
ecx := Tj;
call GcStore(ecx + 4 * ebx + 4, eax);
skip2:
skip1:
call esi := AddChecked(esi, 1);
goto loop;
loopEnd:
assert TVT($toAbs[Tj], $vt);
assert TVL($r2[Tj]);
call Tj := Add(Tj, ebp);
}
procedure scanObject()
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires Tj < Tk;
requires TV(Tj);
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var $vt:int;
assert TO(1);
ecx := Tj;
call ecx := GcLoad(ecx + 4);
$vt := ecx;
assert TO(numFields($r2[Tj]));
call readTag($toAbs[Tj], $vt);
if (eax != ?SPARSE_TAG) { goto skip1; }
call scanObjectSparse($vt);
goto end;
skip1:
if (eax != ?DENSE_TAG) { goto skip2; }
call scanObjectDense($vt);
goto end;
skip2:
if (eax != ?STRING_TAG) { goto skip3; }
call scanObjectString($vt);
goto end;
skip3:
if (eax != ?PTR_VECTOR_TAG) { goto skip4; }
call scanObjectPtrVector($vt);
goto end;
skip4:
if (eax != ?OTHER_VECTOR_TAG) { goto skip5; }
call readArrayOf($r2[Tj], $vt);
call readElementInfo($r2[Tj], $vt);
if (ebp != ?TYPE_STRUCT) { goto noPoint; }
if (ebp != ?TYPE_STRUCT) { goto vecSkip1; }
if (edi != ?SPARSE_TAG) { goto vecSkip1; }
noPoint:
call scanObjectOtherVectorNoPointers($vt);
goto end;
vecSkip1:
if (ebp != ?TYPE_STRUCT) { goto vecSkip2; }
//if (tag(arrayElementClass(vt)) != ?SPARSE_TAG) { goto vecSkip2; }
eax := edi;
call eax := And(eax, 15);
if (eax != ?SPARSE_TAG) { goto vecSkip2; }
call scanObjectOtherVectorPointers($vt);
goto end;
vecSkip2:
assert !(
(ebp != ?TYPE_STRUCT)
|| (ebp == ?TYPE_STRUCT && edi == ?SPARSE_TAG)
|| (ebp == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG));
call DebugBreak();
skip5:
if (eax != ?PTR_ARRAY_TAG) { goto skip6; }
call scanObjectPtrArray($vt);
goto end;
skip6:
if (eax != ?OTHER_ARRAY_TAG) { goto skip7; }
call readArrayOf($r2[Tj], $vt);
if (ebp == ?TYPE_STRUCT) { goto arraySkip1; }
call scanObjectOtherArrayNoPointers($vt);
goto end;
arraySkip1:
call DebugBreak();
goto end;
skip7:
call scanObjectOther($vt);
end:
}
procedure scanObjects()
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
ensures Tj == Tk;
{
entry:
loop:
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
eax := Tk;
if (Tj >= eax) { goto exit; }
call scanObject();
goto loop;
exit:
}
procedure FromInteriorPointer($iptr:int, $offset:int, $abs:int)
requires ecx == $iptr;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires 0 <= $offset && $offset <= 4 * numFields($abs) - 4;
requires Pointer($r1, $iptr - $offset - 4, $abs);
modifies eax, ebx, ecx, edx, esp;
ensures eax == $offset;
{
var save1:int;
var save2:int;
eax := 0;
// while ($r1[$iptr - eax - 4] == NO_ABS)
loop:
assert $iptr - $offset <= $iptr - eax && $iptr - eax <= $iptr;
assert TV($iptr - eax - 4) && TV($iptr - $offset - 4);
assert ecx == $iptr;
assert TV(?gcLo);
assert TO($iptr - eax - 4 - HeapLo);
ebx := ecx;
call ebx := Sub(ebx, eax);
call ebx := Sub(ebx, 4);
call ebx := Sub(ebx, Fi);
edx := ebx;
call edx := And(edx, 3);
call __andAligned(ebx);
call __addAligned(Fi, ebx);
if (edx != 0)
{
goto skip1;
}
save1 := eax;
save2 := ecx;
eax := BF;
call bb4GetBit($r1, NO_ABS, Fi, Fi, Fi, Fl, $iptr - save1 - 4, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl));
eax := save1;
ecx := save2;
if (ebx == 0) { goto skip1; }
esp := esp + 4; return;
skip1:
call eax := Add(eax, 1);
goto loop;
}
procedure scanStackUpdateInvs($r:[int]int, $f1:int, $f2:int, $frame:int, $addr:int, $v:int)
requires $FrameSlice[$addr] == $frame;
requires !($f1 <= $frame && $frame < $f2);
requires (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==>
FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==>
FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem[$addr := $v], $FrameAbs, $FrameOffset, $Time));
{
assert TO(0) && TO(1);
assert (forall f:int::{TV(f)} TV(f) ==> TVF($FrameLayout[f]) && TVFT(f));
}
procedure scanStackWordDenseCopy($frame:int, $addr:int, $desc:int, $addrp:int, $p:int, $args:int)
requires ecx == $addrp;
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires $desc == frameDescriptor($FrameLayout[$frame]);
requires $addr == $FrameAddr[$frame];
requires $FrameSlice[$addrp] == $frame;
requires $args == frameLayoutArgs($FrameLayout[$frame]);
requires $addrp == $addr + 4 * $p;
requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0;
requires frameHasPtr($FrameLayout[$frame], $p);
requires 0 <= $frame && $frame < $FrameCount && TV($frame);
requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires $p <= 1 + $args && $p > $args - 1 - 16 && TO($p);
requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout);
requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout);
ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p - 1 ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p - 1 ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var save1:int;
var v:int;
var offset:int;
assert TVF($FrameLayout[$frame]);
assert TV($FrameMem[$addrp] - 4);
call eax := FrameLoad(($frame), ecx);
//if (gcAddrEx(eax))
if (eax < ?gcLo) { goto skip1; }
if (eax > ?gcHi) { goto skip1; }
save1 := ecx;
v := eax;
ecx := eax;
esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[$addrp], $FrameAbs[$frame][$p]);
offset := eax;
ecx := v;
call ecx := Sub(ecx, eax);
assert TV(ecx - 4);
call forwardFromspacePtr(ecx, $FrameAbs[$frame][$p], Tj);
assert TV(eax - 4);
call eax := Add(eax, offset);
call scanStackUpdateInvs($r1, 0, $frame, $frame, $addrp, eax);
call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, $addrp, eax);
ecx := save1;
call FrameStore(($frame), ecx, eax);
skip1:
}
procedure scanStackDenseCopy($frame:int, $addr:int, $desc:int)
requires ecx == $addr;
requires edx == $desc;
requires $desc == frameDescriptor($FrameLayout[$frame]);
requires $addr == $FrameAddr[$frame];
requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0;
requires 0 <= $frame && $frame < $FrameCount && TV($frame);
requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var b:int;
var v:int;
var offset:int;
var args:int;
var addr:int;
var desc:int;
var addrp:int;
assert TVF($FrameLayout[$frame]);
assert TO(0);
addr := ecx;
desc := edx;
eax := edx;
call eax := Shr(eax, 2);
call eax := And(eax, 15);
args := eax; // frameLayoutArgs($FrameLayout[$frame]);
b := 0;
ebx := 0;
call ebx := Lea(ebx + 4 * eax + 4);
call ebx := AddChecked(ebx, ecx);
addrp := ebx;
//while (b < args)
loop1:
assert addrp == $addr + 4 * (args + 1 - b);
assert $frame < $FrameCount && TV($frame);
assert 0 <= b && b <= args && TO(args + 1 - b);
assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args + 1 - b) ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args + 1 - b) ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
eax := b;
if (eax >= args) { goto loopEnd1; }
assert TVF($FrameLayout[$frame]);
call ecx := Lea(eax + 16);
ebx := desc;
call ebx := Shr(ebx, ecx);
call ebx := And(ebx, 1);
//if (getBit($desc, 16 + b))
if (ebx != 1) { goto skip1; }
ecx := addrp;
call scanStackWordDenseCopy($frame, $addr, $desc, $addr + 4 * (args + 1 - b), args + 1 - b, args);
skip1:
call b := Add(b, 1);
call addrp := Sub(addrp, 4);
goto loop1;
loopEnd1:
assert TO(0);
assert TO(1);
assert TV($FrameMem[$addr]);
assert TV($FrameMem[$addr] - 4);
call addrp := SubChecked(addrp, 8);
//while (b < 16)
loop2:
assert addrp == $addr + 4 * (args - 1 - b);
assert $frame < $FrameCount && TV($frame);
assert (args - 1 - b) < 0 && (args - 1 - b) <= 1 + args && TO(args - 1 - b);
assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args - 1 - b) ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args - 1 - b) ==>
$FrameSlice[$addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
eax := b;
if (eax >= 16) { goto loopEnd2; }
assert TVF($FrameLayout[$frame]);
call ecx := Lea(eax + 16);
ebx := desc;
call ebx := Shr(ebx, ecx);
call ebx := And(ebx, 1);
//if (getBit($desc, 16 + b))
if (ebx != 1) { goto skip2; }
ecx := addrp;
call scanStackWordDenseCopy($frame, $addr, $desc, $addr + 4 * (args - 1 - b), args - 1 - b, args);
skip2:
call b := Add(b, 1);
call addrp := SubChecked(addrp, 4);
goto loop2;
loopEnd2:
}
procedure scanStackSparse8Copy($frame:int, $addr:int, $desc:int)
requires ecx == $addr;
requires edx == $desc;
requires $desc == frameDescriptor($FrameLayout[$frame]);
requires $addr == $FrameAddr[$frame];
requires !getBit($desc, 0) && ro32($desc) == 4096;
requires 0 <= $frame && $frame < $FrameCount && TV($frame);
requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var p:int;
var v:int;
var addr:int;
var desc:int;
var addrp:int;
var offset:int;
var count:int;
assert TVF($FrameLayout[$frame]);
assert TO(0);
addr := ecx;
desc := edx;
p := 0;
call eax := RoLoadU8(edx + 4);
count := eax;
//while (p < count)
loop:
assert p >= 0 && TSlot(p);
assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && !between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==>
$FrameSlice[addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==>
$FrameSlice[addr + 4 * j] == $frame
&& InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j])
&& (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time))
);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
eax := p;
if (eax >= count) { goto loopEnd; }
assert TO(roS8(desc + 6 + p));
assert TV($FrameMem[addr + 4 * roS8(desc + 6 + p)] - 4);
ebx := desc;
esi := addr;
call ebp := RoLoadS8(ebx + 1 * eax + 6);
call ebp := LeaSignedIndex(esi, 4, ebp, 0);
addrp := ebp;
call ecx := FrameLoad(($frame), ebp);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip1; }
if (ecx > ?gcHi) { goto skip1; }
v := ecx;
esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[addr + 4 * roS8(desc + 6 + p)], $FrameAbs[$frame][roS8(desc + 6 + p)]);
offset := eax;
ecx := v;
call ecx := Sub(ecx, eax);
assert TV(ecx - 4);
call forwardFromspacePtr(ecx, $FrameAbs[$frame][roS8(desc + 6 + p)], Tj);
assert TV(eax - 4);
call eax := Add(eax, offset);
call scanStackUpdateInvs($r1, 0, $frame, $frame, addr + 4 * roS8(desc + 6 + p), eax);
call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, addr + 4 * roS8(desc + 6 + p), eax);
ebx := addrp;
call FrameStore(($frame), ebx, eax);
skip1:
// This is just here to improve performance:
assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) &&
p == frameFieldToSlot($FrameLayout[$frame], j) ==> j == roS8(desc + 6 + p));
call p := Add(p, 1);
goto loop;
loopEnd:
}
procedure scanStackCopy($ra:int, $nextFp:int)
requires ecx == $ra && word($ra);
requires edx == $nextFp;
requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout);
requires StackInv($r1, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StackInv($r2, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var $frame:int;
var p:int;
var addr:int;
var desc:int;
var found:int;
var _ra:int;
var _nextFp:int;
_ra := ecx;
_nextFp := edx;
$frame := $FrameCount - 1;
loop:
assert $frame < $FrameCount && TV($frame);
assert word(_ra);
assert FrameNextInv($frame + 1, _ra, _nextFp, $FrameAddr, $FrameLayout);
assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==>
FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==>
FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time));
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
assert TVF($FrameLayout[$frame]);
ecx := _ra;
edx := _nextFp;
esp := esp - 4; call TablesSearch($frame + 1, _ra, _nextFp);
desc := eax;
found := edx;
if (edx == 0) { goto loopEnd; }
ecx := _nextFp;
addr := ecx;
assert TV(addr);
assert TO(0);
assert TO(1);
call eax := FrameLoad(($frame), ecx);
_nextFp := eax;
call eax := FrameLoad(($frame), ecx + 4);
_ra := eax;
// if (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0)
eax := desc;
call eax := Shr(eax, 0);
call eax := And(eax, 1);
if (eax != 1) { goto skip1; }
eax := desc;
call eax := Shr(eax, 1);
call eax := And(eax, 1);
if (eax == 1) { goto skip1; }
eax := desc;
call eax := Shr(eax, 6);
call eax := And(eax, 1023);
if (eax != 0) { goto skip1; }
ecx := addr;
edx := desc;
call scanStackDenseCopy($frame, addr, desc);
$frame := $frame - 1;
goto loop;
skip1:
// else if (!getBit(desc, 0) && ro32(desc) == 4096)
eax := desc;
call eax := Shr(eax, 0);
call eax := And(eax, 1);
if (eax == 1) { goto skip2; }
eax := desc;
call eax := RoLoad32(eax);
if (eax != 4096) { goto skip2; }
ecx := addr;
edx := desc;
call scanStackSparse8Copy($frame, addr, desc);
$frame := $frame - 1;
goto loop;
skip2:
// else
assert !( (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0)
|| (!getBit(desc, 0) && ro32(desc) == 4096));
call DebugBreak();
loopEnd:
assert $frame == 0 - 1;
}
procedure scanStaticSectionCopy($section:int)
requires ecx == $section;
requires 0 <= $section && $section < ?sectionCount && TV($section);
requires (forall s:int::{TV(s)} TV(s) ==> $section <= s && s < ?sectionCount ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time));
requires (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time));
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $SectionMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures (forall s:int::{TV(s)} TV(s) ==> $section + 1 <= s && s < ?sectionCount ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time));
ensures (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section + 1 ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time));
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var sEnd:int;
var addr:int;
var section:int;
var save1:int;
var save2:int;
var save3:int;
section := ecx;
assert TVS(section, 0);
eax := ?dataSectionEnd;
call eax := RoLoad32(eax + 4 * ecx);
sEnd := eax;
eax := ?staticDataPointerBitMap;
call edx := RoLoad32(eax + 4 * ecx);
eax := ?dataSectionBase;
call eax := RoLoad32(eax + 4 * ecx);
addr := eax;
edi := eax;
esi := 0;
//while (edi < sEnd)
loop:
assert edx == ro32(?staticDataPointerBitMap + 4 * $section);
assert edi == addr + 4 * esi;
assert 0 <= section && TV(section);
assert 0 <= esi && TO(esi);
assert (forall s:int::{TV(s)} TV(s) ==> section < s && s < ?sectionCount ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time));
assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time));
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j >= esi ==>
sectionSlice(addr + 4 * j) == section
&& Value(sectionHasPtr(section, j), $r1, $SectionMem[addr + 4 * j], $SectionAbs[section][j])
&& (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time))
);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j < esi ==>
sectionSlice(addr + 4 * j) == section
&& Value(sectionHasPtr(section, j), $r2, $SectionMem[addr + 4 * j], $SectionAbs[section][j])
&& (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time))
);
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
if (edi >= sEnd) { goto loopEnd; }
assert TVS(section, esi);
eax := esi;
call eax := Shr(eax, 5);
call eax := RoLoad32(edx + 4 * eax);
// assert getBit(v, and(esi, 31)) == sectionHasPtr(section, esi);
//if (getBit(v, and(esi, 31)))
ecx := esi;
call ecx := And(ecx, 31);
call eax := Shr(eax, ecx);
call eax := And(eax, 1);
if (eax != 1) { goto skip1; }
assert TV($SectionMem[addr + 4 * esi] - 4);
call ecx := SectionLoad(edi);
//if (gcAddrEx(ecx))
if (ecx < ?gcLo) { goto skip2; }
if (ecx > ?gcHi) { goto skip2; }
save1 := edi;
save2 := esi;
save3 := edx;
call forwardFromspacePtr(ecx, $SectionAbs[section][esi], Tj);
edi := save1;
esi := save2;
edx := save3;
call SectionStore(edi, eax);
skip2:
skip1:
call esi := Add(esi, 1);
call edi := AddChecked(edi, 4);
goto loop;
loopEnd:
}
procedure scanStaticCopy()
requires StaticInv($r1, $SectionMem, $SectionAbs, $Time);
requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $SectionMem;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StaticInv($r2, $SectionMem, $SectionAbs, $Time);
ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
ensures RExtend(old($r2), $r2);
{
var section:int;
section := 0;
//while (section < ?sectionCount)
loop:
assert 0 <= section && TV(section);
assert (forall s:int::{TV(s)} TV(s) ==> section <= s && s < ?sectionCount ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time));
assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==>
SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time));
assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice);
assert RExtend(old($r2), $r2);
ecx := section;
if (ecx >= ?sectionCount) { goto loopEnd; }
call scanStaticSectionCopy(section);
call section := AddChecked(section, 1);
goto loop;
loopEnd:
}
procedure GarbageCollectCopying($ra:int, $nextFp:int)
requires ecx == $ra && word($ra);
requires edx == $nextFp;
requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout);
requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
requires $freshAbs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs);
modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem;
modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
// postcondition same as precondition, plus reached:
ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs);
ensures (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $toAbs[i] != NO_ABS ==>
reached($toAbs[i], $Time));
ensures ebp == old(ebp);
{
var saveEbp:int;
saveEbp := ebp;
$r1 := $toAbs;
$r2 := MAP_NO_ABS;
eax := Ti;
Tj := eax;
Tk := eax;
eax := BT;
ebx := Fi;
if (ebx != HeapLo) { goto skip1; }
ebx := HeapLo;
assert ?gcLo + BitIndex(HeapLo, Tl) == ebx;
goto skip2;
skip1:
ebx := BF;
assert ?gcLo + BitIndex(HeapLo, Tl) == ebx;
skip2:
assert TVM(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi)));
assert TVM(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti)));
esp := esp - 4; call BB4Zero($toAbs, 0, NO_ABS, Ti, Ti, Ti, Tl, 0, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl), Fi, ?gcLo + BitIndex(HeapLo, Fi));
call scanStackCopy($ra, $nextFp);
call scanStaticCopy();
call scanObjects();
$toAbs := $r2;
eax := Fi;
ebx := Ti;
Fi := ebx;
Ti := eax;
eax := Fl;
ebx := Tl;
Fl := ebx;
Tl := eax;
eax := Tk;
Fk := eax;
eax := Ti;
Tk := eax;
Tj := eax;
eax := BF;
ebx := BT;
BF := ebx;
BT := eax;
ebp := saveEbp;
esp := esp + 4; return;
}
procedure doAllocCopyingWord($ret:int, $ind:int)
requires esi == $ret + 4 * $ind;
requires TO($ind) && $ind >= 0;
requires Aligned($ret) && Fk <= $ret && $ret + 4 * $ind + 4 <= Fl;
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret);
requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary?
requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL);
modifies $GcMem, $gcSlice;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $gcSlice[$ret + 4 * j] == $ret);
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> gcAddr($ret + 4 * j)); // REVIEW: necessary?
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $GcMem[$ret + 4 * j] == NULL);
ensures ebp == old(ebp);
{
assert TV($ret);
assert TV($ret + 4 * $ind);// && TV($ret + 4 * $ind + 1) && TV($ret + 4 * $ind + 2) && TV($ret + 4 * $ind + 3);
$gcSlice[$ret + 4 * $ind] := $ret;
call GcStore(esi, 0);
}
procedure doAllocCopyingWords($ret:int, $size:int, $nf:int)
requires eax == $ret;
requires ebx == $ret + $size;
requires $size == $nf * 4;
requires $nf >= 0;
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
requires Aligned($ret) && Fk <= $ret && $ret + $size <= Fl;
modifies $GcMem, $gcSlice;
modifies esi;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $gcSlice[$ret + 4 * j] == $ret);
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> gcAddr($ret + 4 * j)); // REVIEW: necessary?
ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $GcMem[$ret + 4 * j] == NULL);
ensures ebp == old(ebp);
{
var $ind:int;
$ind := 0;
esi := ?gcLo;
esi := eax;
//while (4 * $ind < $size)
if (esi >= ebx) { goto loopEnd; }
loop:
assert 4 * $ind < $size;
assert esi == $ret + 4 * $ind;
assert TO($ind) && $ind >= 0;
assert MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret);
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary?
assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL);
call doAllocCopyingWord($ret, $ind);
$ind := $ind + 1;
call esi := Add(esi, 4);
if (esi < ebx) { goto loop; }
loopEnd:
}
procedure doAllocObjectCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $size:int)
requires eax == $size;
requires ebx == Fk + $size;
requires ecx == $vt;
requires Fk + 4 * numFields($abs) <= Fl;
requires !VFieldPtr($abs, 0);
requires !VFieldPtr($abs, 1);
requires 2 <= numFields($abs);
requires $size == 4 * numFields($abs);
requires ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]);
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on vtable and layout:
requires word($vt) && !gcAddrEx($vt);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, 0, 0);
requires !isVarSize(tag($vt));
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $GcMem, $toAbs, $gcSlice, Fk;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures old($toAbs)[eax - 4] == NO_ABS;
ensures $toAbs == old($toAbs)[eax - 4 := $abs];
ensures Pointer($toAbs, eax - 4, $abs);
ensures ebp == old(ebp);
{
eax := Fk;
assert TV(eax + 0) && TV(eax + 4);
assert TO(1) && TO(2);
assert TO(numFields($abs));
call doAllocCopyingWords(eax, $size, numFields($abs));
call GcStore(eax + 4, ecx);
esi := eax;
call esi := Sub(esi, Fi);
edi := BF;
call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl));
assert TV(Fk - Fi);
$toAbs[eax] := $abs;
assert TV(Fk);
assert TO(numFields($abs));
Fk := ebx;
call eax := Add(eax, 4);
assert TV(eax + 4);
assert TO(0);
}
procedure doAllocStringCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $nElems:int)
requires ecx == pad(16 + 2 * $nElems);
requires ebx == Fk + pad(16 + 2 * $nElems);
requires edx == $nElems;
requires $vt == ?STRING_VTABLE;
requires Fk + pad(16 + 2 * $nElems) + 0 <= Fl;
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on vtable and layout:
requires word($vt) && !gcAddrEx($vt);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $nElems, 0);
requires tag($vt) == ?STRING_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $nElems;
requires $AbsMem[$abs][3] == $nElems - 1;
requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL);
modifies $GcMem, $toAbs, $gcSlice, Fk;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures old($toAbs)[eax - 4] == NO_ABS;
ensures $toAbs == old($toAbs)[eax - 4 := $abs];
ensures Pointer($toAbs, eax - 4, $abs);
ensures ebp == old(ebp);
{
eax := Fk;
assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12);
assert TO(1) && TO(2) && TO(3);
assert TVL($abs);
assert TVT($abs, $vt);
assert TO(numFields($abs));
call doAllocCopyingWords(eax, pad(16 + 2 * $nElems), numFields($abs));
call GcStore(eax + 8, edx);
call edx := SubChecked(edx, 1);
call GcStore(eax + 12, edx);
edx := ?STRING_VTABLE;
call GcStore(eax + 4, edx);
esi := eax;
call esi := Sub(esi, Fi);
edi := BF;
call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl));
assert TV(Fk - Fi);
$toAbs[eax] := $abs;
assert TV(Fk);
assert TO(numFields($abs));
Fk := ebx;
call eax := Add(eax, 4);
assert TV(eax + 4);
assert TO(0);
}
procedure fromArrayInfo($abs:int, $vt:int)
requires VTable($abs, $vt);
requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG;
ensures !VFieldPtr($abs, 0);
ensures !VFieldPtr($abs, 1);
ensures !VFieldPtr($abs, 2);
ensures !VFieldPtr($abs, 3);
{
assert TO(0) && TO(1) && TO(2) && TO(3) && TO(4);
assert TVL($abs);
assert TVT($abs, $vt);
}
procedure doAllocArrayCopyingHelper($abs:int, $vt:int, $rank:int, $nElems:int)
requires ecx == $vt;
requires esi == $nElems;
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $rank, $nElems);
requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG;
modifies eax, ebx, edx, edi;
ensures !VFieldPtr($abs, 0);
ensures !VFieldPtr($abs, 1);
ensures !VFieldPtr($abs, 2);
ensures !VFieldPtr($abs, 3);
ensures between(4, numFields($abs), baseWords($vt));
ensures eax == 4 * numFields($abs);
{
assert TO(2) && TO(3);
assert TVL($abs);
assert TVT($abs, $vt);
call eax := RoLoad32(ecx + ?VT_BASE_LENGTH);
call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE);
call edi := RoLoad32(ecx + ?VT_MASK);
call edi := And(edi, 15);
//if (edi == ?PTR_ARRAY_TAG)
if (edi != ?PTR_ARRAY_TAG) { goto skip1; }
edi := esi;
call edi := AddChecked(edi, edi);
call edi := AddChecked(edi, edi);
call eax := AddChecked(eax, edi);
call eax := AddChecked(eax, 3);
edi := 3;
call edi := Not(edi);
call eax := And(eax, edi);
goto skip2;
//else
skip1:
edi := eax;
eax := ebx;
call eax, edx := MulChecked(eax, esi);
call eax := AddChecked(eax, edi);
call eax := AddChecked(eax, 3);
edi := 3;
call edi := Not(edi);
call eax := And(eax, edi);
skip2:
}
procedure doAllocArrayCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $rank:int, $nElems:int, $size:int)
// requires eax == $size;
requires ebx == Fk + $size;
requires ecx == $vt;
requires edx == $rank;
requires esi == $nElems;
requires Fk + 4 * numFields($abs) <= Fl;
requires !VFieldPtr($abs, 0);
requires !VFieldPtr($abs, 1);
requires !VFieldPtr($abs, 2);
requires !VFieldPtr($abs, 3);
requires 4 <= numFields($abs);
requires $size == 4 * numFields($abs);
requires ObjSize($abs, $vt, $rank, $nElems);
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on vtable and layout:
requires word($vt) && !gcAddrEx($vt);
requires word($rank);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $rank, $nElems);
requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $rank;
requires $AbsMem[$abs][3] == $nElems;
requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $GcMem, $toAbs, $gcSlice, Fk;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures old($toAbs)[eax - 4] == NO_ABS;
ensures $toAbs == old($toAbs)[eax - 4 := $abs];
ensures Pointer($toAbs, eax - 4, $abs);
ensures ebp == old(ebp);
{
var nElems:int;
eax := Fk;
nElems := esi;
assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12);
assert TO(1) && TO(2) && TO(3) && TO(4);
assert TO(numFields($abs));
call doAllocCopyingWords(eax, $size, numFields($abs));
esi := nElems;
call GcStore(eax + 4, ecx);
call GcStore(eax + 8, edx);
call GcStore(eax + 12, esi);
esi := eax;
call esi := Sub(esi, Fi);
edi := BF;
call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl));
assert TV(Fk - Fi);
$toAbs[eax] := $abs;
assert TV(Fk);
assert TO(numFields($abs));
Fk := ebx;
call eax := Add(eax, 4);
assert TV(eax + 4);
assert TO(0);
}
procedure doAllocVectorCopyingHelper($abs:int, $vt:int, $nElems:int)
requires ecx == $vt;
requires edx == $nElems;
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $nElems, 0);
requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG;
modifies eax, ebx, edx, esi, edi;
ensures !VFieldPtr($abs, 0);
ensures !VFieldPtr($abs, 1);
ensures !VFieldPtr($abs, 2);
ensures 3 <= numFields($abs);
ensures eax == 4 * numFields($abs);
ensures ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]);
{
assert TO(2);
assert TVL($abs);
assert TVT($abs, $vt);
call eax := RoLoad32(ecx + ?VT_BASE_LENGTH);
call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE);
call edi := RoLoad32(ecx + ?VT_MASK);
call edi := And(edi, 15);
//if (edi == ?PTR_ARRAY_TAG)
if (edi != ?PTR_VECTOR_TAG) { goto skip1; }
edi := edx;
call edi := AddChecked(edi, edi);
call edi := AddChecked(edi, edi);
call eax := AddChecked(eax, edi);
call eax := AddChecked(eax, 3);
edi := 3;
call edi := Not(edi);
call eax := And(eax, edi);
goto skip2;
//else
skip1:
edi := eax;
eax := ebx;
call eax, edx := MulChecked(eax, edx);
call eax := AddChecked(eax, edi);
call eax := AddChecked(eax, 3);
edi := 3;
call edi := Not(edi);
call eax := And(eax, edi);
skip2:
}
procedure doAllocVectorCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $nElems:int, $size:int)
// requires eax == $size;
requires ebx == Fk + $size;
requires ecx == $vt;
requires edx == $nElems;
requires Fk + 4 * numFields($abs) <= Fl;
requires !VFieldPtr($abs, 0);
requires !VFieldPtr($abs, 1);
requires !VFieldPtr($abs, 2);
requires 3 <= numFields($abs);
requires $size == 4 * numFields($abs);
requires ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]);
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on vtable and layout:
requires word($vt) && !gcAddrEx($vt);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $nElems, 0);
requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $nElems;
requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $GcMem, $toAbs, $gcSlice, Fk;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures old($toAbs)[eax - 4] == NO_ABS;
ensures $toAbs == old($toAbs)[eax - 4 := $abs];
ensures Pointer($toAbs, eax - 4, $abs);
ensures ebp == old(ebp);
{
eax := Fk;
assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8);
assert TO(1) && TO(2) && TO(3);
assert TO(numFields($abs));
call doAllocCopyingWords(eax, $size, numFields($abs));
call GcStore(eax + 4, ecx);
call GcStore(eax + 8, edx);
esi := eax;
call esi := Sub(esi, Fi);
edi := BF;
call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl));
assert TV(Fk - Fi);
$toAbs[eax] := $abs;
assert TV(Fk);
assert TO(numFields($abs));
Fk := ebx;
call eax := Add(eax, 4);
assert TV(eax + 4);
assert TO(0);
}
procedure doAllocObjectCopyingHelper($abs:int, $vt:int)
requires ecx == $vt;
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, 0, 0);
requires !isVarSize(tag($vt));
modifies eax;
ensures !VFieldPtr($abs, 0);
ensures !VFieldPtr($abs, 1);
ensures 2 <= numFields($abs);
ensures eax == 4 * numFields($abs);
ensures ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]);
{
assert TO(2);
assert TVL($abs);
assert TVT($abs, $vt);
call eax := RoLoad32(ecx + ?VT_BASE_LENGTH);
}
//////////////////////////////////////////////////////////////////////////////
//
// The procedures below are the entry points from the mutator.
//
// Therefore, the requires, ensures, and modifies clauses for these procedures
// are part of the trusted computing base!
//
//////////////////////////////////////////////////////////////////////////////
procedure Initialize()
modifies $toAbs, Ti, Tj, Tk, Tl, Fi, Fk, Fl, HeapLo, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
modifies $GcMem;
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures WellFormed($toAbs);
ensures ebp == old(ebp);
{
var save:int;
var unitSize:int;
save := ebp;
// The heap consists of two bitmaps, followed by two semispaces:
// <--4--><--4--><--128--><--128-->
// Each bit map must consist of at least 4 bytes.
// For each bit in the bit map, there is one word in the corresponding semispace.
// Thus, the total gc memory size must be a multiple of:
// 4 + 4 + 128 + 128 = 8 + 256 = 264 bytes
// Compute gcMem size, in bytes and words
esi := ?gcHi;
call esi := Sub(esi, ?gcLo);
edi := esi;
// Break into 264-byte units. Let ebp be the number of units.
edx := 0;
eax := edi;
ebx := 264;
call eax, edx := Div(eax, edx, ebx);
ebp := eax;
unitSize := ebp;
assert 264 * unitSize <= (?gcHi - ?gcLo);
// Divide heap into ?gcLo <--4--> eax <--4--> ebx <--128--> ecx <--128--> ?gcHi
edx := 0;
call ebp := Lea(edx + 4 * ebp);
eax := ?gcLo;
BF := eax;
call eax := Add(eax, ebp);
BT := eax;
call ebx := Lea(eax + ebp);
call ebp := Lea(edx + 4 * ebp);
call ecx := Lea(ebx + 8 * ebp);
call edx := Lea(ecx + 8 * ebp);
$toAbs := MAP_NO_ABS;
HeapLo := ebx;
Fi := ebx;
Fk := ebx;
Fl := ecx;
Ti := ecx;
Tj := ecx;
Tk := ecx;
Tl := edx;
call __initialize(unitSize, HeapLo);
assert TV(?gcLo);
assert TV(HeapLo);
assert TO(0);
assert TO(unitSize);
assert TO(2 * unitSize);
assert TO(32 * unitSize);
eax := ?gcLo;
esi := unitSize;
call ebx := Lea(eax + 4 * esi);
esp := esp - 4; call BB4Zero($toAbs, 0, NO_ABS, Fi, Fi, Fi, Fl, 0, ?gcLo, ?gcLo + 4 * unitSize, 0, 0);
assert TVM(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi)));
assert TVM(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti)));
ebp := save;
esp := esp + 4; return;
}
// Prepare to call GcLoad (don't actually call it -- let the mutator call it)
procedure readCopying($ptr:int, $fld:int) returns ($val:int)
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
requires Pointer($toAbs, $ptr, $toAbs[$ptr]);
requires 0 <= $fld && $fld < numFields($toAbs[$ptr]);
ensures gcAddr($ptr + 4 * $fld);
ensures $val == $GcMem[$ptr + 4 * $fld];
ensures Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $AbsMem[$toAbs[$ptr]][$fld]);
{
// call $val := GcLoad($ptr + 4 * $fld);
$val := $GcMem[$ptr + 4 * $fld];
assert TV($val) && TV($ptr);
assert TO($fld);
}
procedure writeCopying($ptr:int, $fld:int, $val:int, $abs:int) returns ($_gcData:[int]int, $_absData:[int][int]int)
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
requires Pointer($toAbs, $ptr, $toAbs[$ptr]);
requires 0 <= $fld && $fld < numFields($toAbs[$ptr]);
requires !isReadonlyField(tag($AbsMem[$toAbs[$ptr]][1]), $fld);
requires Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $abs);
ensures gcAddr($ptr + 4 * $fld);
ensures word($val);
ensures $_gcData == $GcMem[$ptr + 4 * $fld := $val];
ensures $_absData == $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]];
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $_gcData, $toAbs, $_absData, $gcSlice);
{
// call GcStore($ptr + 4 * $fld, $val);
// call AbsEdgeWrite($ptr, $fld, $val);
$_gcData := $GcMem[$ptr + 4 * $fld := $val];
$_absData := $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]];
assert TVL($toAbs[$ptr]);
assert TV($val) && TV($ptr);
assert TO($fld);
// assert TO(1);
}
procedure AllocObject($ra:int, $abs:int, $vt:int)
// GC invariant:
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on mutator root layout:
requires 0 <= $FrameCount;
requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra;
requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout);
requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
// requirements on vtable and layout:
requires ecx == $vt;
requires word($vt) && !gcAddrEx($vt);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, 0, 0);
requires !isVarSize(tag($vt));
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs;
modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures Pointer($toAbs, eax - 4, $abs);
ensures WellFormed($toAbs);
ensures ebp == old(ebp);
{
var size:int;
var vt:int;
call doAllocObjectCopyingHelper($abs, $vt);
$freshAbs := $abs;
ebx := Fk;
call ebx := AddChecked(ebx, eax);
//if (!(Fk + size <= Fl))
if (ebx <= Fl) { goto skip1; }
size := eax;
vt := ecx;
call ecx := FrameLoad(($FrameCount), esp);
edx := ebp;
esp := esp - 4; call GarbageCollectCopying($ra, ebp);
eax := size;
ecx := vt;
ebx := Fk;
call ebx := AddChecked(ebx, eax);
if (ebx <= Fl) { goto skip1; }
// Out of memory
call DebugBreak();
skip1:
call doAllocObjectCopying($ra, ebp, $abs, $vt, eax);
esp := esp + 4; return;
}
procedure AllocString($ra:int, $abs:int, $vt:int, $nElems:int)
// GC invariant:
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on mutator root layout:
requires 0 <= $FrameCount;
requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra;
requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout);
requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
// requirements on vtable and layout:
requires ecx == $nElems - 1;
requires $vt == ?STRING_VTABLE;
requires word($vt) && !gcAddrEx($vt);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $nElems, 0);
requires tag($vt) == ?STRING_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $nElems;
requires $AbsMem[$abs][3] == $nElems - 1;
requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL);
modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs;
modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures Pointer($toAbs, eax - 4, $abs);
ensures WellFormed($toAbs);
ensures ebp == old(ebp);
{
var size:int;
var nElems:int;
$freshAbs := $abs;
call ecx := AddChecked(ecx, 1);
edx := ecx;
call ecx := AddChecked(ecx, ecx);
call ecx := AddChecked(ecx, 19);
eax := 3;
call eax := Not(eax);
call ecx := And(ecx, eax);
ebx := Fk;
call ebx := AddChecked(ebx, ecx);
//if (!(Fk + pad(16 + 2 * $nElems) + 0 <= Fl))
if (ebx <= Fl) { goto skip1; }
size := ecx;
nElems := edx;
call ecx := FrameLoad(($FrameCount), esp);
edx := ebp;
esp := esp - 4; call GarbageCollectCopying($ra, ebp);
ecx := size;
edx := nElems;
ebx := Fk;
call ebx := AddChecked(ebx, ecx);
if (ebx <= Fl) { goto skip1; }
// Out of memory
call DebugBreak();
skip1:
call doAllocStringCopying($ra, ebp, $abs, $vt, $nElems);
esp := esp + 4; return;
}
procedure AllocArray($ra:int, $abs:int, $vt:int, $rank:int, $nElems:int)
// GC invariant:
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on mutator root layout:
requires 0 <= $FrameCount;
requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra;
requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout);
requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
// requirements on vtable and layout:
requires ecx == $vt;
requires edx == $rank;
requires $FrameSlice[esp + 4] == $FrameCount && $FrameMem[esp + 4] == $nElems;
requires word($vt) && !gcAddrEx($vt);
requires word($rank);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $rank, $nElems);
requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $rank;
requires $AbsMem[$abs][3] == $nElems;
requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs;
modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures Pointer($toAbs, eax - 4, $abs);
ensures WellFormed($toAbs);
ensures ebp == old(ebp);
{
var vt:int;
var rank:int;
var size:int;
var nElems:int;
$freshAbs := $abs;
call esi := FrameLoad(($FrameCount), esp + 4);
rank := edx;
call doAllocArrayCopyingHelper($abs, $vt, $rank, $nElems);
ebx := Fk;
call ebx := AddChecked(ebx, eax);
//if (!(Fk + size <= Fl))
if (ebx <= Fl) { goto skip1; }
size := eax;
vt := ecx;
nElems := esi;
call ecx := FrameLoad(($FrameCount), esp);
edx := ebp;
esp := esp - 4; call GarbageCollectCopying($ra, ebp);
eax := size;
ecx := vt;
esi := nElems;
ebx := Fk;
call ebx := AddChecked(ebx, eax);
if (ebx <= Fl) { goto skip1; }
// Out of memory
call DebugBreak();
skip1:
edx := rank;
call doAllocArrayCopying($ra, ebp, $abs, $vt, $rank, $nElems, eax);
esp := esp + 4; return;
}
procedure AllocVector($ra:int, $abs:int, $vt:int, $nElems:int)
// GC invariant:
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on mutator root layout:
requires 0 <= $FrameCount;
requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra;
requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout);
requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
// requirements on vtable and layout:
requires ecx == $vt;
requires edx == $nElems;
requires word($vt) && !gcAddrEx($vt);
requires word($nElems);
requires VTable($abs, $vt);
requires ObjSize($abs, $vt, $nElems, 0);
requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG;
// require a fresh, empty abstract node:
requires $abs != NO_ABS;
requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs);
requires $AbsMem[$abs][0] == NULL;
requires $AbsMem[$abs][1] == $vt;
requires $AbsMem[$abs][2] == $nElems;
requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL);
modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs;
modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT;
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time);
ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time);
ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice);
ensures Pointer($toAbs, eax - 4, $abs);
ensures WellFormed($toAbs);
ensures ebp == old(ebp);
{
var size:int;
var vt:int;
var nElems:int;
nElems := edx;
call doAllocVectorCopyingHelper($abs, $vt, $nElems);
$freshAbs := $abs;
ebx := Fk;
call ebx := AddChecked(ebx, eax);
//if (!(Fk + size <= Fl))
if (ebx <= Fl) { goto skip1; }
size := eax;
vt := ecx;
call ecx := FrameLoad(($FrameCount), esp);
edx := ebp;
esp := esp - 4; call GarbageCollectCopying($ra, ebp);
eax := size;
ecx := vt;
ebx := Fk;
call ebx := AddChecked(ebx, eax);
if (ebx <= Fl) { goto skip1; }
// Out of memory
call DebugBreak();
skip1:
edx := nElems;
call doAllocVectorCopying($ra, ebp, $abs, $vt, $nElems, eax);
esp := esp + 4; return;
}