diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index c1feb19223..f1b47b06c3 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,15 @@ +2010-12-17: Version 3.0.3 + + Reapplied all changes for version 3.0.1. + + Improved debugger protocol for remote debugging. + + Added experimental support for using gyp to generate build files + for V8. + + Fixed implementation of String::Write in the API (issue 975). + + 2010-12-15: Version 3.0.2 Revert version 3.0.1 and patch 3.0.1.1. diff --git a/deps/v8/include/v8-preparser.h b/deps/v8/include/v8-preparser.h index 68ce50223e..9425f7d467 100644 --- a/deps/v8/include/v8-preparser.h +++ b/deps/v8/include/v8-preparser.h @@ -99,13 +99,6 @@ class UnicodeInputStream { // Returns the next Unicode code-point in the input, or a negative value when // there is no more input in the stream. virtual int32_t Next() = 0; - - // Pushes a read character back into the stream, so that it will be the next - // to be read by Advance(). The character pushed back must be the most - // recently read character that hasn't already been pushed back (i.e., if - // pushing back more than one character, they must occur in the opposite order - // of the one they were read in). - virtual void PushBack(int32_t ch) = 0; }; diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index 08f47ca36e..675a229854 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -245,7 +245,6 @@ class V8EXPORT HeapGraphPath { class V8EXPORT HeapGraphNode { public: enum Type { - kInternal = 0, // For compatibility, will be removed. kHidden = 0, // Hidden node, may be filtered when shown to user. kArray = 1, // An array of elements. kString = 2, // A string. @@ -413,7 +412,8 @@ class V8EXPORT HeapProfiler { */ static const HeapSnapshot* TakeSnapshot( Handle title, - HeapSnapshot::Type type = HeapSnapshot::kFull); + HeapSnapshot::Type type = HeapSnapshot::kFull, + ActivityControl* control = NULL); }; diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 8ecf63aebd..82de6b8b5e 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -992,18 +992,23 @@ class String : public Primitive { * the contents of the string and the NULL terminator into the * buffer. * + * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop + * before the end of the buffer. + * * Copies up to length characters into the output buffer. * Only null-terminates if there is enough space in the buffer. * * \param buffer The buffer into which the string will be copied. * \param start The starting position within the string at which * copying begins. - * \param length The number of bytes to copy from the string. + * \param length The number of characters to copy from the string. For + * WriteUtf8 the number of bytes in the buffer. * \param nchars_ref The number of characters written, can be NULL. * \param hints Various hints that might affect performance of this or * subsequent operations. - * \return The number of bytes copied to the buffer - * excluding the NULL terminator. + * \return The number of characters copied to the buffer excluding the null + * terminator. For WriteUtf8: The number of bytes copied to the buffer + * including the null terminator. */ enum WriteHints { NO_HINTS = 0, @@ -3281,6 +3286,24 @@ class V8EXPORT OutputStream { // NOLINT }; +/** + * An interface for reporting progress and controlling long-running + * activities. + */ +class V8EXPORT ActivityControl { // NOLINT + public: + enum ControlOption { + kContinue = 0, + kAbort = 1 + }; + virtual ~ActivityControl() {} + /** + * Notify about current progress. The activity can be stopped by + * returning kAbort as the callback result. + */ + virtual ControlOption ReportProgressValue(int done, int total) = 0; +}; + // --- I m p l e m e n t a t i o n --- diff --git a/deps/v8/preparser/preparser-process.cc b/deps/v8/preparser/preparser-process.cc index 80e83508e4..26dfc42b53 100644 --- a/deps/v8/preparser/preparser-process.cc +++ b/deps/v8/preparser/preparser-process.cc @@ -127,7 +127,7 @@ uint32_t ReadUInt32(FILE* source, bool* ok) { bool ReadBuffer(FILE* source, void* buffer, size_t length) { - size_t actually_read = fread(buffer, 1, length, stdin); + size_t actually_read = fread(buffer, 1, length, source); return (actually_read == length); } @@ -150,22 +150,25 @@ class ScopedPointer { }; -// Preparse stdin and output result on stdout. -int PreParseIO() { +// Preparse input and output result on stdout. +int PreParseIO(FILE* input) { fprintf(stderr, "LOG: Enter parsing loop\n"); bool ok = true; - uint32_t length = ReadUInt32(stdin, &ok); + uint32_t length = ReadUInt32(input, &ok); + fprintf(stderr, "LOG: Input length: %d\n", length); if (!ok) return kErrorReading; ScopedPointer buffer(new uint8_t[length]); - if (!ReadBuffer(stdin, *buffer, length)) { + if (!ReadBuffer(input, *buffer, length)) { return kErrorReading; } UTF8InputStream input_buffer(*buffer, static_cast(length)); v8::PreParserData data = - v8::Preparse(&input_buffer, 64 * sizeof(void*)); // NOLINT + v8::Preparse(&input_buffer, 64 * 1024 * sizeof(void*)); // NOLINT if (data.stack_overflow()) { + fprintf(stderr, "LOG: Stack overflow\n"); + fflush(stderr); // Report stack overflow error/no-preparser-data. WriteUInt32(stdout, 0, &ok); if (!ok) return kErrorWriting; @@ -173,6 +176,8 @@ int PreParseIO() { } uint32_t size = data.size(); + fprintf(stderr, "LOG: Success, data size: %u\n", size); + fflush(stderr); WriteUInt32(stdout, size, &ok); if (!ok) return kErrorWriting; if (!WriteBuffer(stdout, data.data(), size)) { @@ -185,10 +190,17 @@ int PreParseIO() { int main(int argc, char* argv[]) { + FILE* input = stdin; + if (argc > 1) { + char* arg = argv[1]; + input = fopen(arg, "rb"); + if (input == NULL) return EXIT_FAILURE; + } int status = 0; do { - status = v8::internal::PreParseIO(); + status = v8::internal::PreParseIO(input); } while (status == 0); fprintf(stderr, "EXIT: Failure %d\n", status); + fflush(stderr); return EXIT_FAILURE; } diff --git a/deps/v8/samples/samples.gyp b/deps/v8/samples/samples.gyp new file mode 100644 index 0000000000..f383ee20f7 --- /dev/null +++ b/deps/v8/samples/samples.gyp @@ -0,0 +1,51 @@ +# Copyright 2010 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +{ + 'targets': [ + { + 'target_name': 'shell', + 'type': 'executable', + 'dependencies': [ + '../tools/gyp/v8.gyp:v8', + ], + 'sources': [ + 'shell.cc', + ], + }, + { + 'target_name': 'process', + 'type': 'executable', + 'dependencies': [ + '../tools/gyp/v8.gyp:v8', + ], + 'sources': [ + 'process.cc', + ], + } + ], +} diff --git a/deps/v8/samples/shell.cc b/deps/v8/samples/shell.cc index 460457552c..6b67df6c6c 100644 --- a/deps/v8/samples/shell.cc +++ b/deps/v8/samples/shell.cc @@ -45,7 +45,6 @@ v8::Handle Quit(const v8::Arguments& args); v8::Handle Version(const v8::Arguments& args); v8::Handle ReadFile(const char* name); void ReportException(v8::TryCatch* handler); -void SetFlagsFromString(const char* flags); int RunMain(int argc, char* argv[]) { @@ -345,8 +344,3 @@ void ReportException(v8::TryCatch* try_catch) { } } } - - -void SetFlagsFromString(const char* flags) { - v8::V8::SetFlagsFromString(flags, strlen(flags)); -} diff --git a/deps/v8/src/allocation.h b/deps/v8/src/allocation.h index 6f4bd2fb54..394366ea4d 100644 --- a/deps/v8/src/allocation.h +++ b/deps/v8/src/allocation.h @@ -28,6 +28,9 @@ #ifndef V8_ALLOCATION_H_ #define V8_ALLOCATION_H_ +#include "checks.h" +#include "globals.h" + namespace v8 { namespace internal { diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 0ec8cf123e..b85d658dc3 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -1165,14 +1165,22 @@ void ObjectTemplate::SetInternalFieldCount(int value) { ScriptData* ScriptData::PreCompile(const char* input, int length) { - unibrow::Utf8InputBuffer<> buf(input, length); - return i::ParserApi::PreParse(i::Handle(), &buf, NULL); + i::Utf8ToUC16CharacterStream stream( + reinterpret_cast(input), length); + return i::ParserApi::PreParse(&stream, NULL); } ScriptData* ScriptData::PreCompile(v8::Handle source) { i::Handle str = Utils::OpenHandle(*source); - return i::ParserApi::PreParse(str, NULL, NULL); + if (str->IsExternalTwoByteString()) { + i::ExternalTwoByteStringUC16CharacterStream stream( + i::Handle::cast(str), 0, str->length()); + return i::ParserApi::PreParse(&stream, NULL); + } else { + i::GenericStringUC16CharacterStream stream(str, 0, str->length()); + return i::ParserApi::PreParse(&stream, NULL); + } } @@ -3119,14 +3127,15 @@ int String::Write(uint16_t* buffer, // using StringInputBuffer or Get(i) to access the characters. str->TryFlatten(); } - int end = length; - if ( (length == -1) || (length > str->length() - start) ) - end = str->length() - start; + int end = start + length; + if ((length == -1) || (length > str->length() - start) ) + end = str->length(); if (end < 0) return 0; i::String::WriteToFlat(*str, buffer, start, end); - if (length == -1 || end < length) - buffer[end] = '\0'; - return end; + if (length == -1 || end - start < length) { + buffer[end - start] = '\0'; + } + return end - start; } @@ -4939,7 +4948,8 @@ const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) { const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle title, - HeapSnapshot::Type type) { + HeapSnapshot::Type type, + ActivityControl* control) { IsDeadCheck("v8::HeapProfiler::TakeSnapshot"); i::HeapSnapshot::Type internal_type = i::HeapSnapshot::kFull; switch (type) { @@ -4953,7 +4963,8 @@ const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle title, UNREACHABLE(); } return reinterpret_cast( - i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title), internal_type)); + i::HeapProfiler::TakeSnapshot( + *Utils::OpenHandle(*title), internal_type, control)); } #endif // ENABLE_LOGGING_AND_PROFILING @@ -4968,6 +4979,7 @@ void Testing::SetStressRunType(Testing::StressType type) { } int Testing::GetStressRuns() { + if (internal::FLAG_stress_runs != 0) return internal::FLAG_stress_runs; #ifdef DEBUG // In debug mode the code runs much slower so stressing will only make two // runs. diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 2b0b324e5c..59bc14e72f 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -5592,6 +5592,12 @@ void CodeGenerator::GenerateSwapElements(ZoneList* args) { __ tst(tmp2, Operand(kSmiTagMask)); deferred->Branch(nz); + // Check that both indices are valid. + __ ldr(tmp2, FieldMemOperand(object, JSArray::kLengthOffset)); + __ cmp(tmp2, index1); + __ cmp(tmp2, index2, hi); + deferred->Branch(ls); + // Bring the offsets into the fixed array in tmp1 into index1 and // index2. __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); @@ -6463,7 +6469,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { case Token::INSTANCEOF: { Load(left); Load(right); - InstanceofStub stub; + InstanceofStub stub(InstanceofStub::kNoFlags); frame_->CallStub(&stub, 2); // At this point if instanceof succeeded then r0 == 0. __ tst(r0, Operand(r0)); diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 7e4a28042f..d254918318 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -206,6 +206,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } +void FullCodeGenerator::ClearAccumulator() { + __ mov(r0, Operand(Smi::FromInt(0))); +} + + void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { Comment cmnt(masm_, "[ Stack check"); Label ok; @@ -890,7 +895,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&update_each); __ mov(result_register(), r3); // Perform the assignment as if via '='. - EmitAssignment(stmt->each()); + { EffectContext context(this); + EmitAssignment(stmt->each(), stmt->AssignmentId()); + } // Generate code for the body of the loop. Visit(stmt->body()); @@ -1444,7 +1451,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { // For property compound assignments we need another deoptimization // point after the property load. if (property != NULL) { - PrepareForBailoutForId(expr->compound_bailout_id(), TOS_REG); + PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); } Token::Value op = expr->binary_op(); @@ -1487,6 +1494,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), expr->op()); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(r0); break; case NAMED_PROPERTY: EmitNamedPropertyAssignment(expr); @@ -1536,7 +1545,7 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, } -void FullCodeGenerator::EmitAssignment(Expression* expr) { +void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { // Invalid left-hand sides are rewritten to have a 'throw // ReferenceError' on the left-hand side. if (!expr->IsValidLeftHandSide()) { @@ -1584,6 +1593,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { break; } } + PrepareForBailoutForId(bailout_ast_id, TOS_REG); + context()->Plug(r0); } @@ -1657,8 +1668,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, } __ bind(&done); } - - context()->Plug(result_register()); } @@ -1701,10 +1710,10 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ push(ip); __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(r0); - context()->DropAndPlug(1, r0); - } else { - context()->Plug(r0); + __ Drop(1); } + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(r0); } @@ -1745,10 +1754,10 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ push(ip); __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(r0); - context()->DropAndPlug(1, r0); - } else { - context()->Plug(r0); + __ Drop(1); } + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(r0); } @@ -3200,6 +3209,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { { EffectContext context(this); EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Token::ASSIGN); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context.Plug(r0); } // For all contexts except EffectConstant We have the result on // top of the stack. @@ -3209,6 +3220,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { } else { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Token::ASSIGN); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(r0); } break; case NAMED_PROPERTY: { @@ -3216,6 +3229,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ pop(r1); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { context()->PlugTOS(); @@ -3230,6 +3244,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ pop(r2); // Receiver. Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { context()->PlugTOS(); @@ -3415,7 +3430,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::INSTANCEOF: { VisitForStackValue(expr->right()); - InstanceofStub stub; + InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); // The stub returns 0 for true. diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index a75d96bfdd..e5a1bae968 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -2360,10 +2360,8 @@ Condition CompareIC::ComputeCondition(Token::Value op) { void CompareIC::UpdateCaches(Handle x, Handle y) { HandleScope scope; Handle rewritten; -#ifdef DEBUG State previous_state = GetState(); -#endif - State state = TargetState(x, y); + State state = TargetState(previous_state, false, x, y); if (state == GENERIC) { CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, r1, r0); rewritten = stub.GetCode(); @@ -2383,6 +2381,12 @@ void CompareIC::UpdateCaches(Handle x, Handle y) { #endif } + +void PatchInlinedSmiCode(Address address) { + UNIMPLEMENTED(); +} + + } } // namespace v8::internal #endif // V8_TARGET_ARCH_ARM diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index 682c448fd5..9c792316f5 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -206,6 +206,13 @@ void LIsNullAndBranch::PrintDataTo(StringStream* stream) const { } +void LIsObjectAndBranch::PrintDataTo(StringStream* stream) const { + stream->Add("if is_object("); + input()->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const { stream->Add("if is_smi("); input()->PrintTo(stream); @@ -460,12 +467,6 @@ int LChunk::NearestGapPos(int index) const { } -int LChunk::NearestNextGapPos(int index) const { - while (!IsGapAt(index)) index++; - return index; -} - - void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) { GetGapAt(index)->GetOrCreateParallelMove(LGap::START)->AddMove(from, to); } @@ -1244,6 +1245,17 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { temp, first_id, second_id); + } else if (v->IsIsObject()) { + HIsObject* compare = HIsObject::cast(v); + ASSERT(compare->value()->representation().IsTagged()); + + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()), + temp1, + temp2, + first_id, + second_id); } else if (v->IsCompareJSObjectEq()) { HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v); return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()), @@ -1347,7 +1359,7 @@ LInstruction* LChunkBuilder::DoCallConstantFunction( LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { - MathFunctionId op = instr->op(); + BuiltinFunctionId op = instr->op(); LOperand* input = UseRegisterAtStart(instr->value()); LInstruction* result = new LUnaryMathOperation(input); switch (op) { @@ -1357,6 +1369,12 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { return AssignEnvironment(DefineAsRegister(result)); case kMathSqrt: return DefineSameAsFirst(result); + case kMathPowHalf: + Abort("MathPowHalf LUnaryMathOperation not implemented"); + return NULL; + case kMathLog: + Abort("MathLog LUnaryMathOperation not implemented"); + return NULL; default: UNREACHABLE(); return NULL; @@ -1554,6 +1572,12 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { } +LInstruction* LChunkBuilder::DoPower(HPower* instr) { + Abort("LPower instruction not implemented on ARM"); + return NULL; +} + + LInstruction* LChunkBuilder::DoCompare(HCompare* instr) { Token::Value op = instr->token(); if (instr->left()->representation().IsInteger32()) { @@ -1594,6 +1618,14 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) { } +LInstruction* LChunkBuilder::DoIsObject(HIsObject* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* value = UseRegisterAtStart(instr->value()); + + return DefineAsRegister(new LIsObject(value, TempRegister())); +} + + LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) { ASSERT(instr->value()->representation().IsTagged()); LOperand* value = UseAtStart(instr->value()); @@ -1688,11 +1720,13 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { } else if (from.IsDouble()) { if (to.IsTagged()) { LOperand* value = UseRegister(instr->value()); - LOperand* temp = TempRegister(); + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); - // Make sure that temp and result_temp are different registers. + // Make sure that the temp and result_temp registers are + // different. LUnallocated* result_temp = TempRegister(); - LInstruction* result = new LNumberTagD(value, temp); + LInstruction* result = new LNumberTagD(value, temp1, temp2); Define(result, result_temp); return AssignPointerMap(result); } else { diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h index 0d5ba0f73f..41209c67d5 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -121,6 +121,8 @@ class Translation; // LInteger32ToDouble // LIsNull // LIsNullAndBranch +// LIsObject +// LIsObjectAndBranch // LIsSmi // LIsSmiAndBranch // LLoadNamedField @@ -203,6 +205,8 @@ class Translation; V(Integer32ToDouble) \ V(IsNull) \ V(IsNullAndBranch) \ + V(IsObject) \ + V(IsObjectAndBranch) \ V(IsSmi) \ V(IsSmiAndBranch) \ V(HasInstanceType) \ @@ -665,7 +669,7 @@ class LUnaryMathOperation: public LUnaryOperation { DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) virtual void PrintDataTo(StringStream* stream) const; - MathFunctionId op() const { return hydrogen()->op(); } + BuiltinFunctionId op() const { return hydrogen()->op(); } }; @@ -742,6 +746,48 @@ class LIsNullAndBranch: public LIsNull { }; +class LIsObject: public LUnaryOperation { + public: + LIsObject(LOperand* value, LOperand* temp) + : LUnaryOperation(value), temp_(temp) {} + + DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object") + + LOperand* temp() const { return temp_; } + + private: + LOperand* temp_; +}; + + +class LIsObjectAndBranch: public LIsObject { + public: + LIsObjectAndBranch(LOperand* value, + LOperand* temp, + LOperand* temp2, + int true_block_id, + int false_block_id) + : LIsObject(value, temp), + temp2_(temp2), + true_block_id_(true_block_id), + false_block_id_(false_block_id) { } + + DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch") + virtual void PrintDataTo(StringStream* stream) const; + virtual bool IsControl() const { return true; } + + int true_block_id() const { return true_block_id_; } + int false_block_id() const { return false_block_id_; } + + LOperand* temp2() const { return temp2_; } + + private: + LOperand* temp2_; + int true_block_id_; + int false_block_id_; +}; + + class LIsSmi: public LUnaryOperation { public: explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {} @@ -1395,15 +1441,17 @@ class LNumberTagI: public LUnaryOperation { class LNumberTagD: public LUnaryOperation { public: - explicit LNumberTagD(LOperand* value, LOperand* temp) - : LUnaryOperation(value), temp_(temp) { } + LNumberTagD(LOperand* value, LOperand* temp1, LOperand* temp2) + : LUnaryOperation(value), temp1_(temp1), temp2_(temp2) { } DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d") - LOperand* temp() const { return temp_; } + LOperand* temp1() const { return temp1_; } + LOperand* temp2() const { return temp2_; } private: - LOperand* temp_; + LOperand* temp1_; + LOperand* temp2_; }; @@ -1887,7 +1935,6 @@ class LChunk: public ZoneObject { LGap* GetGapAt(int index) const; bool IsGapAt(int index) const; int NearestGapPos(int index) const; - int NearestNextGapPos(int index) const; void MarkEmptyBlocks(); const ZoneList* pointer_maps() const { return &pointer_maps_; } LLabel* GetLabel(int block_id) const { diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index db8037a62d..533d32c747 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -136,7 +136,7 @@ bool LCodeGen::GeneratePrologue() { Label loop; __ bind(&loop); __ push(r2); - __ sub(r0, r0, Operand(1)); + __ sub(r0, r0, Operand(1), SetCC); __ b(ne, &loop); } else { __ sub(sp, sp, Operand(slots * kPointerSize)); @@ -1213,6 +1213,26 @@ void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) { } +Condition LCodeGen::EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object) { + Abort("EmitIsObject unimplemented."); + return ne; +} + + +void LCodeGen::DoIsObject(LIsObject* instr) { + Abort("DoIsObject unimplemented."); +} + + +void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { + Abort("DoIsObjectAndBranch unimplemented."); +} + + void LCodeGen::DoIsSmi(LIsSmi* instr) { ASSERT(instr->hydrogen()->value()->representation().IsTagged()); Register result = ToRegister(instr->result()); @@ -1733,13 +1753,14 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { DoubleRegister input_reg = ToDoubleRegister(instr->input()); Register reg = ToRegister(instr->result()); - Register tmp = ToRegister(instr->temp()); + Register temp1 = ToRegister(instr->temp1()); + Register temp2 = ToRegister(instr->temp2()); Register scratch = r9; DeferredNumberTagD* deferred = new DeferredNumberTagD(this, instr); if (FLAG_inline_new) { __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex); - __ AllocateHeapNumber(reg, tmp, ip, scratch, deferred->entry()); + __ AllocateHeapNumber(reg, temp1, temp2, scratch, deferred->entry()); } else { __ jmp(deferred->entry()); } diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h index 846acacccc..541a699615 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.h +++ b/deps/v8/src/arm/lithium-codegen-arm.h @@ -208,6 +208,15 @@ class LCodeGen BASE_EMBEDDED { Condition EmitTypeofIs(Label* true_label, Label* false_label, Register input, Handle type_name); + // Emits optimized code for %_IsObject(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 6ad8918f17..6effec1e31 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1060,9 +1060,14 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, return; } + // Assert that the register arguments are different and that none of + // them are ip. ip is used explicitly in the code generated below. ASSERT(!result.is(scratch1)); ASSERT(!result.is(scratch2)); ASSERT(!scratch1.is(scratch2)); + ASSERT(!result.is(ip)); + ASSERT(!scratch1.is(ip)); + ASSERT(!scratch2.is(ip)); // Check relative positions of allocation top and limit addresses. // The values must be adjacent in memory to allow the use of LDM. diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 74ffd3b6f5..c2a9796c87 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -2112,8 +2112,8 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // -- lr : return address // ----------------------------------- SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasCustomCallGenerator()) { - const int id = function_info->custom_call_generator_id(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); MaybeObject* maybe_result = CompileCustomCall( id, object, holder, NULL, function, name); Object* result; @@ -2323,8 +2323,8 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, // ----------------------------------- SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasCustomCallGenerator()) { - const int id = function_info->custom_call_generator_id(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); MaybeObject* maybe_result = CompileCustomCall( id, object, holder, cell, function, name); Object* result; diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index c5ff505c1d..a805157b13 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -159,9 +159,11 @@ function Join(array, length, separator, convert) { } -function ConvertToString(e) { - if (e == null) return ''; - else return ToString(e); +function ConvertToString(x) { + if (IS_STRING(x)) return x; + if (IS_NUMBER(x)) return %_NumberToString(x); + if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; + return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x)); } @@ -365,14 +367,13 @@ function ArrayJoin(separator) { if (IS_UNDEFINED(separator)) { separator = ','; } else if (!IS_STRING(separator)) { - separator = ToString(separator); + separator = NonStringToString(separator); } var result = %_FastAsciiArrayJoin(this, separator); if (!IS_UNDEFINED(result)) return result; - var length = TO_UINT32(this.length); - return Join(this, length, separator, ConvertToString); + return Join(this, TO_UINT32(this.length), separator, ConvertToString); } diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index d71a35a4a5..3b44efa9c0 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -66,6 +66,7 @@ namespace internal { const double DoubleConstant::min_int = kMinInt; const double DoubleConstant::one_half = 0.5; +const double DoubleConstant::negative_infinity = -V8_INFINITY; // ----------------------------------------------------------------------------- @@ -722,6 +723,12 @@ ExternalReference ExternalReference::address_of_one_half() { } +ExternalReference ExternalReference::address_of_negative_infinity() { + return ExternalReference(reinterpret_cast( + const_cast(&DoubleConstant::negative_infinity))); +} + + #ifndef V8_INTERPRETED_REGEXP ExternalReference ExternalReference::re_check_stack_guard_state() { @@ -793,6 +800,51 @@ static double mod_two_doubles(double x, double y) { } +// Helper function to compute x^y, where y is known to be an +// integer. Uses binary decomposition to limit the number of +// multiplications; see the discussion in "Hacker's Delight" by Henry +// S. Warren, Jr., figure 11-6, page 213. +double power_double_int(double x, int y) { + double m = (y < 0) ? 1 / x : x; + unsigned n = (y < 0) ? -y : y; + double p = 1; + while (n != 0) { + if ((n & 1) != 0) p *= m; + m *= m; + if ((n & 2) != 0) p *= m; + m *= m; + n >>= 2; + } + return p; +} + + +double power_double_double(double x, double y) { + int y_int = static_cast(y); + if (y == y_int) { + return power_double_int(x, y_int); // Returns 1.0 for exponent 0. + } + if (!isinf(x)) { + if (y == 0.5) return sqrt(x); + if (y == -0.5) return 1.0 / sqrt(x); + } + if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) { + return OS::nan_value(); + } + return pow(x, y); +} + + +ExternalReference ExternalReference::power_double_double_function() { + return ExternalReference(Redirect(FUNCTION_ADDR(power_double_double))); +} + + +ExternalReference ExternalReference::power_double_int_function() { + return ExternalReference(Redirect(FUNCTION_ADDR(power_double_int))); +} + + static int native_compare_doubles(double y, double x) { if (x == y) return EQUAL; return x < y ? LESS : GREATER; diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index 82c9fc24c5..72a9b15380 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -50,6 +50,7 @@ class DoubleConstant: public AllStatic { public: static const double min_int; static const double one_half; + static const double negative_infinity; }; @@ -539,6 +540,8 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference double_fp_operation(Token::Value operation); static ExternalReference compare_doubles(); + static ExternalReference power_double_double_function(); + static ExternalReference power_double_int_function(); static ExternalReference handle_scope_next_address(); static ExternalReference handle_scope_limit_address(); @@ -549,6 +552,7 @@ class ExternalReference BASE_EMBEDDED { // Static variables containing common double constants. static ExternalReference address_of_min_int(); static ExternalReference address_of_one_half(); + static ExternalReference address_of_negative_infinity(); Address address() const {return reinterpret_cast
(address_);} @@ -710,6 +714,10 @@ static inline int NumberOfBitsSet(uint32_t x) { return num_bits_set; } +// Computes pow(x, y) with the special cases in the spec for Math.pow. +double power_double_int(double x, int y); +double power_double_double(double x, double y); + } } // namespace v8::internal #endif // V8_ASSEMBLER_H_ diff --git a/deps/v8/src/ast-inl.h b/deps/v8/src/ast-inl.h index e88156d6e4..eb81c3a839 100644 --- a/deps/v8/src/ast-inl.h +++ b/deps/v8/src/ast-inl.h @@ -71,14 +71,16 @@ DoWhileStatement::DoWhileStatement(ZoneStringList* labels) : IterationStatement(labels), cond_(NULL), condition_position_(-1), - next_id_(GetNextId()) { + continue_id_(GetNextId()), + back_edge_id_(GetNextId()) { } WhileStatement::WhileStatement(ZoneStringList* labels) : IterationStatement(labels), cond_(NULL), - may_have_function_literal_(true) { + may_have_function_literal_(true), + body_id_(GetNextId()) { } @@ -89,12 +91,14 @@ ForStatement::ForStatement(ZoneStringList* labels) next_(NULL), may_have_function_literal_(true), loop_variable_(NULL), - next_id_(GetNextId()) { + continue_id_(GetNextId()), + body_id_(GetNextId()) { } ForInStatement::ForInStatement(ZoneStringList* labels) - : IterationStatement(labels), each_(NULL), enumerable_(NULL) { + : IterationStatement(labels), each_(NULL), enumerable_(NULL), + assignment_id_(GetNextId()) { } diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index c1ea0a8b3d..895ab67713 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -32,6 +32,7 @@ #include "parser.h" #include "scopes.h" #include "string-stream.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -125,17 +126,18 @@ Assignment::Assignment(Token::Value op, target_(target), value_(value), pos_(pos), - compound_bailout_id_(kNoNumber), + binary_operation_(NULL), + compound_load_id_(kNoNumber), + assignment_id_(GetNextId()), block_start_(false), block_end_(false), is_monomorphic_(false), receiver_types_(NULL) { ASSERT(Token::IsAssignmentOp(op)); - binary_operation_ = is_compound() - ? new BinaryOperation(binary_op(), target, value, pos + 1) - : NULL; if (is_compound()) { - compound_bailout_id_ = GetNextId(); + binary_operation_ = + new BinaryOperation(binary_op(), target, value, pos + 1); + compound_load_id_ = GetNextId(); } } @@ -558,16 +560,18 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) { static bool CallWithoutIC(Handle target, int arity) { + SharedFunctionInfo* info = target->shared(); if (target->NeedsArgumentsAdaption()) { // If the number of formal parameters of the target function // does not match the number of arguments we're passing, we // don't want to deal with it. - return target->shared()->formal_parameter_count() == arity; + return info->formal_parameter_count() == arity; } else { // If the target doesn't need arguments adaption, we can call // it directly, but we avoid to do so if it has a custom call // generator, because that is likely to generate better code. - return !target->shared()->HasCustomCallGenerator(); + return !info->HasBuiltinFunctionId() || + !CallStubCompiler::HasCustomCallGenerator(info->builtin_function_id()); } } diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index cdf456f67b..ed447e343a 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -435,7 +435,6 @@ class IterationStatement: public BreakableStatement { virtual IterationStatement* AsIterationStatement() { return this; } Statement* body() const { return body_; } - void set_body(Statement* stmt) { body_ = stmt; } // Bailout support. int OsrEntryId() const { return osr_entry_id_; } @@ -477,12 +476,14 @@ class DoWhileStatement: public IterationStatement { void set_condition_position(int pos) { condition_position_ = pos; } // Bailout support. - virtual int ContinueId() const { return next_id_; } + virtual int ContinueId() const { return continue_id_; } + int BackEdgeId() const { return back_edge_id_; } private: Expression* cond_; int condition_position_; - int next_id_; + int continue_id_; + int back_edge_id_; }; @@ -507,11 +508,13 @@ class WhileStatement: public IterationStatement { // Bailout support. virtual int ContinueId() const { return EntryId(); } + int BodyId() const { return body_id_; } private: Expression* cond_; // True if there is a function literal subexpression in the condition. bool may_have_function_literal_; + int body_id_; }; @@ -532,11 +535,8 @@ class ForStatement: public IterationStatement { } Statement* init() const { return init_; } - void set_init(Statement* stmt) { init_ = stmt; } Expression* cond() const { return cond_; } - void set_cond(Expression* expr) { cond_ = expr; } Statement* next() const { return next_; } - void set_next(Statement* stmt) { next_ = stmt; } bool may_have_function_literal() const { return may_have_function_literal_; @@ -546,7 +546,8 @@ class ForStatement: public IterationStatement { } // Bailout support. - virtual int ContinueId() const { return next_id_; } + virtual int ContinueId() const { return continue_id_; } + int BodyId() const { return body_id_; } bool is_fast_smi_loop() { return loop_variable_ != NULL; } Variable* loop_variable() { return loop_variable_; } @@ -559,7 +560,8 @@ class ForStatement: public IterationStatement { // True if there is a function literal subexpression in the condition. bool may_have_function_literal_; Variable* loop_variable_; - int next_id_; + int continue_id_; + int body_id_; }; @@ -579,11 +581,13 @@ class ForInStatement: public IterationStatement { Expression* enumerable() const { return enumerable_; } // Bailout support. + int AssignmentId() const { return assignment_id_; } virtual int ContinueId() const { return EntryId(); } private: Expression* each_; Expression* enumerable_; + int assignment_id_; }; @@ -737,7 +741,10 @@ class IfStatement: public Statement { Statement* else_statement) : condition_(condition), then_statement_(then_statement), - else_statement_(else_statement) { } + else_statement_(else_statement), + then_id_(GetNextId()), + else_id_(GetNextId()) { + } DECLARE_NODE_TYPE(IfStatement) @@ -748,14 +755,17 @@ class IfStatement: public Statement { Expression* condition() const { return condition_; } Statement* then_statement() const { return then_statement_; } - void set_then_statement(Statement* stmt) { then_statement_ = stmt; } Statement* else_statement() const { return else_statement_; } - void set_else_statement(Statement* stmt) { else_statement_ = stmt; } + + int ThenId() const { return then_id_; } + int ElseId() const { return else_id_; } private: Expression* condition_; Statement* then_statement_; Statement* else_statement_; + int then_id_; + int else_id_; }; @@ -1380,6 +1390,9 @@ class BinaryOperation: public Expression { int pos) : op_(op), left_(left), right_(right), pos_(pos), is_smi_only_(false) { ASSERT(Token::IsBinaryOp(op)); + right_id_ = (op == Token::AND || op == Token::OR) + ? GetNextId() + : AstNode::kNoNumber; } // Create the binary operation corresponding to a compound assignment. @@ -1400,12 +1413,18 @@ class BinaryOperation: public Expression { void RecordTypeFeedback(TypeFeedbackOracle* oracle); bool IsSmiOnly() const { return is_smi_only_; } + // Bailout support. + int RightId() const { return right_id_; } + private: Token::Value op_; Expression* left_; Expression* right_; int pos_; bool is_smi_only_; + // The short-circuit logical operations have an AST ID for their + // right-hand subexpression. + int right_id_; }; @@ -1432,7 +1451,9 @@ class IncrementOperation: public Expression { class CountOperation: public Expression { public: CountOperation(bool is_prefix, IncrementOperation* increment, int pos) - : is_prefix_(is_prefix), increment_(increment), pos_(pos) { } + : is_prefix_(is_prefix), increment_(increment), pos_(pos), + assignment_id_(GetNextId()) { + } DECLARE_NODE_TYPE(CountOperation) @@ -1452,10 +1473,14 @@ class CountOperation: public Expression { virtual bool IsInlineable() const; + // Bailout support. + int AssignmentId() const { return assignment_id_; } + private: bool is_prefix_; IncrementOperation* increment_; int pos_; + int assignment_id_; }; @@ -1524,7 +1549,10 @@ class Conditional: public Expression { then_expression_(then_expression), else_expression_(else_expression), then_expression_position_(then_expression_position), - else_expression_position_(else_expression_position) { } + else_expression_position_(else_expression_position), + then_id_(GetNextId()), + else_id_(GetNextId()) { + } DECLARE_NODE_TYPE(Conditional) @@ -1534,8 +1562,11 @@ class Conditional: public Expression { Expression* then_expression() const { return then_expression_; } Expression* else_expression() const { return else_expression_; } - int then_expression_position() { return then_expression_position_; } - int else_expression_position() { return else_expression_position_; } + int then_expression_position() const { return then_expression_position_; } + int else_expression_position() const { return else_expression_position_; } + + int ThenId() const { return then_id_; } + int ElseId() const { return else_id_; } private: Expression* condition_; @@ -1543,6 +1574,8 @@ class Conditional: public Expression { Expression* else_expression_; int then_expression_position_; int else_expression_position_; + int then_id_; + int else_id_; }; @@ -1585,7 +1618,8 @@ class Assignment: public Expression { } // Bailout support. - int compound_bailout_id() const { return compound_bailout_id_; } + int CompoundLoadId() const { return compound_load_id_; } + int AssignmentId() const { return assignment_id_; } private: Token::Value op_; @@ -1593,7 +1627,8 @@ class Assignment: public Expression { Expression* value_; int pos_; BinaryOperation* binary_operation_; - int compound_bailout_id_; + int compound_load_id_; + int assignment_id_; bool block_start_; bool block_end_; diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 800c4376ce..cae1a9a288 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -38,7 +38,6 @@ #include "natives.h" #include "objects-visiting.h" #include "snapshot.h" -#include "stub-cache.h" #include "extensions/externalize-string-extension.h" #include "extensions/gc-extension.h" @@ -234,7 +233,7 @@ class Genesis BASE_EMBEDDED { // Used for creating a context from scratch. void InstallNativeFunctions(); bool InstallNatives(); - void InstallCustomCallGenerators(); + void InstallBuiltinFunctionIds(); void InstallJSFunctionResultCaches(); void InitializeNormalizedMapCaches(); // Used both for deserialized and from-scratch contexts to add the extensions @@ -1270,7 +1269,7 @@ bool Genesis::InstallNatives() { global_context()->set_string_function_prototype_map( HeapObject::cast(string_function->initial_map()->prototype())->map()); - InstallCustomCallGenerators(); + InstallBuiltinFunctionIds(); // Install Function.prototype.call and apply. { Handle key = Factory::function_class_symbol(); @@ -1369,7 +1368,7 @@ bool Genesis::InstallNatives() { } -static Handle ResolveCustomCallGeneratorHolder( +static Handle ResolveBuiltinIdHolder( Handle global_context, const char* holder_expr) { Handle global(global_context->global()); @@ -1387,9 +1386,9 @@ static Handle ResolveCustomCallGeneratorHolder( } -static void InstallCustomCallGenerator(Handle holder, - const char* function_name, - int id) { +static void InstallBuiltinFunctionId(Handle holder, + const char* function_name, + BuiltinFunctionId id) { Handle name = Factory::LookupAsciiSymbol(function_name); Object* function_object = holder->GetProperty(*name)->ToObjectUnchecked(); Handle function(JSFunction::cast(function_object)); @@ -1397,17 +1396,17 @@ static void InstallCustomCallGenerator(Handle holder, } -void Genesis::InstallCustomCallGenerators() { +void Genesis::InstallBuiltinFunctionIds() { HandleScope scope; -#define INSTALL_CALL_GENERATOR(holder_expr, fun_name, name) \ - { \ - Handle holder = ResolveCustomCallGeneratorHolder( \ - global_context(), #holder_expr); \ - const int id = CallStubCompiler::k##name##CallGenerator; \ - InstallCustomCallGenerator(holder, #fun_name, id); \ +#define INSTALL_BUILTIN_ID(holder_expr, fun_name, name) \ + { \ + Handle holder = ResolveBuiltinIdHolder( \ + global_context(), #holder_expr); \ + BuiltinFunctionId id = k##name; \ + InstallBuiltinFunctionId(holder, #fun_name, id); \ } - CUSTOM_CALL_IC_GENERATORS(INSTALL_CALL_GENERATOR) -#undef INSTALL_CALL_GENERATOR + FUNCTIONS_WITH_ID_LIST(INSTALL_BUILTIN_ID) +#undef INSTALL_BUILTIN_ID } diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index aa557f00bc..2bb94bb086 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -30,6 +30,7 @@ #include +#include "../include/v8stdint.h" extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); // The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during @@ -231,6 +232,8 @@ static inline void CheckNonEqualsHelper(const char* file, #define CHECK_GT(a, b) CHECK((a) > (b)) #define CHECK_GE(a, b) CHECK((a) >= (b)) +#define CHECK_LT(a, b) CHECK((a) < (b)) +#define CHECK_LE(a, b) CHECK((a) <= (b)) // This is inspired by the static assertion facility in boost. This diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 1010e9512d..8ba9971751 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -47,6 +47,7 @@ namespace internal { V(Compare) \ V(CompareIC) \ V(MathPow) \ + V(TranscendentalCacheSSE2) \ V(RecordWrite) \ V(ConvertToDouble) \ V(WriteInt32ToHeapNumber) \ @@ -325,13 +326,24 @@ class FastCloneShallowArrayStub : public CodeStub { class InstanceofStub: public CodeStub { public: - InstanceofStub() { } + enum Flags { + kNoFlags = 0, + kArgsInRegisters = 1 << 0 + }; + + explicit InstanceofStub(Flags flags) : flags_(flags) { } void Generate(MacroAssembler* masm); private: Major MajorKey() { return Instanceof; } - int MinorKey() { return 0; } + int MinorKey() { return args_in_registers() ? 1 : 0; } + + bool args_in_registers() { + return (flags_ & kArgsInRegisters) != 0; + } + + Flags flags_; }; diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 59a684c69f..e4864e4801 100755 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -116,13 +116,26 @@ static bool AlwaysFullCompiler() { static void FinishOptimization(Handle function, int64_t start) { int opt_count = function->shared()->opt_count(); function->shared()->set_opt_count(opt_count + 1); - if (!FLAG_trace_opt) return; - double ms = static_cast(OS::Ticks() - start) / 1000; - PrintF("[optimizing: "); - function->PrintName(); - PrintF(" / %" V8PRIxPTR, reinterpret_cast(*function)); - PrintF(" - took %0.3f ms]\n", ms); + if (FLAG_trace_opt) { + PrintF("[optimizing: "); + function->PrintName(); + PrintF(" / %" V8PRIxPTR, reinterpret_cast(*function)); + PrintF(" - took %0.3f ms]\n", ms); + } + if (FLAG_trace_opt_stats) { + static double compilation_time = 0.0; + static int compiled_functions = 0; + static int code_size = 0; + + compilation_time += ms; + compiled_functions++; + code_size += function->shared()->SourceSize(); + PrintF("Compiled: %d functions with %d byte source size in %fms.\n", + compiled_functions, + code_size, + compilation_time); + } } @@ -461,7 +474,14 @@ Handle Compiler::Compile(Handle source, ScriptDataImpl* pre_data = input_pre_data; if (pre_data == NULL && source_length >= FLAG_min_preparse_length) { - pre_data = ParserApi::PartialPreParse(source, NULL, extension); + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUC16CharacterStream stream( + Handle::cast(source), 0, source->length()); + pre_data = ParserApi::PartialPreParse(&stream, extension); + } else { + GenericStringUC16CharacterStream stream(source, 0, source->length()); + pre_data = ParserApi::PartialPreParse(&stream, extension); + } } // Create a script object describing the script to be compiled. diff --git a/deps/v8/src/d8.gyp b/deps/v8/src/d8.gyp new file mode 100644 index 0000000000..3283e38ae3 --- /dev/null +++ b/deps/v8/src/d8.gyp @@ -0,0 +1,85 @@ +# Copyright 2010 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +{ + 'targets': [ + { + 'target_name': 'd8', + 'type': 'executable', + 'dependencies': [ + 'd8_js2c#host', + '../tools/gyp/v8.gyp:v8', + ], + 'include_dirs+': [ + '../src', + ], + 'defines': [ + 'ENABLE_DEBUGGER_SUPPORT', + ], + 'sources': [ + 'd8.cc', + 'd8-debug.cc', + '<(SHARED_INTERMEDIATE_DIR)/d8-js.cc', + ], + 'conditions': [ + [ 'OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + 'sources': [ 'd8-posix.cc', ] + }], + ], + }, + { + 'target_name': 'd8_js2c', + 'type': 'none', + 'toolsets': ['host'], + 'variables': { + 'js_files': [ + 'd8.js', + ], + }, + 'actions': [ + { + 'action_name': 'd8_js2c', + 'inputs': [ + '../tools/js2c.py', + '<@(js_files)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/d8-js.cc', + '<(SHARED_INTERMEDIATE_DIR)/d8-js-empty.cc', + ], + 'action': [ + 'python', + '../tools/js2c.py', + '<@(_outputs)', + 'D8', + '<@(js_files)' + ], + }, + ], + } + ], +} diff --git a/deps/v8/src/date.js b/deps/v8/src/date.js index 960147072c..38bb8eb254 100644 --- a/deps/v8/src/date.js +++ b/deps/v8/src/date.js @@ -81,12 +81,12 @@ function TimeFromYear(year) { function InLeapYear(time) { - return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0; + return DaysInYear(YearFromTime(time)) == 366 ? 1 : 0; } function DayWithinYear(time) { - return DAY(time) - DayFromYear(YEAR_FROM_TIME(time)); + return DAY(time) - DayFromYear(YearFromTime(time)); } @@ -114,9 +114,9 @@ function EquivalentTime(t) { // the actual year if it is in the range 1970..2037 if (t >= 0 && t <= 2.1e12) return t; - var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)), - MONTH_FROM_TIME(t), - DATE_FROM_TIME(t)); + var day = MakeDay(EquivalentYear(YearFromTime(t)), + MonthFromTime(t), + DateFromTime(t)); return MakeDate(day, TimeWithinDay(t)); } @@ -253,9 +253,6 @@ var ltcache = { function LocalTimeNoCheck(time) { var ltc = ltcache; if (%_ObjectEquals(time, ltc.key)) return ltc.val; - if (time < -MAX_TIME_MS || time > MAX_TIME_MS) { - return $NaN; - } // Inline the DST offset cache checks for speed. // The cache is hit, or DaylightSavingsOffset is called, @@ -371,16 +368,21 @@ function MakeDay(year, month, date) { // ECMA 262 - 15.9.1.13 function MakeDate(day, time) { - if (!$isFinite(day)) return $NaN; - if (!$isFinite(time)) return $NaN; - return day * msPerDay + time; + var time = day * msPerDay + time; + // Some of our runtime funtions for computing UTC(time) rely on + // times not being significantly larger than MAX_TIME_MS. If there + // is no way that the time can be within range even after UTC + // conversion we return NaN immediately instead of relying on + // TimeClip to do it. + if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN; + return time; } // ECMA 262 - 15.9.1.14 function TimeClip(time) { if (!$isFinite(time)) return $NaN; - if ($abs(time) > 8.64E15) return $NaN; + if ($abs(time) > MAX_TIME_MS) return $NaN; return TO_INTEGER(time); } @@ -424,7 +426,7 @@ var Date_cache = { value = DateParse(year); if (!NUMBER_IS_NAN(value)) { cache.time = value; - cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value)); + cache.year = YearFromTime(LocalTimeNoCheck(value)); cache.string = year; } } @@ -642,7 +644,7 @@ function DateGetFullYear() { if (NUMBER_IS_NAN(t)) return t; var cache = Date_cache; if (cache.time === t) return cache.year; - return YEAR_FROM_TIME(LocalTimeNoCheck(t)); + return YearFromTime(LocalTimeNoCheck(t)); } @@ -650,7 +652,7 @@ function DateGetFullYear() { function DateGetUTCFullYear() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return t; - return YEAR_FROM_TIME(t); + return YearFromTime(t); } @@ -658,7 +660,7 @@ function DateGetUTCFullYear() { function DateGetMonth() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return t; - return MONTH_FROM_TIME(LocalTimeNoCheck(t)); + return MonthFromTime(LocalTimeNoCheck(t)); } @@ -666,7 +668,7 @@ function DateGetMonth() { function DateGetUTCMonth() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return t; - return MONTH_FROM_TIME(t); + return MonthFromTime(t); } @@ -674,7 +676,7 @@ function DateGetUTCMonth() { function DateGetDate() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return t; - return DATE_FROM_TIME(LocalTimeNoCheck(t)); + return DateFromTime(LocalTimeNoCheck(t)); } @@ -869,7 +871,7 @@ function DateSetUTCHours(hour, min, sec, ms) { function DateSetDate(date) { var t = LocalTime(DATE_VALUE(this)); date = ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date); + var day = MakeDay(YearFromTime(t), MonthFromTime(t), date); return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); } @@ -878,7 +880,7 @@ function DateSetDate(date) { function DateSetUTCDate(date) { var t = DATE_VALUE(this); date = ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date); + var day = MakeDay(YearFromTime(t), MonthFromTime(t), date); return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); } @@ -888,7 +890,7 @@ function DateSetMonth(month, date) { var t = LocalTime(DATE_VALUE(this)); month = ToNumber(month); date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), month, date); + var day = MakeDay(YearFromTime(t), month, date); return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); } @@ -898,7 +900,7 @@ function DateSetUTCMonth(month, date) { var t = DATE_VALUE(this); month = ToNumber(month); date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), month, date); + var day = MakeDay(YearFromTime(t), month, date); return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); } @@ -909,8 +911,8 @@ function DateSetFullYear(year, month, date) { t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t); year = ToNumber(year); var argc = %_ArgumentsLength(); - month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month); - date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date); + month = argc < 2 ? MonthFromTime(t) : ToNumber(month); + date = argc < 3 ? DateFromTime(t) : ToNumber(date); var day = MakeDay(year, month, date); return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); } @@ -922,8 +924,8 @@ function DateSetUTCFullYear(year, month, date) { if (NUMBER_IS_NAN(t)) t = 0; var argc = %_ArgumentsLength(); year = ToNumber(year); - month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month); - date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date); + month = argc < 2 ? MonthFromTime(t) : ToNumber(month); + date = argc < 3 ? DateFromTime(t) : ToNumber(date); var day = MakeDay(year, month, date); return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); } @@ -935,9 +937,9 @@ function DateToUTCString() { if (NUMBER_IS_NAN(t)) return kInvalidDate; // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT return WeekDays[WeekDay(t)] + ', ' - + TwoDigitString(DATE_FROM_TIME(t)) + ' ' - + Months[MONTH_FROM_TIME(t)] + ' ' - + YEAR_FROM_TIME(t) + ' ' + + TwoDigitString(DateFromTime(t)) + ' ' + + Months[MonthFromTime(t)] + ' ' + + YearFromTime(t) + ' ' + TimeString(t) + ' GMT'; } @@ -946,7 +948,7 @@ function DateToUTCString() { function DateGetYear() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return $NaN; - return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900; + return YearFromTime(LocalTimeNoCheck(t)) - 1900; } @@ -958,7 +960,7 @@ function DateSetYear(year) { if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN); year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year; - var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t)); + var day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); } @@ -984,16 +986,24 @@ function PadInt(n, digits) { function DateToISOString() { var t = DATE_VALUE(this); if (NUMBER_IS_NAN(t)) return kInvalidDate; - return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) + - '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) + - ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) + + return this.getUTCFullYear() + + '-' + PadInt(this.getUTCMonth() + 1, 2) + + '-' + PadInt(this.getUTCDate(), 2) + + 'T' + PadInt(this.getUTCHours(), 2) + + ':' + PadInt(this.getUTCMinutes(), 2) + + ':' + PadInt(this.getUTCSeconds(), 2) + '.' + PadInt(this.getUTCMilliseconds(), 3) + 'Z'; } function DateToJSON(key) { - return CheckJSONPrimitive(this.toISOString()); + var o = ToObject(this); + var tv = DefaultNumber(o); + if (IS_NUMBER(tv) && !$isFinite(tv)) { + return null; + } + return o.toISOString(); } diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js index d091991a11..090c661dd3 100644 --- a/deps/v8/src/debug-debugger.js +++ b/deps/v8/src/debug-debugger.js @@ -858,6 +858,7 @@ Debug.debuggerFlags = function() { return debugger_flags; }; +Debug.MakeMirror = MakeMirror; function MakeExecutionState(break_id) { return new ExecutionState(break_id); @@ -876,9 +877,11 @@ ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { return %PrepareStep(this.break_id, action, count); } -ExecutionState.prototype.evaluateGlobal = function(source, disable_break) { - return MakeMirror( - %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break))); +ExecutionState.prototype.evaluateGlobal = function(source, disable_break, + opt_additional_context) { + return MakeMirror(%DebugEvaluateGlobal(this.break_id, source, + Boolean(disable_break), + opt_additional_context)); }; ExecutionState.prototype.frameCount = function() { @@ -1837,6 +1840,7 @@ DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { var frame = request.arguments.frame; var global = request.arguments.global; var disable_break = request.arguments.disable_break; + var additional_context = request.arguments.additional_context; // The expression argument could be an integer so we convert it to a // string. @@ -1850,12 +1854,30 @@ DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { if (!IS_UNDEFINED(frame) && global) { return response.failed('Arguments "frame" and "global" are exclusive'); } + + var additional_context_object; + if (additional_context) { + additional_context_object = {}; + for (var i = 0; i < additional_context.length; i++) { + var mapping = additional_context[i]; + if (!IS_STRING(mapping.name) || !IS_NUMBER(mapping.handle)) { + return response.failed("Context element #" + i + + " must contain name:string and handle:number"); + } + var context_value_mirror = LookupMirror(mapping.handle); + if (!context_value_mirror) { + return response.failed("Context object '" + mapping.name + + "' #" + mapping.handle + "# not found"); + } + additional_context_object[mapping.name] = context_value_mirror.value(); + } + } // Global evaluate. if (global) { // Evaluate in the global context. - response.body = - this.exec_state_.evaluateGlobal(expression, Boolean(disable_break)); + response.body = this.exec_state_.evaluateGlobal( + expression, Boolean(disable_break), additional_context_object); return; } @@ -1877,12 +1899,12 @@ DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { } // Evaluate in the specified frame. response.body = this.exec_state_.frame(frame_number).evaluate( - expression, Boolean(disable_break)); + expression, Boolean(disable_break), additional_context_object); return; } else { // Evaluate in the selected frame. response.body = this.exec_state_.frame().evaluate( - expression, Boolean(disable_break)); + expression, Boolean(disable_break), additional_context_object); return; } }; diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index e88d9cd27a..11dacfeede 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -720,7 +720,6 @@ MaybeObject* Execution::HandleStackGuardInterrupt() { return Top::TerminateExecution(); } if (StackGuard::IsInterrupted()) { - // interrupt StackGuard::Continue(INTERRUPT); return Top::StackOverflow(); } diff --git a/deps/v8/src/extensions/experimental/i18n-extension.cc b/deps/v8/src/extensions/experimental/i18n-extension.cc new file mode 100644 index 0000000000..22a1c912d0 --- /dev/null +++ b/deps/v8/src/extensions/experimental/i18n-extension.cc @@ -0,0 +1,263 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "i18n-extension.h" + +#include +#include + +#include "unicode/locid.h" +#include "unicode/uloc.h" + +namespace v8 { +namespace internal { + +I18NExtension* I18NExtension::extension_ = NULL; + +// TODO(cira): maybe move JS code to a .js file and generata cc files from it? +const char* const I18NExtension::kSource = + "Locale = function(optLocale) {" + " native function NativeJSLocale();" + " var properties = NativeJSLocale(optLocale);" + " this.locale = properties.locale;" + " this.language = properties.language;" + " this.script = properties.script;" + " this.region = properties.region;" + "};" + "Locale.availableLocales = function() {" + " native function NativeJSAvailableLocales();" + " return NativeJSAvailableLocales();" + "};" + "Locale.prototype.maximizedLocale = function() {" + " native function NativeJSMaximizedLocale();" + " return new Locale(NativeJSMaximizedLocale(this.locale));" + "};" + "Locale.prototype.minimizedLocale = function() {" + " native function NativeJSMinimizedLocale();" + " return new Locale(NativeJSMinimizedLocale(this.locale));" + "};" + "Locale.prototype.displayLocale_ = function(displayLocale) {" + " var result = this.locale;" + " if (displayLocale !== undefined) {" + " result = displayLocale.locale;" + " }" + " return result;" + "};" + "Locale.prototype.displayLanguage = function(optDisplayLocale) {" + " var displayLocale = this.displayLocale_(optDisplayLocale);" + " native function NativeJSDisplayLanguage();" + " return NativeJSDisplayLanguage(this.locale, displayLocale);" + "};" + "Locale.prototype.displayScript = function(optDisplayLocale) {" + " var displayLocale = this.displayLocale_(optDisplayLocale);" + " native function NativeJSDisplayScript();" + " return NativeJSDisplayScript(this.locale, displayLocale);" + "};" + "Locale.prototype.displayRegion = function(optDisplayLocale) {" + " var displayLocale = this.displayLocale_(optDisplayLocale);" + " native function NativeJSDisplayRegion();" + " return NativeJSDisplayRegion(this.locale, displayLocale);" + "};" + "Locale.prototype.displayName = function(optDisplayLocale) {" + " var displayLocale = this.displayLocale_(optDisplayLocale);" + " native function NativeJSDisplayName();" + " return NativeJSDisplayName(this.locale, displayLocale);" + "};"; + +v8::Handle I18NExtension::GetNativeFunction( + v8::Handle name) { + if (name->Equals(v8::String::New("NativeJSLocale"))) { + return v8::FunctionTemplate::New(JSLocale); + } else if (name->Equals(v8::String::New("NativeJSAvailableLocales"))) { + return v8::FunctionTemplate::New(JSAvailableLocales); + } else if (name->Equals(v8::String::New("NativeJSMaximizedLocale"))) { + return v8::FunctionTemplate::New(JSMaximizedLocale); + } else if (name->Equals(v8::String::New("NativeJSMinimizedLocale"))) { + return v8::FunctionTemplate::New(JSMinimizedLocale); + } else if (name->Equals(v8::String::New("NativeJSDisplayLanguage"))) { + return v8::FunctionTemplate::New(JSDisplayLanguage); + } else if (name->Equals(v8::String::New("NativeJSDisplayScript"))) { + return v8::FunctionTemplate::New(JSDisplayScript); + } else if (name->Equals(v8::String::New("NativeJSDisplayRegion"))) { + return v8::FunctionTemplate::New(JSDisplayRegion); + } else if (name->Equals(v8::String::New("NativeJSDisplayName"))) { + return v8::FunctionTemplate::New(JSDisplayName); + } + + return v8::Handle(); +} + +v8::Handle I18NExtension::JSLocale(const v8::Arguments& args) { + // TODO(cira): Fetch browser locale. Accept en-US as good default for now. + // We could possibly pass browser locale as a parameter in the constructor. + std::string locale_name("en-US"); + if (args.Length() == 1 && args[0]->IsString()) { + locale_name = *v8::String::Utf8Value(args[0]->ToString()); + } + + v8::Local locale = v8::Object::New(); + locale->Set(v8::String::New("locale"), v8::String::New(locale_name.c_str())); + + icu::Locale icu_locale(locale_name.c_str()); + + const char* language = icu_locale.getLanguage(); + locale->Set(v8::String::New("language"), v8::String::New(language)); + + const char* script = icu_locale.getScript(); + if (strlen(script)) { + locale->Set(v8::String::New("script"), v8::String::New(script)); + } + + const char* region = icu_locale.getCountry(); + if (strlen(region)) { + locale->Set(v8::String::New("region"), v8::String::New(region)); + } + + return locale; +} + +// TODO(cira): Filter out locales that Chrome doesn't support. +v8::Handle I18NExtension::JSAvailableLocales( + const v8::Arguments& args) { + v8::Local all_locales = v8::Array::New(); + + int count = 0; + const Locale* icu_locales = icu::Locale::getAvailableLocales(count); + for (int i = 0; i < count; ++i) { + all_locales->Set(i, v8::String::New(icu_locales[i].getName())); + } + + return all_locales; +} + +// Use - as tag separator, not _ that ICU uses. +static std::string NormalizeLocale(const std::string& locale) { + std::string result(locale); + // TODO(cira): remove STL dependency. + std::replace(result.begin(), result.end(), '_', '-'); + return result; +} + +v8::Handle I18NExtension::JSMaximizedLocale( + const v8::Arguments& args) { + if (!args.Length() || !args[0]->IsString()) { + return v8::Undefined(); + } + + UErrorCode status = U_ZERO_ERROR; + std::string locale_name = *v8::String::Utf8Value(args[0]->ToString()); + char max_locale[ULOC_FULLNAME_CAPACITY]; + uloc_addLikelySubtags(locale_name.c_str(), max_locale, + sizeof(max_locale), &status); + if (U_FAILURE(status)) { + return v8::Undefined(); + } + + return v8::String::New(NormalizeLocale(max_locale).c_str()); +} + +v8::Handle I18NExtension::JSMinimizedLocale( + const v8::Arguments& args) { + if (!args.Length() || !args[0]->IsString()) { + return v8::Undefined(); + } + + UErrorCode status = U_ZERO_ERROR; + std::string locale_name = *v8::String::Utf8Value(args[0]->ToString()); + char min_locale[ULOC_FULLNAME_CAPACITY]; + uloc_minimizeSubtags(locale_name.c_str(), min_locale, + sizeof(min_locale), &status); + if (U_FAILURE(status)) { + return v8::Undefined(); + } + + return v8::String::New(NormalizeLocale(min_locale).c_str()); +} + +// Common code for JSDisplayXXX methods. +static v8::Handle GetDisplayItem(const v8::Arguments& args, + const std::string& item) { + if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { + return v8::Undefined(); + } + + std::string base_locale = *v8::String::Utf8Value(args[0]->ToString()); + icu::Locale icu_locale(base_locale.c_str()); + icu::Locale display_locale = + icu::Locale(*v8::String::Utf8Value(args[1]->ToString())); + UnicodeString result; + if (item == "language") { + icu_locale.getDisplayLanguage(display_locale, result); + } else if (item == "script") { + icu_locale.getDisplayScript(display_locale, result); + } else if (item == "region") { + icu_locale.getDisplayCountry(display_locale, result); + } else if (item == "name") { + icu_locale.getDisplayName(display_locale, result); + } else { + return v8::Undefined(); + } + + if (result.length()) { + return v8::String::New( + reinterpret_cast(result.getBuffer()), result.length()); + } + + return v8::Undefined(); +} + +v8::Handle I18NExtension::JSDisplayLanguage( + const v8::Arguments& args) { + return GetDisplayItem(args, "language"); +} + +v8::Handle I18NExtension::JSDisplayScript( + const v8::Arguments& args) { + return GetDisplayItem(args, "script"); +} + +v8::Handle I18NExtension::JSDisplayRegion( + const v8::Arguments& args) { + return GetDisplayItem(args, "region"); +} + +v8::Handle I18NExtension::JSDisplayName(const v8::Arguments& args) { + return GetDisplayItem(args, "name"); +} + +I18NExtension* I18NExtension::get() { + if (!extension_) { + extension_ = new I18NExtension(); + } + return extension_; +} + +void I18NExtension::Register() { + static v8::DeclareExtension i18n_extension_declaration(I18NExtension::get()); +} + +} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/i18n-extension.h b/deps/v8/src/extensions/experimental/i18n-extension.h new file mode 100644 index 0000000000..629332babb --- /dev/null +++ b/deps/v8/src/extensions/experimental/i18n-extension.h @@ -0,0 +1,64 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ +#define V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ + +#include + +namespace v8 { +namespace internal { + + +class I18NExtension : public v8::Extension { + public: + I18NExtension() : v8::Extension("v8/i18n", kSource) {} + virtual v8::Handle GetNativeFunction( + v8::Handle name); + + // Implementations of window.Locale methods. + static v8::Handle JSLocale(const v8::Arguments& args); + static v8::Handle JSAvailableLocales(const v8::Arguments& args); + static v8::Handle JSMaximizedLocale(const v8::Arguments& args); + static v8::Handle JSMinimizedLocale(const v8::Arguments& args); + static v8::Handle JSDisplayLanguage(const v8::Arguments& args); + static v8::Handle JSDisplayScript(const v8::Arguments& args); + static v8::Handle JSDisplayRegion(const v8::Arguments& args); + static v8::Handle JSDisplayName(const v8::Arguments& args); + + // V8 code prefers Register, while Chrome and WebKit use get kind of methods. + static void Register(); + static I18NExtension* get(); + + private: + static const char* const kSource; + static I18NExtension* extension_; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 37653a4a15..facbec273b 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -141,6 +141,7 @@ DEFINE_bool(use_osr, true, "use on-stack replacement") DEFINE_bool(use_osr, false, "use on-stack replacement") #endif DEFINE_bool(trace_osr, false, "trace on-stack replacement") +DEFINE_int(stress_runs, 0, "number of stress runs") // assembler-ia32.cc / assembler-arm.cc / assembler-x64.cc DEFINE_bool(debug_code, false, @@ -194,6 +195,7 @@ DEFINE_bool(mask_constants_with_cookie, // codegen.cc DEFINE_bool(lazy, true, "use lazy compilation") DEFINE_bool(trace_opt, false, "trace lazy optimization") +DEFINE_bool(trace_opt_stats, false, "trace lazy optimization statistics") DEFINE_bool(opt, true, "use adaptive optimizations") DEFINE_bool(opt_eagerly, false, "be more eager when adaptively optimizing") DEFINE_bool(always_opt, false, "always try to optimize functions") @@ -456,8 +458,6 @@ DEFINE_bool(log_snapshot_positions, false, "log positions of (de)serialized objects in the snapshot.") DEFINE_bool(log_suspect, false, "Log suspect operations.") DEFINE_bool(log_producers, false, "Log stack traces of JS objects allocations.") -DEFINE_bool(compress_log, false, - "Compress log to save space (makes log less human-readable).") DEFINE_bool(prof, false, "Log statistical profiling information (implies --log-code).") DEFINE_bool(prof_auto, true, diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index 4eb10c7431..96307a302d 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -761,6 +761,7 @@ void FullCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { context()->EmitLogicalLeft(expr, &eval_right, &done); + PrepareForBailoutForId(expr->RightId(), NO_REGISTERS); __ bind(&eval_right); if (context()->IsTest()) ForwardBailoutToChild(expr); context()->HandleExpression(expr->right()); @@ -925,16 +926,21 @@ void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) { if (stmt->HasElseStatement()) { VisitForControl(stmt->condition(), &then_part, &else_part, &then_part); + PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS); __ bind(&then_part); Visit(stmt->then_statement()); __ jmp(&done); + PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS); __ bind(&else_part); Visit(stmt->else_statement()); } else { VisitForControl(stmt->condition(), &then_part, &done, &then_part); + PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS); __ bind(&then_part); Visit(stmt->then_statement()); + + PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS); } __ bind(&done); PrepareForBailoutForId(stmt->id(), NO_REGISTERS); @@ -946,6 +952,11 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; + // When continuing, we clobber the unpredictable value in the accumulator + // with one that's safe for GC. If we hit an exit from the try block of + // try...finally on our way out, we will unconditionally preserve the + // accumulator on the stack. + ClearAccumulator(); while (!current->IsContinueTarget(stmt->target())) { stack_depth = current->Exit(stack_depth); current = current->outer(); @@ -962,6 +973,11 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; + // When breaking, we clobber the unpredictable value in the accumulator + // with one that's safe for GC. If we hit an exit from the try block of + // try...finally on our way out, we will unconditionally preserve the + // accumulator on the stack. + ClearAccumulator(); while (!current->IsBreakTarget(stmt->target())) { stack_depth = current->Exit(stack_depth); current = current->outer(); @@ -1043,12 +1059,13 @@ void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { &stack_check); // Check stack before looping. + PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS); __ bind(&stack_check); EmitStackCheck(stmt); __ jmp(&body); - __ bind(loop_statement.break_target()); PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); + __ bind(loop_statement.break_target()); decrement_loop_depth(); } @@ -1063,6 +1080,7 @@ void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { // Emit the test at the bottom of the loop. __ jmp(&test); + PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); __ bind(&body); Visit(stmt->body()); @@ -1080,8 +1098,8 @@ void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { loop_statement.break_target(), loop_statement.break_target()); - __ bind(loop_statement.break_target()); PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); + __ bind(loop_statement.break_target()); decrement_loop_depth(); } @@ -1099,12 +1117,12 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { // Emit the test at the bottom of the loop (even if empty). __ jmp(&test); + PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); __ bind(&body); Visit(stmt->body()); - __ bind(loop_statement.continue_target()); PrepareForBailoutForId(stmt->ContinueId(), NO_REGISTERS); - + __ bind(loop_statement.continue_target()); SetStatementPosition(stmt); if (stmt->next() != NULL) { Visit(stmt->next()); @@ -1127,8 +1145,8 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { __ jmp(&body); } - __ bind(loop_statement.break_target()); PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); + __ bind(loop_statement.break_target()); decrement_loop_depth(); } @@ -1235,7 +1253,10 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { Visit(stmt->try_block()); __ PopTryHandler(); } - // Execute the finally block on the way out. + // Execute the finally block on the way out. Clobber the unpredictable + // value in the accumulator with one that's safe for GC. The finally + // block will unconditionally preserve the accumulator on the stack. + ClearAccumulator(); __ Call(&finally_entry); } @@ -1256,6 +1277,7 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) { Label true_case, false_case, done; VisitForControl(expr->condition(), &true_case, &false_case, &true_case); + PrepareForBailoutForId(expr->ThenId(), NO_REGISTERS); __ bind(&true_case); SetExpressionPosition(expr->then_expression(), expr->then_expression_position()); @@ -1270,6 +1292,7 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) { __ jmp(&done); } + PrepareForBailoutForId(expr->ElseId(), NO_REGISTERS); __ bind(&false_case); if (context()->IsTest()) ForwardBailoutToChild(expr); SetExpressionPosition(expr->else_expression(), diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 8d9fe2d332..0482ee8d94 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -38,6 +38,9 @@ namespace v8 { namespace internal { +// Forward declarations. +class JumpPatchSite; + // AST node visitor which can tell whether a given statement will be breakable // when the code is compiled by the full compiler in the debugger. This means // that there will be an IC (load/store/call) in the code generated for the @@ -283,6 +286,10 @@ class FullCodeGenerator: public AstVisitor { static const InlineFunctionGenerator kInlineFunctionGenerators[]; + // A platform-specific utility to overwrite the accumulator register + // with a GC-safe value. + void ClearAccumulator(); + // Compute the frame pointer relative offset for a given local or // parameter slot. int SlotOffset(Slot* slot); @@ -481,7 +488,7 @@ class FullCodeGenerator: public AstVisitor { // Assign to the given expression as if via '='. The right-hand-side value // is expected in the accumulator. - void EmitAssignment(Expression* expr); + void EmitAssignment(Expression* expr, int bailout_ast_id); // Complete a variable assignment. The right-hand-side value is expected // in the accumulator. @@ -533,6 +540,10 @@ class FullCodeGenerator: public AstVisitor { // Helper for calling an IC stub. void EmitCallIC(Handle ic, RelocInfo::Mode mode); + // Calling an IC stub with a patch site. Passing NULL for patch_site + // indicates no inlined smi code and emits a nop after the IC call. + void EmitCallIC(Handle ic, JumpPatchSite* patch_site); + // Set fields in the stack frame. Offsets are the frame pointer relative // offsets defined in, e.g., StandardFrameConstants. void StoreToFrameField(int frame_offset, Register value); diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h index b56b835b96..35156ae606 100644 --- a/deps/v8/src/globals.h +++ b/deps/v8/src/globals.h @@ -28,6 +28,8 @@ #ifndef V8_GLOBALS_H_ #define V8_GLOBALS_H_ +#include "../include/v8stdint.h" + namespace v8 { namespace internal { diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 8f6fb989eb..ef839988da 100644 --- a/deps/v8/src/heap-inl.h +++ b/deps/v8/src/heap-inl.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index 91ac9867a2..002950031b 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2009-2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -348,27 +348,34 @@ void HeapProfiler::TearDown() { #ifdef ENABLE_LOGGING_AND_PROFILING -HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name, int type) { +HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name, + int type, + v8::ActivityControl* control) { ASSERT(singleton_ != NULL); - return singleton_->TakeSnapshotImpl(name, type); + return singleton_->TakeSnapshotImpl(name, type, control); } -HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, int type) { +HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, + int type, + v8::ActivityControl* control) { ASSERT(singleton_ != NULL); - return singleton_->TakeSnapshotImpl(name, type); + return singleton_->TakeSnapshotImpl(name, type, control); } -HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, int type) { +HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, + int type, + v8::ActivityControl* control) { Heap::CollectAllGarbage(true); HeapSnapshot::Type s_type = static_cast(type); HeapSnapshot* result = snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++); + bool generation_completed = true; switch (s_type) { case HeapSnapshot::kFull: { - HeapSnapshotGenerator generator(result); - generator.GenerateSnapshot(); + HeapSnapshotGenerator generator(result, control); + generation_completed = generator.GenerateSnapshot(); break; } case HeapSnapshot::kAggregated: { @@ -381,13 +388,19 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, int type) { default: UNREACHABLE(); } - snapshots_->SnapshotGenerationFinished(); + if (!generation_completed) { + delete result; + result = NULL; + } + snapshots_->SnapshotGenerationFinished(result); return result; } -HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, int type) { - return TakeSnapshotImpl(snapshots_->GetName(name), type); +HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, + int type, + v8::ActivityControl* control) { + return TakeSnapshotImpl(snapshots_->GetName(name), type, control); } diff --git a/deps/v8/src/heap-profiler.h b/deps/v8/src/heap-profiler.h index 2ef081ee29..90c664edee 100644 --- a/deps/v8/src/heap-profiler.h +++ b/deps/v8/src/heap-profiler.h @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2009-2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -56,8 +56,12 @@ class HeapProfiler { static void TearDown(); #ifdef ENABLE_LOGGING_AND_PROFILING - static HeapSnapshot* TakeSnapshot(const char* name, int type); - static HeapSnapshot* TakeSnapshot(String* name, int type); + static HeapSnapshot* TakeSnapshot(const char* name, + int type, + v8::ActivityControl* control); + static HeapSnapshot* TakeSnapshot(String* name, + int type, + v8::ActivityControl* control); static int GetSnapshotsCount(); static HeapSnapshot* GetSnapshot(int index); static HeapSnapshot* FindSnapshot(unsigned uid); @@ -75,8 +79,12 @@ class HeapProfiler { private: HeapProfiler(); ~HeapProfiler(); - HeapSnapshot* TakeSnapshotImpl(const char* name, int type); - HeapSnapshot* TakeSnapshotImpl(String* name, int type); + HeapSnapshot* TakeSnapshotImpl(const char* name, + int type, + v8::ActivityControl* control); + HeapSnapshot* TakeSnapshotImpl(String* name, + int type, + v8::ActivityControl* control); HeapSnapshotsCollection* snapshots_; unsigned next_snapshot_uid_; diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 0497ad5f6a..ccf9b47a35 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -3757,14 +3757,21 @@ bool Heap::IdleNotification() { static const int kIdlesBeforeScavenge = 4; static const int kIdlesBeforeMarkSweep = 7; static const int kIdlesBeforeMarkCompact = 8; + static const int kMaxIdleCount = kIdlesBeforeMarkCompact + 1; + static const int kGCsBetweenCleanup = 4; static int number_idle_notifications = 0; static int last_gc_count = gc_count_; bool uncommit = true; bool finished = false; - if (last_gc_count == gc_count_) { - number_idle_notifications++; + // Reset the number of idle notifications received when a number of + // GCs have taken place. This allows another round of cleanup based + // on idle notifications if enough work has been carried out to + // provoke a number of garbage collections. + if (gc_count_ < last_gc_count + kGCsBetweenCleanup) { + number_idle_notifications = + Min(number_idle_notifications + 1, kMaxIdleCount); } else { number_idle_notifications = 0; last_gc_count = gc_count_; @@ -3779,7 +3786,6 @@ bool Heap::IdleNotification() { } new_space_.Shrink(); last_gc_count = gc_count_; - } else if (number_idle_notifications == kIdlesBeforeMarkSweep) { // Before doing the mark-sweep collections we clear the // compilation cache to avoid hanging on to source code and @@ -3794,7 +3800,6 @@ bool Heap::IdleNotification() { CollectAllGarbage(true); new_space_.Shrink(); last_gc_count = gc_count_; - number_idle_notifications = 0; finished = true; } else if (contexts_disposed_ > 0) { @@ -3813,6 +3818,11 @@ bool Heap::IdleNotification() { number_idle_notifications = 0; uncommit = false; } + } else if (number_idle_notifications > kIdlesBeforeMarkCompact) { + // If we have received more than kIdlesBeforeMarkCompact idle + // notifications we do not perform any cleanup because we don't + // expect to gain much by doing so. + finished = true; } // Make sure that we have no pending context disposals and diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index e4dcb4ad7d..fbcc70df53 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -1119,9 +1119,9 @@ class Heap : public AllStatic { static int contexts_disposed_; #if defined(V8_TARGET_ARCH_X64) - static const int kMaxObjectSizeInNewSpace = 512*KB; + static const int kMaxObjectSizeInNewSpace = 1024*KB; #else - static const int kMaxObjectSizeInNewSpace = 256*KB; + static const int kMaxObjectSizeInNewSpace = 512*KB; #endif static NewSpace new_space_; @@ -2054,8 +2054,9 @@ class TranscendentalCache { // Allow access to the caches_ array as an ExternalReference. friend class ExternalReference; - // Inline implementation of the caching. + // Inline implementation of the cache. friend class TranscendentalCacheStub; + friend class TranscendentalCacheSSE2Stub; static TranscendentalCache* caches_[kNumberOfCaches]; Element elements_[kCacheSize]; diff --git a/deps/v8/src/hydrogen-instructions.cc b/deps/v8/src/hydrogen-instructions.cc index 670dad85c5..a96ee406ff 100644 --- a/deps/v8/src/hydrogen-instructions.cc +++ b/deps/v8/src/hydrogen-instructions.cc @@ -64,69 +64,34 @@ const char* Representation::Mnemonic() const { } -static int32_t AddAssertNoOverflow(int32_t a, int32_t b) { - ASSERT(static_cast(a + b) == (static_cast(a) + - static_cast(b))); - return a + b; -} - - -static int32_t SubAssertNoOverflow(int32_t a, int32_t b) { - ASSERT(static_cast(a - b) == (static_cast(a) - - static_cast(b))); - return a - b; -} - - -static int32_t MulAssertNoOverflow(int32_t a, int32_t b) { - ASSERT(static_cast(a * b) == (static_cast(a) * - static_cast(b))); - return a * b; -} - - -static int32_t AddWithoutOverflow(int32_t a, int32_t b) { - if (b > 0) { - if (a <= kMaxInt - b) return AddAssertNoOverflow(a, b); +static int32_t ConvertAndSetOverflow(int64_t result, bool* overflow) { + if (result > kMaxInt) { + *overflow = true; return kMaxInt; - } else { - if (a >= kMinInt - b) return AddAssertNoOverflow(a, b); + } + if (result < kMinInt) { + *overflow = true; return kMinInt; } + return static_cast(result); } -static int32_t SubWithoutOverflow(int32_t a, int32_t b) { - if (b < 0) { - if (a <= kMaxInt + b) return SubAssertNoOverflow(a, b); - return kMaxInt; - } else { - if (a >= kMinInt + b) return SubAssertNoOverflow(a, b); - return kMinInt; - } +static int32_t AddWithoutOverflow(int32_t a, int32_t b, bool* overflow) { + int64_t result = static_cast(a) + static_cast(b); + return ConvertAndSetOverflow(result, overflow); } -static int32_t MulWithoutOverflow(int32_t a, int32_t b, bool* overflow) { - if (b == 0 || a == 0) return 0; - if (a == 1) return b; - if (b == 1) return a; +static int32_t SubWithoutOverflow(int32_t a, int32_t b, bool* overflow) { + int64_t result = static_cast(a) - static_cast(b); + return ConvertAndSetOverflow(result, overflow); +} - int sign = 1; - if ((a < 0 && b > 0) || (a > 0 && b < 0)) sign = -1; - if (a < 0) a = -a; - if (b < 0) b = -b; - if (kMaxInt / b > a && a != kMinInt && b != kMinInt) { - return MulAssertNoOverflow(a, b) * sign; - } - - *overflow = true; - if (sign == 1) { - return kMaxInt; - } else { - return kMinInt; - } +static int32_t MulWithoutOverflow(int32_t a, int32_t b, bool* overflow) { + int64_t result = static_cast(a) * static_cast(b); + return ConvertAndSetOverflow(result, overflow); } @@ -143,39 +108,32 @@ int32_t Range::Mask() const { } -void Range::Add(int32_t value) { +void Range::AddConstant(int32_t value) { if (value == 0) return; - lower_ = AddWithoutOverflow(lower_, value); - upper_ = AddWithoutOverflow(upper_, value); + bool may_overflow = false; // Overflow is ignored here. + lower_ = AddWithoutOverflow(lower_, value, &may_overflow); + upper_ = AddWithoutOverflow(upper_, value, &may_overflow); Verify(); } -// Returns whether the add may overflow. bool Range::AddAndCheckOverflow(Range* other) { - int old_lower = lower_; - int old_upper = upper_; - lower_ = AddWithoutOverflow(lower_, other->lower()); - upper_ = AddWithoutOverflow(upper_, other->upper()); - bool r = (old_lower + other->lower() != lower_ || - old_upper + other->upper() != upper_); + bool may_overflow = false; + lower_ = AddWithoutOverflow(lower_, other->lower(), &may_overflow); + upper_ = AddWithoutOverflow(upper_, other->upper(), &may_overflow); KeepOrder(); Verify(); - return r; + return may_overflow; } -// Returns whether the sub may overflow. bool Range::SubAndCheckOverflow(Range* other) { - int old_lower = lower_; - int old_upper = upper_; - lower_ = SubWithoutOverflow(lower_, other->lower()); - upper_ = SubWithoutOverflow(upper_, other->upper()); - bool r = (old_lower - other->lower() != lower_ || - old_upper - other->upper() != upper_); + bool may_overflow = false; + lower_ = SubWithoutOverflow(lower_, other->upper(), &may_overflow); + upper_ = SubWithoutOverflow(upper_, other->lower(), &may_overflow); KeepOrder(); Verify(); - return r; + return may_overflow; } @@ -193,7 +151,6 @@ void Range::Verify() const { } -// Returns whether the mul may overflow. bool Range::MulAndCheckOverflow(Range* other) { bool may_overflow = false; int v1 = MulWithoutOverflow(lower_, other->lower(), &may_overflow); diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index ff1ab1a36e..aafa7a8830 100644 --- a/deps/v8/src/hydrogen-instructions.h +++ b/deps/v8/src/hydrogen-instructions.h @@ -77,6 +77,7 @@ class LChunkBuilder; // HLoadKeyedFastElement // HLoadKeyedGeneric // HLoadNamedGeneric +// HPower // HStoreNamed // HStoreNamedField // HStoreNamedGeneric @@ -93,13 +94,13 @@ class LChunkBuilder; // HCallStub // HConstant // HControlInstruction +// HDeoptimize // HGoto // HUnaryControlInstruction // HBranch // HCompareMapAndBranch // HReturn // HThrow -// HDeoptimize // HEnterInlined // HFunctionLiteral // HGlobalObject @@ -139,6 +140,7 @@ class LChunkBuilder; // HHasCachedArrayIndex // HHasInstanceType // HIsNull +// HIsObject // HIsSmi // HValueOf // HUnknownOSRValue @@ -207,6 +209,7 @@ class LChunkBuilder; V(Goto) \ V(InstanceOf) \ V(IsNull) \ + V(IsObject) \ V(IsSmi) \ V(HasInstanceType) \ V(HasCachedArrayIndex) \ @@ -223,6 +226,7 @@ class LChunkBuilder; V(ObjectLiteral) \ V(OsrEntry) \ V(Parameter) \ + V(Power) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -330,6 +334,9 @@ class Range: public ZoneObject { set_can_be_minus_zero(false); } + // Adds a constant to the lower and upper bound of the range. + void AddConstant(int32_t value); + void StackUpon(Range* other) { Intersect(other); next_ = other; @@ -349,7 +356,8 @@ class Range: public ZoneObject { set_can_be_minus_zero(b); } - void Add(int32_t value); + // Compute a new result range and return true, if the operation + // can overflow. bool AddAndCheckOverflow(Range* other); bool SubAndCheckOverflow(Range* other); bool MulAndCheckOverflow(Range* other); @@ -1364,7 +1372,7 @@ class HBitNot: public HUnaryOperation { class HUnaryMathOperation: public HUnaryOperation { public: - HUnaryMathOperation(HValue* value, MathFunctionId op) + HUnaryMathOperation(HValue* value, BuiltinFunctionId op) : HUnaryOperation(value), op_(op) { switch (op) { case kMathFloor: @@ -1377,8 +1385,12 @@ class HUnaryMathOperation: public HUnaryOperation { SetFlag(kFlexibleRepresentation); break; case kMathSqrt: - default: + case kMathPowHalf: + case kMathLog: set_representation(Representation::Double()); + break; + default: + UNREACHABLE(); } SetFlag(kUseGVN); } @@ -1395,6 +1407,8 @@ class HUnaryMathOperation: public HUnaryOperation { case kMathRound: case kMathCeil: case kMathSqrt: + case kMathPowHalf: + case kMathLog: return Representation::Double(); break; case kMathAbs: @@ -1415,13 +1429,19 @@ class HUnaryMathOperation: public HUnaryOperation { return this; } - MathFunctionId op() const { return op_; } + BuiltinFunctionId op() const { return op_; } const char* OpName() const; DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary_math_operation") + protected: + virtual bool DataEquals(HValue* other) const { + HUnaryMathOperation* b = HUnaryMathOperation::cast(other); + return op_ == b->op(); + } + private: - MathFunctionId op_; + BuiltinFunctionId op_; }; @@ -2087,11 +2107,25 @@ class HIsNull: public HUnaryPredicate { DECLARE_CONCRETE_INSTRUCTION(IsNull, "is_null") + protected: + virtual bool DataEquals(HValue* other) const { + HIsNull* b = HIsNull::cast(other); + return is_strict_ == b->is_strict(); + } + private: bool is_strict_; }; +class HIsObject: public HUnaryPredicate { + public: + explicit HIsObject(HValue* value) : HUnaryPredicate(value) { } + + DECLARE_CONCRETE_INSTRUCTION(IsObject, "is_object") +}; + + class HIsSmi: public HUnaryPredicate { public: explicit HIsSmi(HValue* value) : HUnaryPredicate(value) { } @@ -2116,6 +2150,12 @@ class HHasInstanceType: public HUnaryPredicate { DECLARE_CONCRETE_INSTRUCTION(HasInstanceType, "has_instance_type") + protected: + virtual bool DataEquals(HValue* other) const { + HHasInstanceType* b = HHasInstanceType::cast(other); + return (from_ == b->from()) && (to_ == b->to()); + } + private: InstanceType from_; InstanceType to_; // Inclusive range, not all combinations work. @@ -2141,6 +2181,12 @@ class HClassOfTest: public HUnaryPredicate { Handle class_name() const { return class_name_; } + protected: + virtual bool DataEquals(HValue* other) const { + HClassOfTest* b = HClassOfTest::cast(other); + return class_name_.is_identical_to(b->class_name_); + } + private: Handle class_name_; }; @@ -2184,6 +2230,22 @@ class HInstanceOf: public HBinaryOperation { }; +class HPower: public HBinaryOperation { + public: + HPower(HValue* left, HValue* right) + : HBinaryOperation(left, right) { + set_representation(Representation::Double()); + SetFlag(kUseGVN); + } + + virtual Representation RequiredInputRepresentation(int index) const { + return (index == 1) ? Representation::None() : Representation::Double(); + } + + DECLARE_CONCRETE_INSTRUCTION(Power, "power") +}; + + class HAdd: public HArithmeticBinaryOperation { public: HAdd(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) { diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index 0e8c4760dd..32108dc1cd 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -64,9 +64,7 @@ HBasicBlock::HBasicBlock(HGraph* graph) first_instruction_index_(-1), last_instruction_index_(-1), deleted_phis_(4), - is_inline_return_target_(false), - inverted_(false), - deopt_predecessor_(NULL) { + is_inline_return_target_(false) { } @@ -1031,12 +1029,12 @@ void HRangeAnalysis::InferControlFlowRange(Token::Value op, } else if (op == Token::LT || op == Token::LTE) { new_range = range->CopyClearLower(); if (op == Token::LT) { - new_range->Add(-1); + new_range->AddConstant(-1); } } else if (op == Token::GT || op == Token::GTE) { new_range = range->CopyClearUpper(); if (op == Token::GT) { - new_range->Add(1); + new_range->AddConstant(1); } } @@ -1292,15 +1290,15 @@ void HStackCheckEliminator::Process() { for (int i = 0; i < graph_->blocks()->length(); i++) { HBasicBlock* block = graph_->blocks()->at(i); if (block->IsLoopHeader()) { - HBasicBlock* backedge = block->loop_information()->GetLastBackEdge(); - HBasicBlock* dominator = backedge; - bool backedge_dominated_by_call = false; - while (dominator != block && !backedge_dominated_by_call) { + HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge(); + HBasicBlock* dominator = back_edge; + bool back_edge_dominated_by_call = false; + while (dominator != block && !back_edge_dominated_by_call) { HInstruction* instr = dominator->first(); - while (instr != NULL && !backedge_dominated_by_call) { + while (instr != NULL && !back_edge_dominated_by_call) { if (instr->IsCall()) { - RemoveStackCheck(backedge); - backedge_dominated_by_call = true; + RemoveStackCheck(back_edge); + back_edge_dominated_by_call = true; } instr = instr->next(); } @@ -1983,6 +1981,9 @@ void HGraph::InsertRepresentationChanges() { AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) : owner_(owner), kind_(kind), outer_(owner->ast_context()) { owner->set_ast_context(this); // Push. +#ifdef DEBUG + original_count_ = owner->environment()->total_count(); +#endif } @@ -1991,6 +1992,92 @@ AstContext::~AstContext() { } +EffectContext::~EffectContext() { + ASSERT(owner()->HasStackOverflow() || + !owner()->subgraph()->HasExit() || + owner()->environment()->total_count() == original_count_); +} + + +ValueContext::~ValueContext() { + ASSERT(owner()->HasStackOverflow() || + !owner()->subgraph()->HasExit() || + owner()->environment()->total_count() == original_count_ + 1); +} + + +void EffectContext::ReturnValue(HValue* value) { + // The value is simply ignored. +} + + +void ValueContext::ReturnValue(HValue* value) { + // The value is tracked in the bailout environment, and communicated + // through the environment as the result of the expression. + owner()->Push(value); +} + + +void TestContext::ReturnValue(HValue* value) { + BuildBranch(value); +} + + +void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) { + owner()->AddInstruction(instr); + if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); +} + + +void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { + owner()->AddInstruction(instr); + owner()->Push(instr); + if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); +} + + +void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { + HGraphBuilder* builder = owner(); + builder->AddInstruction(instr); + // We expect a simulate after every expression with side effects, though + // this one isn't actually needed (and wouldn't work if it were targeted). + if (instr->HasSideEffects()) { + builder->Push(instr); + builder->AddSimulate(ast_id); + builder->Pop(); + } + BuildBranch(instr); +} + + +void TestContext::BuildBranch(HValue* value) { + // We expect the graph to be in edge-split form: there is no edge that + // connects a branch node to a join node. We conservatively ensure that + // property by always adding an empty block on the outgoing edges of this + // branch. + HGraphBuilder* builder = owner(); + HBasicBlock* empty_true = builder->graph()->CreateBasicBlock(); + HBasicBlock* empty_false = builder->graph()->CreateBasicBlock(); + HBranch* branch = new HBranch(empty_true, empty_false, value); + builder->CurrentBlock()->Finish(branch); + + HValue* const no_return_value = NULL; + HBasicBlock* true_target = if_true(); + if (true_target->IsInlineReturnTarget()) { + empty_true->AddLeaveInlined(no_return_value, true_target); + } else { + empty_true->Goto(true_target); + } + + HBasicBlock* false_target = if_false(); + if (false_target->IsInlineReturnTarget()) { + empty_false->AddLeaveInlined(no_return_value, false_target); + } else { + empty_false->Goto(false_target); + } + builder->subgraph()->set_exit_block(NULL); +} + // HGraphBuilder infrastructure for bailing out and checking bailouts. #define BAILOUT(reason) \ @@ -2020,6 +2107,13 @@ AstContext::~AstContext() { } while (false) +#define VISIT_FOR_CONTROL(expr, true_block, false_block) \ + do { \ + VisitForControl(expr, true_block, false_block); \ + if (HasStackOverflow()) return; \ + } while (false) + + // 'thing' could be an expression, statement, or list of statements. #define ADD_TO_SUBGRAPH(graph, thing) \ do { \ @@ -2061,55 +2155,22 @@ void HGraphBuilder::Bailout(const char* reason) { void HGraphBuilder::VisitForEffect(Expression* expr) { -#ifdef DEBUG - int original_count = environment()->total_count(); -#endif - BinaryOperation* binary_op = expr->AsBinaryOperation(); - - // We use special casing for expression types not handled properly by our - // usual trick of pretending they're in a value context and cleaning up - // later. - if (binary_op != NULL && binary_op->op() == Token::COMMA) { - VISIT_FOR_EFFECT(binary_op->left()); - VISIT_FOR_EFFECT(binary_op->right()); - } else { - { EffectContext for_effect(this); - Visit(expr); - } - if (HasStackOverflow() || !subgraph()->HasExit()) return; - // Discard return value. - Pop(); - // TODO(kasperl): Try to improve the way we compute the last added - // instruction. The NULL check makes me uncomfortable. - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - // We need to ensure we emit a simulate after inlined functions in an - // effect context, to avoid having a bailout target the fictional - // environment with the return value on top. - if ((last != NULL && last->HasSideEffects()) || - subgraph()->exit_block()->IsInlineReturnTarget()) { - AddSimulate(expr->id()); - } - } - - ASSERT(environment()->total_count() == original_count); + EffectContext for_effect(this); + Visit(expr); } void HGraphBuilder::VisitForValue(Expression* expr) { -#ifdef DEBUG - int original_height = environment()->values()->length(); -#endif - { ValueContext for_value(this); - Visit(expr); - } - if (HasStackOverflow() || !subgraph()->HasExit()) return; - // TODO(kasperl): Try to improve the way we compute the last added - // instruction. The NULL check makes me uncomfortable. - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - if (last != NULL && last->HasSideEffects()) { - AddSimulate(expr->id()); - } - ASSERT(environment()->values()->length() == original_height + 1); + ValueContext for_value(this); + Visit(expr); +} + + +void HGraphBuilder::VisitForControl(Expression* expr, + HBasicBlock* true_block, + HBasicBlock* false_block) { + TestContext for_test(this, true_block, false_block); + Visit(expr); } @@ -2202,144 +2263,6 @@ void HGraphBuilder::AddToSubgraph(HSubgraph* graph, Expression* expr) { } -void HGraphBuilder::VisitCondition(Expression* expr, - HBasicBlock* true_block, - HBasicBlock* false_block, - bool invert_true, - bool invert_false) { - VisitForControl(expr, true_block, false_block, invert_true, invert_false); - CHECK_BAILOUT; -#ifdef DEBUG - HValue* value = true_block->predecessors()->at(0)->last_environment()->Top(); - true_block->set_cond(HConstant::cast(value)->handle()); - - value = false_block->predecessors()->at(0)->last_environment()->Top(); - false_block->set_cond(HConstant::cast(value)->handle()); -#endif - - true_block->SetJoinId(expr->id()); - false_block->SetJoinId(expr->id()); - true_block->last_environment()->Pop(); - false_block->last_environment()->Pop(); -} - - -void HGraphBuilder::AddConditionToSubgraph(HSubgraph* subgraph, - Expression* expr, - HSubgraph* true_graph, - HSubgraph* false_graph) { - SubgraphScope scope(this, subgraph); - VisitCondition(expr, - true_graph->entry_block(), - false_graph->entry_block(), - false, - false); -} - - -void HGraphBuilder::VisitForControl(Expression* expr, - HBasicBlock* true_block, - HBasicBlock* false_block, - bool invert_true, - bool invert_false) { - TestContext for_test(this, true_block, false_block, - invert_true, invert_false); - BinaryOperation* binary_op = expr->AsBinaryOperation(); - UnaryOperation* unary_op = expr->AsUnaryOperation(); - - if (unary_op != NULL && unary_op->op() == Token::NOT) { - VisitForControl(unary_op->expression(), - false_block, - true_block, - !invert_false, - !invert_true); - } else if (binary_op != NULL && binary_op->op() == Token::AND) { - // Translate left subexpression. - HBasicBlock* eval_right = graph()->CreateBasicBlock(); - VisitForControl(binary_op->left(), - eval_right, - false_block, - false, - invert_false); - if (HasStackOverflow()) return; - eval_right->SetJoinId(binary_op->left()->id()); - - // Translate right subexpression. - eval_right->last_environment()->Pop(); - subgraph()->set_exit_block(eval_right); - VisitForControl(binary_op->right(), - true_block, - false_block, - invert_true, - invert_false); - } else if (binary_op != NULL && binary_op->op() == Token::OR) { - // Translate left subexpression. - HBasicBlock* eval_right = graph()->CreateBasicBlock(); - VisitForControl(binary_op->left(), - true_block, - eval_right, - invert_true, - false); - if (HasStackOverflow()) return; - eval_right->SetJoinId(binary_op->left()->id()); - - // Translate right subexpression - eval_right->last_environment()->Pop(); - subgraph()->set_exit_block(eval_right); - VisitForControl(binary_op->right(), - true_block, - false_block, - invert_true, - invert_false); - } else { -#ifdef DEBUG - int original_length = environment()->values()->length(); -#endif - // TODO(kmillikin): Refactor to avoid. This code is duplicated from - // VisitForValue, except without pushing a value context on the - // expression context stack. - Visit(expr); - if (HasStackOverflow() || !subgraph()->HasExit()) return; - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - if (last != NULL && last->HasSideEffects()) { - AddSimulate(expr->id()); - } - ASSERT(environment()->values()->length() == original_length + 1); - HValue* value = Pop(); - HBasicBlock* materialize_true = graph()->CreateBasicBlock(); - HBasicBlock* materialize_false = graph()->CreateBasicBlock(); - CurrentBlock()->Finish(new HBranch(materialize_true, - materialize_false, - value)); - HValue* true_value = invert_true - ? graph()->GetConstantFalse() - : graph()->GetConstantTrue(); - materialize_true->set_inverted(invert_true); - true_block->set_deopt_predecessor(materialize_true); - - if (true_block->IsInlineReturnTarget()) { - materialize_true->AddLeaveInlined(true_value, true_block); - } else { - materialize_true->last_environment()->Push(true_value); - materialize_true->Goto(true_block); - } - HValue* false_value = invert_false - ? graph()->GetConstantTrue() - : graph()->GetConstantFalse(); - materialize_false->set_inverted(invert_false); - false_block->set_deopt_predecessor(materialize_false); - - if (false_block->IsInlineReturnTarget()) { - materialize_false->AddLeaveInlined(false_value, false_block); - } else { - materialize_false->last_environment()->Push(false_value); - materialize_false->Goto(false_block); - } - subgraph()->set_exit_block(NULL); - } -} - - void HGraphBuilder::AddToSubgraph(HSubgraph* graph, ZoneList* stmts) { SubgraphScope scope(this, graph); @@ -2372,12 +2295,6 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) { } -void HGraphBuilder::PushAndAdd(HInstruction* instr, int position) { - instr->set_position(position); - PushAndAdd(instr); -} - - void HGraphBuilder::PushArgumentsForStubCall(int argument_count) { const int kMaxStubArguments = 4; ASSERT_GE(kMaxStubArguments, argument_count); @@ -2392,7 +2309,7 @@ void HGraphBuilder::PushArgumentsForStubCall(int argument_count) { } -void HGraphBuilder::ProcessCall(HCall* call, int source_position) { +void HGraphBuilder::ProcessCall(HCall* call) { for (int i = call->argument_count() - 1; i >= 0; --i) { HValue* value = Pop(); HPushArgument* push = new HPushArgument(value); @@ -2402,8 +2319,6 @@ void HGraphBuilder::ProcessCall(HCall* call, int source_position) { for (int i = 0; i < call->argument_count(); ++i) { AddInstruction(call->PushArgumentAt(i)); } - - PushAndAdd(call, source_position); } @@ -2527,19 +2442,24 @@ void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) { void HGraphBuilder::VisitIfStatement(IfStatement* stmt) { if (stmt->condition()->ToBooleanIsTrue()) { + AddSimulate(stmt->ThenId()); Visit(stmt->then_statement()); } else if (stmt->condition()->ToBooleanIsFalse()) { + AddSimulate(stmt->ElseId()); Visit(stmt->else_statement()); } else { HSubgraph* then_graph = CreateEmptySubgraph(); HSubgraph* else_graph = CreateEmptySubgraph(); - VisitCondition(stmt->condition(), - then_graph->entry_block(), - else_graph->entry_block(), - false, false); - if (HasStackOverflow()) return; + VISIT_FOR_CONTROL(stmt->condition(), + then_graph->entry_block(), + else_graph->entry_block()); + + then_graph->entry_block()->SetJoinId(stmt->ThenId()); ADD_TO_SUBGRAPH(then_graph, stmt->then_statement()); + + else_graph->entry_block()->SetJoinId(stmt->ElseId()); ADD_TO_SUBGRAPH(else_graph, stmt->else_statement()); + current_subgraph_->AppendJoin(then_graph, else_graph, stmt); } } @@ -2569,9 +2489,7 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { TestContext* test = TestContext::cast(context); VisitForControl(stmt->expression(), test->if_true(), - test->if_false(), - false, - false); + test->if_false()); } else { HValue* return_value = NULL; if (context->IsEffect()) { @@ -2617,6 +2535,8 @@ HCompare* HGraphBuilder::BuildSwitchCompare(HSubgraph* subgraph, void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { VISIT_FOR_VALUE(stmt->tag()); + // TODO(3168478): simulate added for tag should be enough. + AddSimulate(stmt->EntryId()); HValue* switch_value = Pop(); ZoneList* clauses = stmt->cases(); @@ -2624,11 +2544,17 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { if (num_clauses == 0) return; if (num_clauses > 128) BAILOUT("SwitchStatement: too many clauses"); + int num_smi_clauses = num_clauses; for (int i = 0; i < num_clauses; i++) { CaseClause* clause = clauses->at(i); if (clause->is_default()) continue; clause->RecordTypeFeedback(oracle()); - if (!clause->IsSmiCompare()) BAILOUT("SwitchStatement: non-smi compare"); + if (!clause->IsSmiCompare()) { + if (i == 0) BAILOUT("SwitchStatement: no smi compares"); + // We will deoptimize if the first non-smi compare is reached. + num_smi_clauses = i; + break; + } if (!clause->label()->IsSmiLiteral()) { BAILOUT("SwitchStatement: non-literal switch label"); } @@ -2639,17 +2565,18 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // Build a series of empty subgraphs for the comparisons. // The default clause does not have a comparison subgraph. - ZoneList compare_graphs(num_clauses); - for (int i = 0; i < num_clauses; i++) { - HSubgraph* subgraph = !clauses->at(i)->is_default() - ? CreateEmptySubgraph() - : NULL; - compare_graphs.Add(subgraph); + ZoneList compare_graphs(num_smi_clauses); + for (int i = 0; i < num_smi_clauses; i++) { + if (clauses->at(i)->is_default()) { + compare_graphs.Add(NULL); + } else { + compare_graphs.Add(CreateEmptySubgraph()); + } } HSubgraph* prev_graph = current_subgraph_; HCompare* prev_compare_inst = NULL; - for (int i = 0; i < num_clauses; i++) { + for (int i = 0; i < num_smi_clauses; i++) { CaseClause* clause = clauses->at(i); if (clause->is_default()) continue; @@ -2666,6 +2593,7 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { } // Build instructions for current subgraph. + ASSERT(clause->IsSmiCompare()); prev_compare_inst = BuildSwitchCompare(subgraph, switch_value, clause); if (HasStackOverflow()) return; @@ -2685,33 +2613,52 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { prev_compare_inst)); } + // If we have a non-smi compare clause, we deoptimize after trying + // all the previous compares. + if (num_smi_clauses < num_clauses) { + last_false_block->Finish(new HDeoptimize); + } + // Build statement blocks, connect them to their comparison block and // to the previous statement block, if there is a fall-through. HSubgraph* previous_subgraph = NULL; for (int i = 0; i < num_clauses; i++) { CaseClause* clause = clauses->at(i); - HSubgraph* subgraph = CreateEmptySubgraph(); - - if (clause->is_default()) { - // Default clause: Connect it to the last false block. - last_false_block->Finish(new HGoto(subgraph->entry_block())); - } else { - // Connect with the corresponding comparison. - HBasicBlock* empty = - compare_graphs.at(i)->exit_block()->end()->FirstSuccessor(); - empty->Finish(new HGoto(subgraph->entry_block())); + // Subgraph for the statements of the clause is only created when + // it's reachable either from the corresponding compare or as a + // fall-through from previous statements. + HSubgraph* subgraph = NULL; + + if (i < num_smi_clauses) { + if (clause->is_default()) { + if (!last_false_block->IsFinished()) { + // Default clause: Connect it to the last false block. + subgraph = CreateEmptySubgraph(); + last_false_block->Finish(new HGoto(subgraph->entry_block())); + } + } else { + ASSERT(clause->IsSmiCompare()); + // Connect with the corresponding comparison. + subgraph = CreateEmptySubgraph(); + HBasicBlock* empty = + compare_graphs.at(i)->exit_block()->end()->FirstSuccessor(); + empty->Finish(new HGoto(subgraph->entry_block())); + } } // Check for fall-through from previous statement block. if (previous_subgraph != NULL && previous_subgraph->HasExit()) { + if (subgraph == NULL) subgraph = CreateEmptySubgraph(); previous_subgraph->exit_block()-> Finish(new HGoto(subgraph->entry_block())); } - ADD_TO_SUBGRAPH(subgraph, clause->statements()); - HBasicBlock* break_block = subgraph->BundleBreak(stmt); - if (break_block != NULL) { - break_block->Finish(new HGoto(single_exit_block)); + if (subgraph != NULL) { + ADD_TO_SUBGRAPH(subgraph, clause->statements()); + HBasicBlock* break_block = subgraph->BundleBreak(stmt); + if (break_block != NULL) { + break_block->Finish(new HGoto(single_exit_block)); + } } previous_subgraph = subgraph; @@ -2719,7 +2666,7 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // If the last statement block has a fall-through, connect it to the // single exit block. - if (previous_subgraph->HasExit()) { + if (previous_subgraph != NULL && previous_subgraph->HasExit()) { previous_subgraph->exit_block()->Finish(new HGoto(single_exit_block)); } @@ -2785,8 +2732,14 @@ void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { } else { HSubgraph* go_back = CreateEmptySubgraph(); HSubgraph* exit = CreateEmptySubgraph(); - AddConditionToSubgraph(body_graph, stmt->cond(), go_back, exit); - if (HasStackOverflow()) return; + { + SubgraphScope scope(this, body_graph); + VISIT_FOR_CONTROL(stmt->cond(), + go_back->entry_block(), + exit->entry_block()); + go_back->entry_block()->SetJoinId(stmt->BackEdgeId()); + exit->entry_block()->SetJoinId(stmt->ExitId()); + } current_subgraph_->AppendDoWhile(body_graph, stmt, go_back, exit); } } @@ -2813,8 +2766,14 @@ void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { cond_graph = CreateLoopHeaderSubgraph(environment()); body_graph = CreateEmptySubgraph(); exit_graph = CreateEmptySubgraph(); - AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph); - if (HasStackOverflow()) return; + { + SubgraphScope scope(this, cond_graph); + VISIT_FOR_CONTROL(stmt->cond(), + body_graph->entry_block(), + exit_graph->entry_block()); + body_graph->entry_block()->SetJoinId(stmt->BodyId()); + exit_graph->entry_block()->SetJoinId(stmt->ExitId()); + } ADD_TO_SUBGRAPH(body_graph, stmt->body()); } @@ -2864,13 +2823,18 @@ void HGraphBuilder::VisitForStatement(ForStatement* stmt) { cond_graph = CreateLoopHeaderSubgraph(environment()); body_graph = CreateEmptySubgraph(); exit_graph = CreateEmptySubgraph(); - AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph); - if (HasStackOverflow()) return; - ADD_TO_SUBGRAPH(body_graph, stmt->body()); + { + SubgraphScope scope(this, cond_graph); + VISIT_FOR_CONTROL(stmt->cond(), + body_graph->entry_block(), + exit_graph->entry_block()); + body_graph->entry_block()->SetJoinId(stmt->BodyId()); + exit_graph->entry_block()->SetJoinId(stmt->ExitId()); + } } else { body_graph = CreateLoopHeaderSubgraph(environment()); - ADD_TO_SUBGRAPH(body_graph, stmt->body()); } + ADD_TO_SUBGRAPH(body_graph, stmt->body()); HSubgraph* next_graph = NULL; body_graph->ResolveContinue(stmt); @@ -2914,7 +2878,9 @@ void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { Handle shared_info = Compiler::BuildFunctionInfo(expr, graph_->info()->script()); CHECK_BAILOUT; - PushAndAdd(new HFunctionLiteral(shared_info, expr->pretenure())); + HFunctionLiteral* instr = + new HFunctionLiteral(shared_info, expr->pretenure()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -2927,28 +2893,32 @@ void HGraphBuilder::VisitSharedFunctionInfoLiteral( void HGraphBuilder::VisitConditional(Conditional* expr) { HSubgraph* then_graph = CreateEmptySubgraph(); HSubgraph* else_graph = CreateEmptySubgraph(); - VisitCondition(expr->condition(), - then_graph->entry_block(), - else_graph->entry_block(), - false, false); - if (HasStackOverflow()) return; + VISIT_FOR_CONTROL(expr->condition(), + then_graph->entry_block(), + else_graph->entry_block()); + + then_graph->entry_block()->SetJoinId(expr->ThenId()); ADD_TO_SUBGRAPH(then_graph, expr->then_expression()); + + else_graph->entry_block()->SetJoinId(expr->ElseId()); ADD_TO_SUBGRAPH(else_graph, expr->else_expression()); + current_subgraph_->AppendJoin(then_graph, else_graph, expr); + ast_context()->ReturnValue(Pop()); } -void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr, +void HGraphBuilder::LookupGlobalPropertyCell(Variable* var, LookupResult* lookup, bool is_store) { - if (expr->is_this()) { + if (var->is_this()) { BAILOUT("global this reference"); } if (!graph()->info()->has_global_object()) { BAILOUT("no global object to optimize VariableProxy"); } Handle global(graph()->info()->global_object()); - global->Lookup(*expr->name(), lookup); + global->Lookup(*var->name(), lookup); if (!lookup->IsProperty()) { BAILOUT("global variable cell not yet introduced"); } @@ -2961,23 +2931,6 @@ void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr, } -void HGraphBuilder::HandleGlobalVariableLoad(VariableProxy* expr) { - LookupResult lookup; - LookupGlobalPropertyCell(expr, &lookup, false); - CHECK_BAILOUT; - - Handle global(graph()->info()->global_object()); - // TODO(3039103): Handle global property load through an IC call when access - // checks are enabled. - if (global->IsAccessCheckNeeded()) { - BAILOUT("global object requires access check"); - } - Handle cell(global->GetPropertyCell(&lookup)); - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); - PushAndAdd(new HLoadGlobal(cell, check_hole)); -} - - void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { Variable* variable = expr->AsVariable(); if (variable == NULL) { @@ -2986,9 +2939,22 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) { BAILOUT("unsupported context for arguments object"); } - Push(environment()->Lookup(variable)); + ast_context()->ReturnValue(environment()->Lookup(variable)); } else if (variable->is_global()) { - HandleGlobalVariableLoad(expr); + LookupResult lookup; + LookupGlobalPropertyCell(variable, &lookup, false); + CHECK_BAILOUT; + + Handle global(graph()->info()->global_object()); + // TODO(3039103): Handle global property load through an IC call when access + // checks are enabled. + if (global->IsAccessCheckNeeded()) { + BAILOUT("global object requires access check"); + } + Handle cell(global->GetPropertyCell(&lookup)); + bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); + HLoadGlobal* instr = new HLoadGlobal(cell, check_hole); + ast_context()->ReturnInstruction(instr, expr->id()); } else { BAILOUT("reference to non-stack-allocated/non-global variable"); } @@ -2996,14 +2962,16 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { void HGraphBuilder::VisitLiteral(Literal* expr) { - PushAndAdd(new HConstant(expr->handle(), Representation::Tagged())); + HConstant* instr = new HConstant(expr->handle(), Representation::Tagged()); + ast_context()->ReturnInstruction(instr, expr->id()); } void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { - PushAndAdd(new HRegExpLiteral(expr->pattern(), - expr->flags(), - expr->literal_index())); + HRegExpLiteral* instr = new HRegExpLiteral(expr->pattern(), + expr->flags(), + expr->literal_index()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -3012,6 +2980,8 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { expr->fast_elements(), expr->literal_index(), expr->depth())); + // The object is expected in the bailout environment during computation + // of the property values and is the value of the entire expression. PushAndAdd(literal); expr->CalculateEmitStore(); @@ -3048,6 +3018,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { default: UNREACHABLE(); } } + ast_context()->ReturnValue(Pop()); } @@ -3059,8 +3030,11 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { length, expr->literal_index(), expr->depth()); + // The array is expected in the bailout environment during computation + // of the property values and is the value of the entire expression. PushAndAdd(literal); - HValue* elements = AddInstruction(new HLoadElements(literal)); + + HLoadElements* elements = NULL; for (int i = 0; i < length; i++) { Expression* subexpr = subexprs->at(i); @@ -3071,11 +3045,19 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { VISIT_FOR_VALUE(subexpr); HValue* value = Pop(); if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal"); + + // Load the elements array before the first store. + if (elements == NULL) { + elements = new HLoadElements(literal); + AddInstruction(elements); + } + HValue* key = AddInstruction(new HConstant(Handle(Smi::FromInt(i)), Representation::Integer32())); AddInstruction(new HStoreKeyedFastElement(elements, key, value)); AddSimulate(expr->GetIdForElement(i)); } + ast_context()->ReturnValue(Pop()); } @@ -3257,27 +3239,29 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, Push(value); instr->set_position(expr->position()); AddInstruction(instr); - return; - } - - // Build subgraph for generic store through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HInstruction* instr = new HStoreNamedGeneric(object, name, value); - Push(value); - instr->set_position(expr->position()); - AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->id()); + } else { + // Build subgraph for generic store through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HInstruction* instr = new HStoreNamedGeneric(object, name, value); + Push(value); + instr->set_position(expr->position()); + AddInstruction(instr); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); + + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, object, expr->AssignmentId()); + subgraph()->set_exit_block(new_exit_block); } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop()); } @@ -3333,14 +3317,20 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Push(value); instr->set_position(expr->position()); AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + ast_context()->ReturnValue(Pop()); } -void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy, +// Because not every expression has a position and there is not common +// superclass of Assignment and CountOperation, we cannot just pass the +// owning expression instead of position and ast_id separately. +void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, HValue* value, - int position) { + int position, + int ast_id) { LookupResult lookup; - LookupGlobalPropertyCell(proxy, &lookup, true); + LookupGlobalPropertyCell(var, &lookup, true); CHECK_BAILOUT; Handle global(graph()->info()->global_object()); @@ -3348,6 +3338,7 @@ void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy, HInstruction* instr = new HStoreGlobal(value, cell); instr->set_position(position); AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(ast_id); } @@ -3371,10 +3362,15 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { VISIT_FOR_VALUE(operation); if (var->is_global()) { - HandleGlobalVariableAssignment(proxy, Top(), expr->position()); + HandleGlobalVariableAssignment(var, + Top(), + expr->position(), + expr->AssignmentId()); } else { Bind(var, Top()); } + ast_context()->ReturnValue(Pop()); + } else if (prop != NULL) { prop->RecordTypeFeedback(oracle()); @@ -3392,9 +3388,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) { - AddSimulate(expr->compound_bailout_id()); - } + if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId()); VISIT_FOR_VALUE(expr->value()); HValue* right = Pop(); @@ -3406,10 +3400,11 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* store = BuildStoreNamed(obj, instr, prop); AddInstruction(store); - - // Drop the simulated receiver and value and put back the value. + // Drop the simulated receiver and value. Return the value. Drop(2); Push(instr); + if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + ast_context()->ReturnValue(Pop()); } else { // Keyed property. @@ -3425,9 +3420,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { ? BuildLoadKeyedFastElement(obj, key, prop) : BuildLoadKeyedGeneric(obj, key); PushAndAdd(load); - if (load->HasSideEffects()) { - AddSimulate(expr->compound_bailout_id()); - } + if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId()); VISIT_FOR_VALUE(expr->value()); HValue* right = Pop(); @@ -3441,11 +3434,13 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { ? BuildStoreKeyedFastElement(obj, key, instr, prop) : BuildStoreKeyedGeneric(obj, key, instr); AddInstruction(store); - - // Drop the simulated receiver, key and value and put back the value. + // Drop the simulated receiver, key, and value. Return the value. Drop(3); Push(instr); + if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + ast_context()->ReturnValue(Pop()); } + } else { BAILOUT("invalid lhs in compound assignment"); } @@ -3465,9 +3460,14 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { if (var != NULL) { if (proxy->IsArguments()) BAILOUT("assignment to arguments"); + + // Handle the assignment. if (var->is_global()) { VISIT_FOR_VALUE(expr->value()); - HandleGlobalVariableAssignment(proxy, Top(), expr->position()); + HandleGlobalVariableAssignment(var, + Top(), + expr->position(), + expr->AssignmentId()); } else { // We allow reference to the arguments object only in assignemtns // to local variables to make sure that the arguments object does @@ -3480,9 +3480,11 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { } else { VISIT_FOR_VALUE(expr->value()); } - Bind(proxy->var(), Top()); } + // Return the value. + ast_context()->ReturnValue(Pop()); + } else if (prop != NULL) { HandlePropertyAssignment(expr); } else { @@ -3492,6 +3494,10 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { void HGraphBuilder::VisitThrow(Throw* expr) { + // We don't optimize functions with invalid left-hand sides in + // assignments, count operations, or for-in. Consequently throw can + // currently only occur in an effect context. + ASSERT(ast_context()->IsEffect()); VISIT_FOR_VALUE(expr->exception()); HValue* value = environment()->Pop(); @@ -3525,7 +3531,8 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, SubgraphScope scope(this, subgraph); HInstruction* instr = BuildLoadNamedField(object, expr, map, &lookup, false); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + PushAndAdd(instr); subgraphs.Add(subgraph); } else { needs_generic = true; @@ -3536,26 +3543,30 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, // generic load. if (maps.length() == 0) { HInstruction* instr = BuildLoadNamedGeneric(object, expr); - PushAndAdd(instr, expr->position()); - return; - } - - // Build subgraph for generic load through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HInstruction* instr = BuildLoadNamedGeneric(object, expr); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + PushAndAdd(instr); + if (instr->HasSideEffects()) AddSimulate(expr->id()); + } else { + // Build subgraph for generic load through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HInstruction* instr = BuildLoadNamedGeneric(object, expr); + instr->set_position(expr->position()); + PushAndAdd(instr); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); + + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); + subgraph()->set_exit_block(new_exit_block); } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop()); } @@ -3603,6 +3614,11 @@ HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj, map, &lookup, true); + } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) { + AddInstruction(new HCheckNonSmi(obj)); + AddInstruction(new HCheckMap(obj, map)); + Handle function(lookup.GetConstantFunctionFromMap(*map)); + return new HConstant(function, Representation::Tagged()); } else { return BuildLoadNamedGeneric(obj, expr); } @@ -3668,11 +3684,12 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) { return false; } + HInstruction* result = NULL; if (expr->key()->IsPropertyName()) { Handle name = expr->key()->AsLiteral()->AsPropertyName(); if (!name->IsEqualTo(CStrVector("length"))) return false; HInstruction* elements = AddInstruction(new HArgumentsElements); - PushAndAdd(new HArgumentsLength(elements)); + result = new HArgumentsLength(elements); } else { VisitForValue(expr->key()); if (HasStackOverflow()) return false; @@ -3680,8 +3697,9 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) { HInstruction* elements = AddInstruction(new HArgumentsElements); HInstruction* length = AddInstruction(new HArgumentsLength(elements)); AddInstruction(new HBoundsCheck(key, length)); - PushAndAdd(new HAccessArgumentsAt(elements, length, key)); + result = new HAccessArgumentsAt(elements, length, key); } + ast_context()->ReturnInstruction(result, expr->id()); return true; } @@ -3728,7 +3746,8 @@ void HGraphBuilder::VisitProperty(Property* expr) { ? BuildLoadKeyedFastElement(obj, key, expr) : BuildLoadKeyedGeneric(obj, key); } - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -3763,9 +3782,9 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, // Build subgraphs for each of the specific maps. // - // TODO(ager): We should recognize when the prototype chains for - // different maps are identical. In that case we can avoid - // repeatedly generating the same prototype map checks. + // TODO(ager): We should recognize when the prototype chains for different + // maps are identical. In that case we can avoid repeatedly generating the + // same prototype map checks. for (int i = 0; i < number_of_types; ++i) { Handle map = types->at(i); if (expr->ComputeTarget(map, name)) { @@ -3782,7 +3801,9 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, // during hydrogen processing. CHECK_BAILOUT; HCall* call = new HCallConstantFunction(expr->target(), argument_count); - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + PushAndAdd(call); } subgraphs.Add(subgraph); } else { @@ -3790,30 +3811,34 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, } } - // If we couldn't compute the target for any of the maps just - // perform an IC call. + // If we couldn't compute the target for any of the maps just perform an + // IC call. if (maps.length() == 0) { HCall* call = new HCallNamed(name, argument_count); - ProcessCall(call, expr->position()); - return; - } - - // Build subgraph for generic call through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HCall* call = new HCallNamed(name, argument_count); - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); + } else { + // Build subgraph for generic call through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HCall* call = new HCallNamed(name, argument_count); + call->set_position(expr->position()); + ProcessCall(call); + PushAndAdd(call); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); - } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id()); + subgraph()->set_exit_block(new_exit_block); + if (new_exit_block != NULL) ast_context()->ReturnValue(Pop()); + } } @@ -3940,10 +3965,7 @@ bool HGraphBuilder::TryInline(Call* expr) { if_true->MarkAsInlineReturnTarget(); if_false->MarkAsInlineReturnTarget(); // AstContext constructor pushes on the context stack. - bool invert_true = TestContext::cast(ast_context())->invert_true(); - bool invert_false = TestContext::cast(ast_context())->invert_false(); - test_context = new TestContext(this, if_true, if_false, - invert_true, invert_false); + test_context = new TestContext(this, if_true, if_false); function_return_ = NULL; } else { // Inlined body is treated as if it occurs in the original call context. @@ -3987,16 +4009,15 @@ bool HGraphBuilder::TryInline(Call* expr) { // simply jumping to the false target. // // TODO(3168478): refactor to avoid this. - HBasicBlock* materialize_true = graph()->CreateBasicBlock(); - HBasicBlock* materialize_false = graph()->CreateBasicBlock(); + HBasicBlock* empty_true = graph()->CreateBasicBlock(); + HBasicBlock* empty_false = graph()->CreateBasicBlock(); HBranch* branch = - new HBranch(materialize_true, materialize_false, return_value); + new HBranch(empty_true, empty_false, return_value); body->exit_block()->Finish(branch); - materialize_true->AddLeaveInlined(graph()->GetConstantTrue(), - test_context->if_true()); - materialize_false->AddLeaveInlined(graph()->GetConstantFalse(), - test_context->if_false()); + HValue* const no_return_value = NULL; + empty_true->AddLeaveInlined(no_return_value, test_context->if_true()); + empty_false->AddLeaveInlined(no_return_value, test_context->if_false()); } body->set_exit_block(NULL); } @@ -4015,35 +4036,20 @@ bool HGraphBuilder::TryInline(Call* expr) { if_false->SetJoinId(expr->id()); ASSERT(ast_context() == test_context); delete test_context; // Destructor pops from expression context stack. - // Forward to the real test context. - // Discard the lingering branch value (which may be true or false, - // depending on whether the final condition was negated) and jump to the - // true target with a true branch value. + // Forward to the real test context. + HValue* const no_return_value = NULL; HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); - bool invert_true = TestContext::cast(ast_context())->invert_true(); - HValue* true_value = invert_true - ? graph()->GetConstantFalse() - : graph()->GetConstantTrue(); - if_true->last_environment()->Pop(); if (true_target->IsInlineReturnTarget()) { - if_true->AddLeaveInlined(true_value, true_target); + if_true->AddLeaveInlined(no_return_value, true_target); } else { - if_true->last_environment()->Push(true_value); if_true->Goto(true_target); } - // Do the same for the false target. HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); - bool invert_false = TestContext::cast(ast_context())->invert_false(); - HValue* false_value = invert_false - ? graph()->GetConstantTrue() - : graph()->GetConstantFalse(); - if_false->last_environment()->Pop(); if (false_target->IsInlineReturnTarget()) { - if_false->AddLeaveInlined(false_value, false_target); + if_false->AddLeaveInlined(no_return_value, false_target); } else { - if_false->last_environment()->Push(false_value); if_false->Goto(false_target); } @@ -4061,6 +4067,7 @@ bool HGraphBuilder::TryInline(Call* expr) { function_return_ = saved_function_return; oracle_ = saved_oracle; graph()->info()->SetOsrAstId(saved_osr_ast_id); + return true; } @@ -4069,7 +4076,7 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { ASSERT(target->IsInlineReturnTarget()); AddInstruction(new HLeaveInlined); HEnvironment* outer = last_environment()->outer(); - outer->Push(return_value); + if (return_value != NULL) outer->Push(return_value); UpdateEnvironment(outer); Goto(target); } @@ -4077,24 +4084,71 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { bool HGraphBuilder::TryMathFunctionInline(Call* expr) { // Try to inline calls like Math.* as operations in the calling function. - MathFunctionId id = expr->target()->shared()->math_function_id(); + if (!expr->target()->shared()->IsBuiltinMathFunction()) return false; + BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); int argument_count = expr->arguments()->length() + 1; // Plus receiver. switch (id) { case kMathRound: case kMathFloor: case kMathAbs: case kMathSqrt: + case kMathLog: if (argument_count == 2) { HValue* argument = Pop(); - // Pop receiver. - Pop(); + Drop(1); // Receiver. HUnaryMathOperation* op = new HUnaryMathOperation(argument, id); - PushAndAdd(op, expr->position()); + op->set_position(expr->position()); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; + case kMathPow: + if (argument_count == 3) { + HValue* right = Pop(); + HValue* left = Pop(); + Pop(); // Pop receiver. + HInstruction* result = NULL; + // Use sqrt() if exponent is 0.5 or -0.5. + if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) { + double exponent = HConstant::cast(right)->DoubleValue(); + if (exponent == 0.5) { + result = new HUnaryMathOperation(left, kMathPowHalf); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } else if (exponent == -0.5) { + HConstant* double_one = + new HConstant(Handle(Smi::FromInt(1)), + Representation::Double()); + AddInstruction(double_one); + HUnaryMathOperation* square_root = + new HUnaryMathOperation(left, kMathPowHalf); + AddInstruction(square_root); + // MathPowHalf doesn't have side effects so there's no need for + // an environment simulation here. + ASSERT(!square_root->HasSideEffects()); + result = new HDiv(double_one, square_root); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } else if (exponent == 2.0) { + result = new HMul(left, left); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } + } else if (right->IsConstant() && + HConstant::cast(right)->HasInteger32Value() && + HConstant::cast(right)->Integer32Value() == 2) { + result = new HMul(left, left); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } + + result = new HPower(left, right); + ast_context()->ReturnInstruction(result, expr->id()); return true; } break; default: - // Either not a special math function or not yet supported for inlining. + // Not yet supported for inlining. break; } return false; @@ -4134,8 +4188,10 @@ bool HGraphBuilder::TryCallApply(Call* expr) { function, expr->GetReceiverTypes()->first(), true); - PushAndAdd(new HApplyArguments(function, receiver, length, elements), - expr->position()); + HInstruction* result = + new HApplyArguments(function, receiver, length, elements); + result->set_position(expr->position()); + ast_context()->ReturnInstruction(result, expr->id()); return true; } @@ -4163,12 +4219,10 @@ void HGraphBuilder::VisitCall(Call* expr) { CHECK_BAILOUT; call = new HCallKeyed(key, argument_count); - ProcessCall(call, expr->position()); - HValue* result = Pop(); - // Drop the receiver from the environment and put back the result of - // the call. - Drop(1); - Push(result); + call->set_position(expr->position()); + ProcessCall(call); + Drop(1); // Key. + ast_context()->ReturnInstruction(call, expr->id()); return; } @@ -4191,7 +4245,19 @@ void HGraphBuilder::VisitCall(Call* expr) { if (expr->IsMonomorphic()) { AddCheckConstantFunction(expr, receiver, types->first(), true); - if (TryMathFunctionInline(expr) || TryInline(expr)) { + if (TryMathFunctionInline(expr)) { + return; + } else if (TryInline(expr)) { + if (subgraph()->HasExit()) { + HValue* return_value = Pop(); + // If we inlined a function in a test context then we need to emit + // a simulate here to shadow the ones at the end of the + // predecessor blocks. Those environments contain the return + // value on top and do not correspond to any actual state of the + // unoptimized code. + if (ast_context()->IsEffect()) AddSimulate(expr->id()); + ast_context()->ReturnValue(return_value); + } return; } else { // Check for bailout, as the TryInline call in the if condition above @@ -4199,6 +4265,7 @@ void HGraphBuilder::VisitCall(Call* expr) { CHECK_BAILOUT; call = new HCallConstantFunction(expr->target(), argument_count); } + } else if (types != NULL && types->length() > 1) { HandlePolymorphicCallNamed(expr, receiver, types, name); return; @@ -4246,7 +4313,19 @@ void HGraphBuilder::VisitCall(Call* expr) { IsGlobalObject()); environment()->SetExpressionStackAt(receiver_index, global_receiver); - if (TryInline(expr)) return; + if (TryInline(expr)) { + if (subgraph()->HasExit()) { + HValue* return_value = Pop(); + // If we inlined a function in a test context then we need to + // emit a simulate here to shadow the ones at the end of the + // predecessor blocks. Those environments contain the return + // value on top and do not correspond to any actual state of the + // unoptimized code. + if (ast_context()->IsEffect()) AddSimulate(expr->id()); + ast_context()->ReturnValue(return_value); + } + return; + } // Check for bailout, as trying to inline might fail due to bailout // during hydrogen processing. CHECK_BAILOUT; @@ -4269,7 +4348,9 @@ void HGraphBuilder::VisitCall(Call* expr) { } } - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } @@ -4283,8 +4364,9 @@ void HGraphBuilder::VisitCallNew(CallNew* expr) { int argument_count = expr->arguments()->length() + 1; // Plus constructor. HCall* call = new HCallNew(argument_count); - - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } @@ -4292,7 +4374,7 @@ void HGraphBuilder::VisitCallNew(CallNew* expr) { // Lookup table for generators for runtime calls that are generated inline. // Elements of the table are member pointers to functions of HGraphBuilder. -#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \ +#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \ &HGraphBuilder::Generate##Name, const HGraphBuilder::InlineFunctionGenerator @@ -4306,7 +4388,7 @@ const HGraphBuilder::InlineFunctionGenerator void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->IsEqualTo(CStrVector("_Log"))) { - Push(graph()->GetConstantUndefined()); + ast_context()->ReturnValue(graph()->GetConstantUndefined()); return; } @@ -4332,11 +4414,13 @@ void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index]; // Call the inline code generator using the pointer-to-member. - (this->*generator)(argument_count); + (this->*generator)(argument_count, expr->id()); } else { ASSERT(function->intrinsic_type == Runtime::RUNTIME); HCall* call = new HCallRuntime(name, expr->function(), argument_count); - ProcessCall(call, RelocInfo::kNoPosition); + call->set_position(RelocInfo::kNoPosition); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } } @@ -4345,7 +4429,7 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { Token::Value op = expr->op(); if (op == Token::VOID) { VISIT_FOR_EFFECT(expr->expression()); - Push(graph()->GetConstantUndefined()); + ast_context()->ReturnValue(graph()->GetConstantUndefined()); } else if (op == Token::DELETE) { Property* prop = expr->expression()->AsProperty(); Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); @@ -4353,36 +4437,47 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { // Result of deleting non-property, non-variable reference is true. // Evaluate the subexpression for side effects. VISIT_FOR_EFFECT(expr->expression()); - Push(graph_->GetConstantTrue()); + ast_context()->ReturnValue(graph()->GetConstantTrue()); } else if (var != NULL && !var->is_global() && var->AsSlot() != NULL && var->AsSlot()->type() != Slot::LOOKUP) { // Result of deleting non-global, non-dynamic variables is false. // The subexpression does not have side effects. - Push(graph_->GetConstantFalse()); + ast_context()->ReturnValue(graph()->GetConstantFalse()); } else if (prop != NULL) { VISIT_FOR_VALUE(prop->obj()); VISIT_FOR_VALUE(prop->key()); HValue* key = Pop(); HValue* obj = Pop(); - PushAndAdd(new HDeleteProperty(obj, key)); + ast_context()->ReturnInstruction(new HDeleteProperty(obj, key), + expr->id()); } else if (var->is_global()) { BAILOUT("delete with global variable"); } else { BAILOUT("delete with non-global variable"); } } else if (op == Token::NOT) { - HSubgraph* true_graph = CreateEmptySubgraph(); - HSubgraph* false_graph = CreateEmptySubgraph(); - VisitCondition(expr->expression(), - false_graph->entry_block(), - true_graph->entry_block(), - true, true); - if (HasStackOverflow()) return; - true_graph->environment()->Push(graph_->GetConstantTrue()); - false_graph->environment()->Push(graph_->GetConstantFalse()); - current_subgraph_->AppendJoin(true_graph, false_graph, expr); + if (ast_context()->IsTest()) { + TestContext* context = TestContext::cast(ast_context()); + VisitForControl(expr->expression(), + context->if_false(), + context->if_true()); + } else { + HSubgraph* true_graph = CreateEmptySubgraph(); + HSubgraph* false_graph = CreateEmptySubgraph(); + VISIT_FOR_CONTROL(expr->expression(), + false_graph->entry_block(), + true_graph->entry_block()); + true_graph->entry_block()->SetJoinId(expr->expression()->id()); + true_graph->environment()->Push(graph_->GetConstantTrue()); + + false_graph->entry_block()->SetJoinId(expr->expression()->id()); + false_graph->environment()->Push(graph_->GetConstantFalse()); + + current_subgraph_->AppendJoin(true_graph, false_graph, expr); + ast_context()->ReturnValue(Pop()); + } } else if (op == Token::BIT_NOT || op == Token::SUB) { VISIT_FOR_VALUE(expr->expression()); HValue* value = Pop(); @@ -4398,11 +4493,11 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { UNREACHABLE(); break; } - PushAndAdd(instr); + ast_context()->ReturnInstruction(instr, expr->id()); } else if (op == Token::TYPEOF) { VISIT_FOR_VALUE(expr->expression()); HValue* value = Pop(); - PushAndAdd(new HTypeof(value)); + ast_context()->ReturnInstruction(new HTypeof(value), expr->id()); } else { BAILOUT("Value: unsupported unary operation"); } @@ -4442,22 +4537,25 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { VISIT_FOR_VALUE(target); - HValue* value = Pop(); - HInstruction* instr = BuildIncrement(value, inc); - AddInstruction(instr); - - if (expr->is_prefix()) { - Push(instr); - } else { - Push(value); - } + // Match the full code generator stack by simulating an extra stack + // element for postfix operations in a non-effect context. + bool has_extra = expr->is_postfix() && !ast_context()->IsEffect(); + HValue* before = has_extra ? Top() : Pop(); + HInstruction* after = BuildIncrement(before, inc); + AddInstruction(after); + Push(after); if (var->is_global()) { - HandleGlobalVariableAssignment(proxy, instr, expr->position()); + HandleGlobalVariableAssignment(var, + after, + expr->position(), + expr->AssignmentId()); } else { ASSERT(var->IsStackAllocated()); - Bind(var, instr); + Bind(var, after); } + Drop(has_extra ? 2 : 1); + ast_context()->ReturnValue(expr->is_postfix() ? before : after); } else if (prop != NULL) { prop->RecordTypeFeedback(oracle()); @@ -4465,11 +4563,10 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { if (prop->key()->IsPropertyName()) { // Named property. - // Match the full code generator stack by simulate an extra stack element - // for postfix operations in a value context. - if (expr->is_postfix() && !ast_context()->IsEffect()) { - Push(graph_->GetConstantUndefined()); - } + // Match the full code generator stack by simulating an extra stack + // element for postfix operations in a non-effect context. + bool has_extra = expr->is_postfix() && !ast_context()->IsEffect(); + if (has_extra) Push(graph_->GetConstantUndefined()); VISIT_FOR_VALUE(prop->obj()); HValue* obj = Top(); @@ -4485,37 +4582,35 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { PushAndAdd(load); if (load->HasSideEffects()) AddSimulate(increment->id()); - HValue* value = Pop(); + HValue* before = Pop(); + // There is no deoptimization to after the increment, so we don't need + // to simulate the expression stack after this instruction. + HInstruction* after = BuildIncrement(before, inc); + AddInstruction(after); - HInstruction* instr = BuildIncrement(value, inc); - AddInstruction(instr); - - HInstruction* store = BuildStoreNamed(obj, instr, prop); + HInstruction* store = BuildStoreNamed(obj, after, prop); AddInstruction(store); - // Drop simulated receiver and push the result. - // There is no deoptimization to after the increment, so we can simulate - // the expression stack here. - Drop(1); - if (expr->is_prefix()) { - Push(instr); - } else { - if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero. - Push(value); - } + // Overwrite the receiver in the bailout environment with the result + // of the operation, and the placeholder with the original value if + // necessary. + environment()->SetExpressionStackAt(0, after); + if (has_extra) environment()->SetExpressionStackAt(1, before); + if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + Drop(has_extra ? 2 : 1); + + ast_context()->ReturnValue(expr->is_postfix() ? before : after); } else { // Keyed property. // Match the full code generator stack by simulate an extra stack element - // for postfix operations in a value context. - if (expr->is_postfix() && !ast_context()->IsEffect()) { - Push(graph_->GetConstantUndefined()); - } + // for postfix operations in a non-effect context. + bool has_extra = expr->is_postfix() && !ast_context()->IsEffect(); + if (has_extra) Push(graph_->GetConstantUndefined()); VISIT_FOR_VALUE(prop->obj()); VISIT_FOR_VALUE(prop->key()); - HValue* obj = environment()->ExpressionStackAt(1); HValue* key = environment()->ExpressionStackAt(0); @@ -4528,27 +4623,29 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { PushAndAdd(load); if (load->HasSideEffects()) AddSimulate(increment->id()); - HValue* value = Pop(); - - HInstruction* instr = BuildIncrement(value, inc); - AddInstruction(instr); + HValue* before = Pop(); + // There is no deoptimization to after the increment, so we don't need + // to simulate the expression stack after this instruction. + HInstruction* after = BuildIncrement(before, inc); + AddInstruction(after); HInstruction* store = is_fast_elements - ? BuildStoreKeyedFastElement(obj, key, instr, prop) - : new HStoreKeyedGeneric(obj, key, instr); + ? BuildStoreKeyedFastElement(obj, key, after, prop) + : new HStoreKeyedGeneric(obj, key, after); AddInstruction(store); - // Drop simulated receiver and key and push the result. - // There is no deoptimization to after the increment, so we can simulate - // the expression stack here. - Drop(2); - if (expr->is_prefix()) { - Push(instr); - } else { - if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero. - Push(value); - } + // Drop the key from the bailout environment. Overwrite the receiver + // with the result of the operation, and the placeholder with the + // original value if necessary. + Drop(1); + environment()->SetExpressionStackAt(0, after); + if (has_extra) environment()->SetExpressionStackAt(1, before); + if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + Drop(has_extra ? 2 : 1); + + ast_context()->ReturnValue(expr->is_postfix() ? before : after); } + } else { BAILOUT("invalid lhs in count operation"); } @@ -4630,21 +4727,43 @@ static bool IsClassOfTest(CompareOperation* expr) { void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { if (expr->op() == Token::COMMA) { VISIT_FOR_EFFECT(expr->left()); - VISIT_FOR_VALUE(expr->right()); - } else if (expr->op() == Token::AND || expr->op() == Token::OR) { - VISIT_FOR_VALUE(expr->left()); - ASSERT(current_subgraph_->HasExit()); + // Visit the right subexpression in the same AST context as the entire + // expression. + Visit(expr->right()); - HValue* left = Top(); + } else if (expr->op() == Token::AND || expr->op() == Token::OR) { bool is_logical_and = (expr->op() == Token::AND); + if (ast_context()->IsTest()) { + TestContext* context = TestContext::cast(ast_context()); + // Translate left subexpression. + HBasicBlock* eval_right = graph()->CreateBasicBlock(); + if (is_logical_and) { + VISIT_FOR_CONTROL(expr->left(), eval_right, context->if_false()); + } else { + VISIT_FOR_CONTROL(expr->left(), context->if_true(), eval_right); + } + eval_right->SetJoinId(expr->RightId()); + + // Translate right subexpression by visiting it in the same AST + // context as the entire expression. + subgraph()->set_exit_block(eval_right); + Visit(expr->right()); + + } else { + VISIT_FOR_VALUE(expr->left()); + ASSERT(current_subgraph_->HasExit()); + + HValue* left = Top(); + HEnvironment* environment_copy = environment()->Copy(); + environment_copy->Pop(); + HSubgraph* right_subgraph; + right_subgraph = CreateBranchSubgraph(environment_copy); + ADD_TO_SUBGRAPH(right_subgraph, expr->right()); + current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left); + current_subgraph_->exit_block()->SetJoinId(expr->id()); + ast_context()->ReturnValue(Pop()); + } - HEnvironment* environment_copy = environment()->Copy(); - environment_copy->Pop(); - HSubgraph* right_subgraph; - right_subgraph = CreateBranchSubgraph(environment_copy); - ADD_TO_SUBGRAPH(right_subgraph, expr->right()); - current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left); - current_subgraph_->exit_block()->SetJoinId(expr->id()); } else { VISIT_FOR_VALUE(expr->left()); VISIT_FOR_VALUE(expr->right()); @@ -4652,7 +4771,8 @@ void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { HValue* right = Pop(); HValue* left = Pop(); HInstruction* instr = BuildBinaryOperation(expr, left, right); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } } @@ -4691,7 +4811,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { Literal* literal = expr->right()->AsLiteral(); Handle rhs = Handle::cast(literal->handle()); HInstruction* instr = new HClassOfTest(value, rhs); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); return; } @@ -4705,7 +4826,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { HValue* left = Pop(); HInstruction* instr = new HTypeofIs(left, Handle::cast(right_literal->handle())); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); return; } @@ -4741,7 +4863,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { compare->SetInputRepresentation(r); instr = compare; } - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -4750,8 +4873,7 @@ void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { HValue* value = Pop(); HIsNull* compare = new HIsNull(value, expr->is_strict()); - - PushAndAdd(compare); + ast_context()->ReturnInstruction(compare, expr->id()); } @@ -4778,301 +4900,309 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) { // Generators for inline runtime functions. // Support for types. -void HGraphBuilder::GenerateIsSmi(int argument_count) { +void HGraphBuilder::GenerateIsSmi(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - PushAndAdd(new HIsSmi(value)); + HIsSmi* result = new HIsSmi(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsSpecObject(int argument_count) { +void HGraphBuilder::GenerateIsSpecObject(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = + HHasInstanceType* result = new HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE); - PushAndAdd(test); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsFunction(int argument_count) { +void HGraphBuilder::GenerateIsFunction(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_FUNCTION_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_FUNCTION_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count) { +void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count, + int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasCachedArrayIndex* spec_test = new HHasCachedArrayIndex(value); - PushAndAdd(spec_test); + HHasCachedArrayIndex* result = new HHasCachedArrayIndex(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsArray(int argument_count) { +void HGraphBuilder::GenerateIsArray(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_ARRAY_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_ARRAY_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsRegExp(int argument_count) { +void HGraphBuilder::GenerateIsRegExp(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_REGEXP_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_REGEXP_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count) { - BAILOUT("inlined runtime function: IsNonNegativeSmi"); +void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) { + ASSERT(argument_count == 1); + + HValue* value = Pop(); + HIsObject* test = new HIsObject(value); + ast_context()->ReturnInstruction(test, ast_id); } -void HGraphBuilder::GenerateIsObject(int argument_count) { - BAILOUT("inlined runtime function: IsObject"); +void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count, + int ast_id) { + BAILOUT("inlined runtime function: IsNonNegativeSmi"); } -void HGraphBuilder::GenerateIsUndetectableObject(int argument_count) { +void HGraphBuilder::GenerateIsUndetectableObject(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsUndetectableObject"); } void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf( - int argument_count) { + int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf"); } // Support for construct call checks. -void HGraphBuilder::GenerateIsConstructCall(int argument_count) { +void HGraphBuilder::GenerateIsConstructCall(int argument_count, int ast_id) { BAILOUT("inlined runtime function: IsConstructCall"); } // Support for arguments.length and arguments[?]. -void HGraphBuilder::GenerateArgumentsLength(int argument_count) { +void HGraphBuilder::GenerateArgumentsLength(int argument_count, int ast_id) { ASSERT(argument_count == 0); HInstruction* elements = AddInstruction(new HArgumentsElements); - PushAndAdd(new HArgumentsLength(elements)); + HArgumentsLength* result = new HArgumentsLength(elements); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateArguments(int argument_count) { +void HGraphBuilder::GenerateArguments(int argument_count, int ast_id) { ASSERT(argument_count == 1); HValue* index = Pop(); HInstruction* elements = AddInstruction(new HArgumentsElements); HInstruction* length = AddInstruction(new HArgumentsLength(elements)); - PushAndAdd(new HAccessArgumentsAt(elements, length, index)); + HAccessArgumentsAt* result = new HAccessArgumentsAt(elements, length, index); + ast_context()->ReturnInstruction(result, ast_id); } // Support for accessing the class and value fields of an object. -void HGraphBuilder::GenerateClassOf(int argument_count) { +void HGraphBuilder::GenerateClassOf(int argument_count, int ast_id) { // The special form detected by IsClassOfTest is detected before we get here // and does not cause a bailout. BAILOUT("inlined runtime function: ClassOf"); } -void HGraphBuilder::GenerateValueOf(int argument_count) { +void HGraphBuilder::GenerateValueOf(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HValueOf* op = new HValueOf(value); - PushAndAdd(op); + HValueOf* result = new HValueOf(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateSetValueOf(int argument_count) { +void HGraphBuilder::GenerateSetValueOf(int argument_count, int ast_id) { BAILOUT("inlined runtime function: SetValueOf"); } // Fast support for charCodeAt(n). -void HGraphBuilder::GenerateStringCharCodeAt(int argument_count) { +void HGraphBuilder::GenerateStringCharCodeAt(int argument_count, int ast_id) { BAILOUT("inlined runtime function: StringCharCodeAt"); } // Fast support for string.charAt(n) and string[n]. -void HGraphBuilder::GenerateStringCharFromCode(int argument_count) { +void HGraphBuilder::GenerateStringCharFromCode(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: StringCharFromCode"); } // Fast support for string.charAt(n) and string[n]. -void HGraphBuilder::GenerateStringCharAt(int argument_count) { +void HGraphBuilder::GenerateStringCharAt(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringCharAt, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringCharAt, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for object equality testing. -void HGraphBuilder::GenerateObjectEquals(int argument_count) { +void HGraphBuilder::GenerateObjectEquals(int argument_count, int ast_id) { ASSERT(argument_count == 2); - HValue* right = Pop(); HValue* left = Pop(); - PushAndAdd(new HCompareJSObjectEq(left, right)); + HCompareJSObjectEq* result = new HCompareJSObjectEq(left, right); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateLog(int argument_count) { +void HGraphBuilder::GenerateLog(int argument_count, int ast_id) { UNREACHABLE(); // We caught this in VisitCallRuntime. } // Fast support for Math.random(). -void HGraphBuilder::GenerateRandomHeapNumber(int argument_count) { +void HGraphBuilder::GenerateRandomHeapNumber(int argument_count, int ast_id) { BAILOUT("inlined runtime function: RandomHeapNumber"); } // Fast support for StringAdd. -void HGraphBuilder::GenerateStringAdd(int argument_count) { +void HGraphBuilder::GenerateStringAdd(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringAdd, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringAdd, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for SubString. -void HGraphBuilder::GenerateSubString(int argument_count) { +void HGraphBuilder::GenerateSubString(int argument_count, int ast_id) { ASSERT_EQ(3, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::SubString, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::SubString, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for StringCompare. -void HGraphBuilder::GenerateStringCompare(int argument_count) { +void HGraphBuilder::GenerateStringCompare(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringCompare, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringCompare, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Support for direct calls from JavaScript to native RegExp code. -void HGraphBuilder::GenerateRegExpExec(int argument_count) { +void HGraphBuilder::GenerateRegExpExec(int argument_count, int ast_id) { ASSERT_EQ(4, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::RegExpExec, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::RegExpExec, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Construct a RegExp exec result with two in-object properties. -void HGraphBuilder::GenerateRegExpConstructResult(int argument_count) { +void HGraphBuilder::GenerateRegExpConstructResult(int argument_count, + int ast_id) { ASSERT_EQ(3, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::RegExpConstructResult, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = + new HCallStub(CodeStub::RegExpConstructResult, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Support for fast native caches. -void HGraphBuilder::GenerateGetFromCache(int argument_count) { +void HGraphBuilder::GenerateGetFromCache(int argument_count, int ast_id) { BAILOUT("inlined runtime function: GetFromCache"); } // Fast support for number to string. -void HGraphBuilder::GenerateNumberToString(int argument_count) { +void HGraphBuilder::GenerateNumberToString(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::NumberToString, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::NumberToString, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast swapping of elements. Takes three expressions, the object and two // indices. This should only be used if the indices are known to be // non-negative and within bounds of the elements array at the call site. -void HGraphBuilder::GenerateSwapElements(int argument_count) { +void HGraphBuilder::GenerateSwapElements(int argument_count, int ast_id) { BAILOUT("inlined runtime function: SwapElements"); } // Fast call for custom callbacks. -void HGraphBuilder::GenerateCallFunction(int argument_count) { +void HGraphBuilder::GenerateCallFunction(int argument_count, int ast_id) { BAILOUT("inlined runtime function: CallFunction"); } // Fast call to math functions. -void HGraphBuilder::GenerateMathPow(int argument_count) { +void HGraphBuilder::GenerateMathPow(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); - PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::MathPow, argument_count), - RelocInfo::kNoPosition); + HValue* right = Pop(); + HValue* left = Pop(); + HPower* result = new HPower(left, right); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathSin(int argument_count) { +void HGraphBuilder::GenerateMathSin(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::SIN); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::SIN); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathCos(int argument_count) { +void HGraphBuilder::GenerateMathCos(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::COS); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::COS); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathLog(int argument_count) { +void HGraphBuilder::GenerateMathLog(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::LOG); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::LOG); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathSqrt(int argument_count) { +void HGraphBuilder::GenerateMathSqrt(int argument_count, int ast_id) { BAILOUT("inlined runtime function: MathSqrt"); } // Check whether two RegExps are equivalent -void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count) { +void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsRegExpEquivalent"); } -void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count) { +void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: GetCachedArrayIndex"); } -void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count) { +void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: FastAsciiArrayJoin"); } diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h index 91f3c9e2d6..2c9aeac75c 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -136,14 +136,6 @@ class HBasicBlock: public ZoneObject { bool IsInlineReturnTarget() const { return is_inline_return_target_; } void MarkAsInlineReturnTarget() { is_inline_return_target_ = true; } - // If this block is a successor of a branch, his flags tells whether the - // preceding branch was inverted or not. - bool inverted() { return inverted_; } - void set_inverted(bool b) { inverted_ = b; } - - HBasicBlock* deopt_predecessor() { return deopt_predecessor_; } - void set_deopt_predecessor(HBasicBlock* block) { deopt_predecessor_ = block; } - Handle cond() { return cond_; } void set_cond(Handle value) { cond_ = value; } @@ -176,8 +168,6 @@ class HBasicBlock: public ZoneObject { ZoneList deleted_phis_; SetOncePointer parent_loop_header_; bool is_inline_return_target_; - bool inverted_; - HBasicBlock* deopt_predecessor_; Handle cond_; }; @@ -557,10 +547,29 @@ class AstContext { bool IsValue() const { return kind_ == Expression::kValue; } bool IsTest() const { return kind_ == Expression::kTest; } + // 'Fill' this context with a hydrogen value. The value is assumed to + // have already been inserted in the instruction stream (or not need to + // be, e.g., HPhi). Call this function in tail position in the Visit + // functions for expressions. + virtual void ReturnValue(HValue* value) = 0; + + // Add a hydrogen instruction to the instruction stream (recording an + // environment simulation if necessary) and then fill this context with + // the instruction as value. + virtual void ReturnInstruction(HInstruction* instr, int ast_id) = 0; + protected: AstContext(HGraphBuilder* owner, Expression::Context kind); virtual ~AstContext(); + HGraphBuilder* owner() const { return owner_; } + + // We want to be able to assert, in a context-specific way, that the stack + // height makes sense when the context is filled. +#ifdef DEBUG + int original_count_; +#endif + private: HGraphBuilder* owner_; Expression::Context kind_; @@ -573,6 +582,10 @@ class EffectContext: public AstContext { explicit EffectContext(HGraphBuilder* owner) : AstContext(owner, Expression::kEffect) { } + virtual ~EffectContext(); + + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); }; @@ -581,6 +594,10 @@ class ValueContext: public AstContext { explicit ValueContext(HGraphBuilder* owner) : AstContext(owner, Expression::kValue) { } + virtual ~ValueContext(); + + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); }; @@ -588,16 +605,15 @@ class TestContext: public AstContext { public: TestContext(HGraphBuilder* owner, HBasicBlock* if_true, - HBasicBlock* if_false, - bool invert_true, - bool invert_false) + HBasicBlock* if_false) : AstContext(owner, Expression::kTest), if_true_(if_true), - if_false_(if_false), - invert_true_(invert_true), - invert_false_(invert_false) { + if_false_(if_false) { } + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); + static TestContext* cast(AstContext* context) { ASSERT(context->IsTest()); return reinterpret_cast(context); @@ -606,14 +622,13 @@ class TestContext: public AstContext { HBasicBlock* if_true() const { return if_true_; } HBasicBlock* if_false() const { return if_false_; } - bool invert_true() { return invert_true_; } - bool invert_false() { return invert_false_; } - private: + // Build the shared core part of the translation unpacking a value into + // control flow. + void BuildBranch(HValue* value); + HBasicBlock* if_true_; HBasicBlock* if_false_; - bool invert_true_; - bool invert_false_; }; @@ -631,9 +646,25 @@ class HGraphBuilder: public AstVisitor { HGraph* CreateGraph(CompilationInfo* info); + // Simple accessors. + HGraph* graph() const { return graph_; } + HSubgraph* subgraph() const { return current_subgraph_; } + + HEnvironment* environment() const { return subgraph()->environment(); } + HBasicBlock* CurrentBlock() const { return subgraph()->exit_block(); } + + // Adding instructions. + HInstruction* AddInstruction(HInstruction* instr); + void AddSimulate(int id); + + // Bailout environment manipulation. + void Push(HValue* value) { environment()->Push(value); } + HValue* Pop() { return environment()->Pop(); } + private: // Type of a member function that generates inline code for a native function. - typedef void (HGraphBuilder::*InlineFunctionGenerator)(int argument_count); + typedef void (HGraphBuilder::*InlineFunctionGenerator)(int argument_count, + int ast_id); // Forward declarations for inner scope classes. class SubgraphScope; @@ -650,19 +681,14 @@ class HGraphBuilder: public AstVisitor { // Simple accessors. TypeFeedbackOracle* oracle() const { return oracle_; } - HGraph* graph() const { return graph_; } - HSubgraph* subgraph() const { return current_subgraph_; } AstContext* ast_context() const { return ast_context_; } void set_ast_context(AstContext* context) { ast_context_ = context; } AstContext* call_context() const { return call_context_; } HBasicBlock* function_return() const { return function_return_; } - HEnvironment* environment() const { return subgraph()->environment(); } - - HBasicBlock* CurrentBlock() const { return subgraph()->exit_block(); } // Generators for inline runtime functions. -#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \ - void Generate##Name(int argument_count); +#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \ + void Generate##Name(int argument_count, int ast_id); INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) @@ -678,13 +704,7 @@ class HGraphBuilder: public AstVisitor { void AddToSubgraph(HSubgraph* graph, ZoneList* stmts); void AddToSubgraph(HSubgraph* graph, Statement* stmt); void AddToSubgraph(HSubgraph* graph, Expression* expr); - void AddConditionToSubgraph(HSubgraph* subgraph, - Expression* expr, - HSubgraph* true_graph, - HSubgraph* false_graph); - void Push(HValue* value) { environment()->Push(value); } - HValue* Pop() { return environment()->Pop(); } HValue* Top() const { return environment()->Top(); } void Drop(int n) { environment()->Drop(n); } void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); } @@ -693,33 +713,21 @@ class HGraphBuilder: public AstVisitor { void VisitForEffect(Expression* expr); void VisitForControl(Expression* expr, HBasicBlock* true_block, - HBasicBlock* false_block, - bool invert_true, - bool invert_false); - - // Visit an expression in a 'condition' context, i.e., in a control - // context but not a subexpression of logical and, or, or not. - void VisitCondition(Expression* expr, - HBasicBlock* true_graph, - HBasicBlock* false_graph, - bool invert_true, - bool invert_false); + HBasicBlock* false_block); + // Visit an argument and wrap it in a PushArgument instruction. HValue* VisitArgument(Expression* expr); void VisitArgumentList(ZoneList* arguments); - HInstruction* AddInstruction(HInstruction* instr); - void AddSimulate(int id); void AddPhi(HPhi* phi); void PushAndAdd(HInstruction* instr); - void PushAndAdd(HInstruction* instr, int position); void PushArgumentsForStubCall(int argument_count); - // Initialize the arguments to the call based on then environment, add it - // to the graph, and drop the arguments from the environment. - void ProcessCall(HCall* call, int source_position); + // Remove the arguments from the bailout environment and emit instructions + // to push them as outgoing parameters. + void ProcessCall(HCall* call); void AssumeRepresentation(HValue* value, Representation r); static Representation ToRepresentation(TypeInfo info); @@ -743,7 +751,7 @@ class HGraphBuilder: public AstVisitor { FunctionLiteral* function); // Helpers for flow graph construction. - void LookupGlobalPropertyCell(VariableProxy* expr, + void LookupGlobalPropertyCell(Variable* var, LookupResult* lookup, bool is_store); @@ -753,10 +761,11 @@ class HGraphBuilder: public AstVisitor { bool TryMathFunctionInline(Call* expr); void TraceInline(Handle target, bool result); - void HandleGlobalVariableAssignment(VariableProxy* proxy, + void HandleGlobalVariableAssignment(Variable* var, HValue* value, - int position); - void HandleGlobalVariableLoad(VariableProxy* expr); + int position, + int ast_id); + void HandlePropertyAssignment(Assignment* expr); void HandleCompoundAssignment(Assignment* expr); void HandlePolymorphicLoadNamedField(Property* expr, diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 95826562fc..c173a3dc5e 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -2409,6 +2409,7 @@ void Assembler::movsd(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } + void Assembler::movsd(XMMRegister dst, XMMRegister src) { ASSERT(CpuFeatures::IsEnabled(SSE2)); EnsureSpace ensure_space(this); @@ -2431,6 +2432,17 @@ void Assembler::movd(XMMRegister dst, const Operand& src) { } +void Assembler::movd(const Operand& dst, XMMRegister src) { + ASSERT(CpuFeatures::IsEnabled(SSE2)); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0x66); + EMIT(0x0F); + EMIT(0x7E); + emit_sse_operand(src, dst); +} + + void Assembler::pand(XMMRegister dst, XMMRegister src) { ASSERT(CpuFeatures::IsEnabled(SSE2)); EnsureSpace ensure_space(this); @@ -2465,7 +2477,7 @@ void Assembler::ptest(XMMRegister dst, XMMRegister src) { } -void Assembler::psllq(XMMRegister reg, int8_t imm8) { +void Assembler::psllq(XMMRegister reg, int8_t shift) { ASSERT(CpuFeatures::IsEnabled(SSE2)); EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -2473,7 +2485,32 @@ void Assembler::psllq(XMMRegister reg, int8_t imm8) { EMIT(0x0F); EMIT(0x73); emit_sse_operand(esi, reg); // esi == 6 - EMIT(imm8); + EMIT(shift); +} + + +void Assembler::pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle) { + ASSERT(CpuFeatures::IsEnabled(SSE2)); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0x66); + EMIT(0x0F); + EMIT(0x70); + emit_sse_operand(dst, src); + EMIT(shuffle); +} + + +void Assembler::pextrd(const Operand& dst, XMMRegister src, int8_t offset) { + ASSERT(CpuFeatures::IsEnabled(SSE4_1)); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0x66); + EMIT(0x0F); + EMIT(0x3A); + EMIT(0x16); + emit_sse_operand(src, dst); + EMIT(offset); } diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 2b4624c6f7..11acb56110 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -571,6 +571,15 @@ class Assembler : public Malloced { static const byte kTestEaxByte = 0xA9; // One byte opcode for test al, 0xXX. static const byte kTestAlByte = 0xA8; + // One byte opcode for nop. + static const byte kNopByte = 0x90; + + // One byte opcode for a short unconditional jump. + static const byte kJmpShortOpcode = 0xEB; + // One byte prefix for a short conditional jump. + static const byte kJccShortPrefix = 0x70; + static const byte kJncShortOpcode = kJccShortPrefix | not_carry; + static const byte kJcShortOpcode = kJccShortPrefix | carry; // --------------------------------------------------------------------------- // Code generation @@ -905,13 +914,16 @@ class Assembler : public Malloced { void movdbl(const Operand& dst, XMMRegister src); void movd(XMMRegister dst, const Operand& src); + void movd(const Operand& src, XMMRegister dst); void movsd(XMMRegister dst, XMMRegister src); void pand(XMMRegister dst, XMMRegister src); void pxor(XMMRegister dst, XMMRegister src); void ptest(XMMRegister dst, XMMRegister src); - void psllq(XMMRegister reg, int8_t imm8); + void psllq(XMMRegister reg, int8_t shift); + void pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle); + void pextrd(const Operand& dst, XMMRegister src, int8_t offset); // Parallel XMM operations. void movntdqa(XMMRegister src, const Operand& dst); diff --git a/deps/v8/src/ia32/builtins-ia32.cc b/deps/v8/src/ia32/builtins-ia32.cc index c28e144410..918f346d89 100644 --- a/deps/v8/src/ia32/builtins-ia32.cc +++ b/deps/v8/src/ia32/builtins-ia32.cc @@ -29,7 +29,6 @@ #if defined(V8_TARGET_ARCH_IA32) -#include "code-stubs.h" #include "codegen-inl.h" #include "deoptimizer.h" #include "full-codegen.h" diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index 3233be716d..d75acab082 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -1366,8 +1366,8 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, if (op_ == Token::DIV || op_ == Token::MOD) { left = eax; right = ebx; - __ mov(ebx, eax); - __ mov(eax, edx); + __ mov(ebx, eax); + __ mov(eax, edx); } @@ -2683,6 +2683,145 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { } +void TranscendentalCacheSSE2Stub::Generate(MacroAssembler* masm) { + // Input on stack: + // esp[0]: return address. + // Input in registers: + // xmm1: untagged double input argument. + // Output: + // xmm1: untagged double result. + Label skip_cache; + Label call_runtime; + + // Input is an untagged double in xmm1. + // Compute hash (the shifts are arithmetic): + // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1); + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatures::Scope sse4_scope(SSE4_1); + __ pextrd(Operand(edx), xmm1, 0x1); // copy xmm1[63..32] to edx. + } else { + __ pshufd(xmm0, xmm1, 0x1); + __ movd(Operand(edx), xmm0); + } + __ movd(Operand(ebx), xmm1); + + // xmm1 = double value + // ebx = low 32 bits of double value + // edx = high 32 bits of double value + // Compute hash (the shifts are arithmetic): + // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1); + __ mov(ecx, ebx); + __ xor_(ecx, Operand(edx)); + __ mov(eax, ecx); + __ sar(eax, 16); + __ xor_(ecx, Operand(eax)); + __ mov(eax, ecx); + __ sar(eax, 8); + __ xor_(ecx, Operand(eax)); + ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize)); + __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1)); + + // xmm1 = double value. + // ebx = low 32 bits of double value. + // edx = high 32 bits of double value. + // ecx = TranscendentalCache::hash(double value). + __ mov(eax, + Immediate(ExternalReference::transcendental_cache_array_address())); + // Eax points to cache array. + __ mov(eax, Operand(eax, type_ * sizeof(TranscendentalCache::caches_[0]))); + // Eax points to the cache for the type type_. + // If NULL, the cache hasn't been initialized yet, so go through runtime. + __ test(eax, Operand(eax)); + __ j(zero, &call_runtime); +#ifdef DEBUG + // Check that the layout of cache elements match expectations. + { TranscendentalCache::Element test_elem[2]; + char* elem_start = reinterpret_cast(&test_elem[0]); + char* elem2_start = reinterpret_cast(&test_elem[1]); + char* elem_in0 = reinterpret_cast(&(test_elem[0].in[0])); + char* elem_in1 = reinterpret_cast(&(test_elem[0].in[1])); + char* elem_out = reinterpret_cast(&(test_elem[0].output)); + CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer. + CHECK_EQ(0, elem_in0 - elem_start); + CHECK_EQ(kIntSize, elem_in1 - elem_start); + CHECK_EQ(2 * kIntSize, elem_out - elem_start); + } +#endif + // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12]. + __ lea(ecx, Operand(ecx, ecx, times_2, 0)); + __ lea(ecx, Operand(eax, ecx, times_4, 0)); + // Check if cache matches: Double value is stored in uint32_t[2] array. + NearLabel cache_miss; + __ cmp(ebx, Operand(ecx, 0)); + __ j(not_equal, &cache_miss); + __ cmp(edx, Operand(ecx, kIntSize)); + __ j(not_equal, &cache_miss); + // Cache hit! + __ mov(eax, Operand(ecx, 2 * kIntSize)); + __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); + __ Ret(); + + __ bind(&cache_miss); + // Update cache with new value. + // We are short on registers, so use no_reg as scratch. + // This gives slightly larger code. + __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache); + __ sub(Operand(esp), Immediate(kDoubleSize)); + __ movdbl(Operand(esp, 0), xmm1); + __ fld_d(Operand(esp, 0)); + __ add(Operand(esp), Immediate(kDoubleSize)); + GenerateOperation(masm); + __ mov(Operand(ecx, 0), ebx); + __ mov(Operand(ecx, kIntSize), edx); + __ mov(Operand(ecx, 2 * kIntSize), eax); + __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); + __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); + __ Ret(); + + __ bind(&skip_cache); + __ sub(Operand(esp), Immediate(kDoubleSize)); + __ movdbl(Operand(esp, 0), xmm1); + __ fld_d(Operand(esp, 0)); + GenerateOperation(masm); + __ fstp_d(Operand(esp, 0)); + __ movdbl(xmm1, Operand(esp, 0)); + __ add(Operand(esp), Immediate(kDoubleSize)); + __ Ret(); + + __ bind(&call_runtime); + __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache); + __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1); + __ EnterInternalFrame(); + __ push(eax); + __ CallRuntime(RuntimeFunction(), 1); + __ LeaveInternalFrame(); + __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); + __ Ret(); +} + + +Runtime::FunctionId TranscendentalCacheSSE2Stub::RuntimeFunction() { + switch (type_) { + // Add more cases when necessary. + case TranscendentalCache::LOG: return Runtime::kMath_log; + default: + UNIMPLEMENTED(); + return Runtime::kAbort; + } +} + + +void TranscendentalCacheSSE2Stub::GenerateOperation(MacroAssembler* masm) { + // Only free register is edi. + // Input value is on FP stack and in xmm1. + + ASSERT(type_ == TranscendentalCache::LOG); + __ fldln2(); + __ fxch(); + __ fyl2x(); +} + + // Get the integer part of a heap number. Surprisingly, all this bit twiddling // is faster than using the built-in instructions on floating point registers. // Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the @@ -4901,76 +5040,125 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { void InstanceofStub::Generate(MacroAssembler* masm) { - // Get the object - go slow case if it's a smi. - Label slow; - __ mov(eax, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, function - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &slow, not_taken); + // Fixed register usage throughout the stub. + Register object = eax; // Object (lhs). + Register map = ebx; // Map of the object. + Register function = edx; // Function (rhs). + Register prototype = edi; // Prototype of the function. + Register scratch = ecx; + + // Get the object and function - they are always both needed. + Label slow, not_js_object; + if (!args_in_registers()) { + __ mov(object, Operand(esp, 2 * kPointerSize)); + __ mov(function, Operand(esp, 1 * kPointerSize)); + } // Check that the left hand is a JS object. - __ IsObjectJSObjectType(eax, eax, edx, &slow); - - // Get the prototype of the function. - __ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address - // edx is function, eax is map. + __ test(object, Immediate(kSmiTagMask)); + __ j(zero, ¬_js_object, not_taken); + __ IsObjectJSObjectType(object, map, scratch, ¬_js_object); // Look up the function and the map in the instanceof cache. NearLabel miss; ExternalReference roots_address = ExternalReference::roots_address(); - __ mov(ecx, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ cmp(edx, Operand::StaticArray(ecx, times_pointer_size, roots_address)); + __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); + __ cmp(function, + Operand::StaticArray(scratch, times_pointer_size, roots_address)); __ j(not_equal, &miss); - __ mov(ecx, Immediate(Heap::kInstanceofCacheMapRootIndex)); - __ cmp(eax, Operand::StaticArray(ecx, times_pointer_size, roots_address)); + __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); + __ cmp(map, Operand::StaticArray(scratch, times_pointer_size, roots_address)); __ j(not_equal, &miss); - __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(eax, Operand::StaticArray(ecx, times_pointer_size, roots_address)); - __ ret(2 * kPointerSize); + __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); + __ mov(eax, Operand::StaticArray(scratch, times_pointer_size, roots_address)); + __ IncrementCounter(&Counters::instance_of_cache, 1); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); __ bind(&miss); - __ TryGetFunctionPrototype(edx, ebx, ecx, &slow); + // Get the prototype of the function. + __ TryGetFunctionPrototype(function, prototype, scratch, &slow); // Check that the function prototype is a JS object. - __ test(ebx, Immediate(kSmiTagMask)); + __ test(prototype, Immediate(kSmiTagMask)); __ j(zero, &slow, not_taken); - __ IsObjectJSObjectType(ebx, ecx, ecx, &slow); - - // Register mapping: - // eax is object map. - // edx is function. - // ebx is function prototype. - __ mov(ecx, Immediate(Heap::kInstanceofCacheMapRootIndex)); - __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax); - __ mov(ecx, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), edx); - - __ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset)); - - // Loop through the prototype chain looking for the function prototype. + __ IsObjectJSObjectType(prototype, scratch, scratch, &slow); + + // Update the golbal instanceof cache with the current map and function. The + // cached answer will be set when it is known. + __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), map); + __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), + function); + + // Loop through the prototype chain of the object looking for the function + // prototype. + __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset)); NearLabel loop, is_instance, is_not_instance; __ bind(&loop); - __ cmp(ecx, Operand(ebx)); + __ cmp(scratch, Operand(prototype)); __ j(equal, &is_instance); - __ cmp(Operand(ecx), Immediate(Factory::null_value())); + __ cmp(Operand(scratch), Immediate(Factory::null_value())); __ j(equal, &is_not_instance); - __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset)); - __ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset)); + __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); + __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset)); __ jmp(&loop); __ bind(&is_instance); + __ IncrementCounter(&Counters::instance_of_stub_true, 1); __ Set(eax, Immediate(0)); - __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax); - __ ret(2 * kPointerSize); + __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); __ bind(&is_not_instance); + __ IncrementCounter(&Counters::instance_of_stub_false, 1); __ Set(eax, Immediate(Smi::FromInt(1))); - __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax); - __ ret(2 * kPointerSize); + __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); + + Label object_not_null, object_not_null_or_smi; + __ bind(¬_js_object); + // Before null, smi and string value checks, check that the rhs is a function + // as for a non-function rhs an exception needs to be thrown. + __ test(function, Immediate(kSmiTagMask)); + __ j(zero, &slow, not_taken); + __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch); + __ j(not_equal, &slow, not_taken); + + // Null is not instance of anything. + __ cmp(object, Factory::null_value()); + __ j(not_equal, &object_not_null); + __ IncrementCounter(&Counters::instance_of_stub_false_null, 1); + __ Set(eax, Immediate(Smi::FromInt(1))); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); + + __ bind(&object_not_null); + // Smi values is not instance of anything. + __ test(object, Immediate(kSmiTagMask)); + __ j(not_zero, &object_not_null_or_smi, not_taken); + __ Set(eax, Immediate(Smi::FromInt(1))); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); + + __ bind(&object_not_null_or_smi); + // String values is not instance of anything. + Condition is_string = masm->IsObjectStringType(object, scratch, scratch); + __ j(NegateCondition(is_string), &slow); + __ IncrementCounter(&Counters::instance_of_stub_false_string, 1); + __ Set(eax, Immediate(Smi::FromInt(1))); + __ ret((args_in_registers() ? 0 : 2) * kPointerSize); // Slow-case: Go through the JavaScript implementation. __ bind(&slow); + if (args_in_registers()) { + // Push arguments below return address. + __ pop(scratch); + __ push(object); + __ push(function); + __ push(scratch); + } + __ IncrementCounter(&Counters::instance_of_slow, 1); __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); } diff --git a/deps/v8/src/ia32/code-stubs-ia32.h b/deps/v8/src/ia32/code-stubs-ia32.h index 2973101877..04f23ace9b 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.h +++ b/deps/v8/src/ia32/code-stubs-ia32.h @@ -45,6 +45,7 @@ class TranscendentalCacheStub: public CodeStub { void Generate(MacroAssembler* masm); private: TranscendentalCache::Type type_; + Major MajorKey() { return TranscendentalCache; } int MinorKey() { return type_; } Runtime::FunctionId RuntimeFunction(); @@ -52,6 +53,24 @@ class TranscendentalCacheStub: public CodeStub { }; +// Check the transcendental cache, or generate the result, using SSE2. +// The argument and result will be in xmm1. +// Only supports TranscendentalCache::LOG at this point. +class TranscendentalCacheSSE2Stub: public CodeStub { + public: + explicit TranscendentalCacheSSE2Stub(TranscendentalCache::Type type) + : type_(type) {} + void Generate(MacroAssembler* masm); + private: + TranscendentalCache::Type type_; + + Major MajorKey() { return TranscendentalCacheSSE2; } + int MinorKey() { return type_; } + Runtime::FunctionId RuntimeFunction(); + void GenerateOperation(MacroAssembler* masm); +}; + + class ToBooleanStub: public CodeStub { public: ToBooleanStub() { } @@ -231,7 +250,8 @@ class TypeRecordingBinaryOpStub: public CodeStub { ASSERT(OpBits::is_valid(Token::NUM_TOKENS)); } - TypeRecordingBinaryOpStub(int key, + TypeRecordingBinaryOpStub( + int key, TRBinaryOpIC::TypeInfo operands_type, TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED) : op_(OpBits::decode(key)), @@ -239,8 +259,7 @@ class TypeRecordingBinaryOpStub: public CodeStub { use_sse3_(SSE3Bits::decode(key)), operands_type_(operands_type), result_type_(result_type), - name_(NULL) { - } + name_(NULL) { } // Generate code to call the stub with the supplied arguments. This will add // code at the call site to prepare arguments either in registers or on the diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 022c117198..4c9d05558b 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -7676,6 +7676,13 @@ void CodeGenerator::GenerateSwapElements(ZoneList* args) { __ test(tmp2.reg(), Immediate(kSmiTagMask)); deferred->Branch(not_zero); + // Check that both indices are valid. + __ mov(tmp2.reg(), FieldOperand(object.reg(), JSArray::kLengthOffset)); + __ cmp(tmp2.reg(), Operand(index1.reg())); + deferred->Branch(below_equal); + __ cmp(tmp2.reg(), Operand(index2.reg())); + deferred->Branch(below_equal); + // Bring addresses into index1 and index2. __ lea(index1.reg(), FixedArrayElementOperand(tmp1.reg(), index1.reg())); __ lea(index2.reg(), FixedArrayElementOperand(tmp1.reg(), index2.reg())); @@ -9133,7 +9140,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { case Token::INSTANCEOF: { if (!left_already_loaded) Load(left); Load(right); - InstanceofStub stub; + InstanceofStub stub(InstanceofStub::kNoFlags); Result answer = frame_->CallStub(&stub, 2); answer.ToRegister(); __ test(answer.reg(), Operand(answer.reg())); diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc index 3734fca46a..dfbcbb76d5 100644 --- a/deps/v8/src/ia32/disasm-ia32.cc +++ b/deps/v8/src/ia32/disasm-ia32.cc @@ -1107,6 +1107,21 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, } else { UnimplementedInstruction(); } + } else if (*data == 0x3A) { + data++; + if (*data == 0x16) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("pextrd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else { + UnimplementedInstruction(); + } } else if (*data == 0x2E || *data == 0x2F) { const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd"; data++; @@ -1129,6 +1144,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfCPURegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0x54) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("andpd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0x57) { data++; int mod, regop, rm; @@ -1149,6 +1172,25 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, get_modrm(*data, &mod, ®op, &rm); AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop)); data += PrintRightOperand(data); + } else if (*data == 0x70) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("pshufd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0x73) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("psllq %s,%d", + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; } else if (*data == 0x7F) { AppendToBuffer("movdqa "); data++; @@ -1156,6 +1198,21 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, get_modrm(*data, &mod, ®op, &rm); data += PrintRightOperand(data); AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (*data == 0x7E) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movd "); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (*data == 0xDB) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pand %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0xE7) { AppendToBuffer("movntdq "); data++; @@ -1164,38 +1221,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, data += PrintRightOperand(data); AppendToBuffer(",%s", NameOfXMMRegister(regop)); } else if (*data == 0xEF) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("pxor %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0xDB) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("pand %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x73) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("psllq %s,%d", - NameOfXMMRegister(rm), - static_cast(imm8)); - data += 2; - } else if (*data == 0x54) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("andpd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pxor %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else { UnimplementedInstruction(); } diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index 1f7095f578..be059cdfe3 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -41,8 +41,61 @@ namespace v8 { namespace internal { + #define __ ACCESS_MASM(masm_) + +class JumpPatchSite BASE_EMBEDDED { + public: + explicit JumpPatchSite(MacroAssembler* masm) + : masm_(masm) { +#ifdef DEBUG + info_emitted_ = false; +#endif + } + + ~JumpPatchSite() { + ASSERT(patch_site_.is_bound() == info_emitted_); + } + + void EmitJumpIfNotSmi(Register reg, NearLabel* target) { + __ test(reg, Immediate(kSmiTagMask)); + EmitJump(not_carry, target); // Always taken before patched. + } + + void EmitJumpIfSmi(Register reg, NearLabel* target) { + __ test(reg, Immediate(kSmiTagMask)); + EmitJump(carry, target); // Never taken before patched. + } + + void EmitPatchInfo() { + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_); + ASSERT(is_int8(delta_to_patch_site)); + __ test(eax, Immediate(delta_to_patch_site)); +#ifdef DEBUG + info_emitted_ = true; +#endif + } + + bool is_bound() const { return patch_site_.is_bound(); } + + private: + // jc will be patched with jz, jnc will become jnz. + void EmitJump(Condition cc, NearLabel* target) { + ASSERT(!patch_site_.is_bound() && !info_emitted_); + ASSERT(cc == carry || cc == not_carry); + __ bind(&patch_site_); + __ j(cc, target); + } + + MacroAssembler* masm_; + Label patch_site_; +#ifdef DEBUG + bool info_emitted_; +#endif +}; + + // Generate code for a JS function. On entry to the function the receiver // and arguments have been pushed on the stack left to right, with the // return address on top of them. The actual argument count matches the @@ -198,6 +251,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } +void FullCodeGenerator::ClearAccumulator() { + __ Set(eax, Immediate(Smi::FromInt(0))); +} + + void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { Comment cmnt(masm_, "[ Stack check"); NearLabel ok; @@ -687,10 +745,9 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { Breakable nested_statement(this, stmt); SetStatementPosition(stmt); - PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); - // Keep the switch value on the stack until a case matches. VisitForStackValue(stmt->tag()); + PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); ZoneList* clauses = stmt->cases(); CaseClause* default_clause = NULL; // Can occur anywhere in the list. @@ -715,12 +772,13 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { // Perform the comparison as if via '==='. __ mov(edx, Operand(esp, 0)); // Switch value. bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); + JumpPatchSite patch_site(masm_); if (inline_smi_code) { NearLabel slow_case; __ mov(ecx, edx); __ or_(ecx, Operand(eax)); - __ test(ecx, Immediate(kSmiTagMask)); - __ j(not_zero, &slow_case, not_taken); + patch_site.EmitJumpIfNotSmi(ecx, &slow_case); + __ cmp(edx, Operand(eax)); __ j(not_equal, &next_test); __ Drop(1); // Switch value is no longer needed. @@ -730,9 +788,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { // Record position before stub call for type feedback. SetSourcePosition(clause->position()); - Handle ic = CompareIC::GetUninitialized(Token::EQ_STRICT); - __ call(ic, RelocInfo::CODE_TARGET); + EmitCallIC(ic, &patch_site); __ test(eax, Operand(eax)); __ j(not_equal, &next_test); @@ -911,7 +968,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&update_each); __ mov(result_register(), ebx); // Perform the assignment as if via '='. - EmitAssignment(stmt->each()); + { EffectContext context(this); + EmitAssignment(stmt->each(), stmt->AssignmentId()); + } // Generate code for the body of the loop. Visit(stmt->body()); @@ -1478,7 +1537,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { // For property compound assignments we need another deoptimization // point after the property load. if (property != NULL) { - PrepareForBailoutForId(expr->compound_bailout_id(), TOS_REG); + PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); } Token::Value op = expr->binary_op(); @@ -1521,6 +1580,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), expr->op()); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(eax); break; case NAMED_PROPERTY: EmitNamedPropertyAssignment(expr); @@ -1552,12 +1613,11 @@ void FullCodeGenerator::EmitConstantSmiAdd(Expression* expr, OverwriteMode mode, bool left_is_constant_smi, Smi* value) { - NearLabel call_stub; - Label done; + NearLabel call_stub, done; __ add(Operand(eax), Immediate(value)); __ j(overflow, &call_stub); - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &done); + JumpPatchSite patch_site(masm_); + patch_site.EmitJumpIfSmi(eax, &done); // Undo the optimistic add operation and call the shared stub. __ bind(&call_stub); @@ -1570,7 +1630,8 @@ void FullCodeGenerator::EmitConstantSmiAdd(Expression* expr, __ mov(edx, eax); __ mov(eax, Immediate(value)); } - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); + __ bind(&done); context()->Plug(eax); } @@ -1580,7 +1641,7 @@ void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, OverwriteMode mode, bool left_is_constant_smi, Smi* value) { - Label call_stub, done; + NearLabel call_stub, done; if (left_is_constant_smi) { __ mov(ecx, eax); __ mov(eax, Immediate(value)); @@ -1589,8 +1650,8 @@ void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, __ sub(Operand(eax), Immediate(value)); } __ j(overflow, &call_stub); - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &done); + JumpPatchSite patch_site(masm_); + patch_site.EmitJumpIfSmi(eax, &done); __ bind(&call_stub); if (left_is_constant_smi) { @@ -1603,7 +1664,8 @@ void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, } Token::Value op = Token::SUB; TypeRecordingBinaryOpStub stub(op, mode); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); + __ bind(&done); context()->Plug(eax); } @@ -1613,19 +1675,21 @@ void FullCodeGenerator::EmitConstantSmiShiftOp(Expression* expr, Token::Value op, OverwriteMode mode, Smi* value) { - Label call_stub, smi_case, done; + NearLabel call_stub, smi_case, done; int shift_value = value->value() & 0x1f; - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &smi_case); + JumpPatchSite patch_site(masm_); + patch_site.EmitJumpIfSmi(eax, &smi_case); + // Call stub. __ bind(&call_stub); __ mov(edx, eax); __ mov(eax, Immediate(value)); TypeRecordingBinaryOpStub stub(op, mode); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); __ jmp(&done); + // Smi case. __ bind(&smi_case); switch (op) { case Token::SHL: @@ -1675,17 +1739,19 @@ void FullCodeGenerator::EmitConstantSmiBitOp(Expression* expr, Token::Value op, OverwriteMode mode, Smi* value) { - Label smi_case, done; - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &smi_case); + NearLabel smi_case, done; + + JumpPatchSite patch_site(masm_); + patch_site.EmitJumpIfSmi(eax, &smi_case); // The order of the arguments does not matter for bit-ops with a // constant operand. __ mov(edx, Immediate(value)); TypeRecordingBinaryOpStub stub(op, mode); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); __ jmp(&done); + // Smi case. __ bind(&smi_case); switch (op) { case Token::BIT_OR: @@ -1753,19 +1819,20 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, // Do combined smi check of the operands. Left operand is on the // stack. Right operand is in eax. - Label done, stub_call, smi_case; + NearLabel done, smi_case, stub_call; __ pop(edx); __ mov(ecx, eax); __ or_(eax, Operand(edx)); - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &smi_case); + JumpPatchSite patch_site(masm_); + patch_site.EmitJumpIfSmi(eax, &smi_case); __ bind(&stub_call); __ mov(eax, ecx); TypeRecordingBinaryOpStub stub(op, mode); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); __ jmp(&done); + // Smi case. __ bind(&smi_case); __ mov(eax, edx); // Copy left operand in case of a stub call. @@ -1844,12 +1911,12 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, OverwriteMode mode) { __ pop(edx); TypeRecordingBinaryOpStub stub(op, mode); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), NULL); // NULL signals no inlined smi code. context()->Plug(eax); } -void FullCodeGenerator::EmitAssignment(Expression* expr) { +void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { // Invalid left-hand sides are rewritten to have a 'throw // ReferenceError' on the left-hand side. if (!expr->IsValidLeftHandSide()) { @@ -1897,6 +1964,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { break; } } + PrepareForBailoutForId(bailout_ast_id, TOS_REG); + context()->Plug(eax); } @@ -1969,8 +2038,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, } __ bind(&done); } - - context()->Plug(eax); } @@ -2007,10 +2074,10 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ push(Operand(esp, kPointerSize)); // Receiver is under value. __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(eax); - context()->DropAndPlug(1, eax); - } else { - context()->Plug(eax); + __ Drop(1); } + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(eax); } @@ -2048,6 +2115,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(eax); } + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(eax); } @@ -3103,6 +3171,13 @@ void FullCodeGenerator::EmitSwapElements(ZoneList* args) { __ test(temp, Immediate(kSmiTagMask)); __ j(not_zero, &slow_case); + // Check that both indices are valid. + __ mov(temp, FieldOperand(object, JSArray::kLengthOffset)); + __ cmp(temp, Operand(index_1)); + __ j(below_equal, &slow_case); + __ cmp(temp, Operand(index_2)); + __ j(below_equal, &slow_case); + // Bring addresses into index1 and index2. __ lea(index_1, CodeGenerator::FixedArrayElementOperand(elements, index_1)); __ lea(index_2, CodeGenerator::FixedArrayElementOperand(elements, index_2)); @@ -3708,8 +3783,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { } // Inline smi case if we are in a loop. - NearLabel stub_call; - Label done; + NearLabel stub_call, done; + JumpPatchSite patch_site(masm_); + if (ShouldInlineSmiCase(expr->op())) { if (expr->op() == Token::INC) { __ add(Operand(eax), Immediate(Smi::FromInt(1))); @@ -3719,8 +3795,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ j(overflow, &stub_call); // We could eliminate this smi check if we split the code at // the first smi check before calling ToNumber. - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &done); + patch_site.EmitJumpIfSmi(eax, &done); + __ bind(&stub_call); // Call stub. Undo operation first. if (expr->op() == Token::INC) { @@ -3738,7 +3814,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(eax, Immediate(Smi::FromInt(1))); TypeRecordingBinaryOpStub stub(expr->binary_op(), NO_OVERWRITE); - __ CallStub(&stub); + EmitCallIC(stub.GetCode(), &patch_site); __ bind(&done); // Store the value returned in eax. @@ -3749,6 +3825,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { { EffectContext context(this); EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Token::ASSIGN); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context.Plug(eax); } // For all contexts except EffectContext We have the result on // top of the stack. @@ -3759,6 +3837,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Token::ASSIGN); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); + context()->Plug(eax); } break; case NAMED_PROPERTY: { @@ -3766,6 +3846,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ pop(edx); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { context()->PlugTOS(); @@ -3780,6 +3861,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ pop(edx); Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { // Result is on the stack if (!context()->IsEffect()) { @@ -3957,7 +4039,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::INSTANCEOF: { VisitForStackValue(expr->right()); - InstanceofStub stub; + __ IncrementCounter(&Counters::instance_of_full, 1); + InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); __ test(eax, Operand(eax)); @@ -4005,21 +4088,22 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } bool inline_smi_code = ShouldInlineSmiCase(op); + JumpPatchSite patch_site(masm_); if (inline_smi_code) { NearLabel slow_case; __ mov(ecx, Operand(edx)); __ or_(ecx, Operand(eax)); - __ test(ecx, Immediate(kSmiTagMask)); - __ j(not_zero, &slow_case, not_taken); + patch_site.EmitJumpIfNotSmi(ecx, &slow_case); __ cmp(edx, Operand(eax)); Split(cc, if_true, if_false, NULL); __ bind(&slow_case); } // Record position and call the compare IC. - Handle ic = CompareIC::GetUninitialized(op); SetSourcePosition(expr->position()); - __ call(ic, RelocInfo::CODE_TARGET); + Handle ic = CompareIC::GetUninitialized(op); + EmitCallIC(ic, &patch_site); + PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); __ test(eax, Operand(eax)); Split(cc, if_true, if_false, fall_through); @@ -4123,6 +4207,16 @@ void FullCodeGenerator::EmitCallIC(Handle ic, RelocInfo::Mode mode) { } +void FullCodeGenerator::EmitCallIC(Handle ic, JumpPatchSite* patch_site) { + __ call(ic, RelocInfo::CODE_TARGET); + if (patch_site != NULL && patch_site->is_bound()) { + patch_site->EmitPatchInfo(); + } else { + __ nop(); // Signals no inlined code. + } +} + + void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); __ mov(Operand(ebp, frame_offset), value); diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index b34179a41c..9c9304d5a4 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -2049,13 +2049,23 @@ Condition CompareIC::ComputeCondition(Token::Value op) { } +static bool HasInlinedSmiCode(Address address) { + // The address of the instruction following the call. + Address test_instruction_address = + address + Assembler::kCallTargetAddressOffset; + + // If the instruction following the call is not a test al, nothing + // was inlined. + return *test_instruction_address == Assembler::kTestAlByte; +} + + void CompareIC::UpdateCaches(Handle x, Handle y) { HandleScope scope; Handle rewritten; -#ifdef DEBUG State previous_state = GetState(); -#endif - State state = TargetState(x, y); + + State state = TargetState(previous_state, HasInlinedSmiCode(address()), x, y); if (state == GENERIC) { CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS); rewritten = stub.GetCode(); @@ -2073,6 +2083,44 @@ void CompareIC::UpdateCaches(Handle x, Handle y) { Token::Name(op_)); } #endif + + // Activate inlined smi code. + if (previous_state == UNINITIALIZED) { + PatchInlinedSmiCode(address()); + } +} + + +void PatchInlinedSmiCode(Address address) { + // The address of the instruction following the call. + Address test_instruction_address = + address + Assembler::kCallTargetAddressOffset; + + // If the instruction following the call is not a test al, nothing + // was inlined. + if (*test_instruction_address != Assembler::kTestAlByte) { + ASSERT(*test_instruction_address == Assembler::kNopByte); + return; + } + + Address delta_address = test_instruction_address + 1; + // The delta to the start of the map check instruction and the + // condition code uses at the patched jump. + int8_t delta = *reinterpret_cast(delta_address); + if (FLAG_trace_ic) { + PrintF("[ patching ic at %p, test=%p, delta=%d\n", + address, test_instruction_address, delta); + } + + // Patch with a short conditional jump. There must be a + // short jump-if-carry/not-carry at this position. + Address jmp_address = test_instruction_address - delta; + ASSERT(*jmp_address == Assembler::kJncShortOpcode || + *jmp_address == Assembler::kJcShortOpcode); + Condition cc = *jmp_address == Assembler::kJncShortOpcode + ? not_zero + : zero; + *jmp_address = static_cast(Assembler::kJccShortPrefix | cc); } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index dc0f5e90f9..0e9773748e 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -315,6 +315,13 @@ void LCodeGen::CallCode(Handle code, __ call(code, mode); RecordSafepoint(&no_pointers, Safepoint::kNoDeoptimizationIndex); } + + // Signal that we don't inline smi code before these stubs in the + // optimizing code generator. + if (code->kind() == Code::TYPE_RECORDING_BINARY_OP_IC || + code->kind() == Code::COMPARE_IC) { + __ nop(); + } } @@ -1403,6 +1410,71 @@ void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) { } +Condition LCodeGen::EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object) { + ASSERT(!input.is(temp1)); + ASSERT(!input.is(temp2)); + ASSERT(!temp1.is(temp2)); + + __ test(input, Immediate(kSmiTagMask)); + __ j(equal, is_not_object); + + __ cmp(input, Factory::null_value()); + __ j(equal, is_object); + + __ mov(temp1, FieldOperand(input, HeapObject::kMapOffset)); + // Undetectable objects behave like undefined. + __ movzx_b(temp2, FieldOperand(temp1, Map::kBitFieldOffset)); + __ test(temp2, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, is_not_object); + + __ movzx_b(temp2, FieldOperand(temp1, Map::kInstanceTypeOffset)); + __ cmp(temp2, FIRST_JS_OBJECT_TYPE); + __ j(below, is_not_object); + __ cmp(temp2, LAST_JS_OBJECT_TYPE); + return below_equal; +} + + +void LCodeGen::DoIsObject(LIsObject* instr) { + Register reg = ToRegister(instr->input()); + Register result = ToRegister(instr->result()); + Register temp = ToRegister(instr->temp()); + Label is_false, is_true, done; + + Condition true_cond = EmitIsObject(reg, result, temp, &is_false, &is_true); + __ j(true_cond, &is_true); + + __ bind(&is_false); + __ mov(result, Handle(Heap::false_value())); + __ jmp(&done); + + __ bind(&is_true); + __ mov(result, Handle(Heap::true_value())); + + __ bind(&done); +} + + +void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { + Register reg = ToRegister(instr->input()); + Register temp = ToRegister(instr->temp()); + Register temp2 = ToRegister(instr->temp2()); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = EmitIsObject(reg, temp, temp2, false_label, true_label); + + EmitBranch(true_block, false_block, true_cond); +} + + void LCodeGen::DoIsSmi(LIsSmi* instr) { Operand input = ToOperand(instr->input()); Register result = ToRegister(instr->result()); @@ -1627,9 +1699,8 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { void LCodeGen::DoInstanceOf(LInstanceOf* instr) { - InstanceofStub stub; - __ push(ToOperand(instr->left())); - __ push(ToOperand(instr->right())); + // Object and function are in fixed registers eax and edx. + InstanceofStub stub(InstanceofStub::kArgsInRegisters); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); NearLabel true_value, done; @@ -1647,9 +1718,7 @@ void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) { int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); - InstanceofStub stub; - __ push(ToOperand(instr->left())); - __ push(ToOperand(instr->right())); + InstanceofStub stub(InstanceofStub::kArgsInRegisters); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); __ test(eax, Operand(eax)); EmitBranch(true_block, false_block, zero); @@ -2174,6 +2243,82 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { } +void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { + XMMRegister xmm_scratch = xmm0; + XMMRegister input_reg = ToDoubleRegister(instr->input()); + ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); + ExternalReference negative_infinity = + ExternalReference::address_of_negative_infinity(); + __ movdbl(xmm_scratch, Operand::StaticVariable(negative_infinity)); + __ ucomisd(xmm_scratch, input_reg); + DeoptimizeIf(equal, instr->environment()); + __ sqrtsd(input_reg, input_reg); +} + + +void LCodeGen::DoPower(LPower* instr) { + LOperand* left = instr->left(); + LOperand* right = instr->right(); + DoubleRegister result_reg = ToDoubleRegister(instr->result()); + Representation exponent_type = instr->hydrogen()->right()->representation(); + if (exponent_type.IsDouble()) { + // It is safe to use ebx directly since the instruction is marked + // as a call. + __ PrepareCallCFunction(4, ebx); + __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); + __ movdbl(Operand(esp, 1 * kDoubleSize), ToDoubleRegister(right)); + __ CallCFunction(ExternalReference::power_double_double_function(), 4); + } else if (exponent_type.IsInteger32()) { + // It is safe to use ebx directly since the instruction is marked + // as a call. + ASSERT(!ToRegister(right).is(ebx)); + __ PrepareCallCFunction(4, ebx); + __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); + __ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right)); + __ CallCFunction(ExternalReference::power_double_int_function(), 4); + } else { + ASSERT(exponent_type.IsTagged()); + CpuFeatures::Scope scope(SSE2); + Register right_reg = ToRegister(right); + + Label non_smi, call; + __ test(right_reg, Immediate(kSmiTagMask)); + __ j(not_zero, &non_smi); + __ SmiUntag(right_reg); + __ cvtsi2sd(result_reg, Operand(right_reg)); + __ jmp(&call); + + __ bind(&non_smi); + // It is safe to use ebx directly since the instruction is marked + // as a call. + ASSERT(!right_reg.is(ebx)); + __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , ebx); + DeoptimizeIf(not_equal, instr->environment()); + __ movdbl(result_reg, FieldOperand(right_reg, HeapNumber::kValueOffset)); + + __ bind(&call); + __ PrepareCallCFunction(4, ebx); + __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); + __ movdbl(Operand(esp, 1 * kDoubleSize), result_reg); + __ CallCFunction(ExternalReference::power_double_double_function(), 4); + } + + // Return value is in st(0) on ia32. + // Store it into the (fixed) result register. + __ sub(Operand(esp), Immediate(kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + __ movdbl(result_reg, Operand(esp, 0)); + __ add(Operand(esp), Immediate(kDoubleSize)); +} + + +void LCodeGen::DoMathLog(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + TranscendentalCacheSSE2Stub stub(TranscendentalCache::LOG); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { switch (instr->op()) { case kMathAbs: @@ -2188,6 +2333,13 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { case kMathSqrt: DoMathSqrt(instr); break; + case kMathPowHalf: + DoMathPowHalf(instr); + break; + case kMathLog: + DoMathLog(instr); + break; + default: UNREACHABLE(); } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h index 91b3fabca3..5ac7b1edb2 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -175,6 +175,8 @@ class LCodeGen BASE_EMBEDDED { void DoMathFloor(LUnaryMathOperation* instr); void DoMathRound(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr); + void DoMathPowHalf(LUnaryMathOperation* instr); + void DoMathLog(LUnaryMathOperation* instr); // Support for recording safepoint and position information. void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); @@ -195,6 +197,15 @@ class LCodeGen BASE_EMBEDDED { Condition EmitTypeofIs(Label* true_label, Label* false_label, Register input, Handle type_name); + // Emits optimized code for %_IsObject(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index e3a3d7bcb0..e1148fc186 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -206,6 +206,13 @@ void LIsNullAndBranch::PrintDataTo(StringStream* stream) const { } +void LIsObjectAndBranch::PrintDataTo(StringStream* stream) const { + stream->Add("if is_object("); + input()->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const { stream->Add("if is_smi("); input()->PrintTo(stream); @@ -460,12 +467,6 @@ int LChunk::NearestGapPos(int index) const { } -int LChunk::NearestNextGapPos(int index) const { - while (!IsGapAt(index)) index++; - return index; -} - - void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) { GetGapAt(index)->GetOrCreateParallelMove(LGap::START)->AddMove(from, to); } @@ -880,19 +881,6 @@ LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) { LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { - HBasicBlock* deopt_predecessor = instr->block()->deopt_predecessor(); - if (deopt_predecessor != NULL && - deopt_predecessor->inverted()) { - HEnvironment* env = current_block_->last_environment(); - HValue* value = env->Pop(); - ASSERT(value->IsConstant()); - Handle obj = HConstant::cast(value)->handle(); - ASSERT(*obj == *Factory::true_value() || *obj == *Factory::false_value()); - env->Push(*obj == *Factory::true_value() - ? current_block_->graph()->GetConstantFalse() - : current_block_->graph()->GetConstantTrue()); - } - return new LLabel(instr->block()); } @@ -1257,6 +1245,17 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { temp, first_id, second_id); + } else if (v->IsIsObject()) { + HIsObject* compare = HIsObject::cast(v); + ASSERT(compare->value()->representation().IsTagged()); + + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()), + temp1, + temp2, + first_id, + second_id); } else if (v->IsCompareJSObjectEq()) { HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v); return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()), @@ -1266,8 +1265,8 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { } else if (v->IsInstanceOf()) { HInstanceOf* instance_of = HInstanceOf::cast(v); LInstruction* result = - new LInstanceOfAndBranch(Use(instance_of->left()), - Use(instance_of->right()), + new LInstanceOfAndBranch(UseFixed(instance_of->left(), eax), + UseFixed(instance_of->right(), edx), first_id, second_id); return MarkAsCall(result, instr); @@ -1317,7 +1316,8 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) { LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LInstruction* result = - new LInstanceOf(Use(instr->left()), Use(instr->right())); + new LInstanceOf(UseFixed(instr->left(), eax), + UseFixed(instr->right(), edx)); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1337,7 +1337,7 @@ LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) { ++argument_count_; - LOperand* argument = Use(instr->argument()); + LOperand* argument = UseOrConstant(instr->argument()); return new LPushArgument(argument); } @@ -1360,21 +1360,29 @@ LInstruction* LChunkBuilder::DoCallConstantFunction( LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { - MathFunctionId op = instr->op(); - LOperand* input = UseRegisterAtStart(instr->value()); - LInstruction* result = new LUnaryMathOperation(input); - switch (op) { - case kMathAbs: - return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); - case kMathFloor: - return AssignEnvironment(DefineAsRegister(result)); - case kMathRound: - return AssignEnvironment(DefineAsRegister(result)); - case kMathSqrt: - return DefineSameAsFirst(result); - default: - UNREACHABLE(); - return NULL; + BuiltinFunctionId op = instr->op(); + if (op == kMathLog) { + LOperand* input = UseFixedDouble(instr->value(), xmm1); + LInstruction* result = new LUnaryMathOperation(input); + return MarkAsCall(DefineFixedDouble(result, xmm1), instr); + } else { + LOperand* input = UseRegisterAtStart(instr->value()); + LInstruction* result = new LUnaryMathOperation(input); + switch (op) { + case kMathAbs: + return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); + case kMathFloor: + return AssignEnvironment(DefineAsRegister(result)); + case kMathRound: + return AssignEnvironment(DefineAsRegister(result)); + case kMathSqrt: + return DefineSameAsFirst(result); + case kMathPowHalf: + return AssignEnvironment(DefineSameAsFirst(result)); + default: + UNREACHABLE(); + return NULL; + } } } @@ -1572,6 +1580,22 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { } +LInstruction* LChunkBuilder::DoPower(HPower* instr) { + ASSERT(instr->representation().IsDouble()); + // We call a C function for double power. It can't trigger a GC. + // We need to use fixed result register for the call. + Representation exponent_type = instr->right()->representation(); + ASSERT(instr->left()->representation().IsDouble()); + LOperand* left = UseFixedDouble(instr->left(), xmm1); + LOperand* right = exponent_type.IsDouble() ? + UseFixedDouble(instr->right(), xmm2) : + UseFixed(instr->right(), eax); + LPower* result = new LPower(left, right); + return MarkAsCall(DefineFixedDouble(result, xmm3), instr, + CAN_DEOPTIMIZE_EAGERLY); +} + + LInstruction* LChunkBuilder::DoCompare(HCompare* instr) { Token::Value op = instr->token(); if (instr->left()->representation().IsInteger32()) { @@ -1612,6 +1636,14 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) { } +LInstruction* LChunkBuilder::DoIsObject(HIsObject* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* value = UseRegister(instr->value()); + + return DefineAsRegister(new LIsObject(value, TempRegister())); +} + + LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) { ASSERT(instr->value()->representation().IsTagged()); LOperand* value = UseAtStart(instr->value()); diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index af0d5604dd..3f48e50e22 100644 --- a/deps/v8/src/ia32/lithium-ia32.h +++ b/deps/v8/src/ia32/lithium-ia32.h @@ -67,6 +67,7 @@ class LGapNode; // LLoadKeyedGeneric // LModI // LMulI +// LPower // LShiftI // LSubI // LCallConstantFunction @@ -123,6 +124,8 @@ class LGapNode; // LInteger32ToDouble // LIsNull // LIsNullAndBranch +// LIsObject +// LIsObjectAndBranch // LIsSmi // LIsSmiAndBranch // LLoadNamedField @@ -205,6 +208,8 @@ class LGapNode; V(Integer32ToDouble) \ V(IsNull) \ V(IsNullAndBranch) \ + V(IsObject) \ + V(IsObjectAndBranch) \ V(IsSmi) \ V(IsSmiAndBranch) \ V(HasInstanceType) \ @@ -229,6 +234,7 @@ class LGapNode; V(ObjectLiteral) \ V(OsrEntry) \ V(Parameter) \ + V(Power) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -668,7 +674,7 @@ class LUnaryMathOperation: public LUnaryOperation { DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) virtual void PrintDataTo(StringStream* stream) const; - MathFunctionId op() const { return hydrogen()->op(); } + BuiltinFunctionId op() const { return hydrogen()->op(); } }; @@ -745,6 +751,48 @@ class LIsNullAndBranch: public LIsNull { }; +class LIsObject: public LUnaryOperation { + public: + LIsObject(LOperand* value, LOperand* temp) + : LUnaryOperation(value), temp_(temp) {} + + DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object") + + LOperand* temp() const { return temp_; } + + private: + LOperand* temp_; +}; + + +class LIsObjectAndBranch: public LIsObject { + public: + LIsObjectAndBranch(LOperand* value, + LOperand* temp, + LOperand* temp2, + int true_block_id, + int false_block_id) + : LIsObject(value, temp), + temp2_(temp2), + true_block_id_(true_block_id), + false_block_id_(false_block_id) { } + + DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch") + virtual void PrintDataTo(StringStream* stream) const; + virtual bool IsControl() const { return true; } + + int true_block_id() const { return true_block_id_; } + int false_block_id() const { return false_block_id_; } + + LOperand* temp2() const { return temp2_; } + + private: + LOperand* temp2_; + int true_block_id_; + int false_block_id_; +}; + + class LIsSmi: public LUnaryOperation { public: explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {} @@ -1154,6 +1202,16 @@ class LAddI: public LBinaryOperation { }; +class LPower: public LBinaryOperation { + public: + LPower(LOperand* left, LOperand* right) + : LBinaryOperation(left, right) { } + + DECLARE_CONCRETE_INSTRUCTION(Power, "power") + DECLARE_HYDROGEN_ACCESSOR(Power) +}; + + class LArithmeticD: public LBinaryOperation { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) @@ -1890,7 +1948,6 @@ class LChunk: public ZoneObject { LGap* GetGapAt(int index) const; bool IsGapAt(int index) const; int NearestGapPos(int index) const; - int NearestNextGapPos(int index) const; void MarkEmptyBlocks(); const ZoneList* pointer_maps() const { return &pointer_maps_; } LLabel* GetLabel(int block_id) const { diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 84911ecec5..7c33906527 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -74,30 +74,6 @@ void MacroAssembler::RecordWriteHelper(Register object, } -void MacroAssembler::InNewSpace(Register object, - Register scratch, - Condition cc, - Label* branch) { - ASSERT(cc == equal || cc == not_equal); - if (Serializer::enabled()) { - // Can't do arithmetic on external references if it might get serialized. - mov(scratch, Operand(object)); - // The mask isn't really an address. We load it as an external reference in - // case the size of the new space is different between the snapshot maker - // and the running system. - and_(Operand(scratch), Immediate(ExternalReference::new_space_mask())); - cmp(Operand(scratch), Immediate(ExternalReference::new_space_start())); - j(cc, branch); - } else { - int32_t new_space_start = reinterpret_cast( - ExternalReference::new_space_start().address()); - lea(scratch, Operand(object, -new_space_start)); - and_(scratch, Heap::NewSpaceMask()); - j(cc, branch); - } -} - - void MacroAssembler::RecordWrite(Register object, int offset, Register value, @@ -109,7 +85,7 @@ void MacroAssembler::RecordWrite(Register object, // First, check if a write barrier is even needed. The tests below // catch stores of Smis and stores into young gen. - Label done; + NearLabel done; // Skip barrier if writing a smi. ASSERT_EQ(0, kSmiTag); @@ -1216,25 +1192,29 @@ MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, } -// If true, a Handle passed by value is passed and returned by -// using the location_ field directly. If false, it is passed and -// returned as a pointer to a handle. -#ifdef USING_BSD_ABI -static const bool kPassHandlesDirectly = true; +// If true, a Handle returned by value from a function with cdecl calling +// convention will be returned directly as a value of location_ field in a +// register eax. +// If false, it is returned as a pointer to a preallocated by caller memory +// region. Pointer to this region should be passed to a function as an +// implicit first argument. +#if defined(USING_BSD_ABI) || defined(__MINGW32__) +static const bool kReturnHandlesDirectly = true; #else -static const bool kPassHandlesDirectly = false; +static const bool kReturnHandlesDirectly = false; #endif Operand ApiParameterOperand(int index) { - return Operand(esp, (index + (kPassHandlesDirectly ? 0 : 1)) * kPointerSize); + return Operand( + esp, (index + (kReturnHandlesDirectly ? 0 : 1)) * kPointerSize); } void MacroAssembler::PrepareCallApiFunction(int argc, Register scratch) { - if (kPassHandlesDirectly) { + if (kReturnHandlesDirectly) { EnterApiExitFrame(argc); - // When handles as passed directly we don't have to allocate extra + // When handles are returned directly we don't have to allocate extra // space for and pass an out parameter. } else { // We allocate two additional slots: return value and pointer to it. @@ -1279,7 +1259,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, // Call the api function! call(function->address(), RelocInfo::RUNTIME_ENTRY); - if (!kPassHandlesDirectly) { + if (!kReturnHandlesDirectly) { // The returned value is a pointer to the handle holding the result. // Dereference this to get to the location. mov(eax, Operand(eax, 0)); diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 8407802104..6f5fa87297 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -70,10 +70,11 @@ class MacroAssembler: public Assembler { // Check if object is in new space. // scratch can be object itself, but it will be clobbered. + template void InNewSpace(Register object, Register scratch, Condition cc, // equal for new space, not_equal otherwise. - Label* branch); + LabelType* branch); // For page containing |object| mark region covering [object+offset] // dirty. |object| is the object being stored into, |value| is the @@ -658,6 +659,31 @@ class MacroAssembler: public Assembler { }; +template +void MacroAssembler::InNewSpace(Register object, + Register scratch, + Condition cc, + LabelType* branch) { + ASSERT(cc == equal || cc == not_equal); + if (Serializer::enabled()) { + // Can't do arithmetic on external references if it might get serialized. + mov(scratch, Operand(object)); + // The mask isn't really an address. We load it as an external reference in + // case the size of the new space is different between the snapshot maker + // and the running system. + and_(Operand(scratch), Immediate(ExternalReference::new_space_mask())); + cmp(Operand(scratch), Immediate(ExternalReference::new_space_start())); + j(cc, branch); + } else { + int32_t new_space_start = reinterpret_cast( + ExternalReference::new_space_start().address()); + lea(scratch, Operand(object, -new_space_start)); + and_(scratch, Heap::NewSpaceMask()); + j(cc, branch); + } +} + + // The code patcher is used to patch (typically) small parts of code e.g. for // debugging and other types of instrumentation. When using the code patcher // the exact number of bytes specified must be emitted. Is not legal to emit diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 352eae1f92..99888b0898 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -2133,8 +2133,8 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // ----------------------------------- SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasCustomCallGenerator()) { - const int id = function_info->custom_call_generator_id(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); MaybeObject* maybe_result = CompileCustomCall( id, object, holder, NULL, function, name); Object* result; @@ -2375,8 +2375,8 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, // ----------------------------------- SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasCustomCallGenerator()) { - const int id = function_info->custom_call_generator_id(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); MaybeObject* maybe_result = CompileCustomCall( id, object, holder, cell, function, name); Object* result; diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index cda0b1504a..645c6fdcf6 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -1951,7 +1951,7 @@ TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) { TRBinaryOpIC::TypeInfo TRBinaryOpIC::JoinTypes(TRBinaryOpIC::TypeInfo x, - TRBinaryOpIC::TypeInfo y) { + TRBinaryOpIC::TypeInfo y) { if (x == UNINITIALIZED) return y; if (y == UNINITIALIZED) return x; if (x == STRING && y == STRING) return STRING; @@ -2041,6 +2041,11 @@ MaybeObject* TypeRecordingBinaryOp_Patch(Arguments args) { TRBinaryOpIC::GetName(result_type), Token::Name(op)); } + + // Activate inlined smi code. + if (previous_type == TRBinaryOpIC::UNINITIALIZED) { + PatchInlinedSmiCode(ic.address()); + } } Handle builtins = Top::builtins(); @@ -2127,13 +2132,17 @@ const char* CompareIC::GetStateName(State state) { } -CompareIC::State CompareIC::TargetState(Handle x, Handle y) { - State state = GetState(); - if (state != UNINITIALIZED) return GENERIC; - if (x->IsSmi() && y->IsSmi()) return SMIS; - if (x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS; +CompareIC::State CompareIC::TargetState(State state, + bool has_inlined_smi_code, + Handle x, + Handle y) { + if (!has_inlined_smi_code && state != UNINITIALIZED) return GENERIC; + if (state == UNINITIALIZED && x->IsSmi() && y->IsSmi()) return SMIS; + if ((state == UNINITIALIZED || (state == SMIS && has_inlined_smi_code)) && + x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS; if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return GENERIC; - if (x->IsJSObject() && y->IsJSObject()) return OBJECTS; + if (state == UNINITIALIZED && + x->IsJSObject() && y->IsJSObject()) return OBJECTS; return GENERIC; } diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index 434c5024eb..8562bcc24a 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -582,7 +582,8 @@ class CompareIC: public IC { static const char* GetStateName(State state); private: - State TargetState(Handle x, Handle y); + State TargetState(State state, bool has_inlined_smi_code, + Handle x, Handle y); bool strict() const { return op_ == Token::EQ_STRICT; } Condition GetCondition() const { return ComputeCondition(op_); } @@ -591,6 +592,8 @@ class CompareIC: public IC { Token::Value op_; }; +// Helper for TRBinaryOpIC and CompareIC. +void PatchInlinedSmiCode(Address address); } } // namespace v8::internal diff --git a/deps/v8/src/json.js b/deps/v8/src/json.js index e8b732a52a..89009a9630 100644 --- a/deps/v8/src/json.js +++ b/deps/v8/src/json.js @@ -66,21 +66,10 @@ function JSONParse(text, reviver) { } } -function StackContains(stack, val) { - var length = stack.length; - for (var i = 0; i < length; i++) { - if (stack[i] === val) { - return true; - } - } - return false; -} - function SerializeArray(value, replacer, stack, indent, gap) { - if (StackContains(stack, value)) { + if (!%PushIfAbsent(stack, value)) { throw MakeTypeError('circular_structure', []); } - stack.push(value); var stepback = indent; indent += gap; var partial = []; @@ -108,10 +97,9 @@ function SerializeArray(value, replacer, stack, indent, gap) { } function SerializeObject(value, replacer, stack, indent, gap) { - if (StackContains(stack, value)) { + if (!%PushIfAbsent(stack, value)) { throw MakeTypeError('circular_structure', []); } - stack.push(value); var stepback = indent; indent += gap; var partial = []; @@ -158,49 +146,47 @@ function SerializeObject(value, replacer, stack, indent, gap) { function JSONSerialize(key, holder, replacer, stack, indent, gap) { var value = holder[key]; - if (IS_OBJECT(value) && value) { + if (IS_SPEC_OBJECT(value)) { var toJSON = value.toJSON; if (IS_FUNCTION(toJSON)) { - value = toJSON.call(value, key); + value = %_CallFunction(value, key, toJSON); } } if (IS_FUNCTION(replacer)) { - value = replacer.call(holder, key, value); + value = %_CallFunction(holder, key, value, replacer); } - // Unwrap value if necessary - if (IS_OBJECT(value)) { - if (IS_NUMBER_WRAPPER(value)) { - value = $Number(value); + if (IS_STRING(value)) { + return %QuoteJSONString(value); + } else if (IS_NUMBER(value)) { + return $isFinite(value) ? $String(value) : "null"; + } else if (IS_BOOLEAN(value)) { + return value ? "true" : "false"; + } else if (IS_NULL(value)) { + return "null"; + } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) { + // Non-callable object. If it's a primitive wrapper, it must be unwrapped. + if (IS_ARRAY(value)) { + return SerializeArray(value, replacer, stack, indent, gap); + } else if (IS_NUMBER_WRAPPER(value)) { + value = ToNumber(value); + return $isFinite(value) ? ToString(value) : "null"; } else if (IS_STRING_WRAPPER(value)) { - value = $String(value); + return %QuoteJSONString(ToString(value)); } else if (IS_BOOLEAN_WRAPPER(value)) { - value = %_ValueOf(value); + return %_ValueOf(value) ? "true" : "false"; + } else { + return SerializeObject(value, replacer, stack, indent, gap); } } - switch (typeof value) { - case "string": - return %QuoteJSONString(value); - case "object": - if (!value) { - return "null"; - } else if (IS_ARRAY(value)) { - return SerializeArray(value, replacer, stack, indent, gap); - } else { - return SerializeObject(value, replacer, stack, indent, gap); - } - case "number": - return $isFinite(value) ? $String(value) : "null"; - case "boolean": - return value ? "true" : "false"; - } + // Undefined or a callable object. + return void 0; } function BasicSerializeArray(value, stack, builder) { - if (StackContains(stack, value)) { + if (!%PushIfAbsent(stack, value)) { throw MakeTypeError('circular_structure', []); } - stack.push(value); builder.push("["); var len = value.length; for (var i = 0; i < len; i++) { @@ -220,10 +206,9 @@ function BasicSerializeArray(value, stack, builder) { function BasicSerializeObject(value, stack, builder) { - if (StackContains(stack, value)) { + if (!%PushIfAbsent(stack, value)) { throw MakeTypeError('circular_structure', []); } - stack.push(value); builder.push("{"); for (var p in value) { if (%HasLocalProperty(value, p)) { @@ -250,40 +235,41 @@ function BasicSerializeObject(value, stack, builder) { function BasicJSONSerialize(key, holder, stack, builder) { var value = holder[key]; - if (IS_OBJECT(value) && value) { + if (IS_SPEC_OBJECT(value)) { var toJSON = value.toJSON; - if (IS_FUNCTION(toJSON)) value = toJSON.call(value, $String(key)); + if (IS_FUNCTION(toJSON)) { + value = %_CallFunction(value, ToString(key), toJSON); + } } if (IS_STRING(value)) { builder.push(%QuoteJSONString(value)); } else if (IS_NUMBER(value)) { builder.push(($isFinite(value) ? %_NumberToString(value) : "null")); } else if (IS_BOOLEAN(value)) { - builder.push((value ? "true" : "false")); - } else if (IS_OBJECT(value)) { + builder.push(value ? "true" : "false"); + } else if (IS_NULL(value)) { + builder.push("null"); + } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) { + // Value is a non-callable object. // Unwrap value if necessary if (IS_NUMBER_WRAPPER(value)) { - value = %_ValueOf(value); - builder.push(($isFinite(value) ? %_NumberToString(value) : "null")); + value = ToNumber(value); + builder.push(($isFinite(value) ? %_NumberToString(value) : "null")); } else if (IS_STRING_WRAPPER(value)) { - builder.push(%QuoteJSONString(%_ValueOf(value))); + builder.push(%QuoteJSONString(ToString(value))); } else if (IS_BOOLEAN_WRAPPER(value)) { - builder.push((%_ValueOf(value) ? "true" : "false")); + builder.push(%_ValueOf(value) ? "true" : "false"); + } else if (IS_ARRAY(value)) { + BasicSerializeArray(value, stack, builder); } else { - // Regular non-wrapped object - if (!value) { - builder.push("null"); - } else if (IS_ARRAY(value)) { - BasicSerializeArray(value, stack, builder); - } else { - BasicSerializeObject(value, stack, builder); - } + BasicSerializeObject(value, stack, builder); } } } + function JSONStringify(value, replacer, space) { - if (IS_UNDEFINED(replacer) && IS_UNDEFINED(space)) { + if (%_ArgumentsLength() == 1) { var builder = []; BasicJSONSerialize('', {'': value}, [], builder); if (builder.length == 0) return; @@ -294,21 +280,18 @@ function JSONStringify(value, replacer, space) { if (IS_OBJECT(space)) { // Unwrap 'space' if it is wrapped if (IS_NUMBER_WRAPPER(space)) { - space = $Number(space); + space = ToNumber(space); } else if (IS_STRING_WRAPPER(space)) { - space = $String(space); + space = ToString(space); } } var gap; if (IS_NUMBER(space)) { - space = $Math.min(ToInteger(space), 10); - gap = ""; - for (var i = 0; i < space; i++) { - gap += " "; - } + space = MathMax(0, MathMin(ToInteger(space), 10)); + gap = SubString(" ", 0, space); } else if (IS_STRING(space)) { if (space.length > 10) { - gap = space.substring(0, 10); + gap = SubString(space, 0, 10); } else { gap = space; } diff --git a/deps/v8/src/lithium-allocator.cc b/deps/v8/src/lithium-allocator.cc index db0bc8b72d..ac61c17ba7 100644 --- a/deps/v8/src/lithium-allocator.cc +++ b/deps/v8/src/lithium-allocator.cc @@ -247,7 +247,7 @@ LOperand* LiveRange::CreateAssignedOperand() { LOperand* op = NULL; if (HasRegisterAssigned()) { ASSERT(!IsSpilled()); - if (assigned_double_) { + if (IsDouble()) { op = LDoubleRegister::Create(assigned_register()); } else { op = LRegister::Create(assigned_register()); @@ -290,19 +290,27 @@ void LiveRange::AdvanceLastProcessedMarker( void LiveRange::SplitAt(LifetimePosition position, LiveRange* result) { - ASSERT(Start().Value() <= position.Value()); + ASSERT(Start().Value() < position.Value()); ASSERT(result->IsEmpty()); // Find the last interval that ends before the position. If the // position is contained in one of the intervals in the chain, we // split that interval and use the first part. UseInterval* current = FirstSearchIntervalForPosition(position); + + // If the split position coincides with the beginning of a use interval + // we need to split use positons in a special way. + bool split_at_start = false; + while (current != NULL) { if (current->Contains(position)) { current->SplitAt(position); break; } UseInterval* next = current->next(); - if (next->start().Value() >= position.Value()) break; + if (next->start().Value() >= position.Value()) { + split_at_start = (next->start().Value() == position.Value()); + break; + } current = next; } @@ -319,9 +327,19 @@ void LiveRange::SplitAt(LifetimePosition position, LiveRange* result) { // position after it. UsePosition* use_after = first_pos_; UsePosition* use_before = NULL; - while (use_after != NULL && use_after->pos().Value() <= position.Value()) { - use_before = use_after; - use_after = use_after->next(); + if (split_at_start) { + // The split position coincides with the beginning of a use interval (the + // end of a lifetime hole). Use at this position should be attributed to + // the split child because split child owns use interval covering it. + while (use_after != NULL && use_after->pos().Value() < position.Value()) { + use_before = use_after; + use_after = use_after->next(); + } + } else { + while (use_after != NULL && use_after->pos().Value() <= position.Value()) { + use_before = use_after; + use_after = use_after->next(); + } } // Partition original use positions to the two live ranges. @@ -508,7 +526,7 @@ LifetimePosition LiveRange::FirstIntersection(LiveRange* other) { } if (a->start().Value() < b->start().Value()) { a = a->next(); - if (a == NULL && a->start().Value() > other->End().Value()) break; + if (a == NULL || a->start().Value() > other->End().Value()) break; AdvanceLastProcessedMarker(a, advance_last_processed_up_to); } else { b = b->next(); @@ -567,17 +585,12 @@ void LAllocator::AddInitialIntervals(HBasicBlock* block, LifetimePosition start = LifetimePosition::FromInstructionIndex( block->first_instruction_index()); LifetimePosition end = LifetimePosition::FromInstructionIndex( - block->last_instruction_index()); + block->last_instruction_index()).NextInstruction(); BitVector::Iterator iterator(live_out); while (!iterator.Done()) { int operand_index = iterator.Current(); LiveRange* range = LiveRangeFor(operand_index); - if (!range->IsEmpty() && - range->Start().Value() == end.NextInstruction().Value()) { - range->AddUseInterval(start, end.NextInstruction()); - } else { - range->AddUseInterval(start, end); - } + range->AddUseInterval(start, end); iterator.Advance(); } } @@ -625,7 +638,7 @@ LiveRange* LAllocator::FixedLiveRangeFor(int index) { if (result == NULL) { result = new LiveRange(FixedLiveRangeID(index)); ASSERT(result->IsFixed()); - result->set_assigned_register(index, false); + result->set_assigned_register(index, GENERAL_REGISTERS); fixed_live_ranges_[index] = result; } return result; @@ -642,7 +655,7 @@ LiveRange* LAllocator::FixedDoubleLiveRangeFor(int index) { if (result == NULL) { result = new LiveRange(FixedDoubleLiveRangeID(index)); ASSERT(result->IsFixed()); - result->set_assigned_register(index, true); + result->set_assigned_register(index, DOUBLE_REGISTERS); fixed_double_live_ranges_[index] = result; } return result; @@ -960,8 +973,8 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) { } } } - Use(block_start_position, curr_position, temp, NULL); - Define(curr_position.PrevInstruction(), temp, NULL); + Use(block_start_position, curr_position.InstructionEnd(), temp, NULL); + Define(curr_position, temp, NULL); } } } @@ -1258,14 +1271,6 @@ void LAllocator::BuildLiveRanges() { } -void LAllocator::AllocateGeneralRegisters() { - HPhase phase("Allocate general registers", this); - num_registers_ = Register::kNumAllocatableRegisters; - mode_ = CPU_REGISTERS; - AllocateRegisters(); -} - - bool LAllocator::SafePointsAreInOrder() const { const ZoneList* pointer_maps = chunk_->pointer_maps(); int safe_point = 0; @@ -1397,10 +1402,18 @@ void LAllocator::ProcessOsrEntry() { } +void LAllocator::AllocateGeneralRegisters() { + HPhase phase("Allocate general registers", this); + num_registers_ = Register::kNumAllocatableRegisters; + mode_ = GENERAL_REGISTERS; + AllocateRegisters(); +} + + void LAllocator::AllocateDoubleRegisters() { HPhase phase("Allocate double registers", this); num_registers_ = DoubleRegister::kNumAllocatableRegisters; - mode_ = XMM_REGISTERS; + mode_ = DOUBLE_REGISTERS; AllocateRegisters(); } @@ -1411,7 +1424,7 @@ void LAllocator::AllocateRegisters() { for (int i = 0; i < live_ranges_.length(); ++i) { if (live_ranges_[i] != NULL) { - if (HasDoubleValue(live_ranges_[i]->id()) == (mode_ == XMM_REGISTERS)) { + if (RequiredRegisterKind(live_ranges_[i]->id()) == mode_) { AddToUnhandledUnsorted(live_ranges_[i]); } } @@ -1422,7 +1435,7 @@ void LAllocator::AllocateRegisters() { ASSERT(active_live_ranges_.is_empty()); ASSERT(inactive_live_ranges_.is_empty()); - if (mode_ == XMM_REGISTERS) { + if (mode_ == DOUBLE_REGISTERS) { for (int i = 0; i < fixed_double_live_ranges_.length(); ++i) { LiveRange* current = fixed_double_live_ranges_.at(i); if (current != NULL) { @@ -1463,11 +1476,7 @@ void LAllocator::AllocateRegisters() { current->Start().NextInstruction().Value()) { // Do not spill live range eagerly if use position that can benefit from // the register is too close to the start of live range. - LiveRange* part = Split(current, - current->Start().NextInstruction(), - pos->pos()); - Spill(current); - AddToUnhandledSorted(part); + SpillBetween(current, current->Start(), pos->pos()); ASSERT(UnhandledIsSorted()); continue; } @@ -1521,6 +1530,16 @@ void LAllocator::Setup() { } +const char* LAllocator::RegisterName(int allocation_index) { + ASSERT(mode_ != NONE); + if (mode_ == GENERAL_REGISTERS) { + return Register::AllocationIndexToString(allocation_index); + } else { + return DoubleRegister::AllocationIndexToString(allocation_index); + } +} + + void LAllocator::TraceAlloc(const char* msg, ...) { if (FLAG_trace_alloc) { va_list arguments; @@ -1544,10 +1563,12 @@ bool LAllocator::HasTaggedValue(int virtual_register) const { } -bool LAllocator::HasDoubleValue(int virtual_register) const { +RegisterKind LAllocator::RequiredRegisterKind(int virtual_register) const { HValue* value = graph()->LookupValue(virtual_register); - if (value == NULL) return false; - return value->representation().IsDouble(); + if (value != NULL && value->representation().IsDouble()) { + return DOUBLE_REGISTERS; + } + return GENERAL_REGISTERS; } @@ -1728,16 +1749,22 @@ void LAllocator::InactiveToActive(LiveRange* range) { } +// TryAllocateFreeReg and AllocateBlockedReg assume this +// when allocating local arrays. +STATIC_ASSERT(DoubleRegister::kNumAllocatableRegisters >= + Register::kNumAllocatableRegisters); + + bool LAllocator::TryAllocateFreeReg(LiveRange* current) { - LifetimePosition max_pos = LifetimePosition::FromInstructionIndex( - chunk_->instructions()->length() + 1); - ASSERT(DoubleRegister::kNumAllocatableRegisters >= - Register::kNumAllocatableRegisters); - EmbeddedVector - free_pos(max_pos); + LifetimePosition free_until_pos[DoubleRegister::kNumAllocatableRegisters]; + + for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) { + free_until_pos[i] = LifetimePosition::MaxPosition(); + } + for (int i = 0; i < active_live_ranges_.length(); ++i) { LiveRange* cur_active = active_live_ranges_.at(i); - free_pos[cur_active->assigned_register()] = + free_until_pos[cur_active->assigned_register()] = LifetimePosition::FromInstructionIndex(0); } @@ -1748,67 +1775,83 @@ bool LAllocator::TryAllocateFreeReg(LiveRange* current) { cur_inactive->FirstIntersection(current); if (!next_intersection.IsValid()) continue; int cur_reg = cur_inactive->assigned_register(); - free_pos[cur_reg] = Min(free_pos[cur_reg], next_intersection); + free_until_pos[cur_reg] = Min(free_until_pos[cur_reg], next_intersection); } - UsePosition* pos = current->FirstPosWithHint(); - if (pos != NULL) { - LOperand* hint = pos->hint(); + UsePosition* hinted_use = current->FirstPosWithHint(); + if (hinted_use != NULL) { + LOperand* hint = hinted_use->hint(); if (hint->IsRegister() || hint->IsDoubleRegister()) { int register_index = hint->index(); - TraceAlloc("Found reg hint %d for live range %d (free [%d, end %d[)\n", - register_index, - current->id(), - free_pos[register_index].Value(), - current->End().Value()); - if (free_pos[register_index].Value() >= current->End().Value()) { - TraceAlloc("Assigning preferred reg %d to live range %d\n", - register_index, + TraceAlloc( + "Found reg hint %s (free until [%d) for live range %d (end %d[).\n", + RegisterName(register_index), + free_until_pos[register_index].Value(), + current->id(), + current->End().Value()); + + // The desired register is free until the end of the current live range. + if (free_until_pos[register_index].Value() >= current->End().Value()) { + TraceAlloc("Assigning preferred reg %s to live range %d\n", + RegisterName(register_index), current->id()); - current->set_assigned_register(register_index, mode_ == XMM_REGISTERS); + current->set_assigned_register(register_index, mode_); return true; } } } - int max_reg = 0; + // Find the register which stays free for the longest time. + int reg = 0; for (int i = 1; i < RegisterCount(); ++i) { - if (free_pos[i].Value() > free_pos[max_reg].Value()) { - max_reg = i; + if (free_until_pos[i].Value() > free_until_pos[reg].Value()) { + reg = i; } } - if (free_pos[max_reg].InstructionIndex() == 0) { + LifetimePosition pos = free_until_pos[reg]; + + if (pos.Value() <= current->Start().Value()) { + // All registers are blocked. return false; - } else if (free_pos[max_reg].Value() >= current->End().Value()) { - TraceAlloc("Assigning reg %d to live range %d\n", max_reg, current->id()); - current->set_assigned_register(max_reg, mode_ == XMM_REGISTERS); - } else { - // Split the interval at the nearest gap and never split an interval at its - // start position. - LifetimePosition pos = - LifetimePosition::FromInstructionIndex( - chunk_->NearestGapPos(free_pos[max_reg].InstructionIndex())); - if (pos.Value() <= current->Start().Value()) return false; - LiveRange* second_range = Split(current, pos); - AddToUnhandledSorted(second_range); - current->set_assigned_register(max_reg, mode_ == XMM_REGISTERS); } + if (pos.Value() < current->End().Value()) { + // Register reg is available at the range start but becomes blocked before + // the range end. Split current at position where it becomes blocked. + LiveRange* tail = SplitAt(current, pos); + AddToUnhandledSorted(tail); + } + + + // Register reg is available at the range start and is free until + // the range end. + ASSERT(pos.Value() >= current->End().Value()); + TraceAlloc("Assigning free reg %s to live range %d\n", + RegisterName(reg), + current->id()); + current->set_assigned_register(reg, mode_); + return true; } void LAllocator::AllocateBlockedReg(LiveRange* current) { - LifetimePosition max_pos = - LifetimePosition::FromInstructionIndex( - chunk_->instructions()->length() + 1); - ASSERT(DoubleRegister::kNumAllocatableRegisters >= - Register::kNumAllocatableRegisters); - EmbeddedVector - use_pos(max_pos); - EmbeddedVector - block_pos(max_pos); + UsePosition* register_use = current->NextRegisterPosition(current->Start()); + if (register_use == NULL) { + // There is no use in the current live range that requires a register. + // We can just spill it. + Spill(current); + return; + } + + + LifetimePosition use_pos[DoubleRegister::kNumAllocatableRegisters]; + LifetimePosition block_pos[DoubleRegister::kNumAllocatableRegisters]; + + for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) { + use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition(); + } for (int i = 0; i < active_live_ranges_.length(); ++i) { LiveRange* range = active_live_ranges_[i]; @@ -1841,47 +1884,63 @@ void LAllocator::AllocateBlockedReg(LiveRange* current) { } } - int max_reg = 0; + int reg = 0; for (int i = 1; i < RegisterCount(); ++i) { - if (use_pos[i].Value() > use_pos[max_reg].Value()) { - max_reg = i; + if (use_pos[i].Value() > use_pos[reg].Value()) { + reg = i; } } - UsePosition* first_usage = current->NextRegisterPosition(current->Start()); - if (first_usage == NULL) { - Spill(current); - } else if (use_pos[max_reg].Value() < first_usage->pos().Value()) { - SplitAndSpill(current, current->Start(), first_usage->pos()); - } else { - if (block_pos[max_reg].Value() < current->End().Value()) { - // Split current before blocked position. - LiveRange* second_range = Split(current, - current->Start(), - block_pos[max_reg]); - AddToUnhandledSorted(second_range); - } + LifetimePosition pos = use_pos[reg]; - current->set_assigned_register(max_reg, mode_ == XMM_REGISTERS); - SplitAndSpillIntersecting(current); + if (pos.Value() < register_use->pos().Value()) { + // All registers are blocked before the first use that requires a register. + // Spill starting part of live range up to that use. + // + // Corner case: the first use position is equal to the start of the range. + // In this case we have nothing to spill and SpillBetween will just return + // this range to the list of unhandled ones. This will lead to the infinite + // loop. + ASSERT(current->Start().Value() < register_use->pos().Value()); + SpillBetween(current, current->Start(), register_use->pos()); + return; } + + if (block_pos[reg].Value() < current->End().Value()) { + // Register becomes blocked before the current range end. Split before that + // position. + LiveRange* tail = SplitBetween(current, + current->Start(), + block_pos[reg].InstructionStart()); + AddToUnhandledSorted(tail); + } + + // Register reg is not blocked for the whole range. + ASSERT(block_pos[reg].Value() >= current->End().Value()); + TraceAlloc("Assigning blocked reg %s to live range %d\n", + RegisterName(reg), + current->id()); + current->set_assigned_register(reg, mode_); + + // This register was not free. Thus we need to find and spill + // parts of active and inactive live regions that use the same register + // at the same lifetime positions as current. + SplitAndSpillIntersecting(current); } void LAllocator::SplitAndSpillIntersecting(LiveRange* current) { ASSERT(current->HasRegisterAssigned()); int reg = current->assigned_register(); - LifetimePosition split_pos = - LifetimePosition::FromInstructionIndex( - chunk_->NearestGapPos(current->Start().InstructionIndex())); + LifetimePosition split_pos = current->Start(); for (int i = 0; i < active_live_ranges_.length(); ++i) { LiveRange* range = active_live_ranges_[i]; if (range->assigned_register() == reg) { UsePosition* next_pos = range->NextRegisterPosition(current->Start()); if (next_pos == NULL) { - SplitAndSpill(range, split_pos); + SpillAfter(range, split_pos); } else { - SplitAndSpill(range, split_pos, next_pos->pos()); + SpillBetween(range, split_pos, next_pos->pos()); } ActiveToHandled(range); --i; @@ -1896,10 +1955,10 @@ void LAllocator::SplitAndSpillIntersecting(LiveRange* current) { if (next_intersection.IsValid()) { UsePosition* next_pos = range->NextRegisterPosition(current->Start()); if (next_pos == NULL) { - SplitAndSpill(range, split_pos); + SpillAfter(range, split_pos); } else { next_intersection = Min(next_intersection, next_pos->pos()); - SplitAndSpill(range, split_pos, next_intersection); + SpillBetween(range, split_pos, next_intersection); } InactiveToHandled(range); --i; @@ -1909,19 +1968,50 @@ void LAllocator::SplitAndSpillIntersecting(LiveRange* current) { } -LiveRange* LAllocator::Split(LiveRange* range, - LifetimePosition start, - LifetimePosition end) { +bool LAllocator::IsBlockBoundary(LifetimePosition pos) { + return pos.IsInstructionStart() && + chunk_->instructions()->at(pos.InstructionIndex())->IsLabel(); +} + + +void LAllocator::AddGapMove(int pos, LiveRange* prev, LiveRange* next) { + UsePosition* prev_pos = prev->AddUsePosition( + LifetimePosition::FromInstructionIndex(pos)); + UsePosition* next_pos = next->AddUsePosition( + LifetimePosition::FromInstructionIndex(pos)); + LOperand* prev_operand = prev_pos->operand(); + LOperand* next_operand = next_pos->operand(); + LGap* gap = chunk_->GetGapAt(pos); + gap->GetOrCreateParallelMove(LGap::START)-> + AddMove(prev_operand, next_operand); + next_pos->set_hint(prev_operand); +} + + +LiveRange* LAllocator::SplitAt(LiveRange* range, LifetimePosition pos) { ASSERT(!range->IsFixed()); - TraceAlloc("Splitting live range %d in position between [%d, %d[\n", + TraceAlloc("Splitting live range %d at %d\n", range->id(), pos.Value()); + + if (pos.Value() <= range->Start().Value()) return range; + + LiveRange* result = LiveRangeFor(next_virtual_register_++); + range->SplitAt(pos, result); + return result; +} + + +LiveRange* LAllocator::SplitBetween(LiveRange* range, + LifetimePosition start, + LifetimePosition end) { + ASSERT(!range->IsFixed()); + TraceAlloc("Splitting live range %d in position between [%d, %d]\n", range->id(), start.Value(), end.Value()); - LifetimePosition split_pos = FindOptimalSplitPos( - start, end.PrevInstruction().InstructionEnd()); + LifetimePosition split_pos = FindOptimalSplitPos(start, end); ASSERT(split_pos.Value() >= start.Value()); - return Split(range, split_pos); + return SplitAt(range, split_pos); } @@ -1944,81 +2034,52 @@ LifetimePosition LAllocator::FindOptimalSplitPos(LifetimePosition start, } HBasicBlock* block = end_block; - // Move to the most outside loop header. + // Find header of outermost loop. while (block->parent_loop_header() != NULL && block->parent_loop_header()->block_id() > start_block->block_id()) { block = block->parent_loop_header(); } - if (block == end_block) { - return end; - } + if (block == end_block) return end; return LifetimePosition::FromInstructionIndex( block->first_instruction_index()); } -bool LAllocator::IsBlockBoundary(LifetimePosition pos) { - return pos.IsInstructionStart() && - chunk_->instructions()->at(pos.InstructionIndex())->IsLabel(); +void LAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { + LiveRange* second_part = SplitAt(range, pos); + Spill(second_part); } -void LAllocator::AddGapMove(int pos, LiveRange* prev, LiveRange* next) { - UsePosition* prev_pos = prev->AddUsePosition( - LifetimePosition::FromInstructionIndex(pos)); - UsePosition* next_pos = next->AddUsePosition( - LifetimePosition::FromInstructionIndex(pos)); - LOperand* prev_operand = prev_pos->operand(); - LOperand* next_operand = next_pos->operand(); - LGap* gap = chunk_->GetGapAt(pos); - gap->GetOrCreateParallelMove(LGap::START)-> - AddMove(prev_operand, next_operand); - next_pos->set_hint(prev_operand); -} - +void LAllocator::SpillBetween(LiveRange* range, + LifetimePosition start, + LifetimePosition end) { + ASSERT(start.Value() < end.Value()); + LiveRange* second_part = SplitAt(range, start); -LiveRange* LAllocator::Split(LiveRange* range, LifetimePosition pos) { - ASSERT(!range->IsFixed()); - TraceAlloc("Splitting live range %d at %d\n", range->id(), pos.Value()); - if (pos.Value() <= range->Start().Value()) { - return range; - } - LiveRange* result = LiveRangeFor(next_virtual_register_++); - range->SplitAt(pos, result); - return result; -} + if (second_part->Start().Value() < end.Value()) { + // The split result intersects with [start, end[. + // Split it at position between ]start+1, end[, spill the middle part + // and put the rest to unhandled. + LiveRange* third_part = SplitBetween( + second_part, + second_part->Start().InstructionEnd(), + end.PrevInstruction().InstructionEnd()); + ASSERT(third_part != second_part); -void LAllocator::SplitAndSpill(LiveRange* range, - LifetimePosition start, - LifetimePosition end) { - // We have an interval range and want to make sure that it is - // spilled at start and at most spilled until end. - ASSERT(start.Value() < end.Value()); - LiveRange* tail_part = Split(range, start); - if (tail_part->Start().Value() < end.Value()) { - LiveRange* third_part = Split(tail_part, - tail_part->Start().NextInstruction(), - end); - Spill(tail_part); - ASSERT(third_part != tail_part); + Spill(second_part); AddToUnhandledSorted(third_part); } else { - AddToUnhandledSorted(tail_part); + // The split result does not intersect with [start, end[. + // Nothing to spill. Just put it to unhandled as whole. + AddToUnhandledSorted(second_part); } } -void LAllocator::SplitAndSpill(LiveRange* range, LifetimePosition at) { - at = LifetimePosition::FromInstructionIndex( - chunk_->NearestGapPos(at.InstructionIndex())); - LiveRange* second_part = Split(range, at); - Spill(second_part); -} - - void LAllocator::Spill(LiveRange* range) { ASSERT(!range->IsSpilled()); TraceAlloc("Spilling live range %d\n", range->id()); @@ -2026,7 +2087,7 @@ void LAllocator::Spill(LiveRange* range) { if (!first->HasAllocatedSpillOperand()) { LOperand* op = TryReuseSpillSlot(range); - if (op == NULL) op = chunk_->GetNextSpillSlot(mode_ == XMM_REGISTERS); + if (op == NULL) op = chunk_->GetNextSpillSlot(mode_ == DOUBLE_REGISTERS); first->SetSpillOperand(op); } range->MakeSpilled(); diff --git a/deps/v8/src/lithium-allocator.h b/deps/v8/src/lithium-allocator.h index 52fee6455f..3ec984e262 100644 --- a/deps/v8/src/lithium-allocator.h +++ b/deps/v8/src/lithium-allocator.h @@ -55,6 +55,7 @@ class LPointerMap; class LStackSlot; class LRegister; + // This class represents a single point of a LOperand's lifetime. // For each lithium instruction there are exactly two lifetime positions: // the beginning and the end of the instruction. Lifetime positions for @@ -121,7 +122,13 @@ class LifetimePosition { // instruction. bool IsValid() const { return value_ != -1; } - static LifetimePosition Invalid() { return LifetimePosition(); } + static inline LifetimePosition Invalid() { return LifetimePosition(); } + + static inline LifetimePosition MaxPosition() { + // We have to use this kind of getter instead of static member due to + // crash bug in GDB. + return LifetimePosition(kMaxInt); + } private: static const int kStep = 2; @@ -135,6 +142,13 @@ class LifetimePosition { }; +enum RegisterKind { + NONE, + GENERAL_REGISTERS, + DOUBLE_REGISTERS +}; + + class LOperand: public ZoneObject { public: enum Kind { @@ -594,8 +608,8 @@ class LiveRange: public ZoneObject { explicit LiveRange(int id) : id_(id), spilled_(false), - assigned_double_(false), assigned_register_(kInvalidAssignment), + assigned_register_kind_(NONE), last_interval_(NULL), first_interval_(NULL), first_pos_(NULL), @@ -620,10 +634,10 @@ class LiveRange: public ZoneObject { LOperand* CreateAssignedOperand(); int assigned_register() const { return assigned_register_; } int spill_start_index() const { return spill_start_index_; } - void set_assigned_register(int reg, bool double_reg) { + void set_assigned_register(int reg, RegisterKind register_kind) { ASSERT(!HasRegisterAssigned() && !IsSpilled()); assigned_register_ = reg; - assigned_double_ = double_reg; + assigned_register_kind_ = register_kind; ConvertOperands(); } void MakeSpilled() { @@ -652,9 +666,13 @@ class LiveRange: public ZoneObject { // Can this live range be spilled at this position. bool CanBeSpilled(LifetimePosition pos); + // Split this live range at the given position which must follow the start of + // the range. + // All uses following the given position will be moved from this + // live range to the result live range. void SplitAt(LifetimePosition position, LiveRange* result); - bool IsDouble() const { return assigned_double_; } + bool IsDouble() const { return assigned_register_kind_ == DOUBLE_REGISTERS; } bool HasRegisterAssigned() const { return assigned_register_ != kInvalidAssignment; } @@ -721,8 +739,8 @@ class LiveRange: public ZoneObject { int id_; bool spilled_; - bool assigned_double_; int assigned_register_; + RegisterKind assigned_register_kind_; UseInterval* last_interval_; UseInterval* first_interval_; UsePosition* first_pos_; @@ -774,8 +792,8 @@ class LAllocator BASE_EMBEDDED { // Checks whether the value of a given virtual register is tagged. bool HasTaggedValue(int virtual_register) const; - // Checks whether the value of a given virtual register is a double. - bool HasDoubleValue(int virtual_register) const; + // Returns the register kind required by the given virtual register. + RegisterKind RequiredRegisterKind(int virtual_register) const; // Begin a new instruction. void BeginInstruction(); @@ -814,12 +832,6 @@ class LAllocator BASE_EMBEDDED { #endif private: - enum OperationMode { - NONE, - CPU_REGISTERS, - XMM_REGISTERS - }; - void MeetRegisterConstraints(); void ResolvePhis(); void BuildLiveRanges(); @@ -871,17 +883,38 @@ class LAllocator BASE_EMBEDDED { // Helper methods for allocating registers. bool TryAllocateFreeReg(LiveRange* range); void AllocateBlockedReg(LiveRange* range); - void SplitAndSpillIntersecting(LiveRange* range); + + // Live range splitting helpers. + + // Split the given range at the given position. + // If range starts at or after the given position then the + // original range is returned. + // Otherwise returns the live range that starts at pos and contains + // all uses from the original range that follow pos. Uses at pos will + // still be owned by the original range after splitting. + LiveRange* SplitAt(LiveRange* range, LifetimePosition pos); + + // Split the given range in a position from the interval [start, end]. + LiveRange* SplitBetween(LiveRange* range, + LifetimePosition start, + LifetimePosition end); + + // Find a lifetime position in the interval [start, end] which + // is optimal for splitting: it is either header of the outermost + // loop covered by this interval or the latest possible position. LifetimePosition FindOptimalSplitPos(LifetimePosition start, LifetimePosition end); - LiveRange* Split(LiveRange* range, - LifetimePosition start, - LifetimePosition end); - LiveRange* Split(LiveRange* range, LifetimePosition split_pos); - void SplitAndSpill(LiveRange* range, - LifetimePosition start, - LifetimePosition end); - void SplitAndSpill(LiveRange* range, LifetimePosition at); + + // Spill the given life range after position pos. + void SpillAfter(LiveRange* range, LifetimePosition pos); + + // Spill the given life range after position start and up to position end. + void SpillBetween(LiveRange* range, + LifetimePosition start, + LifetimePosition end); + + void SplitAndSpillIntersecting(LiveRange* range); + void Spill(LiveRange* range); bool IsBlockBoundary(LifetimePosition pos); void AddGapMove(int pos, LiveRange* prev, LiveRange* next); @@ -914,6 +947,8 @@ class LAllocator BASE_EMBEDDED { HPhi* LookupPhi(LOperand* operand) const; LGap* GetLastGap(HBasicBlock* block) const; + const char* RegisterName(int allocation_index); + LChunk* chunk_; ZoneList summaries_; InstructionSummary* next_summary_; @@ -938,7 +973,7 @@ class LAllocator BASE_EMBEDDED { // Next virtual register number to be assigned to temporaries. int next_virtual_register_; - OperationMode mode_; + RegisterKind mode_; int num_registers_; HGraph* graph_; diff --git a/deps/v8/src/log-utils.cc b/deps/v8/src/log-utils.cc index d6d8754b23..c7b75679ea 100644 --- a/deps/v8/src/log-utils.cc +++ b/deps/v8/src/log-utils.cc @@ -273,29 +273,7 @@ void LogMessageBuilder::Append(String* str) { void LogMessageBuilder::AppendAddress(Address addr) { - static Address last_address_ = NULL; - AppendAddress(addr, last_address_); - last_address_ = addr; -} - - -void LogMessageBuilder::AppendAddress(Address addr, Address bias) { - if (!FLAG_compress_log) { - Append("0x%" V8PRIxPTR, addr); - } else if (bias == NULL) { - Append("%" V8PRIxPTR, addr); - } else { - uintptr_t delta; - char sign; - if (addr >= bias) { - delta = addr - bias; - sign = '+'; - } else { - delta = bias - addr; - sign = '-'; - } - Append("%c%" V8PRIxPTR, sign, delta); - } + Append("0x%" V8PRIxPTR, addr); } @@ -343,24 +321,6 @@ void LogMessageBuilder::AppendStringPart(const char* str, int len) { } -bool LogMessageBuilder::StoreInCompressor(LogRecordCompressor* compressor) { - return compressor->Store(Vector(Log::message_buffer_, pos_)); -} - - -bool LogMessageBuilder::RetrieveCompressedPrevious( - LogRecordCompressor* compressor, const char* prefix) { - pos_ = 0; - if (prefix[0] != '\0') Append(prefix); - Vector prev_record(Log::message_buffer_ + pos_, - Log::kMessageBufferSize - pos_); - const bool has_prev = compressor->RetrievePreviousCompressed(&prev_record); - if (!has_prev) return false; - pos_ += prev_record.length(); - return true; -} - - void LogMessageBuilder::WriteToLogFile() { ASSERT(pos_ <= Log::kMessageBufferSize); const int written = Log::Write(Log::message_buffer_, pos_); @@ -369,145 +329,6 @@ void LogMessageBuilder::WriteToLogFile() { } } - -// Formatting string for back references to the whole line. E.g. "#2" means -// "the second line above". -const char* LogRecordCompressor::kLineBackwardReferenceFormat = "#%d"; - -// Formatting string for back references. E.g. "#2:10" means -// "the second line above, start from char 10 (0-based)". -const char* LogRecordCompressor::kBackwardReferenceFormat = "#%d:%d"; - - -LogRecordCompressor::~LogRecordCompressor() { - for (int i = 0; i < buffer_.length(); ++i) { - buffer_[i].Dispose(); - } -} - - -static int GetNumberLength(int number) { - ASSERT(number >= 0); - ASSERT(number < 10000); - if (number < 10) return 1; - if (number < 100) return 2; - if (number < 1000) return 3; - return 4; -} - - -int LogRecordCompressor::GetBackwardReferenceSize(int distance, int pos) { - // See kLineBackwardReferenceFormat and kBackwardReferenceFormat. - return pos == 0 ? GetNumberLength(distance) + 1 - : GetNumberLength(distance) + GetNumberLength(pos) + 2; -} - - -void LogRecordCompressor::PrintBackwardReference(Vector dest, - int distance, - int pos) { - if (pos == 0) { - OS::SNPrintF(dest, kLineBackwardReferenceFormat, distance); - } else { - OS::SNPrintF(dest, kBackwardReferenceFormat, distance, pos); - } -} - - -bool LogRecordCompressor::Store(const Vector& record) { - // Check if the record is the same as the last stored one. - if (curr_ != -1) { - Vector& curr = buffer_[curr_]; - if (record.length() == curr.length() - && strncmp(record.start(), curr.start(), record.length()) == 0) { - return false; - } - } - // buffer_ is circular. - prev_ = curr_++; - curr_ %= buffer_.length(); - Vector record_copy = Vector::New(record.length()); - memcpy(record_copy.start(), record.start(), record.length()); - buffer_[curr_].Dispose(); - buffer_[curr_] = - Vector(record_copy.start(), record_copy.length()); - return true; -} - - -bool LogRecordCompressor::RetrievePreviousCompressed( - Vector* prev_record) { - if (prev_ == -1) return false; - - int index = prev_; - // Distance from prev_. - int distance = 0; - // Best compression result among records in the buffer. - struct { - intptr_t truncated_len; - int distance; - int copy_from_pos; - int backref_size; - } best = {-1, 0, 0, 0}; - Vector& prev = buffer_[prev_]; - const char* const prev_start = prev.start(); - const char* const prev_end = prev.start() + prev.length(); - do { - // We're moving backwards until we reach the current record. - // Remember that buffer_ is circular. - if (--index == -1) index = buffer_.length() - 1; - ++distance; - if (index == curr_) break; - - Vector& data = buffer_[index]; - if (data.start() == NULL) break; - const char* const data_end = data.start() + data.length(); - const char* prev_ptr = prev_end; - const char* data_ptr = data_end; - // Compare strings backwards, stop on the last matching character. - while (prev_ptr != prev_start && data_ptr != data.start() - && *(prev_ptr - 1) == *(data_ptr - 1)) { - --prev_ptr; - --data_ptr; - } - const intptr_t truncated_len = prev_end - prev_ptr; - const int copy_from_pos = static_cast(data_ptr - data.start()); - // Check if the length of compressed tail is enough. - if (truncated_len <= kMaxBackwardReferenceSize - && truncated_len <= GetBackwardReferenceSize(distance, copy_from_pos)) { - continue; - } - - // Record compression results. - if (truncated_len > best.truncated_len) { - best.truncated_len = truncated_len; - best.distance = distance; - best.copy_from_pos = copy_from_pos; - best.backref_size = GetBackwardReferenceSize(distance, copy_from_pos); - } - } while (true); - - if (best.distance == 0) { - // Can't compress the previous record. Return as is. - ASSERT(prev_record->length() >= prev.length()); - memcpy(prev_record->start(), prev.start(), prev.length()); - prev_record->Truncate(prev.length()); - } else { - // Copy the uncompressible part unchanged. - const intptr_t unchanged_len = prev.length() - best.truncated_len; - // + 1 for '\0'. - ASSERT(prev_record->length() >= unchanged_len + best.backref_size + 1); - memcpy(prev_record->start(), prev.start(), unchanged_len); - // Append the backward reference. - Vector backref( - prev_record->start() + unchanged_len, best.backref_size + 1); - PrintBackwardReference(backref, best.distance, best.copy_from_pos); - ASSERT(strlen(backref.start()) - best.backref_size == 0); - prev_record->Truncate(static_cast(unchanged_len + best.backref_size)); - } - return true; -} - #endif // ENABLE_LOGGING_AND_PROFILING } } // namespace v8::internal diff --git a/deps/v8/src/log-utils.h b/deps/v8/src/log-utils.h index ffea9282cb..719d37030e 100644 --- a/deps/v8/src/log-utils.h +++ b/deps/v8/src/log-utils.h @@ -176,50 +176,6 @@ class Log : public AllStatic { friend class Logger; friend class LogMessageBuilder; - friend class LogRecordCompressor; -}; - - -// An utility class for performing backward reference compression -// of string ends. It operates using a window of previous strings. -class LogRecordCompressor { - public: - // 'window_size' is the size of backward lookup window. - explicit LogRecordCompressor(int window_size) - : buffer_(window_size + kNoCompressionWindowSize), - kMaxBackwardReferenceSize( - GetBackwardReferenceSize(window_size, Log::kMessageBufferSize)), - curr_(-1), prev_(-1) { - } - - ~LogRecordCompressor(); - - // Fills vector with a compressed version of the previous record. - // Returns false if there is no previous record. - bool RetrievePreviousCompressed(Vector* prev_record); - - // Stores a record if it differs from a previous one (or there's no previous). - // Returns true, if the record has been stored. - bool Store(const Vector& record); - - private: - // The minimum size of a buffer: a place needed for the current and - // the previous record. Since there is no place for precedessors of a previous - // record, it can't be compressed at all. - static const int kNoCompressionWindowSize = 2; - - // Formatting strings for back references. - static const char* kLineBackwardReferenceFormat; - static const char* kBackwardReferenceFormat; - - static int GetBackwardReferenceSize(int distance, int pos); - - static void PrintBackwardReference(Vector dest, int distance, int pos); - - ScopedVector< Vector > buffer_; - const int kMaxBackwardReferenceSize; - int curr_; - int prev_; }; @@ -244,32 +200,14 @@ class LogMessageBuilder BASE_EMBEDDED { // Append a heap string. void Append(String* str); - // Appends an address, compressing it if needed by offsetting - // from Logger::last_address_. + // Appends an address. void AppendAddress(Address addr); - // Appends an address, compressing it if needed. - void AppendAddress(Address addr, Address bias); - void AppendDetailed(String* str, bool show_impl_info); // Append a portion of a string. void AppendStringPart(const char* str, int len); - // Stores log message into compressor, returns true if the message - // was stored (i.e. doesn't repeat the previous one). - bool StoreInCompressor(LogRecordCompressor* compressor); - - // Sets log message to a previous version of compressed message. - // Returns false, if there is no previous message. - bool RetrieveCompressedPrevious(LogRecordCompressor* compressor) { - return RetrieveCompressedPrevious(compressor, ""); - } - - // Does the same at the version without arguments, and sets a prefix. - bool RetrieveCompressedPrevious(LogRecordCompressor* compressor, - const char* prefix); - // Write the log message to the log file currently opened. void WriteToLogFile(); diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index 6723347924..db9ff7a1f1 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -303,7 +303,6 @@ void Profiler::Engage() { Logger::ticker_->SetProfiler(this); Logger::ProfilerBeginEvent(); - Logger::LogAliases(); } @@ -343,43 +342,21 @@ void Profiler::Run() { Ticker* Logger::ticker_ = NULL; Profiler* Logger::profiler_ = NULL; SlidingStateWindow* Logger::sliding_state_window_ = NULL; -const char** Logger::log_events_ = NULL; -CompressionHelper* Logger::compression_helper_ = NULL; int Logger::logging_nesting_ = 0; int Logger::cpu_profiler_nesting_ = 0; int Logger::heap_profiler_nesting_ = 0; -#define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name, -const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { - LOG_EVENTS_AND_TAGS_LIST(DECLARE_LONG_EVENT) +#define DECLARE_EVENT(ignore1, name) name, +const char* kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { + LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT) }; -#undef DECLARE_LONG_EVENT - -#define DECLARE_SHORT_EVENT(ignore1, ignore2, short_name) short_name, -const char* kCompressedLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { - LOG_EVENTS_AND_TAGS_LIST(DECLARE_SHORT_EVENT) -}; -#undef DECLARE_SHORT_EVENT +#undef DECLARE_EVENT void Logger::ProfilerBeginEvent() { if (!Log::IsEnabled()) return; LogMessageBuilder msg; msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs); - if (FLAG_compress_log) { - msg.Append("profiler,\"compression\",%d\n", kCompressionWindowSize); - } - msg.WriteToLogFile(); -} - - -void Logger::LogAliases() { - if (!Log::IsEnabled() || !FLAG_compress_log) return; - LogMessageBuilder msg; - for (int i = 0; i < NUMBER_OF_LOG_EVENTS; ++i) { - msg.Append("alias,%s,%s\n", - kCompressedLogEventsNames[i], kLongLogEventsNames[i]); - } msg.WriteToLogFile(); } @@ -686,55 +663,16 @@ void Logger::DeleteEvent(const char* name, void* object) { } -#ifdef ENABLE_LOGGING_AND_PROFILING - -// A class that contains all common code dealing with record compression. -class CompressionHelper { - public: - explicit CompressionHelper(int window_size) - : compressor_(window_size), repeat_count_(0) { } - - // Handles storing message in compressor, retrieving the previous one and - // prefixing it with repeat count, if needed. - // Returns true if message needs to be written to log. - bool HandleMessage(LogMessageBuilder* msg) { - if (!msg->StoreInCompressor(&compressor_)) { - // Current message repeats the previous one, don't write it. - ++repeat_count_; - return false; - } - if (repeat_count_ == 0) { - return msg->RetrieveCompressedPrevious(&compressor_); - } - OS::SNPrintF(prefix_, "%s,%d,", - Logger::log_events_[Logger::REPEAT_META_EVENT], - repeat_count_ + 1); - repeat_count_ = 0; - return msg->RetrieveCompressedPrevious(&compressor_, prefix_.start()); - } - - private: - LogRecordCompressor compressor_; - int repeat_count_; - EmbeddedVector prefix_; -}; - -#endif // ENABLE_LOGGING_AND_PROFILING - - #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::CallbackEventInternal(const char* prefix, const char* name, Address entry_point) { if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; msg.Append("%s,%s,", - log_events_[CODE_CREATION_EVENT], log_events_[CALLBACK_TAG]); + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[CALLBACK_TAG]); msg.AppendAddress(entry_point); msg.Append(",1,\"%s%s\"", prefix, name); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); } @@ -771,6 +709,7 @@ void Logger::SetterCallbackEvent(String* name, Address entry_point) { } +#ifdef ENABLE_LOGGING_AND_PROFILING static const char* ComputeMarker(Code* code) { switch (code->kind()) { case Code::FUNCTION: return code->optimizable() ? "~" : ""; @@ -778,6 +717,7 @@ static const char* ComputeMarker(Code* code) { default: return ""; } } +#endif void Logger::CodeCreateEvent(LogEventsAndTags tag, @@ -786,7 +726,9 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.Append("%s,%s,", + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[tag]); msg.AppendAddress(code->address()); msg.Append(",%d,\"%s", code->ExecutableSize(), ComputeMarker(code)); for (const char* p = comment; *p != '\0'; p++) { @@ -797,10 +739,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, } msg.Append('"'); LowLevelCodeCreateEvent(code, &msg); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -813,14 +751,12 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) { LogMessageBuilder msg; SmartPointer str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.Append("%s,%s,", + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[tag]); msg.AppendAddress(code->address()); msg.Append(",%d,\"%s%s\"", code->ExecutableSize(), ComputeMarker(code), *str); LowLevelCodeCreateEvent(code, &msg); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -837,7 +773,9 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); SmartPointer sourcestr = source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.Append("%s,%s,", + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[tag]); msg.AppendAddress(code->address()); msg.Append(",%d,\"%s%s %s:%d\"", code->ExecutableSize(), @@ -846,10 +784,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, *sourcestr, line); LowLevelCodeCreateEvent(code, &msg); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -860,14 +794,12 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.Append("%s,%s,", + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[tag]); msg.AppendAddress(code->address()); msg.Append(",%d,\"args_count: %d\"", code->ExecutableSize(), args_count); LowLevelCodeCreateEvent(code, &msg); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -878,7 +810,7 @@ void Logger::CodeMovingGCEvent() { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return; LogMessageBuilder msg; - msg.Append("%s\n", log_events_[CODE_MOVING_GC]); + msg.Append("%s\n", kLogEventsNames[CODE_MOVING_GC]); msg.WriteToLogFile(); OS::SignalCodeMovingGC(); #endif @@ -890,16 +822,13 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) { if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; msg.Append("%s,%s,", - log_events_[CODE_CREATION_EVENT], log_events_[REG_EXP_TAG]); + kLogEventsNames[CODE_CREATION_EVENT], + kLogEventsNames[REG_EXP_TAG]); msg.AppendAddress(code->address()); msg.Append(",%d,\"", code->ExecutableSize()); msg.AppendDetailed(source, false); msg.Append('\"'); LowLevelCodeCreateEvent(code, &msg); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -924,13 +853,9 @@ void Logger::SnapshotPositionEvent(Address addr, int pos) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_snapshot_positions) return; LogMessageBuilder msg; - msg.Append("%s,", log_events_[SNAPSHOT_POSITION_EVENT]); + msg.Append("%s,", kLogEventsNames[SNAPSHOT_POSITION_EVENT]); msg.AppendAddress(addr); msg.Append(",%d", pos); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -942,18 +867,12 @@ void Logger::FunctionCreateEvent(JSFunction* function) { // This function can be called from GC iterators (during Scavenge, // MC, and MS), so marking bits can be set on objects. That's // why unchecked accessors are used here. - static Address prev_code = NULL; if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("%s,", log_events_[FUNCTION_CREATION_EVENT]); + msg.Append("%s,", kLogEventsNames[FUNCTION_CREATION_EVENT]); msg.AppendAddress(function->address()); msg.Append(','); - msg.AppendAddress(function->unchecked_code()->address(), prev_code); - prev_code = function->unchecked_code()->address(); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } + msg.AppendAddress(function->unchecked_code()->address()); msg.Append('\n'); msg.WriteToLogFile(); #endif @@ -987,18 +906,12 @@ void Logger::FunctionDeleteEvent(Address from) { void Logger::MoveEventInternal(LogEventsAndTags event, Address from, Address to) { - static Address prev_to_ = NULL; if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("%s,", log_events_[event]); + msg.Append("%s,", kLogEventsNames[event]); msg.AppendAddress(from); msg.Append(','); - msg.AppendAddress(to, prev_to_); - prev_to_ = to; - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } + msg.AppendAddress(to); msg.Append('\n'); msg.WriteToLogFile(); } @@ -1009,12 +922,8 @@ void Logger::MoveEventInternal(LogEventsAndTags event, void Logger::DeleteEventInternal(LogEventsAndTags event, Address from) { if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("%s,", log_events_[event]); + msg.Append("%s,", kLogEventsNames[event]); msg.AppendAddress(from); - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; - } msg.Append('\n'); msg.WriteToLogFile(); } @@ -1202,30 +1111,20 @@ void Logger::DebugEvent(const char* event_type, Vector parameter) { #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::TickEvent(TickSample* sample, bool overflow) { if (!Log::IsEnabled() || !FLAG_prof) return; - static Address prev_sp = NULL; - static Address prev_function = NULL; LogMessageBuilder msg; - msg.Append("%s,", log_events_[TICK_EVENT]); - Address prev_addr = sample->pc; - msg.AppendAddress(prev_addr); + msg.Append("%s,", kLogEventsNames[TICK_EVENT]); + msg.AppendAddress(sample->pc); msg.Append(','); - msg.AppendAddress(sample->sp, prev_sp); - prev_sp = sample->sp; + msg.AppendAddress(sample->sp); msg.Append(','); - msg.AppendAddress(sample->function, prev_function); - prev_function = sample->function; + msg.AppendAddress(sample->function); msg.Append(",%d", static_cast(sample->state)); if (overflow) { msg.Append(",overflow"); } for (int i = 0; i < sample->frames_count; ++i) { msg.Append(','); - msg.AppendAddress(sample->stack[i], prev_addr); - prev_addr = sample->stack[i]; - } - if (FLAG_compress_log) { - ASSERT(compression_helper_ != NULL); - if (!compression_helper_->HandleMessage(&msg)) return; + msg.AppendAddress(sample->stack[i]); } msg.Append('\n'); msg.WriteToLogFile(); @@ -1654,12 +1553,6 @@ bool Logger::Setup() { sliding_state_window_ = new SlidingStateWindow(); } - log_events_ = FLAG_compress_log ? - kCompressedLogEventsNames : kLongLogEventsNames; - if (FLAG_compress_log) { - compression_helper_ = new CompressionHelper(kCompressionWindowSize); - } - if (start_logging) { logging_nesting_ = 1; } @@ -1686,13 +1579,17 @@ bool Logger::Setup() { void Logger::EnsureTickerStarted() { +#ifdef ENABLE_LOGGING_AND_PROFILING ASSERT(ticker_ != NULL); if (!ticker_->IsActive()) ticker_->Start(); +#endif } void Logger::EnsureTickerStopped() { +#ifdef ENABLE_LOGGING_AND_PROFILING if (ticker_ != NULL && ticker_->IsActive()) ticker_->Stop(); +#endif } @@ -1707,9 +1604,6 @@ void Logger::TearDown() { profiler_ = NULL; } - delete compression_helper_; - compression_helper_ = NULL; - delete sliding_state_window_; sliding_state_window_ = NULL; diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index 54b131bb0c..771709c8a1 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -74,7 +74,6 @@ class Profiler; class Semaphore; class SlidingStateWindow; class LogMessageBuilder; -class CompressionHelper; #undef LOG #ifdef ENABLE_LOGGING_AND_PROFILING @@ -88,58 +87,55 @@ class CompressionHelper; #endif #define LOG_EVENTS_AND_TAGS_LIST(V) \ - V(CODE_CREATION_EVENT, "code-creation", "cc") \ - V(CODE_MOVE_EVENT, "code-move", "cm") \ - V(CODE_DELETE_EVENT, "code-delete", "cd") \ - V(CODE_MOVING_GC, "code-moving-gc", "cg") \ - V(FUNCTION_CREATION_EVENT, "function-creation", "fc") \ - V(FUNCTION_MOVE_EVENT, "function-move", "fm") \ - V(FUNCTION_DELETE_EVENT, "function-delete", "fd") \ - V(SNAPSHOT_POSITION_EVENT, "snapshot-pos", "sp") \ - V(TICK_EVENT, "tick", "t") \ - V(REPEAT_META_EVENT, "repeat", "r") \ - V(BUILTIN_TAG, "Builtin", "bi") \ - V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak", "cdb") \ - V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn", "cdbsi") \ - V(CALL_IC_TAG, "CallIC", "cic") \ - V(CALL_INITIALIZE_TAG, "CallInitialize", "ci") \ - V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic", "cmm") \ - V(CALL_MISS_TAG, "CallMiss", "cm") \ - V(CALL_NORMAL_TAG, "CallNormal", "cn") \ - V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic", "cpm") \ - V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak", "kcdb") \ - V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \ - "KeyedCallDebugPrepareStepIn", \ - "kcdbsi") \ - V(KEYED_CALL_IC_TAG, "KeyedCallIC", "kcic") \ - V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize", "kci") \ - V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic", "kcmm") \ - V(KEYED_CALL_MISS_TAG, "KeyedCallMiss", "kcm") \ - V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal", "kcn") \ - V(KEYED_CALL_PRE_MONOMORPHIC_TAG, \ - "KeyedCallPreMonomorphic", \ - "kcpm") \ - V(CALLBACK_TAG, "Callback", "cb") \ - V(EVAL_TAG, "Eval", "e") \ - V(FUNCTION_TAG, "Function", "f") \ - V(KEYED_LOAD_IC_TAG, "KeyedLoadIC", "klic") \ - V(KEYED_STORE_IC_TAG, "KeyedStoreIC", "ksic") \ - V(LAZY_COMPILE_TAG, "LazyCompile", "lc") \ - V(LOAD_IC_TAG, "LoadIC", "lic") \ - V(REG_EXP_TAG, "RegExp", "re") \ - V(SCRIPT_TAG, "Script", "sc") \ - V(STORE_IC_TAG, "StoreIC", "sic") \ - V(STUB_TAG, "Stub", "s") \ - V(NATIVE_FUNCTION_TAG, "Function", "f") \ - V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile", "lc") \ - V(NATIVE_SCRIPT_TAG, "Script", "sc") + V(CODE_CREATION_EVENT, "code-creation") \ + V(CODE_MOVE_EVENT, "code-move") \ + V(CODE_DELETE_EVENT, "code-delete") \ + V(CODE_MOVING_GC, "code-moving-gc") \ + V(FUNCTION_CREATION_EVENT, "function-creation") \ + V(FUNCTION_MOVE_EVENT, "function-move") \ + V(FUNCTION_DELETE_EVENT, "function-delete") \ + V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \ + V(TICK_EVENT, "tick") \ + V(REPEAT_META_EVENT, "repeat") \ + V(BUILTIN_TAG, "Builtin") \ + V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak") \ + V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn") \ + V(CALL_IC_TAG, "CallIC") \ + V(CALL_INITIALIZE_TAG, "CallInitialize") \ + V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic") \ + V(CALL_MISS_TAG, "CallMiss") \ + V(CALL_NORMAL_TAG, "CallNormal") \ + V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic") \ + V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak") \ + V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \ + "KeyedCallDebugPrepareStepIn") \ + V(KEYED_CALL_IC_TAG, "KeyedCallIC") \ + V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize") \ + V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic") \ + V(KEYED_CALL_MISS_TAG, "KeyedCallMiss") \ + V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal") \ + V(KEYED_CALL_PRE_MONOMORPHIC_TAG, "KeyedCallPreMonomorphic") \ + V(CALLBACK_TAG, "Callback") \ + V(EVAL_TAG, "Eval") \ + V(FUNCTION_TAG, "Function") \ + V(KEYED_LOAD_IC_TAG, "KeyedLoadIC") \ + V(KEYED_STORE_IC_TAG, "KeyedStoreIC") \ + V(LAZY_COMPILE_TAG, "LazyCompile") \ + V(LOAD_IC_TAG, "LoadIC") \ + V(REG_EXP_TAG, "RegExp") \ + V(SCRIPT_TAG, "Script") \ + V(STORE_IC_TAG, "StoreIC") \ + V(STUB_TAG, "Stub") \ + V(NATIVE_FUNCTION_TAG, "Function") \ + V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile") \ + V(NATIVE_SCRIPT_TAG, "Script") // Note that 'NATIVE_' cases for functions and scripts are mapped onto // original tags when writing to the log. class Logger { public: -#define DECLARE_ENUM(enum_item, ignore1, ignore2) enum_item, +#define DECLARE_ENUM(enum_item, ignore) enum_item, enum LogEventsAndTags { LOG_EVENTS_AND_TAGS_LIST(DECLARE_ENUM) NUMBER_OF_LOG_EVENTS @@ -292,9 +288,6 @@ class Logger { private: - // Size of window used for log records compression. - static const int kCompressionWindowSize = 4; - // Emits the profiler's first message. static void ProfilerBeginEvent(); @@ -312,9 +305,6 @@ class Logger { static void DeleteEventInternal(LogEventsAndTags event, Address from); - // Emits aliases for compressed messages. - static void LogAliases(); - // Emits the source code of a regexp. Used by regexp events. static void LogRegExpSource(Handle regexp); @@ -357,15 +347,8 @@ class Logger { // recent VM states. static SlidingStateWindow* sliding_state_window_; - // An array of log events names. - static const char** log_events_; - - // An instance of helper created if log compression is enabled. - static CompressionHelper* compression_helper_; - // Internal implementation classes with access to // private members. - friend class CompressionHelper; friend class EventLog; friend class TimeLog; friend class Profiler; diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py index 1ceb620146..6d66defb63 100644 --- a/deps/v8/src/macros.py +++ b/deps/v8/src/macros.py @@ -140,15 +140,14 @@ macro NUMBER_OF_CAPTURES(array) = ((array)[0]); # Limit according to ECMA 262 15.9.1.1 const MAX_TIME_MS = 8640000000000000; +# Limit which is MAX_TIME_MS + msPerMonth. +const MAX_TIME_BEFORE_UTC = 8640002592000000; # Gets the value of a Date object. If arg is not a Date object # a type error is thrown. macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError()); macro DAY(time) = ($floor(time / 86400000)); -macro MONTH_FROM_TIME(time) = (MonthFromTime(time)); -macro DATE_FROM_TIME(time) = (DateFromTime(time)); -macro NAN_OR_DATE_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : DATE_FROM_TIME(time)); -macro YEAR_FROM_TIME(time) = (YearFromTime(time)); +macro NAN_OR_DATE_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : DateFromTime(time)); macro HOUR_FROM_TIME(time) = (Modulo($floor(time / 3600000), 24)); macro MIN_FROM_TIME(time) = (Modulo($floor(time / 60000), 60)); macro NAN_OR_MIN_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MIN_FROM_TIME(time)); diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index b570db9e34..8ade41cd2a 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -1281,6 +1281,11 @@ void MarkCompactCollector::ProcessObjectGroups() { void MarkCompactCollector::MarkLiveObjects() { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_MARK); + // The recursive GC marker detects when it is nearing stack overflow, + // and switches to a different marking system. JS interrupts interfere + // with the C stack limit check. + PostponeInterruptsScope postpone; + #ifdef DEBUG ASSERT(state_ == PREPARE_GC); state_ = MARK_LIVE_OBJECTS; diff --git a/deps/v8/src/math.js b/deps/v8/src/math.js index fa1934da41..90667d76cf 100644 --- a/deps/v8/src/math.js +++ b/deps/v8/src/math.js @@ -258,14 +258,6 @@ function SetupMath() { "max", MathMax, "min", MathMin )); - - // The values here are from the MathFunctionId enum in objects.h. - %SetMathFunctionId($Math.floor, 1); - %SetMathFunctionId($Math.round, 2); - %SetMathFunctionId($Math.abs, 4); - %SetMathFunctionId($Math.sqrt, 0xd); - // TODO(erikcorry): Set the id of the other functions so they can be - // optimized. }; diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index c7835660d3..c19f4a9a68 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -190,7 +190,6 @@ function FormatMessage(message) { illegal_return: "Illegal return statement", error_loading_debugger: "Error loading debugger", no_input_to_regexp: "No input to %0", - result_not_primitive: "Result of %0 must be a primitive, was %1", invalid_json: "String '%0' is not valid JSON", circular_structure: "Converting circular structure to JSON", obj_ctor_property_non_object: "Object.%0 called on non-object", diff --git a/deps/v8/src/mirror-debugger.js b/deps/v8/src/mirror-debugger.js index 6b9e965587..55836ce7ec 100644 --- a/deps/v8/src/mirror-debugger.js +++ b/deps/v8/src/mirror-debugger.js @@ -1533,9 +1533,9 @@ FrameMirror.prototype.scope = function(index) { }; -FrameMirror.prototype.evaluate = function(source, disable_break) { +FrameMirror.prototype.evaluate = function(source, disable_break, opt_context_object) { var result = %DebugEvaluate(this.break_id_, this.details_.frameId(), - source, Boolean(disable_break)); + source, Boolean(disable_break), opt_context_object); return MakeMirror(result); }; diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 591012805b..7935912478 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -3036,27 +3036,20 @@ FunctionTemplateInfo* SharedFunctionInfo::get_api_func_data() { } -bool SharedFunctionInfo::HasCustomCallGenerator() { +bool SharedFunctionInfo::HasBuiltinFunctionId() { return function_data()->IsSmi(); } -MathFunctionId SharedFunctionInfo::math_function_id() { - return static_cast( - (compiler_hints() >> kMathFunctionShift) & kMathFunctionMask); +bool SharedFunctionInfo::IsBuiltinMathFunction() { + return HasBuiltinFunctionId() && + builtin_function_id() >= kFirstMathFunctionId; } -void SharedFunctionInfo::set_math_function_id(int math_fn) { - ASSERT(math_fn <= max_math_id_number()); - set_compiler_hints(compiler_hints() | - ((math_fn & kMathFunctionMask) << kMathFunctionShift)); -} - - -int SharedFunctionInfo::custom_call_generator_id() { - ASSERT(HasCustomCallGenerator()); - return Smi::cast(function_data())->value(); +BuiltinFunctionId SharedFunctionInfo::builtin_function_id() { + ASSERT(HasBuiltinFunctionId()); + return static_cast(Smi::cast(function_data())->value()); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 399ab092a7..96f5c4b247 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -3097,8 +3097,9 @@ MaybeObject* JSObject::SetPropertyCallback(String* name, MaybeObject* JSObject::DefineAccessor(String* name, bool is_getter, - JSFunction* fun, + Object* fun, PropertyAttributes attributes) { + ASSERT(fun->IsJSFunction() || fun->IsUndefined()); // Check access rights if needed. if (IsAccessCheckNeeded() && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 1827ab0114..498ee451bc 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1368,7 +1368,7 @@ class JSObject: public HeapObject { MUST_USE_RESULT MaybeObject* DefineAccessor(String* name, bool is_getter, - JSFunction* fun, + Object* fun, PropertyAttributes attributes); Object* LookupAccessor(String* name, bool is_getter); @@ -3714,22 +3714,49 @@ class Script: public Struct { }; -enum MathFunctionId { - kNotSpecialMathFunction = 0, - // These numbers must be kept in sync with the ones in math.js. - kMathFloor = 1, - kMathRound = 2, - kMathCeil = 3, - kMathAbs = 4, - kMathLog = 5, - kMathSin = 6, - kMathCos = 7, - kMathTan = 8, - kMathASin = 9, - kMathACos = 0xa, - kMathATan = 0xb, - kMathExp = 0xc, - kMathSqrt = 0xd +// List of builtin functions we want to identify to improve code +// generation. +// +// Each entry has a name of a global object property holding an object +// optionally followed by ".prototype", a name of a builtin function +// on the object (the one the id is set for), and a label. +// +// Installation of ids for the selected builtin functions is handled +// by the bootstrapper. +// +// NOTE: Order is important: math functions should be at the end of +// the list and MathFloor should be the first math function. +#define FUNCTIONS_WITH_ID_LIST(V) \ + V(Array.prototype, push, ArrayPush) \ + V(Array.prototype, pop, ArrayPop) \ + V(String.prototype, charCodeAt, StringCharCodeAt) \ + V(String.prototype, charAt, StringCharAt) \ + V(String, fromCharCode, StringFromCharCode) \ + V(Math, floor, MathFloor) \ + V(Math, round, MathRound) \ + V(Math, ceil, MathCeil) \ + V(Math, abs, MathAbs) \ + V(Math, log, MathLog) \ + V(Math, sin, MathSin) \ + V(Math, cos, MathCos) \ + V(Math, tan, MathTan) \ + V(Math, asin, MathASin) \ + V(Math, acos, MathACos) \ + V(Math, atan, MathATan) \ + V(Math, exp, MathExp) \ + V(Math, sqrt, MathSqrt) \ + V(Math, pow, MathPow) + + +enum BuiltinFunctionId { +#define DECLARE_FUNCTION_ID(ignored1, ignore2, name) \ + k##name, + FUNCTIONS_WITH_ID_LIST(DECLARE_FUNCTION_ID) +#undef DECLARE_FUNCTION_ID + // Fake id for a special case of Math.pow. Note, it continues the + // list of math functions. + kMathPowHalf, + kFirstMathFunctionId = kMathFloor }; @@ -3870,7 +3897,7 @@ class SharedFunctionInfo: public HeapObject { // [function data]: This field holds some additional data for function. // Currently it either has FunctionTemplateInfo to make benefit the API - // or Smi identifying a custom call generator. + // or Smi identifying a builtin function. // In the long run we don't want all functions to have this field but // we can fix that when we have a better model for storing hidden data // on objects. @@ -3878,8 +3905,9 @@ class SharedFunctionInfo: public HeapObject { inline bool IsApiFunction(); inline FunctionTemplateInfo* get_api_func_data(); - inline bool HasCustomCallGenerator(); - inline int custom_call_generator_id(); + inline bool HasBuiltinFunctionId(); + inline bool IsBuiltinMathFunction(); + inline BuiltinFunctionId builtin_function_id(); // [script info]: Script from which the function originates. DECL_ACCESSORS(script, Object) @@ -4130,12 +4158,6 @@ class SharedFunctionInfo: public HeapObject { static const int kAlignedSize = POINTER_SIZE_ALIGN(kSize); - // Get/set a special tag on the functions from math.js so we can inline - // efficient versions of them in the code. - inline MathFunctionId math_function_id(); - inline void set_math_function_id(int id); - static inline int max_math_id_number() { return kMathFunctionMask; } - typedef FixedBodyDescriptor BodyDescriptor; @@ -4153,12 +4175,10 @@ class SharedFunctionInfo: public HeapObject { static const int kHasOnlySimpleThisPropertyAssignments = 0; static const int kTryFullCodegen = 1; static const int kAllowLazyCompilation = 2; - static const int kMathFunctionShift = 3; - static const int kMathFunctionMask = 0xf; - static const int kLiveObjectsMayExist = 7; - static const int kCodeAgeShift = 8; + static const int kLiveObjectsMayExist = 3; + static const int kCodeAgeShift = 4; static const int kCodeAgeMask = 0x7; - static const int kOptimizationDisabled = 11; + static const int kOptimizationDisabled = 7; DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo); }; diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 5473f25164..94ad57c9c2 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -609,7 +609,25 @@ FunctionLiteral* Parser::ParseProgram(Handle source, // Initialize parser state. source->TryFlatten(); - scanner_.Initialize(source); + if (source->IsExternalTwoByteString()) { + // Notice that the stream is destroyed at the end of the branch block. + // The last line of the blocks can't be moved outside, even though they're + // identical calls. + ExternalTwoByteStringUC16CharacterStream stream( + Handle::cast(source), 0, source->length()); + scanner_.Initialize(&stream, JavaScriptScanner::kAllLiterals); + return DoParseProgram(source, in_global_context, &zone_scope); + } else { + GenericStringUC16CharacterStream stream(source, 0, source->length()); + scanner_.Initialize(&stream, JavaScriptScanner::kAllLiterals); + return DoParseProgram(source, in_global_context, &zone_scope); + } +} + + +FunctionLiteral* Parser::DoParseProgram(Handle source, + bool in_global_context, + ZoneScope* zone_scope) { ASSERT(target_stack_ == NULL); if (pre_data_ != NULL) pre_data_->Initialize(); @@ -655,25 +673,45 @@ FunctionLiteral* Parser::ParseProgram(Handle source, // If there was a syntax error we have to get rid of the AST // and it is not safe to do so before the scope has been deleted. - if (result == NULL) zone_scope.DeleteOnExit(); + if (result == NULL) zone_scope->DeleteOnExit(); return result; } - FunctionLiteral* Parser::ParseLazy(Handle info) { CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); HistogramTimerScope timer(&Counters::parse_lazy); Handle source(String::cast(script_->source())); Counters::total_parse_size.Increment(source->length()); + // Initialize parser state. + source->TryFlatten(); + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUC16CharacterStream stream( + Handle::cast(source), + info->start_position(), + info->end_position()); + FunctionLiteral* result = ParseLazy(info, &stream, &zone_scope); + return result; + } else { + GenericStringUC16CharacterStream stream(source, + info->start_position(), + info->end_position()); + FunctionLiteral* result = ParseLazy(info, &stream, &zone_scope); + return result; + } +} + + +FunctionLiteral* Parser::ParseLazy(Handle info, + UC16CharacterStream* source, + ZoneScope* zone_scope) { + scanner_.Initialize(source, JavaScriptScanner::kAllLiterals); + ASSERT(target_stack_ == NULL); + Handle name(String::cast(info->name())); fni_ = new FuncNameInferrer(); fni_->PushEnclosingName(name); - // Initialize parser state. - source->TryFlatten(); - scanner_.Initialize(source, info->start_position(), info->end_position()); - ASSERT(target_stack_ == NULL); mode_ = PARSE_EAGERLY; // Place holder for the result. @@ -705,7 +743,7 @@ FunctionLiteral* Parser::ParseLazy(Handle info) { // not safe to do before scope has been deleted. if (result == NULL) { Top::StackOverflow(); - zone_scope.DeleteOnExit(); + zone_scope->DeleteOnExit(); } else { Handle inferred_name(info->inferred_name()); result->set_inferred_name(inferred_name); @@ -719,12 +757,12 @@ Handle Parser::GetSymbol(bool* ok) { if (pre_data() != NULL) { symbol_id = pre_data()->GetSymbolIdentifier(); } - return LookupSymbol(symbol_id, scanner_.literal()); + return LookupSymbol(symbol_id, scanner().literal()); } void Parser::ReportMessage(const char* type, Vector args) { - Scanner::Location source_location = scanner_.location(); + Scanner::Location source_location = scanner().location(); ReportMessageAt(source_location, type, args); } @@ -1641,7 +1679,7 @@ Statement* Parser::ParseContinueStatement(bool* ok) { Expect(Token::CONTINUE, CHECK_OK); Handle label = Handle::null(); Token::Value tok = peek(); - if (!scanner_.has_line_terminator_before_next() && + if (!scanner().has_line_terminator_before_next() && tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { label = ParseIdentifier(CHECK_OK); } @@ -1667,7 +1705,7 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) { Expect(Token::BREAK, CHECK_OK); Handle label; Token::Value tok = peek(); - if (!scanner_.has_line_terminator_before_next() && + if (!scanner().has_line_terminator_before_next() && tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { label = ParseIdentifier(CHECK_OK); } @@ -1712,7 +1750,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) { } Token::Value tok = peek(); - if (scanner_.has_line_terminator_before_next() || + if (scanner().has_line_terminator_before_next() || tok == Token::SEMICOLON || tok == Token::RBRACE || tok == Token::EOS) { @@ -1844,7 +1882,7 @@ Statement* Parser::ParseThrowStatement(bool* ok) { Expect(Token::THROW, CHECK_OK); int pos = scanner().location().beg_pos; - if (scanner_.has_line_terminator_before_next()) { + if (scanner().has_line_terminator_before_next()) { ReportMessage("newline_after_throw", Vector::empty()); *ok = false; return NULL; @@ -2408,7 +2446,8 @@ Expression* Parser::ParsePostfixExpression(bool* ok) { // LeftHandSideExpression ('++' | '--')? Expression* expression = ParseLeftHandSideExpression(CHECK_OK); - if (!scanner_.has_line_terminator_before_next() && Token::IsCountOp(peek())) { + if (!scanner().has_line_terminator_before_next() && + Token::IsCountOp(peek())) { // Signal a reference error if the expression is an invalid // left-hand side expression. We could report this as a syntax // error here but for compatibility with JSC we choose to report the @@ -2677,7 +2716,7 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { case Token::NUMBER: { Consume(Token::NUMBER); double value = - StringToDouble(scanner_.literal(), ALLOW_HEX | ALLOW_OCTALS); + StringToDouble(scanner().literal(), ALLOW_HEX | ALLOW_OCTALS); result = NewNumberLiteral(value); break; } @@ -3028,7 +3067,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { case Token::NUMBER: { Consume(Token::NUMBER); double value = - StringToDouble(scanner_.literal(), ALLOW_HEX | ALLOW_OCTALS); + StringToDouble(scanner().literal(), ALLOW_HEX | ALLOW_OCTALS); key = NewNumberLiteral(value); break; } @@ -3089,7 +3128,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { - if (!scanner_.ScanRegExpPattern(seen_equal)) { + if (!scanner().ScanRegExpPattern(seen_equal)) { Next(); ReportMessage("unterminated_regexp", Vector::empty()); *ok = false; @@ -3099,10 +3138,10 @@ Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { int literal_index = temp_scope_->NextMaterializedLiteralIndex(); Handle js_pattern = - Factory::NewStringFromUtf8(scanner_.next_literal(), TENURED); - scanner_.ScanRegExpFlags(); + Factory::NewStringFromUtf8(scanner().next_literal(), TENURED); + scanner().ScanRegExpFlags(); Handle js_flags = - Factory::NewStringFromUtf8(scanner_.next_literal(), TENURED); + Factory::NewStringFromUtf8(scanner().next_literal(), TENURED); Next(); return new RegExpLiteral(js_pattern, js_flags, literal_index); @@ -3158,7 +3197,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, // FormalParameterList :: // '(' (Identifier)*[','] ')' Expect(Token::LPAREN, CHECK_OK); - int start_pos = scanner_.location().beg_pos; + int start_pos = scanner().location().beg_pos; bool done = (peek() == Token::RPAREN); while (!done) { Handle param_name = ParseIdentifier(CHECK_OK); @@ -3195,7 +3234,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, bool is_lazily_compiled = mode() == PARSE_LAZILY && top_scope_->HasTrivialOuterContext(); - int function_block_pos = scanner_.location().beg_pos; + int function_block_pos = scanner().location().beg_pos; int materialized_literal_count; int expected_property_count; int end_pos; @@ -3212,7 +3251,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, ReportInvalidPreparseData(name, CHECK_OK); } Counters::total_preparse_skipped.Increment(end_pos - function_block_pos); - scanner_.SeekForward(end_pos); + // Seek to position just before terminal '}'. + scanner().SeekForward(end_pos - 1); materialized_literal_count = entry.literal_count(); expected_property_count = entry.property_count(); only_simple_this_property_assignments = false; @@ -3228,7 +3268,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, this_property_assignments = temp_scope.this_property_assignments(); Expect(Token::RBRACE, CHECK_OK); - end_pos = scanner_.location().end_pos; + end_pos = scanner().location().end_pos; } FunctionLiteral* function_literal = @@ -3332,7 +3372,7 @@ void Parser::ExpectSemicolon(bool* ok) { Next(); return; } - if (scanner_.has_line_terminator_before_next() || + if (scanner().has_line_terminator_before_next() || tok == Token::RBRACE || tok == Token::EOS) { return; @@ -3383,8 +3423,8 @@ Handle Parser::ParseIdentifierOrGetOrSet(bool* is_get, bool* ok) { Expect(Token::IDENTIFIER, ok); if (!*ok) return Handle(); - if (scanner_.literal_length() == 3) { - const char* token = scanner_.literal_string(); + if (scanner().literal_length() == 3) { + const char* token = scanner().literal_string(); *is_get = strcmp(token, "get") == 0; *is_set = !*is_get && strcmp(token, "set") == 0; } @@ -3503,8 +3543,8 @@ Expression* Parser::NewThrowError(Handle constructor, // ---------------------------------------------------------------------------- // JSON -Handle JsonParser::ParseJson(Handle source) { - source->TryFlatten(); +Handle JsonParser::ParseJson(Handle script, + UC16CharacterStream* source) { scanner_.Initialize(source); stack_overflow_ = false; Handle result = ParseJsonValue(); @@ -3540,7 +3580,7 @@ Handle JsonParser::ParseJson(Handle source) { } Scanner::Location source_location = scanner_.location(); - MessageLocation location(Factory::NewScript(source), + MessageLocation location(Factory::NewScript(script), source_location.beg_pos, source_location.end_pos); int argc = (name_opt == NULL) ? 0 : 1; @@ -4555,13 +4595,12 @@ int ScriptDataImpl::ReadNumber(byte** source) { // Create a Scanner for the preparser to use as input, and preparse the source. -static ScriptDataImpl* DoPreParse(Handle source, - unibrow::CharacterStream* stream, +static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, bool allow_lazy, ParserRecorder* recorder, int literal_flags) { V8JavaScriptScanner scanner; - scanner.Initialize(source, stream, literal_flags); + scanner.Initialize(source, literal_flags); intptr_t stack_limit = StackGuard::real_climit(); if (!preparser::PreParser::PreParseProgram(&scanner, recorder, @@ -4580,8 +4619,7 @@ static ScriptDataImpl* DoPreParse(Handle source, // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. -ScriptDataImpl* ParserApi::PartialPreParse(Handle source, - unibrow::CharacterStream* stream, +ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, v8::Extension* extension) { bool allow_lazy = FLAG_lazy && (extension == NULL); if (!allow_lazy) { @@ -4590,22 +4628,19 @@ ScriptDataImpl* ParserApi::PartialPreParse(Handle source, return NULL; } PartialParserRecorder recorder; - - return DoPreParse(source, stream, allow_lazy, &recorder, + return DoPreParse(source, allow_lazy, &recorder, JavaScriptScanner::kNoLiterals); } -ScriptDataImpl* ParserApi::PreParse(Handle source, - unibrow::CharacterStream* stream, +ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source, v8::Extension* extension) { Handle