From e7332192c09d7f8883acb2589d264d36a46dbc2c Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Tue, 14 Jun 2022 22:29:42 +0200 Subject: [PATCH] Fix over eager elimination of byte to word conversion --- oscar64/InterCode.cpp | 104 ++++++++++++++++++--------- oscar64/InterCode.h | 8 +-- oscar64/NativeCodeGenerator.cpp | 122 ++++++++++++++++++++++++++++++-- samples/memmap/charsetload.d64 | Bin 174848 -> 174848 bytes 4 files changed, 192 insertions(+), 42 deletions(-) diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index cd97d66..9f92dd6 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -4970,7 +4970,7 @@ void InterCodeBasicBlock::SimplifyIntegerRangeRelops(void) } -bool InterCodeBasicBlock::BuildGlobalIntegerRangeSets(bool initial) +bool InterCodeBasicBlock::BuildGlobalIntegerRangeSets(bool initial, const GrowingVariableArray& localVars) { bool changed = false; @@ -5020,13 +5020,13 @@ bool InterCodeBasicBlock::BuildGlobalIntegerRangeSets(bool initial) { mEntryValueRange = mLocalValueRange; - UpdateLocalIntegerRangeSets(); + UpdateLocalIntegerRangeSets(localVars); } - if (mTrueJump && mTrueJump->BuildGlobalIntegerRangeSets(initial)) + if (mTrueJump && mTrueJump->BuildGlobalIntegerRangeSets(initial, localVars)) changed = true; - if (mFalseJump && mFalseJump->BuildGlobalIntegerRangeSets(initial)) + if (mFalseJump && mFalseJump->BuildGlobalIntegerRangeSets(initial, localVars)) changed = true; } @@ -5070,7 +5070,7 @@ static int64 BuildLowerBitsMask(int64 v) return v; } -void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) +void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(const GrowingVariableArray& localVars) { mLocalValueRange = mEntryValueRange; @@ -5122,13 +5122,28 @@ void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) LinkerObject* lo = mInstructions[i - 1]->mSrc[1].mLinkerObject; int mi = 0, ma = 0; - for (int j = 0; j < lo->mSize; j++) + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMaxState == IntegerValueRange::S_BOUND && + vr.mMinValue >= -128 && vr.mMaxValue <= 127) { - int v = lo->mData[j]; - if (v & 0x80) - mi = -128; - if (v > ma) - ma = v; + for (int j = 0; j < lo->mSize; j++) + { + int v = (int8)(lo->mData[j]); + if (v < mi) + mi = v; + if (v > ma) + ma = v; + } + } + else + { + for (int j = 0; j < lo->mSize; j++) + { + int v = lo->mData[j]; + if (v & 0x80) + mi = -128; + if (v > ma) + ma = v; + } } vr.LimitMax(ma); @@ -5606,6 +5621,8 @@ void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) int asize = 0; if (ins->mSrc[1].mMemory == IM_GLOBAL) asize = ins->mSrc[1].mLinkerObject->mSize; + else if (ins->mSrc[1].mMemory == IM_LOCAL) + asize = localVars[ins->mSrc[1].mVarIndex]->mSize; if (asize > 0) { @@ -5627,7 +5644,7 @@ void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) { case IA_EXT8TO16U: case IA_EXT8TO16S: - if (ins->mSrc[0].mTemp >= 0) + if (ins->mSrc[0].mTemp >= 0 && (vr.mMaxValue != 255 || vr.mMinValue != 0)) mReverseValueRange[ins->mSrc[0].mTemp].Limit(vr); break; } @@ -5928,7 +5945,7 @@ void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) -void InterCodeBasicBlock::RestartLocalIntegerRangeSets(void) +void InterCodeBasicBlock::RestartLocalIntegerRangeSets(const GrowingVariableArray& localVars) { if (!mVisited) { @@ -5943,14 +5960,14 @@ void InterCodeBasicBlock::RestartLocalIntegerRangeSets(void) vr.mMaxState = IntegerValueRange::S_UNKNOWN; } - UpdateLocalIntegerRangeSets(); + UpdateLocalIntegerRangeSets(localVars); - if (mTrueJump) mTrueJump->RestartLocalIntegerRangeSets(); - if (mFalseJump) mFalseJump->RestartLocalIntegerRangeSets(); + if (mTrueJump) mTrueJump->RestartLocalIntegerRangeSets(localVars); + if (mFalseJump) mFalseJump->RestartLocalIntegerRangeSets(localVars); } } -void InterCodeBasicBlock::BuildLocalIntegerRangeSets(int num) +void InterCodeBasicBlock::BuildLocalIntegerRangeSets(int num, const GrowingVariableArray& localVars) { if (!mVisited) { @@ -5963,10 +5980,10 @@ void InterCodeBasicBlock::BuildLocalIntegerRangeSets(int num) mMemoryValueSize.SetSize(num, true); mEntryMemoryValueSize.SetSize(num, true); - UpdateLocalIntegerRangeSets(); + UpdateLocalIntegerRangeSets(localVars); - if (mTrueJump) mTrueJump->BuildLocalIntegerRangeSets(num); - if (mFalseJump) mFalseJump->BuildLocalIntegerRangeSets(num); + if (mTrueJump) mTrueJump->BuildLocalIntegerRangeSets(num, localVars); + if (mFalseJump) mFalseJump->BuildLocalIntegerRangeSets(num, localVars); } } @@ -6949,11 +6966,32 @@ bool InterCodeBasicBlock::SimplifyIntegerNumeric(const GrowingInstructionPtrArra { InterInstruction* ains = ltvalue[pins->mSrc[0].mTemp]; - if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[0].mTemp < 0 && ains->mDst.mType == IT_INT16) + if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[0].mTemp < 0) { - ins->mSrc[0] = ains->mSrc[1]; - ins->mSrc[1].mIntConst += ains->mSrc[0].mIntConst; - changed = true; + if (ains->mSrc[0].mType == IT_INT16) + { + ins->mSrc[0] = ains->mSrc[1]; + ins->mSrc[1].mIntConst += ains->mSrc[0].mIntConst; + changed = true; + } + else if (ains->mSrc[0].mType == IT_INT8) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* nins = new InterInstruction(); + nins->mCode = IC_CONVERSION_OPERATOR; + nins->mOperator = IA_EXT8TO16U; + nins->mSrc[0] = ains->mSrc[1]; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_INT16; + nins->mDst.mRange = pins->mDst.mRange; + mInstructions.Insert(i, nins); + + ins->mSrc[0] = ains->mSrc[1]; + ins->mSrc[1].mIntConst += ains->mSrc[0].mIntConst; + changed = true; + } } } #endif @@ -11367,37 +11405,37 @@ void InterCodeProcedure::Close(void) BuildDataFlowSets(); #if 1 ResetVisited(); - mEntryBlock->BuildLocalIntegerRangeSets(mTemporaries.Size()); + mEntryBlock->BuildLocalIntegerRangeSets(mTemporaries.Size(), mLocalVars); do { DisassembleDebug("tt"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(true)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(true, mLocalVars)); do { DisassembleDebug("tq"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(false)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(false, mLocalVars)); DisassembleDebug("Estimated value range"); #if 1 ResetVisited(); - mEntryBlock->RestartLocalIntegerRangeSets(); + mEntryBlock->RestartLocalIntegerRangeSets(mLocalVars); do { DisassembleDebug("tr"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(true)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(true, mLocalVars)); do { DisassembleDebug("tr"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(false)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(false, mLocalVars)); DisassembleDebug("Estimated value range 2"); #endif @@ -11465,19 +11503,19 @@ void InterCodeProcedure::Close(void) #if 1 ResetVisited(); - mEntryBlock->RestartLocalIntegerRangeSets(); + mEntryBlock->RestartLocalIntegerRangeSets(mLocalVars); do { DisassembleDebug("tr"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(true)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(true, mLocalVars)); do { DisassembleDebug("tr"); ResetVisited(); - } while (mEntryBlock->BuildGlobalIntegerRangeSets(false)); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(false, mLocalVars)); DisassembleDebug("Estimated value range 2"); #endif diff --git a/oscar64/InterCode.h b/oscar64/InterCode.h index 75a5ec4..7e92696 100644 --- a/oscar64/InterCode.h +++ b/oscar64/InterCode.h @@ -404,10 +404,10 @@ public: bool BuildGlobalRequiredStaticVariableSet(const GrowingVariableArray& staticVars, NumberSet& fromRequiredVars); bool RemoveUnusedStaticStoreInstructions(const GrowingVariableArray& staticVars); - void RestartLocalIntegerRangeSets(void); - void BuildLocalIntegerRangeSets(int num); - void UpdateLocalIntegerRangeSets(void); - bool BuildGlobalIntegerRangeSets(bool initial); + void RestartLocalIntegerRangeSets(const GrowingVariableArray& localVars); + void BuildLocalIntegerRangeSets(int num, const GrowingVariableArray& localVars); + void UpdateLocalIntegerRangeSets(const GrowingVariableArray& localVars); + bool BuildGlobalIntegerRangeSets(bool initial, const GrowingVariableArray& localVars); void SimplifyIntegerRangeRelops(void); GrowingIntArray mEntryRenameTable; diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index b6220a9..eb27d31 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -23077,6 +23077,74 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass progress = true; } } + else if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (!(mIns[i + 4].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_X; + 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; + mIns[i + 3].mType = ASMIT_STX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_STX; + progress = true; + } + else if (!(mIns[i + 4].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_INY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_STY; + progress = true; + } + } + else if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (!(mIns[i + 4].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_X; + 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; + mIns[i + 3].mType = ASMIT_STX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_STX; + progress = true; + } + else if (!(mIns[i + 4].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_DEY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_STY; + progress = true; + } + } } #endif CheckLive(); @@ -23273,6 +23341,22 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; progress = true; } + else if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 1 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_CLC && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress && !(mIns[i + 5].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROL; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + progress = true; + } #endif else if (pass > 0 && mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && @@ -23384,6 +23468,26 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; progress = true; } + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 1 && + mIns[i + 5].mType == ASMIT_CLC && + mIns[i + 6].mType == ASMIT_ADC && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress && !(mIns[i + 6].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + int addr = mIns[i + 0].mAddress; + mIns[i + 0].mAddress = mIns[i + 3].mAddress; + mIns[i + 3].mAddress = addr; + mIns[i + 1].mType = ASMIT_LSR; mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + mIns[i + 5].mType = ASMIT_ROL; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + progress = true; + } #if 1 if (pass == 0 && mIns[i + 0].mType == ASMIT_CLC && @@ -23494,7 +23598,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass progress = true; } } - +#if 1 if ( mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && @@ -23507,13 +23611,21 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass j++; if (j < mIns.Size() && mIns[j].mType == ASMIT_LDY && mIns[j].mMode == ASMIM_IMMEDIATE && mIns[j].mAddress == mIns[i + 0].mAddress) { - int reg = mIns[1].mAddress; mIns[1].mAddress = mIns[4].mAddress; mIns[4].mAddress = reg; - int yr = mIns[0].mAddress; mIns[0].mAddress = mIns[3].mAddress; mIns[3].mAddress = yr; - mIns[1].mLive |= LIVE_MEM; - mIns[4].mLive |= LIVE_MEM; + int reg = mIns[i + 1].mAddress; mIns[i + 1].mAddress = mIns[i + 4].mAddress; mIns[i + 4].mAddress = reg; + int yr = mIns[i + 0].mAddress; mIns[i + 0].mAddress = mIns[i + 3].mAddress; mIns[i + 3].mAddress = yr; + mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 4].mLive |= LIVE_MEM; + mIns[j].mType = ASMIT_NOP; mIns[j].mMode = ASMIM_IMPLIED; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_Y; + } progress = true; } } +#endif + #endif } diff --git a/samples/memmap/charsetload.d64 b/samples/memmap/charsetload.d64 index 02a24d07a35e23be3f29d18043cd46ace8059414..ae07601a3ca0909d5ec04fc33e8048d5eda9efe4 100644 GIT binary patch delta 546 zcmYjOKWGzS9R2<7j%&z$_uU!V;^ov_)E0-JRlzDEZTU*uNV-S{hZHjO1X0wfLl=YO zG=~(7LO)s&8srO2z~F(#$v-%yP-3Qx;-F}k5IQ(?QJMzvE$_YG@4e~e+TmO~oO^@P zgd%OGe)8}H{#NYP_ihPg$s}B5_;R2^8-cY_<*ykIhu%{Wy$LHHc$48d#XaGT)Ta?; zT<7X6?iEd?K8F#aJTfXiA5P;jd40!!H=f3(JhnjYg|g%pfgYAgfjlJ}eFsqMt9Fm~ zNLVl8$(hc-KR`@Bs~>d#2h#zr_wToRe82DNeU8u~9;Z`uf!?5Z>3+9Bix?ez*3&iG zqyq-Pl;IjnMu4)sn!)pz%bKD?g2HyfgipJ#G?l?=2HR-{OAH~xp8S$OcXs3CAf+Ts zF!xjT_~;tHlfg^q^UVyNGJRufvZYqQv%7j;cb56L3=V0JHDP;6XH^q+3FY{q!LG73 z*)jziyV5!;l3mqf!tQ<*c6s`RZ#X4BlEu-erX3cBJL{!{>CSoc7e(qv(o2f@9pG*j zpDPb+n(YmzaCH&S3?)bhBycVe&#r)XBkn!=@OZWmB}pv zZA_CYnIq352e1~Y-8TOwRIlNgOzYntpijS~A9epnRtNB+`&66nNA69;J+y|W=_oDH z8}v3k>{e+F<6XS1*XU=e833ckq;c11pe)aMc=75BP0=AmVLN5Q+g3wUT{!2$cEN=^ zE<&d}`%T_BXeG%}N=cbu9;WPp{sn&1!^;@(6%Wsvfw48*RO`U(P@mDAX};%SxAsUA zHp__w8+VpaP9E=iq6s@xwuYOgaMRbCCqyn(nJesYSJ=xV3xVMXeyWK5aYO4BhM#45 zVftfiVo>Dwb1Ww&_Q!dth)>%)D)($!Wcc;k#NZ?!{%&r4V6rE3hR;xTy#>e=c O@0Bs}z{h1@%KQaX1&%!c