// Parse the loop block and ignore its result int Compiler::ParseOptimizeBlock(int arguments) { if (!ThisTokenIsBinary('[')) { CompileError(CErrExpectLiteralBlock); return 0; } int nTextStart = ThisTokenRange().m_start; _ASSERTE(IsInOptimizedBlock()); // Parse the arguments - note we parse them anyway, regardless of whether they are wanted, // and subsequently complain if there are too many NextToken(); int argument = 0; while (m_ok && ThisTokenIsSpecial(':') ) { if (NextToken()==NameConst) { if (argument < arguments) RenameTemporary(argument, ThisTokenText(), ThisTokenRange()); else AddTemporary(ThisTokenText(), ThisTokenRange(), true); argument++; NextToken(); } else CompileError(CErrExpectVariable); } int argBar = -1; if (m_ok && argument > 0) { if (ThisTokenIsBinary(TEMPSDELIMITER)) { argBar = ThisTokenRange().m_stop; NextToken(); } else m_ok = false; } int nBlockTemps = 0; if (m_ok) { // Temporarily commented out for interim release ParseTemporaries(); ParseBlockStatements(); if (m_ok && ThisToken() != CloseSquare) CompileError(TEXTRANGE(nTextStart, LastTokenRange().m_stop), CErrBlockNotClosed); } if (m_ok && argument != arguments) CompileError(TEXTRANGE(nTextStart, argBar < 0 ? ThisTokenRange().m_stop : argBar), CErrIncorrectBlockArgCount); return nBlockTemps; }
bool Compiler::ParseOrCondition(const TEXTRANGE& messageRange) { POTE oteSelector = AddSymbolToFrame("or:", messageRange); if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)oteSelector); return false; } // Note "reordering" to allow us to use the smaller // jump on false instruction. In the case of #or: this // seems to be worth doing, even if it isn't for #ifFalse:. It does // result in a small overall size reduction. Which has a speed // advantage is difficult to say. // int branchMark = GenJumpInstruction(LongJumpIfFalse); AddTextMap(branchMark, messageRange); GenInstruction(ShortPushTrue); int jumpOutMark = GenJumpInstruction(LongJump); int ifFalse = m_codePointer; ParseZeroArgOptimizedBlock(); if (m_ok) { SetJumpTarget(branchMark, ifFalse); SetJumpTarget(jumpOutMark, GenNop()); } return true; }
bool Compiler::ParseAndCondition(const TEXTRANGE& messageRange) { POTE oteSelector = AddSymbolToFrame("and:", messageRange); // Assume we can reorder blocks to allow us to use the smaller // jump on false instruction. // int popAndJumpInstruction=LongJumpIfFalse; if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)oteSelector); return false; } // If the receiver is false, then the whole expression is false, so // jump over (shortcut) the block argument int branchMark = GenJumpInstruction(LongJumpIfFalse); AddTextMap(branchMark, messageRange); ParseZeroArgOptimizedBlock(); int jumpOutMark = GenJumpInstruction(LongJump); SetJumpTarget(branchMark, GenInstruction(ShortPushFalse)); SetJumpTarget(jumpOutMark, GenNop()); return true; }
bool Compiler::ParseIfFalse(const TEXTRANGE& messageRange) { if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)InternSymbol("ifFalse:")); return false; } int condJumpMark = GenJumpInstruction(LongJumpIfTrue); AddTextMap(condJumpMark, messageRange); ParseZeroArgOptimizedBlock(); // Need to jump over the false block at end of true block int jumpOutMark = GenJumpInstruction(LongJump); int elseMark = m_codePointer; if (strcmp(ThisTokenText(), "ifTrue:") == 0) { // An else block exists POTE oteSelector = AddSymbolToFrame("ifFalse:ifTrue:", messageRange); NextToken(); ParseZeroArgOptimizedBlock(); } else { // When ifTrue: branch is missing, value of expression if condition false is nil POTE oteSelector = AddSymbolToFrame("ifFalse:", messageRange); // N.B. We used (pre 5.5) to reorder the "blocks" to take advantage of the shorter jump on false // instruction (i.e. we put the empty true block first), but it turns out that this // apparent optimization is actually a bad idea since it seems to reduce the effectiveness // of the peephole optimizer. Here are some stats from an image with 34319 methods: // // Methods affected: 1276 // Shorter with inversion: 182 // Shorter without inversion: 849 (1) // Size of bytecodes with: 60268 // Size of bytecodes without: 58564 (2) // // i.e. more methods are lengthened by the apparent optimization than without it (1), and // the overall size of the methods is actually increased in most cases (2). GenInstruction(ShortPushNil); } SetJumpTarget(jumpOutMark, GenNop()); // Now the jump on condition SetJumpTarget(condJumpMark, elseMark); return true; }
// Return whether we it was suitable to optimize this loop block bool Compiler::ParseWhileLoopBlock(const bool bIsWhileTrue, const int loopmark, const TEXTRANGE& tokenRange, const int textStart) { POTE oteSelector = AddSymbolToFrame(bIsWhileTrue ? "whileTrue:" : "whileFalse:", tokenRange); if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)oteSelector); return false; } if (!InlineLoopBlock(loopmark, tokenRange)) { Warning(tokenRange, CWarnExpectNiladicBlockReceiver, (Oop)oteSelector); return false; } // We've generated (and inlined) the loop condition block already // Now we need to insert conditional jump over the loop body block which should // be taken if the condition is false if a while true loop, or true if a while // false loop (since it is the jump out) // To have a breakpoint on the loop condition check uncomment the breakpoint and the text map lines marked with *1* //BreakPoint(); // *1* const int popAndJumpInstruction = bIsWhileTrue ? LongJumpIfFalse : LongJumpIfTrue; int condJumpMark = GenJumpInstruction(popAndJumpInstruction); // We need a text map entry for the loop jump in case a mustBeBoolean error gets raised here int nLoopTextMap = AddTextMap(condJumpMark, tokenRange);// *1* textStart, LastTokenRange().m_stop); // Parse the loop body { PushOptimizedScope(); // Parse the loop body ... ParseOptimizeBlock(0); PopOptimizedScope(ThisTokenRange().m_stop); //... and ignore its result GenPopStack(); NextToken(); } // Unconditionally jump back to the loop condition int jumpPos = GenJump(LongJump, loopmark); //if (WantTextMap()) m_textMaps[nLoopTextMap].stop = LastTokenRange().m_stop; // *1* // Return Nil int exitMark = GenInstruction(ShortPushNil); // We can now set the target of the forward conditional jump SetJumpTarget(condJumpMark, exitMark); return true; }
// produce optimized form of to:do: message bool Compiler::ParseToDoBlock(int exprStart, int toPointer) { POTE oteSelector = AddSymbolToFrame("to:do:", TEXTRANGE(toPointer, toPointer + 2)); // Only optimize if a block is next if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectMonadicBlockArg, (Oop)oteSelector); return false; } ParseToByNumberDo(toPointer, IntegerObjectOf(1), false); return true; }
// Produce optimized form of to:by:do: message bool Compiler::ParseToByDoBlock(int exprStart, int toPointer, int byPointer) { _ASSERTE(toPointer>0 && byPointer>0); POTE oteSelector = AddSymbolToFrame("to:by:do:", TEXTRANGE(toPointer, toPointer+2)); // Only optimize if a block is next if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectMonadicBlockArg, (Oop)oteSelector); return false; } Oop oopStep = LastIsPushNumber(); if (oopStep != NULL) { GenPopStack(); // Pop off the "by" argument bool bNegativeStep; if (IsIntegerObject(oopStep)) bNegativeStep = IntegerValueOf(oopStep) < 0; else { POTE stepClass = m_piVM->FetchClassOf(oopStep); if (stepClass == GetVMPointers().ClassFloat) { double* pfValue = reinterpret_cast<double*>(FetchBytesOf(reinterpret_cast<POTE>(oopStep))); bNegativeStep = *pfValue < 0; } else { // Have to call into Smalltalk to find out if it is negative Oop oopIsNegative = m_piVM->Perform(oopStep, GetVMPointers().negativeSymbol); bNegativeStep = oopIsNegative == reinterpret_cast<Oop>(GetVMPointers().True); } } ParseToByNumberDo(toPointer, oopStep, bNegativeStep); return true; } return false; }
bool Compiler::ParseIfTrue(const TEXTRANGE& messageRange) { if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)InternSymbol("ifTrue:")); return false; } int condJumpMark = GenJumpInstruction(LongJumpIfFalse); AddTextMap(condJumpMark, messageRange); ParseZeroArgOptimizedBlock(); // Need to jump over the false block at end of true block int jumpOutMark = GenJumpInstruction(LongJump); int elseMark = m_codePointer; if (strcmp(ThisTokenText(), "ifFalse:") == 0) { // An else block exists POTE oteSelector = AddSymbolToFrame("ifTrue:ifFalse:", messageRange); NextToken(); ParseZeroArgOptimizedBlock(); } else { // When #ifFalse: branch is missing, value of expression if condition false is nil POTE oteSelector = AddSymbolToFrame("ifTrue:", messageRange); GenInstruction(ShortPushNil); } SetJumpTarget(jumpOutMark, GenNop()); // Now the jump on condition SetJumpTarget(condJumpMark, elseMark); return true; }
// Produce optimized form of timesRepeat: [...]. // Returns true if optimization performed. // Note that we perform the conditional jump as a backwards jump at the end for optimal performance bool Compiler::ParseTimesRepeatLoop(const TEXTRANGE& messageRange) { POTE oteSelector = AddSymbolToFrame("timesRepeat:", messageRange); if (!ThisTokenIsBinary('[')) { Warning(messageRange, CWarnExpectNiladicBlockArg, (Oop)oteSelector); return false; } // Can apply extra optimizations if receiver is known SmallInteger int loopTimes=0; bool isIntReceiver = LastIsPushSmallInteger(loopTimes); int startMark = GenDup(); int jumpOver = GenJumpInstruction(LongJump); int loopHead = m_codePointer; PushOptimizedScope(); // Parse the loop block and ignore its result ParseOptimizeBlock(0); GenPopStack(); PopOptimizedScope(ThisTokenRange().m_stop); NextToken(); if (isIntReceiver && loopTimes <= 0) { // Blank out all our bytecodes if we have 0 or less loops const int loopEnd = m_codePointer; for (int p = startMark; p < loopEnd; p++) UngenInstruction(p); return true; } // Decrement counter GenInstruction(DecrementStackTop); // Dup counter for compare int testMark = GenDup(); // Fill in forward unconditional jump before the head of the loop // which jumps to the conditional test SetJumpTarget(jumpOver, testMark); if (isIntReceiver) { // Using IsZero speeds up empty loop by about 5%. _ASSERTE(loopTimes > 0); GenInstruction(SpecialSendIsZero); } else { // Test for another run around the wheel - favour use of #<, therefore need to test against 1 rather than 0 GenInteger(1, TEXTRANGE()); // No breakpoint wanted here //GenMessage("<", 1); GenInstruction(SendArithmeticLT); } // Conditional jump back to loop head GenJump(LongJumpIfFalse, loopHead); // Pop off the loop counter, leaving the integer receiver on the stack GenPopStack(); return true; }
bool Compiler::ParseIfNotNil(const TEXTRANGE& messageRange, int exprStartPos) { if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectMonadicOrNiladicBlockArg, (Oop)InternSymbol("ifNotNil:")); return false; } int dupMark = GenDup(); BreakPoint(); const int sendIsNil = GenInstruction(SpecialSendIsNil); AddTextMap(sendIsNil, exprStartPos, LastTokenRange().m_stop); // We're going to add a pop and jump on condition instruction here // Its a forward jump, so we need to patch up the target later const int popAndJumpMark = GenJumpInstruction(LongJumpIfTrue); int argc = ParseIfNotNilBlock(); // If the ifNotNil: block did not have any arguments, then we do not need the Dup, and we also // need to patch out the corresponding pop. if (!argc) { UngenInstruction(dupMark); UngenInstruction(popAndJumpMark + lengthOfByteCode(LongJumpIfTrue)); } int ifNilMark; // Has an #ifNil: branch? if (strcmp(ThisTokenText(), "ifNil:") == 0) { POTE oteSelector = AddSymbolToFrame("ifNotNil:ifNil:", messageRange); // Generate the jump out instruction (forward jump, so target not yet known) int jumpOutMark = GenJumpInstruction(LongJump); // Mark first instruction of the "else" branch ifNilMark = m_codePointer; // ifNotNil:ifNil: form NextToken(); ParseIfNilBlock(!argc); SetJumpTarget(jumpOutMark, GenNop()); } else { // No "ifNil:" branch POTE oteSelector = AddSymbolToFrame("ifNotNil:", messageRange); if (!argc) { // Since we've removed the Dup if the ifNotNil: block had no args, we need to ensure there is nil atop the stack // This should normally get optimized away later if the expression value is not used. // Generate the jump out instruction int jumpOutMark = GenJumpInstruction(LongJump); ifNilMark = GenInstruction(ShortPushNil); SetJumpTarget(jumpOutMark, GenNop()); } else { ifNilMark = GenNop(); } } SetJumpTarget(popAndJumpMark, ifNilMark); return true; }
bool Compiler::ParseIfNil(const TEXTRANGE& messageRange, int exprStartPos) { if (!ThisTokenIsBinary('[')) { Warning(CWarnExpectNiladicBlockArg, (Oop)InternSymbol("ifNil:")); return false; } int dupMark = GenDup(); BreakPoint(); const int sendIsNil = GenInstruction(SpecialSendIsNil); const int mapEntry = AddTextMap(sendIsNil, exprStartPos, LastTokenRange().m_stop); // We're going to add a pop and jump on condition instruction here // Its a forward jump, so we need to patch up the target later const int popAndJumpMark = GenJumpInstruction(LongJumpIfFalse); ParseIfNilBlock(false); int ifNotNilMark; if (strcmp(ThisTokenText(), "ifNotNil:") == 0) { POTE oteSelector = AddSymbolToFrame("ifNil:ifNotNil:", messageRange); // Generate the jump out instruction (forward jump, so target not yet known) int jumpOutMark = GenJumpInstruction(LongJump); // Mark first instruction of the "else" branch ifNotNilMark = m_codePointer; // #ifNil:ifNotNil: form NextToken(); int argc = ParseIfNotNilBlock(); // Be careful, may not actually be a literal block there if (m_ok) { // If the ifNotNil: block does not need an argument, we can patch out the Dup // and corresponding pop if (!argc) { UngenInstruction(dupMark); _ASSERTE(m_bytecodes[popAndJumpMark + lengthOfByteCode(LongJumpIfFalse)].byte == PopStackTop); UngenInstruction(popAndJumpMark + lengthOfByteCode(LongJumpIfFalse)); _ASSERTE(m_bytecodes[ifNotNilMark].byte == PopStackTop); UngenInstruction(ifNotNilMark); } // Set unconditional jump at the end of the ifNil to jump over the "else" branch to a Nop SetJumpTarget(jumpOutMark, GenNop()); } } else { POTE oteSelector = AddSymbolToFrame("ifNil:", messageRange); // No "else" branch, but we still need an instruction to jump to ifNotNilMark = GenNop(); } if (m_ok) { // Conditional jump to the "else" branch (or the Nop if no else branch) SetJumpTarget(popAndJumpMark, ifNotNilMark); if (mapEntry >= 0) m_textMaps[mapEntry].stop = LastTokenRange().m_stop; } return true; }
int Compiler::ParseIfNotNilBlock() { if (!ThisTokenIsBinary('[')) { CompileError(CErrExpectLiteralBlock); return 0; } PushOptimizedScope(); // We now allow either zero or one arguments to the ifNotNil: block //ParseOptimizeBlock(1); int nTextStart = ThisTokenRange().m_start; _ASSERTE(IsInOptimizedBlock()); // Generate the body code for an optimized block NextToken(); int argc = 0; while (m_ok && ThisTokenIsSpecial(':')) { if (NextToken() == NameConst) { argc++; CheckTemporaryName(ThisTokenText(), ThisTokenRange(), true); if (m_ok) { TempVarRef* pValueTempRef = AddOptimizedTemp(ThisTokenText(), ThisTokenRange()); GenPopAndStoreTemp(pValueTempRef); } NextToken(); } else CompileError(CErrExpectVariable); } switch (argc) { case 0: // Zero arg block, we don't need the implicit arg so just discard it GenPopStack(); break; case 1: m_ok = m_ok && ThisTokenIsBinary(TEMPSDELIMITER); NextToken(); break; default: CompileError(TEXTRANGE(nTextStart, ThisTokenRange().m_stop), CErrTooManyIfNotNilBlockArgs); break; } int nBlockTemps = 0; if (m_ok) { // Temporarily commented out for interim release ParseTemporaries(); ParseBlockStatements(); if (m_ok && ThisToken() != CloseSquare) CompileError(TEXTRANGE(nTextStart, LastTokenRange().m_stop), CErrBlockNotClosed); } PopOptimizedScope(ThisTokenRange().m_stop); NextToken(); return argc; }