Native code size reduction
This commit is contained in:
parent
2fd8d8673a
commit
a2293a0ed1
|
@ -8088,6 +8088,7 @@ void InterCodeBasicBlock::PeepholeOptimization(void)
|
||||||
mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal &&
|
mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal &&
|
||||||
(mInstructions[i + 2]->mSrc[0].mIntConst & 1) == 0)
|
(mInstructions[i + 2]->mSrc[0].mIntConst & 1) == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
int shift = mInstructions[i + 0]->mSrc[0].mIntConst;
|
int shift = mInstructions[i + 0]->mSrc[0].mIntConst;
|
||||||
int mshift = 1;
|
int mshift = 1;
|
||||||
while (!(mInstructions[i + 2]->mSrc[0].mIntConst & (1ULL << mshift)))
|
while (!(mInstructions[i + 2]->mSrc[0].mIntConst & (1ULL << mshift)))
|
||||||
|
@ -8110,7 +8111,7 @@ void InterCodeBasicBlock::PeepholeOptimization(void)
|
||||||
{
|
{
|
||||||
mInstructions[i + 0]->mCode = IC_LOAD_TEMPORARY;
|
mInstructions[i + 0]->mCode = IC_LOAD_TEMPORARY;
|
||||||
mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1];
|
mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1];
|
||||||
mInstructions[i + 0]->mSrc[0].mTemp = -1;
|
mInstructions[i + 0]->mSrc[1].mTemp = -1;
|
||||||
|
|
||||||
mInstructions[i + 1]->mSrc[1].mIntConst = 255ULL >> shift << shift;
|
mInstructions[i + 1]->mSrc[1].mIntConst = 255ULL >> shift << shift;
|
||||||
mInstructions[i + 2]->mSrc[0].mIntConst >>= shift;
|
mInstructions[i + 2]->mSrc[0].mIntConst >>= shift;
|
||||||
|
|
|
@ -9492,7 +9492,64 @@ void NativeCodeBasicBlock::RemEntryBlock(NativeCodeBasicBlock* block)
|
||||||
mEntryBlocks.Remove(i);
|
mEntryBlocks.Remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeCodeBasicBlock::JoinTailCodeSequences(void)
|
NativeCodeBasicBlock * NativeCodeBasicBlock::SplitMatchingTails(NativeCodeProcedure* proc)
|
||||||
|
{
|
||||||
|
NativeCodeBasicBlock* nblock = nullptr;
|
||||||
|
|
||||||
|
for (int i = 0; i < mEntryBlocks.Size() - 1; i++)
|
||||||
|
{
|
||||||
|
NativeCodeBasicBlock* bi(mEntryBlocks[i]);
|
||||||
|
|
||||||
|
if (bi->mBranch == ASMIT_JMP && bi->mIns.Size() > 1)
|
||||||
|
{
|
||||||
|
for (int j = i + 1; j < mEntryBlocks.Size(); j++)
|
||||||
|
{
|
||||||
|
NativeCodeBasicBlock* bj(mEntryBlocks[j]);
|
||||||
|
|
||||||
|
if (bj->mBranch == ASMIT_JMP && bj->mIns.Size() > 1)
|
||||||
|
{
|
||||||
|
if (bi->mIns[bi->mIns.Size() - 1].IsSame(bj->mIns[bj->mIns.Size() - 1]) &&
|
||||||
|
bi->mIns[bi->mIns.Size() - 2].IsSame(bj->mIns[bj->mIns.Size() - 2]))
|
||||||
|
{
|
||||||
|
if (!nblock)
|
||||||
|
{
|
||||||
|
nblock = proc->AllocateBlock();
|
||||||
|
nblock->mBranch = ASMIT_JMP;
|
||||||
|
nblock->mVisited = false;
|
||||||
|
nblock->mTrueJump = this;
|
||||||
|
|
||||||
|
nblock->mEntryBlocks.Push(bi);
|
||||||
|
bi->mTrueJump = nblock;
|
||||||
|
mEntryBlocks[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nblock->mEntryBlocks.Push(bj);
|
||||||
|
bj->mTrueJump = nblock;
|
||||||
|
mEntryBlocks[j] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nblock)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while (i < mEntryBlocks.Size())
|
||||||
|
{
|
||||||
|
if (mEntryBlocks[i])
|
||||||
|
i++;
|
||||||
|
else
|
||||||
|
mEntryBlocks.Remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nblock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
|
@ -9529,6 +9586,16 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mEntryBlocks.Size() > 2)
|
||||||
|
{
|
||||||
|
NativeCodeBasicBlock* nblock = SplitMatchingTails(proc);
|
||||||
|
if (nblock)
|
||||||
|
{
|
||||||
|
if (nblock->JoinTailCodeSequences(proc))
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if 1
|
#if 1
|
||||||
|
@ -9630,9 +9697,9 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (mTrueJump && mTrueJump->JoinTailCodeSequences())
|
if (mTrueJump && mTrueJump->JoinTailCodeSequences(proc))
|
||||||
changed = true;
|
changed = true;
|
||||||
if (mFalseJump && mFalseJump->JoinTailCodeSequences())
|
if (mFalseJump && mFalseJump->JoinTailCodeSequences(proc))
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16168,6 +16235,26 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BypassEmptyBlocks(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int NativeCodeBasicBlock::LeadsInto(NativeCodeBasicBlock* block, int dist)
|
||||||
|
{
|
||||||
|
if (mPlaced)
|
||||||
|
return 6;
|
||||||
|
else if (mTrueJump == block || mFalseJump == block)
|
||||||
|
return dist;
|
||||||
|
else if (dist < 5)
|
||||||
|
{
|
||||||
|
int d0 = mTrueJump ? mTrueJump->LeadsInto(block, dist + 1) : 6;
|
||||||
|
int d1 = mFalseJump ? mFalseJump->LeadsInto(block, dist + 1) : 6;
|
||||||
|
|
||||||
|
if (d0 < d1)
|
||||||
|
return d0;
|
||||||
|
else
|
||||||
|
return d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
void NativeCodeBasicBlock::BuildPlacement(GrowingArray<NativeCodeBasicBlock*>& placement)
|
void NativeCodeBasicBlock::BuildPlacement(GrowingArray<NativeCodeBasicBlock*>& placement)
|
||||||
{
|
{
|
||||||
if (!mPlaced)
|
if (!mPlaced)
|
||||||
|
@ -16190,12 +16277,12 @@ void NativeCodeBasicBlock::BuildPlacement(GrowingArray<NativeCodeBasicBlock*>& p
|
||||||
|
|
||||||
mTrueJump->BuildPlacement(placement);
|
mTrueJump->BuildPlacement(placement);
|
||||||
}
|
}
|
||||||
else if (mTrueJump->mFalseJump == mFalseJump || mTrueJump->mTrueJump == mFalseJump)
|
else if (mTrueJump->LeadsInto(mFalseJump, 0) < mFalseJump->LeadsInto(mTrueJump, 0))
|
||||||
{
|
{
|
||||||
mTrueJump->BuildPlacement(placement);
|
mTrueJump->BuildPlacement(placement);
|
||||||
mFalseJump->BuildPlacement(placement);
|
mFalseJump->BuildPlacement(placement);
|
||||||
}
|
}
|
||||||
else if (mFalseJump->mFalseJump == mTrueJump || mFalseJump->mTrueJump == mTrueJump)
|
else if (mTrueJump->LeadsInto(mFalseJump, 0) > mFalseJump->LeadsInto(mTrueJump, 0))
|
||||||
{
|
{
|
||||||
mFalseJump->BuildPlacement(placement);
|
mFalseJump->BuildPlacement(placement);
|
||||||
mTrueJump->BuildPlacement(placement);
|
mTrueJump->BuildPlacement(placement);
|
||||||
|
@ -17136,7 +17223,7 @@ void NativeCodeProcedure::Optimize(void)
|
||||||
if (step > 2)
|
if (step > 2)
|
||||||
{
|
{
|
||||||
ResetVisited();
|
ResetVisited();
|
||||||
if (mEntryBlock->JoinTailCodeSequences())
|
if (mEntryBlock->JoinTailCodeSequences(this))
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -135,6 +135,7 @@ public:
|
||||||
|
|
||||||
NativeCodeBasicBlock* BypassEmptyBlocks(void);
|
NativeCodeBasicBlock* BypassEmptyBlocks(void);
|
||||||
|
|
||||||
|
int LeadsInto(NativeCodeBasicBlock* block, int dist);
|
||||||
void BuildPlacement(GrowingArray<NativeCodeBasicBlock*>& placement);
|
void BuildPlacement(GrowingArray<NativeCodeBasicBlock*>& placement);
|
||||||
void InitialOffset(int& total);
|
void InitialOffset(int& total);
|
||||||
bool CalculateOffset(int& total);
|
bool CalculateOffset(int& total);
|
||||||
|
@ -246,7 +247,9 @@ public:
|
||||||
void AddEntryBlock(NativeCodeBasicBlock* block);
|
void AddEntryBlock(NativeCodeBasicBlock* block);
|
||||||
void RemEntryBlock(NativeCodeBasicBlock* block);
|
void RemEntryBlock(NativeCodeBasicBlock* block);
|
||||||
|
|
||||||
bool JoinTailCodeSequences(void);
|
NativeCodeBasicBlock * SplitMatchingTails(NativeCodeProcedure* proc);
|
||||||
|
|
||||||
|
bool JoinTailCodeSequences(NativeCodeProcedure* proc);
|
||||||
bool SameTail(const NativeCodeInstruction& ins) const;
|
bool SameTail(const NativeCodeInstruction& ins) const;
|
||||||
|
|
||||||
NativeRegisterDataSet mEntryRegisterDataSet;
|
NativeRegisterDataSet mEntryRegisterDataSet;
|
||||||
|
|
|
@ -73,7 +73,7 @@ int main2(int argc, const char** argv)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
strcpy(strProductName, "oscar64");
|
strcpy(strProductName, "oscar64");
|
||||||
strcpy(strProductVersion, "1.4.94");
|
strcpy(strProductVersion, "1.4.95");
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
uint32_t length = sizeof(basePath);
|
uint32_t length = sizeof(basePath);
|
||||||
|
|
|
@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,4,94,0
|
FILEVERSION 1,4,95,0
|
||||||
PRODUCTVERSION 1,4,94,0
|
PRODUCTVERSION 1,4,95,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -43,12 +43,12 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "oscar64"
|
VALUE "CompanyName", "oscar64"
|
||||||
VALUE "FileDescription", "oscar64 compiler"
|
VALUE "FileDescription", "oscar64 compiler"
|
||||||
VALUE "FileVersion", "1.4.94.0"
|
VALUE "FileVersion", "1.4.95.0"
|
||||||
VALUE "InternalName", "oscar64.exe"
|
VALUE "InternalName", "oscar64.exe"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2021"
|
VALUE "LegalCopyright", "Copyright (C) 2021"
|
||||||
VALUE "OriginalFilename", "oscar64.exe"
|
VALUE "OriginalFilename", "oscar64.exe"
|
||||||
VALUE "ProductName", "oscar64"
|
VALUE "ProductName", "oscar64"
|
||||||
VALUE "ProductVersion", "1.4.94.0"
|
VALUE "ProductVersion", "1.4.95.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -3752,15 +3752,15 @@
|
||||||
{
|
{
|
||||||
"Name" = "8:Microsoft Visual Studio"
|
"Name" = "8:Microsoft Visual Studio"
|
||||||
"ProductName" = "8:oscar64"
|
"ProductName" = "8:oscar64"
|
||||||
"ProductCode" = "8:{DB788758-808B-4A56-B99C-F2BDBCD45FE3}"
|
"ProductCode" = "8:{37C3F00F-2A58-4FAD-B5BA-5574475C7831}"
|
||||||
"PackageCode" = "8:{9E6C1E34-6564-490E-B047-ED558001FF36}"
|
"PackageCode" = "8:{0F1773AB-6C73-4E53-B2D9-A794C417CED2}"
|
||||||
"UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}"
|
"UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}"
|
||||||
"AspNetVersion" = "8:2.0.50727.0"
|
"AspNetVersion" = "8:2.0.50727.0"
|
||||||
"RestartWWWService" = "11:FALSE"
|
"RestartWWWService" = "11:FALSE"
|
||||||
"RemovePreviousVersions" = "11:TRUE"
|
"RemovePreviousVersions" = "11:TRUE"
|
||||||
"DetectNewerInstalledVersion" = "11:TRUE"
|
"DetectNewerInstalledVersion" = "11:TRUE"
|
||||||
"InstallAllUsers" = "11:FALSE"
|
"InstallAllUsers" = "11:FALSE"
|
||||||
"ProductVersion" = "8:1.4.94"
|
"ProductVersion" = "8:1.4.95"
|
||||||
"Manufacturer" = "8:oscar64"
|
"Manufacturer" = "8:oscar64"
|
||||||
"ARPHELPTELEPHONE" = "8:"
|
"ARPHELPTELEPHONE" = "8:"
|
||||||
"ARPHELPLINK" = "8:"
|
"ARPHELPLINK" = "8:"
|
||||||
|
|
|
@ -171,14 +171,15 @@ void tiles_draw1(char * dp, char * tm)
|
||||||
|
|
||||||
struct Shot
|
struct Shot
|
||||||
{
|
{
|
||||||
char x, y, dx, n;
|
byte ty, x, ry, n;
|
||||||
} shots[5][4];
|
sbyte dx;
|
||||||
|
} shots[18];
|
||||||
|
|
||||||
|
Shot * firstShot;
|
||||||
|
Shot * lastShot;
|
||||||
|
|
||||||
inline void shot_draw(char * dp, char i, char xp, char yp)
|
inline void shot_draw(char * dp, char i, char xp, char yp)
|
||||||
{
|
{
|
||||||
__assume(i < 32);
|
|
||||||
__assume(yp < 8);
|
|
||||||
|
|
||||||
char c = dp[xp];
|
char c = dp[xp];
|
||||||
dp[xp] = i | 0xe0;
|
dp[xp] = i | 0xe0;
|
||||||
|
|
||||||
|
@ -189,7 +190,42 @@ inline void shot_draw(char * dp, char i, char xp, char yp)
|
||||||
fdp[4] = fsp[4]; fdp[5] = fsp[5]; fdp[6] = fsp[6]; fdp[7] = fsp[7];
|
fdp[4] = fsp[4]; fdp[5] = fsp[5]; fdp[6] = fsp[6]; fdp[7] = fsp[7];
|
||||||
|
|
||||||
fdp[yp] = 0x00;
|
fdp[yp] = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shot_add(int dx, int sy)
|
||||||
|
{
|
||||||
|
char py = sy - 6;
|
||||||
|
char gy = py >> 5;
|
||||||
|
char ey = (py >> 3) & 3;
|
||||||
|
char ry = py & 7;
|
||||||
|
|
||||||
|
Shot * s = lastShot - 1;
|
||||||
|
while (s->ty > gy)
|
||||||
|
{
|
||||||
|
s[1] = s[0];
|
||||||
|
s--;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
|
||||||
|
lastShot++;
|
||||||
|
lastShot->ty = 6;
|
||||||
|
|
||||||
|
s->ty = gy;
|
||||||
|
s->ry = ry;
|
||||||
|
if (dx < 0)
|
||||||
|
{
|
||||||
|
s->dx = -1;
|
||||||
|
char x = (148 - 4 * dx) >> 3;
|
||||||
|
s->n = x - 1;
|
||||||
|
s->x = 40 * ey + x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s->dx = 1;
|
||||||
|
char x = (156 - 4 * dx) >> 3;
|
||||||
|
s->x = 40 * ey + x;
|
||||||
|
s->n = 39 - x;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tiles_draw(unsigned x)
|
void tiles_draw(unsigned x)
|
||||||
|
@ -202,6 +238,8 @@ void tiles_draw(unsigned x)
|
||||||
char yl = 0;
|
char yl = 0;
|
||||||
char ci = 0;
|
char ci = 0;
|
||||||
|
|
||||||
|
Shot * ss = firstShot, * ts = firstShot;
|
||||||
|
|
||||||
for(int iy=0; iy<5; iy++)
|
for(int iy=0; iy<5; iy++)
|
||||||
{
|
{
|
||||||
char * dp = Screen + 80 + 160 * iy;
|
char * dp = Screen + 80 + 160 * iy;
|
||||||
|
@ -262,22 +300,26 @@ void tiles_draw(unsigned x)
|
||||||
dp[k] = 0xf8;
|
dp[k] = 0xf8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (ss->ty == iy)
|
||||||
Shot * s = shots[iy];
|
|
||||||
for(char si=0; si<4; si++)
|
|
||||||
{
|
{
|
||||||
if (s->n)
|
ss->x += ss->dx;
|
||||||
|
ss->n--;
|
||||||
|
shot_draw(dp, ci++, ss->x, ss->ry);
|
||||||
|
if (ss->n)
|
||||||
{
|
{
|
||||||
s->x += s->dx;
|
if (ss != ts)
|
||||||
s->n--;
|
*ts = *ss;
|
||||||
shot_draw(dp, ci++, s->x, s->y);
|
ts++;
|
||||||
}
|
}
|
||||||
s++;
|
ss++;
|
||||||
}
|
}
|
||||||
|
|
||||||
yl += 4;
|
yl += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastShot = ts;
|
||||||
|
lastShot->ty = 6;
|
||||||
|
|
||||||
Font[248 * 8 + 2] = ~(1 << xs);
|
Font[248 * 8 + 2] = ~(1 << xs);
|
||||||
|
|
||||||
vic.ctrl2 = VIC_CTRL2_MCM + xs;
|
vic.ctrl2 = VIC_CTRL2_MCM + xs;
|
||||||
|
@ -321,14 +363,10 @@ int main(void)
|
||||||
for(int i=0; i<24; i++)
|
for(int i=0; i<24; i++)
|
||||||
stars[i] = rand() % 40 + 40 * (i & 3);
|
stars[i] = rand() % 40 + 40 * (i & 3);
|
||||||
|
|
||||||
for(int i=0; i<5; i++)
|
shots[0].ty = 0;
|
||||||
{
|
firstShot = shots + 1;
|
||||||
for(int j=0; j<8; j++)
|
lastShot = firstShot;
|
||||||
{
|
lastShot->ty = 6;
|
||||||
shots[i][j].x = rand() % 160;
|
|
||||||
shots[i][j].y = rand() & 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spr_set(0, true, 160, 100, 64, VCOL_BLUE, true, false, false);
|
spr_set(0, true, 160, 100, 64, VCOL_BLUE, true, false, false);
|
||||||
spr_set(1, true, 160, 100, 64 + 16, VCOL_MED_GREY, true, false, false);
|
spr_set(1, true, 160, 100, 64 + 16, VCOL_MED_GREY, true, false, false);
|
||||||
|
@ -394,36 +432,8 @@ int main(void)
|
||||||
fdelay--;
|
fdelay--;
|
||||||
else if (joyb[0] && vpx != 0)
|
else if (joyb[0] && vpx != 0)
|
||||||
{
|
{
|
||||||
char py = spy - 6;
|
shot_add(vpx, spy);
|
||||||
char gy = py >> 5;
|
fdelay = 5;
|
||||||
char ey = (py >> 3) & 3;
|
|
||||||
char ry = py & 7;
|
|
||||||
|
|
||||||
Shot * s = shots[gy];
|
|
||||||
|
|
||||||
char i = 0;
|
|
||||||
while (i < 4 && s[i].n != 0)
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if (i < 4)
|
|
||||||
{
|
|
||||||
s[i].y = ry;
|
|
||||||
if (vpx < 0)
|
|
||||||
{
|
|
||||||
s[i].dx = -1;
|
|
||||||
char x = (148 - 4 * vpx) >> 3;
|
|
||||||
s[i].n = x - 1;
|
|
||||||
s[i].x = 40 * ey + x;
|
|
||||||
}
|
|
||||||
else if (vpx > 0)
|
|
||||||
{
|
|
||||||
s[i].dx = 1;
|
|
||||||
char x = (156 - 4 * vpx) >> 3;
|
|
||||||
s[i].x = 40 * ey + x;
|
|
||||||
s[i].n = 39 - x;
|
|
||||||
}
|
|
||||||
fdelay = 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spr_move(0, 172 - 4 * vpx, 50 + spy);
|
spr_move(0, 172 - 4 * vpx, 50 + spy);
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue