Optimize single use of global variables in function

This commit is contained in:
drmortalwombat 2022-02-27 17:19:51 +01:00
parent 756245694f
commit fbdb513697
6 changed files with 405 additions and 61 deletions

View File

@ -114,6 +114,9 @@ rem @echo off
@call :test structmembertest.c
@if %errorlevel% neq 0 goto :error
@call :test structarraycopy.c
@if %errorlevel% neq 0 goto :error
@call :test randsumtest.c
@if %errorlevel% neq 0 goto :error

View File

@ -0,0 +1,49 @@
struct Point
{
int x, y;
};
Point tcorners[8], pcorners[8];
int bm_line(void)
{
}
void drawCube(void)
{
for(char i=0; i<8; i++)
{
if (!(i & 1))
bm_line();
if (!(i & 2))
bm_line();
if (!(i & 4))
bm_line();
pcorners[i] = tcorners[i];
}
}
int main(void)
{
for(int i=0; i<8; i++)
{
tcorners[i].x = (i + 1) * 3;
tcorners[i].y = (i + 1) * 7;
}
drawCube();
int sum = 0;
for(int i=0; i<8; i++)
{
sum += pcorners[i].x;
sum -= tcorners[i].y;
}
return sum + 144;
}

View File

@ -10,6 +10,8 @@ static const uint64 COPT_OPTIMIZE_AUTO_INLINE_ALL = 0x00000020;
static const uint64 COPT_OPTIMIZE_AUTO_UNROLL = 0x00000040;
static const uint64 COPT_OPTIMIZE_CONST_EXPRESSIONS = 0x00000080;
static const uint64 COPT_OPTIMIZE_CODE_SIZE = 0x00000100;
static const uint64 COPT_TARGET_PRG = 0x100000000ULL;
static const uint64 COPT_TARGET_CRT16 = 0x200000000ULL;
static const uint64 COPT_TARGET_CRT512 = 0x400000000ULL;
@ -24,7 +26,7 @@ static const uint64 COPT_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE |
static const uint64 COPT_OPTIMIZE_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS;
static const uint64 COPT_OPTIMIZE_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS;
static const uint64 COPT_OPTIMIZE_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_CODE_SIZE;
static const uint64 COPT_OPTIMIZE_SPEED = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS;

View File

@ -690,6 +690,42 @@ bool NativeCodeInstruction::UsesZeroPage(int address) const
return true;
else if (mMode == ASMIM_INDIRECT_Y && (mAddress == address || mAddress + 1 == address))
return true;
else if (mType == ASMIT_JSR)
{
if (address >= BC_REG_ACCU && address < BC_REG_ACCU + 4)
return true;
if (address >= BC_REG_WORK && address < BC_REG_WORK + 4)
return true;
if (mFlags & NCIF_RUNTIME)
{
if (mFlags & NCIF_USE_ZP_32_X)
{
if (address >= mParam && address < mParam + 4)
return true;
}
if (mFlags & NCIF_FEXEC)
{
if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END)
return true;
}
}
else
{
if (mLinkerObject)
{
for (int i = 0; i < mLinkerObject->mNumTemporaries; i++)
{
if (address >= mLinkerObject->mTemporaries[i] && address < mLinkerObject->mTemporaries[i] + mLinkerObject->mTempSizes[i])
return true;
}
}
}
return false;
}
else
return false;
}
@ -778,6 +814,20 @@ bool NativeCodeInstruction::ChangesAccu(void) const
bool NativeCodeInstruction::UsesAddress(void) const
{
if (mMode != ASMIM_IMPLIED)
{
return
mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR ||
mType == ASMIT_LDA || mType == ASMIT_LDX || mType == ASMIT_LDY ||
mType == ASMIT_CMP || mType == ASMIT_CPX || mType == ASMIT_CPY ||
mType == ASMIT_ADC || mType == ASMIT_SBC || mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR;
}
else
return false;
}
bool NativeCodeInstruction::ChangesAddress(void) const
{
if (mMode != ASMIM_IMPLIED)
@ -4897,7 +4947,14 @@ NativeCodeBasicBlock * NativeCodeBasicBlock::CopyValue(InterCodeProcedure* proc,
int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp];
int size = ins->mConst.mOperandSize;
if (size < 4)
int msize = 4;
if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL)
msize = 8;
else if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)
msize = 2;
if (size <= msize)
{
for (int i = 0; i < size; i++)
{
@ -9559,6 +9616,33 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc)
pblock->mIns.Push(mIns[0]);
mIns.Remove(0);
changed = true;
}
}
else if (mIns[0].mType == ASMIT_LDX && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & LIVE_CPU_REG_A))
{
if (lblock->mIns[ls - 2].mType == ASMIT_LDA && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress &&
lblock->mIns[ls - 1].mType == ASMIT_CMP && !(lblock->mIns[ls - 1].mLive & LIVE_CPU_REG_A))
{
lblock->mIns[ls - 2].mType = ASMIT_LDX; lblock->mIns[ls - 2].mLive |= LIVE_CPU_REG_X;
lblock->mIns[ls - 1].mType = ASMIT_CPX; lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_X;
pblock->mIns.Push(mIns[0]);
mIns.Remove(0);
changed = true;
}
}
else if (mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE)
{
if (lblock->mIns[ls - 2].mType == ASMIT_LDA && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress &&
lblock->mIns[ls - 1].mType == ASMIT_CMP)
{
pblock->mIns.Push(mIns[0]);
mIns.Remove(0);
lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_A;
changed = true;
}
}
@ -9737,6 +9821,103 @@ bool NativeCodeBasicBlock::FindImmediateStore(int at, int reg, const NativeCodeI
return false;
}
bool NativeCodeBasicBlock::CheckSingleUseGlobalLoad(int reg, int at, const NativeCodeInstruction& ains)
{
if (!mPatched)
{
mPatched = true;
if (at == 0)
{
if (!mEntryRequiredRegs[reg])
return true;
if (mNumEntries > 1)
return false;
}
while (at < mIns.Size())
{
NativeCodeInstruction& ins(mIns[at]);
if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg)
{
if (ins.UsesAddress())
{
if (ins.ChangesAddress())
return false;
return !(ins.mLive & LIVE_MEM);
}
else
return true;
}
else if (ins.mType == ASMIT_JSR)
{
if (ins.mFlags & NCIF_RUNTIME)
{
if (ins.UsesZeroPage(reg))
return false;
}
else
return false;
}
else if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress + 1 == reg))
return false;
else if (ins.ChangesZeroPage(reg))
return true;
else if (ains.MayBeChangedOnAddress(ins))
return false;
at++;
}
if (mTrueJump && !mTrueJump->CheckSingleUseGlobalLoad(reg, 0, ains))
return false;
if (mFalseJump && !mFalseJump->CheckSingleUseGlobalLoad(reg, 0, ains))
return false;
}
return true;
}
bool NativeCodeBasicBlock::PatchSingleUseGlobalLoad(int reg, int at, const NativeCodeInstruction& ains)
{
bool changed = false;
if (!mPatched)
{
mPatched = true;
if (at == 0 && !mEntryRequiredRegs[reg])
return false;
while (at < mIns.Size())
{
NativeCodeInstruction& ins(mIns[at]);
if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg)
{
if (ins.UsesAddress())
{
assert(!(ins.mLive & LIVE_MEM));
ins.CopyMode(ains);
return true;
}
return false;
}
at++;
}
if (mTrueJump && mTrueJump->PatchSingleUseGlobalLoad(reg, 0, ains))
changed = true;
if (mFalseJump && mFalseJump->PatchSingleUseGlobalLoad(reg, 0, ains))
changed = true;
}
return changed;
}
bool NativeCodeBasicBlock::CheckGlobalAddressSumYPointer(int reg, int at, int yval)
{
@ -9744,6 +9925,15 @@ bool NativeCodeBasicBlock::CheckGlobalAddressSumYPointer(int reg, int at, int yv
{
mPatched = true;
if (at == 0)
{
if (!mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1])
return true;
if (mNumEntries > 1)
yval = -1;
}
while (at < mIns.Size())
{
NativeCodeInstruction& ins(mIns[at]);
@ -9764,6 +9954,12 @@ bool NativeCodeBasicBlock::CheckGlobalAddressSumYPointer(int reg, int at, int yv
yval = (yval + 1) & 255;
else if (ins.mType == ASMIT_DEY && yval >= 0)
yval = (yval - 1) & 255;
else if (ins.mType == ASMIT_JSR)
{
if (ins.UsesZeroPage(reg) || ins.UsesZeroPage(reg + 1))
return false;
yval = -1;
}
else if (ins.ChangesYReg())
yval = -1;
@ -9786,6 +9982,8 @@ bool NativeCodeBasicBlock::PatchGlobalAddressSumYPointer(int reg, int at, int yv
if (!mPatched)
{
mPatched = true;
if (at == 0 && !mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1])
return false;
while (at < mIns.Size())
{
@ -10838,6 +11036,10 @@ bool NativeCodeBasicBlock::MoveLoadStoreOutOfXYRangeUp(int at)
return false;
if (mIns[j].ChangesAddress() && mIns[j].SameEffectiveAddress(mIns[at + 1]))
return false;
if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_X && mIns[j].ChangesXReg())
return false;
if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y && mIns[j].ChangesYReg())
return false;
if (mIns[j].mType == ASMIT_LDA)
{
@ -10973,33 +11175,55 @@ bool NativeCodeBasicBlock::MoveStoreXUp(int at)
{
bool done = false;
int n = 0, inc = 0;;
int reg = mIns[at].mAddress;
while (at > 0)
{
if (mIns[at - 1].ChangesXReg() || mIns[at - 1].mType == ASMIT_STX)
return done;
if (mIns[at].mMode == ASMIM_ZERO_PAGE)
if (mIns[at - 1].mType == ASMIT_INX)
{
if ((mIns[at - 1].mMode == ASMIM_ZERO_PAGE || mIns[at - 1].mMode == ASMIM_INDIRECT_Y) && mIns[at - 1].mAddress == mIns[at].mAddress)
n++;
inc++;
}
else if (mIns[at - 1].mType == ASMIT_DEX)
{
n++;
inc--;
}
else
{
if (mIns[at - 1].mType == ASMIT_LDX || mIns[at - 1].mType == ASMIT_TAX)
return done;
if (mIns[at - 1].mMode == ASMIM_INDIRECT_Y && mIns[at - 1].mAddress + 1 == mIns[at].mAddress)
else if (mIns[at - 1].ChangesXReg() || mIns[at - 1].mType == ASMIT_STX)
return done;
else if (mIns[at + n].mMode == ASMIM_ZERO_PAGE)
{
if ((mIns[at - 1].mMode == ASMIM_ZERO_PAGE || mIns[at - 1].mMode == ASMIM_INDIRECT_Y) && mIns[at - 1].mAddress == reg)
return done;
if (mIns[at - 1].mMode == ASMIM_INDIRECT_Y && mIns[at - 1].mAddress + 1 == reg)
return done;
}
else
{
if (mIns[at - 1].mMode == ASMIM_ABSOLUTE && mIns[at - 1].mLinkerObject == mIns[at].mLinkerObject && mIns[at - 1].mAddress == mIns[at].mAddress)
if (mIns[at - 1].mMode == ASMIM_ABSOLUTE && mIns[at - 1].mLinkerObject == mIns[at + n].mLinkerObject && mIns[at - 1].mAddress == mIns[at + n].mAddress)
return done;
else if ((mIns[at - 1].mMode == ASMIM_ABSOLUTE_X || mIns[at - 1].mMode == ASMIM_ABSOLUTE_Y) && mIns[at - 1].mLinkerObject == mIns[at].mLinkerObject)
else if ((mIns[at - 1].mMode == ASMIM_ABSOLUTE_X || mIns[at - 1].mMode == ASMIM_ABSOLUTE_Y) && mIns[at - 1].mLinkerObject == mIns[at + n].mLinkerObject)
return done;
}
mIns[at].mLive |= mIns[at - 1].mLive;
NativeCodeInstruction ins = mIns[at - 1];
mIns[at - 1] = mIns[at];
mIns[at] = ins;
at--;
if (ins.mMode == ASMIM_ABSOLUTE_X)
ins.mAddress -= inc;
for (int i = 0; i <= n; i++)
mIns[at - 1 + i] = mIns[at + i];
mIns[at + n] = ins;
done = true;
}
at--;
}
return done;
}
@ -13857,8 +14081,16 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass
#if 1
for (int i = 0; i + 2 < mIns.Size(); i++)
{
if ((mIns[i + 0].mType == ASMIT_TAX || mIns[i + 0].mType == ASMIT_TAY) &&
mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) &&
if (mIns[i + 0].mType == ASMIT_TAX &&
mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) &&
mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A)))
{
if (MoveLoadStoreOutOfXYRangeUp(i))
changed = true;
}
else if (
mIns[i + 0].mType == ASMIT_TAY &&
mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) &&
mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A)))
{
if (MoveLoadStoreOutOfXYRangeUp(i))
@ -15790,6 +16022,41 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass
progress = true;
}
#endif
else if (
mIns[i + 0].mType == ASMIT_TXA &&
mIns[i + 1].mType == ASMIT_CLC &&
mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 &&
mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & LIVE_CPU_REG_C))
{
mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED;
mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X;
if (mIns[i + 2].mAddress == 2)
mIns[i + 2].mType = ASMIT_INX;
else
mIns[i + 2].mType = ASMIT_NOP;
mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X;
if (mIns[i + 3].mLive & LIVE_CPU_REG_A)
mIns[i + 3].mType = ASMIT_TXA;
progress = true;
}
else if (
mIns[i + 0].mType == ASMIT_TXA &&
mIns[i + 1].mType == ASMIT_SEC &&
mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 &&
mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & LIVE_CPU_REG_C))
{
mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED;
mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X;
if (mIns[i + 2].mAddress == 2)
mIns[i + 2].mType = ASMIT_DEX;
else
mIns[i + 2].mType = ASMIT_NOP;
mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X;
if (mIns[i + 3].mLive & LIVE_CPU_REG_A)
mIns[i + 3].mType = ASMIT_TXA;
progress = true;
}
else if (
mIns[i + 0].mType == ASMIT_TYA &&
mIns[i + 1].mType == ASMIT_CLC &&
@ -16183,6 +16450,22 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass
}
}
#endif
if (i + 1 < mIns.Size())
{
if (
mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE &&
mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)))
{
proc->ResetPatched();
if (CheckSingleUseGlobalLoad(mIns[i + 1].mAddress, i + 2, mIns[i]))
{
proc->ResetPatched();
if (PatchSingleUseGlobalLoad(mIns[i + 1].mAddress, i + 2, mIns[i]))
progress = true;
}
}
}
if (i + 5 < mIns.Size())
{
if (
@ -16221,7 +16504,8 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass
mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED;
proc->ResetPatched();
progress = PatchGlobalAddressSumYPointer(mIns[i + 2].mAddress, i + 6, -1, mIns[i + 3].mLinkerObject, mIns[i + 3].mAddress);
if (PatchGlobalAddressSumYPointer(mIns[i + 2].mAddress, i + 6, -1, mIns[i + 3].mLinkerObject, mIns[i + 3].mAddress))
progress = true;
}
}
#endif

View File

@ -84,6 +84,7 @@ public:
bool LoadsAccu(void) const;
bool ChangesAccuAndFlag(void) const;
bool ChangesAddress(void) const;
bool UsesAddress(void) const;
bool ChangesAccu(void) const;
bool UsesAccu(void) const;
bool ChangesCarry(void) const;
@ -273,6 +274,9 @@ public:
bool CheckGlobalAddressSumYPointer(int reg, int at, int yval);
bool PatchGlobalAddressSumYPointer(int reg, int at, int yval, LinkerObject * lobj, int address);
bool CheckSingleUseGlobalLoad(int reg, int at, const NativeCodeInstruction& ains);
bool PatchSingleUseGlobalLoad(int reg, int at, const NativeCodeInstruction& ains);
};
class NativeCodeProcedure

View File

@ -330,59 +330,53 @@ struct Enemy
int px;
byte py;
sbyte dx;
byte n;
} enemies[5];
int spx = 40;
int vpx = 16;
int ax = 0;
char spy = 100;
char fdelay = 0;
int spx = 40;
int vpx = 16;
int ax = 0;
char spy = 100;
char fdelay = 0;
char edelay = 5;
char ecount = 0;
void enemies_move(void)
{
bool elive = false;
for(char i=0; i<5; i++)
{
if (enemies[i].n)
if (enemies[i].dx)
{
enemies[i].n--;
enemies[i].px += enemies[i].dx;
int rx = enemies[i].px - spx;
if (enemies[i].dx < 0)
if (rx < -192 || rx >= 480)
{
if (rx < -24)
enemies[i].n = 0;
}
else
{
if (rx > 320)
enemies[i].n = 0;
enemies[i].dx = 0;
ecount--;
}
spr_move(2 + i, rx + 24, enemies[i].py + 50);
elive = true;
}
}
}
if (!elive)
{
for(char i=0; i<5; i++)
{
sbyte v = 4 + (rand() & 1);
enemies[i].py = 20 + 30 * i;
enemies[i].dx = vpx < 0 ? v : -v;
enemies[i].px = (vpx < 0 ? spx - 56 : spx + 320) + (rand() & 31);
enemies[i].n = 100;
void enemies_spwan(void)
{
char u = rand();
int rx = enemies[i].px - spx;
char e = ecount;
spr_set(2 + i, true, rx + 24, enemies[i].py + 50, 96, VCOL_YELLOW, true, false, false);
}
}
__assume(e < 5);
sbyte v = 1 + (u & 3);
enemies[ecount].py = 20 + 30 * e;
enemies[ecount].dx = vpx < 0 ? v : -v;
enemies[ecount].px = (vpx < 0 ? spx - 56 : spx + 320) + ((u >> 1) & 31);
int rx = enemies[ecount].px - spx;
spr_set(2 + ecount, true, rx + 24, enemies[ecount].py + 50, vpx < 0 ? 97 : 96, VCOL_YELLOW, true, false, false);
ecount++;
}
int main(void)
@ -430,7 +424,7 @@ int main(void)
spr_set(0, true, 160, 100, 64, VCOL_BLUE, true, false, false);
spr_set(7, true, 160, 100, 64 + 16, VCOL_MED_GREY, true, false, false);
vic.spr_priority = 2;
vic.spr_priority = 0x80;
vpx = 2;
for(;;)
@ -497,15 +491,23 @@ int main(void)
vic.color_border++;
vic_waitLine(82);
vic.color_border++;
tiles_draw(spx);
tiles_draw(spx & 4095);
vic.color_border--;
if (edelay)
{
edelay--;
if (edelay < 5)
enemies_spwan();
}
else
{
enemies_move();
if (!ecount)
edelay = 64 + (rand() & 63);
}
vic.color_border--;
spx += vpx >> 1;
spx &= 4095;
spx += vpx >> 2;
}
return 0;