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

403 lines
13 KiB
Plaintext
Raw Normal View History

2008-11-17 18:29:00 -05:00
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Common definitions and procedures shared between garbage collectors.
// This file is included by the garbage collectors.
// Imports:
// - Trusted.bpl
// - VerifiedBitVectors.bpl
// Imported from VerifiedBitVectors.bpl:
function BitIndex(i0:int, i:int) returns(int);
function BitZero(x:int, i0:int, i:int) returns(bool);
function ColorIndex(i0:int, i:int) returns(int);
function ColorGet(x:int, i0:int, i:int) returns(int);
/*****************************************************************************
******************************************************************************
**** VERIFIED
******************************************************************************
*****************************************************************************/
function AlignedHeapAddr(i:int) returns(bool) { gcAddr(i) && Aligned(i) }
//////////////////////////////////////////////////////////////////////////////
// LOCAL REASONING
//////////////////////////////////////////////////////////////////////////////
// As a region evolves, it adds new mappings, but each mapping is
// permanent: RExtend ensures that new mappings do not overwrite old mappings.
function RExtend(rOld:[int]int, rNew:[int]int) returns (bool)
{
(forall i:int::{rOld[i]}{rNew[i]} rOld[i] != NO_ABS ==> rOld[i] == rNew[i])
}
function AbsMapped(val:int, rev:[int]int, abs:int) returns (bool)
{
abs != NO_ABS && abs == rev[val]
}
// Both the mark-sweep and copying collectors only have two regions at
// any given time.
var $r1:[int]int;
var $r2:[int]int;
//////////////////////////////////////////////////////////////////////////////
// OBJECTS
//////////////////////////////////////////////////////////////////////////////
// Each object occupies a "slice" of the heap. If an object occupies
// addresses i + 0 ... i + m, then slice[i + 0] == i && ... && slice[i + m] == i.
// This helps distinguish addresses that belong to different objects.
var $gcSlice:[int]int;
// REVIEW: cut $toAbs here?
function ObjInvBase(i:int, rs:[int]int, $toAbs:[int]int,
$AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool)
{
gcAddr(i) && rs[i] != NO_ABS ==>
Aligned(i)
&& AlignedHeapAddr(i + 4) // REVIEW: this is convenient, but is it necessary?
&& VTable(rs[i], $AbsMem[rs[i]][1])
&& !VFieldPtr(rs[i], 1) // REVIEW: necessary?
&& numFields(rs[i]) >= 2 // REVIEW: necessary?
&& ObjSize(rs[i], $AbsMem[rs[i]][1], $AbsMem[rs[i]][2], $AbsMem[rs[i]][3])
&& $gcSlice[i] == i
&& $gcSlice[i + 4] == i // REVIEW: this is convenient, but is it necessary?
}
function ObjInvField(i:int, j:int, rs:[int]int, rt:[int]int, $toAbs:[int]int,
$AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool)
{
gcAddr(i) && rs[i] != NO_ABS ==>
gcAddr(i + 4 * j) // REVIEW: necessary?
&& $gcSlice[i + 4 * j] == i
&& Value(VFieldPtr(rs[i], j), rt, $GcMem[i + 4 * j], $AbsMem[$toAbs[i]][j])
}
function ObjInvPartial(i:int, j1:int, j2:int, rs:[int]int, rt:[int]int, $toAbs:[int]int,
$AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool)
{
ObjInvBase(i, rs, $toAbs, $AbsMem, $GcMem, $gcSlice)
&& (forall j:int::{TO(j)} TO(j) ==> j1 <= j && j < j2 ==>
ObjInvField(i, j, rs, rt, $toAbs, $AbsMem, $GcMem, $gcSlice))
}
function ObjInv(i:int, rs:[int]int, rt:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int,
$GcMem:[int]int, $gcSlice:[int]int) returns (bool)
{
ObjInvPartial(i, 0, numFields(rs[i]), rs, rt, $toAbs, $AbsMem, $GcMem, $gcSlice)
}
procedure TableSearch($base:int, $count:int, $x:int)
requires ecx == $base && edx == $count && ebp == $x;
requires word(ecx) && word(edx) && word(ebp);
requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j <= $count ==> inRo($base + 4 * j, 4));
requires (forall j1:int, j2:int::{TO(j1), TO(j2)} TO(j1) && TO(j2) ==> 0 <= j1 && j1 < j2 && j2 <= $count ==>
ro32($base + 4 * j1) < ro32($base + 4 * j2));
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
// eax = ret
// edx = found
ensures edx != 0 <==> (exists j:int::{TO(j)} TO(j) && 0 <= j && j < $count && between(ro32($base + 4 * j), ro32($base + 4 * (j + 1)), $x));
ensures edx != 0 ==> 0 <= eax && eax < $count && between(ro32($base + 4 * eax), ro32($base + 4 * (eax + 1)), $x);
{
if (edx >= 0) { goto skip1; }
edx := 0;
esp := esp + 4; return;
skip1:
assert TO(0);
call eax := RoLoad32(ecx);
if (ebp >= eax) { goto skip2; }
assert TO(0);
edx := 0;
esp := esp + 4; return;
skip2:
assert TO($count);
call eax := RoLoad32(ecx + 4 * edx);
if (ebp < eax) { goto skip3; }
assert TO($count);
// REVIEW: can this be cleaned up?
assert (forall j:int::{TSlot(j)} TSlot(j) && TO(j) && TO(j + 1) && 0 <= j && j < $count ==> $x >= ro32($base + 4 * (j + 1)));
assert (forall j:int::{TO(j)} TO(j) && TSlot(j) && 0 <= j && j < $count ==> $x >= ro32($base + 4 * (j + 1)));
edx := 0;
esp := esp + 4; return;
skip3:
esi := 0;
edi := edx;
// while (esi + 1 < edi)
loop:
assert TO(esi) && TO(edi) && 0 <= esi && esi < edi && edi <= $count;
assert (exists j:int::{TO(j)} TO(j) && 0 <= j && j < $count && between(ro32($base + 4 * j), ro32($base + 4 * (j + 1)), $x)) ==>
ro32($base + 4 * esi) <= $x && $x < ro32($base + 4 * edi);
call eax := Lea(esi + 1);
if (eax >= edi) { goto loopEnd; }
call ebx := LeaUnchecked(esi + 1 * edi);
call ebx := Shr(ebx, 1);
if (ebx <= esi) { goto do4; }
if (ebx >= edi) { goto do4; }
goto skip4;
do4:
call ebx := Lea(esi + 1);
skip4:
assert TO(ebx);
call eax := RoLoad32(ecx + 4 * ebx);
if (eax <= ebp) { goto do5; }
edi := ebx;
goto skip5;
do5:
esi := ebx;
skip5:
goto loop;
loopEnd:
eax := esi;
call ebx := RoLoad32(ecx + 4 * eax);
if (ebp < ebx) { goto skip6; }
call ebx := RoLoad32(ecx + 4 * eax + 4);
if (ebp >= ebx) { goto skip6; }
edx := 1;
esp := esp + 4; return;
skip6:
edx := 0;
esp := esp + 4; return;
}
procedure TablesSearch($f:int, $ra:int, $nextFp:int)
requires ecx == $ra && edx == $nextFp;
requires word($ra);
requires FrameNextInv($f, $ra, $nextFp, $FrameAddr, $FrameLayout);
modifies eax, ebx, ecx, edx, esi, edi, ebp, esp;
// eax = ret
// edx = found
ensures edx != 0 ==> $f > 0 && eax == frameDescriptor($FrameLayout[$f - 1]) && $FrameAddr[$f - 1] == $nextFp;
ensures edx == 0 ==> $f == 0;
{
var ra:int;
var nextFp:int;
var table:int;
var index:int;
var tableBase:int;
var tmp1:int;
var tmp2:int;
ra := ecx;
nextFp := edx;
table := 0;
edx := 0;
//while (table < ?callSiteTableCount)
loop:
assert TVFT($f);
assert TT(table) && 0 <= table;
assert edx == 0;
assert !(exists t:int, j:int::{TT(t), TO(j)} TT(t) && TO(j) && t < table && RetAddrAt(t, j, ra));
assert word(table) && word(ra);
eax := table;
if (?callSiteTableCount <= eax) { goto loopEnd; }
ebx := ?returnAddressToCallSiteSetNumbers;
call ecx := RoLoad32(ebx + 4 * eax);
ebx := ?callSiteSetCount;
call edx := RoLoad32(ebx + 4 * eax);
call edx := RoLoad32(edx);
ebx := ?codeBaseStartTable;
call esi := RoLoad32(ebx + 4 * eax);
ebp := ra;
assert TO(0);
call eax := RoLoad32(ecx); // REVIEW: better way to prove word(ro32(ecx)).
if (esi > ebp) { goto skip1; }
call ebp := Sub(ebp, esi);
esp := esp - 4; call TableSearch(ecx, edx, ra - esi);
index := eax;
assert TO(index);
if (edx == 0) { goto skip1; }
esi := table;
edi := ?activationDescriptorTable;
call ecx := RoLoad32(edi + 4 * esi);
edi := ?callSiteSetNumberToIndex;
call ebp := RoLoad32(edi + 4 * esi);
call ebp := RoLoadU16(ebp + 2 * eax);
call eax := RoLoad32(ecx + 4 * ebp);
esp := esp + 4; return;
skip1:
call table := AddChecked(table, 1);
edx := 0;
goto loop;
loopEnd:
esp := esp + 4; return;
}
procedure getSize($abs:int, $ptr:int, $vt:int, $_nElems1:int, $_nElems2:int)
requires ecx == $ptr && edx == $vt;
requires ObjSize($abs, $vt, $_nElems1, $_nElems2);
requires VTable($abs, $vt);
requires numFields($abs) >= 3 ==> AlignedHeapAddr($ptr + 8);
requires numFields($abs) >= 3 && !VFieldPtr($abs, 2) ==> $GcMem[$ptr + 8] == $_nElems1;
requires numFields($abs) >= 4 ==> AlignedHeapAddr($ptr + 12);
requires numFields($abs) >= 4 && !VFieldPtr($abs, 3) ==> $GcMem[$ptr + 12] == $_nElems2;
modifies eax, ebx, edx, esi, edi, ebp, esp;
ensures eax == 4 * numFields($abs);
{
assert TVL($abs);
assert TVT($abs, $vt);
call ebp := RoLoad32(edx + ?VT_MASK);
call ebp := And(ebp, 15);
if (ebp != ?SPARSE_TAG) { goto skip1; }
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
goto end;
skip1:
if (ebp != ?DENSE_TAG) { goto skip2; }
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
goto end;
skip2:
if (ebp != ?STRING_TAG) { goto skip3; }
assert TO(2);
call esi := GcLoad(ecx + 8);
//eax := pad(16 + 2 * esi);
eax := esi;
call eax := AddChecked(eax, eax);
call eax := AddChecked(eax, 19);
ebx := 3;
call ebx := Not(ebx);
call eax := And(eax, ebx);
goto end;
skip3:
if (ebp != ?PTR_VECTOR_TAG) { goto skip4; }
assert TO(2);
call esi := GcLoad(ecx + 8);
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
//eax := pad(eax + 4 * esi);
call esi := AddChecked(esi, esi);
call esi := AddChecked(esi, esi);
call eax := AddChecked(eax, esi);
call eax := AddChecked(eax, 3);
ebx := 3;
call ebx := Not(ebx);
call eax := And(eax, ebx);
goto end;
skip4:
if (ebp != ?OTHER_VECTOR_TAG) { goto skip5; }
assert TO(2);
call esi := GcLoad(ecx + 8);
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
call ebx := RoLoad32(edx + ?VT_ARRAY_ELEMENT_SIZE);
//eax := pad(eax + Mult(ebx, esi));
ebp := eax;
eax := ebx;
call eax, edx := MulChecked(eax, esi);
call eax := AddChecked(eax, ebp);
call eax := AddChecked(eax, 3);
ebx := 3;
call ebx := Not(ebx);
call eax := And(eax, ebx);
goto end;
skip5:
if (ebp != ?PTR_ARRAY_TAG) { goto skip6; }
assert TO(3);
call esi := GcLoad(ecx + 12);
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
//eax := pad(eax + 4 * esi);
call esi := AddChecked(esi, esi);
call esi := AddChecked(esi, esi);
call eax := AddChecked(eax, esi);
call eax := AddChecked(eax, 3);
ebx := 3;
call ebx := Not(ebx);
call eax := And(eax, ebx);
goto end;
skip6:
if (ebp != ?OTHER_ARRAY_TAG) { goto skip7; }
assert TO(3);
call esi := GcLoad(ecx + 12);
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
call ebx := RoLoad32(edx + ?VT_ARRAY_ELEMENT_SIZE);
//eax := pad(eax + Mult(ebx, esi));
ebp := eax;
eax := ebx;
call eax, edx := MulChecked(eax, esi);
call eax := AddChecked(eax, ebp);
call eax := AddChecked(eax, 3);
ebx := 3;
call ebx := Not(ebx);
call eax := And(eax, ebx);
goto end;
skip7:
// else
call eax := RoLoad32(edx + ?VT_BASE_LENGTH);
end:
}
procedure GetSize($ptr:int, $vt:int, $rs:[int]int, $rt:[int]int)
requires ecx == $ptr && edx == $vt;
requires gcAddr($ptr);
requires $rs[$ptr] != NO_ABS && $rs[$ptr] == $toAbs[$ptr];
requires ObjInv($ptr, $rs, $rt, $toAbs, $AbsMem, $GcMem, $gcSlice);
requires $vt == $AbsMem[$rs[$ptr]][1] || $vt == $GcMem[$ptr + 4];
modifies eax, ebx, edx, esi, edi, ebp, esp;
ensures eax == 4 * numFields($rs[$ptr]);
{
assert TV($ptr);
assert TO(1);
assert TO(2);
assert TO(3);
call getSize($rs[$ptr], $ptr, $vt, $AbsMem[$rs[$ptr]][2], $AbsMem[$rs[$ptr]][3]);
esp := esp + 4; return;
}
procedure readTag($abs:int, $vt:int)
requires ecx == $vt;
requires VTable($abs, $vt);
modifies eax;
ensures eax == tag($vt);
{
assert TVT($abs, $vt);
call eax := RoLoad32(ecx + ?VT_MASK);
call eax := And(eax, 15);
}
procedure readArrayOf($abs:int, $vt:int)
requires ecx == $vt;
requires VTable($abs, $vt);
modifies ebp;
ensures ebp == arrayOf($vt);
{
assert TVT($abs, $vt);
call ebp := RoLoad32(ecx + ?VT_ARRAY_OF);
}
procedure readElementInfo($abs:int, $vt:int)
requires ecx == $vt;
requires VTable($abs, $vt);
requires tag($vt) == ?OTHER_VECTOR_TAG;
modifies esi, edi;
ensures esi == arrayElementSize($vt);
ensures edi == mask(arrayElementClass($vt));
{
assert TVT($abs, $vt);
call esi := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE);
call edi := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_CLASS);
call edi := RoLoad32(edi + ?VT_MASK);
}