diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 91b583fd0d..2debaa09fc 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,26 @@ +2010-12-13: Version 3.0.1 + + Added support for an experimental internationalization API as an + extension. This extension is disabled by default but can be enabled + when building V8. The ECMAScript internationalization strawman is + at http://wiki.ecmascript.org/doku.php?id=strawman:i18n_api. + + Made RegExp character class parsing stricter. This mirrors a change + to RegExp parsing in WebKit. + + Fixed a bug in Object.defineProperty when used to change attributes + of an existing property. It incorrectly set the property value to + undefined (issue 965). + + Fixed several different compilation failures on various platforms + caused by the 3.0.0 release. + + Optimized Math.pow so it can work on unboxed doubles. + + Sped up quoting of JSON strings by removing one traversal of the + string. + + 2010-12-07: Version 3.0.0 Improved performance by (partially) addressing issue 957 on @@ -132,7 +155,7 @@ Added USE_SIMULATOR macro that explicitly indicates that we wish to use the simulator as the execution engine (by Mark Lam - from Hewlett-Packard Development Company, LP). + from Hewlett-Packard Development Company, LP). Fixed compilation error on ARM with gcc 4.4 (issue 894). 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..7fd063197e 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -3281,6 +3281,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/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/api.cc b/deps/v8/src/api.cc index 0ec8cf123e..e169bd08a0 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); + } } @@ -4939,7 +4947,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 +4962,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 diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 7e4a28042f..921629d6f2 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -890,7 +890,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 +1446,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(); @@ -1536,7 +1538,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 +1586,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { break; } } + PrepareForBailoutForId(bailout_ast_id, TOS_REG); + context()->Plug(r0); } @@ -1657,8 +1661,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, } __ bind(&done); } - - context()->Plug(result_register()); } @@ -1701,10 +1703,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 +1747,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 +3202,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 +3213,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 +3222,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 +3237,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(); diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index 682c448fd5..ef982f1076 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -460,12 +460,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); } @@ -1357,6 +1351,9 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { return AssignEnvironment(DefineAsRegister(result)); case kMathSqrt: return DefineSameAsFirst(result); + case kMathPowHalf: + Abort("MathPowHalf LUnaryMathOperation not implemented"); + return NULL; default: UNREACHABLE(); return NULL; @@ -1554,6 +1551,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()) { @@ -1688,11 +1691,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..048d4fc80b 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -1395,15 +1395,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 +1889,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..5b3f23bb02 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)); @@ -1733,13 +1733,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/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/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..d1017121da 100644 --- a/deps/v8/src/ast-inl.h +++ b/deps/v8/src/ast-inl.h @@ -94,7 +94,8 @@ ForStatement::ForStatement(ZoneStringList* labels) 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..7ddb01e3e4 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -125,17 +125,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(); } } diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index cdf456f67b..c33838e011 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_; } @@ -532,11 +531,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_; @@ -579,11 +575,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_; }; @@ -748,9 +746,7 @@ 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; } private: Expression* condition_; @@ -1432,7 +1428,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 +1450,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_; }; @@ -1585,7 +1587,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 +1596,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/checks.h b/deps/v8/src/checks.h index aa557f00bc..8d13d65f6e 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -231,6 +231,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/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/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..fc5fe1ee0f 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -194,6 +194,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 +457,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.h b/deps/v8/src/full-codegen.h index 8d9fe2d332..e0fd192a21 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -481,7 +481,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. diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index 91ac9867a2..6700d38b25 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -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..31d0aff02f 100644 --- a/deps/v8/src/heap-profiler.h +++ b/deps/v8/src/heap-profiler.h @@ -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..06b3ee4689 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_; diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index ff1ab1a36e..34316319aa 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 @@ -223,6 +224,7 @@ class LChunkBuilder; V(ObjectLiteral) \ V(OsrEntry) \ V(Parameter) \ + V(Power) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -1377,6 +1379,7 @@ class HUnaryMathOperation: public HUnaryOperation { SetFlag(kFlexibleRepresentation); break; case kMathSqrt: + case kMathPowHalf: default: set_representation(Representation::Double()); } @@ -1395,6 +1398,7 @@ class HUnaryMathOperation: public HUnaryOperation { case kMathRound: case kMathCeil: case kMathSqrt: + case kMathPowHalf: return Representation::Double(); break; case kMathAbs: @@ -2184,6 +2188,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..bc49f06a20 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -1983,6 +1983,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 +1994,101 @@ 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) { + HGraphBuilder* builder = owner(); + HBasicBlock* materialize_true = builder->graph()->CreateBasicBlock(); + HBasicBlock* materialize_false = builder->graph()->CreateBasicBlock(); + HBranch* branch = new HBranch(materialize_true, materialize_false, value); + builder->CurrentBlock()->Finish(branch); + + HBasicBlock* true_block = if_true(); + HValue* true_value = invert_true() + ? builder->graph()->GetConstantFalse() + : builder->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); + } + + HBasicBlock* false_block = if_false(); + HValue* false_value = invert_false() + ? builder->graph()->GetConstantTrue() + : builder->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); + } + builder->subgraph()->set_exit_block(NULL); +} + // HGraphBuilder infrastructure for bailing out and checking bailouts. #define BAILOUT(reason) \ @@ -2061,55 +2159,14 @@ 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); } @@ -2244,99 +2301,7 @@ void HGraphBuilder::VisitForControl(Expression* expr, 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); - } + Visit(expr); } @@ -2372,12 +2337,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 +2351,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 +2361,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); } @@ -2914,7 +2871,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()); } @@ -2935,20 +2894,21 @@ void HGraphBuilder::VisitConditional(Conditional* expr) { ADD_TO_SUBGRAPH(then_graph, expr->then_expression()); 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 +2921,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 +2929,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 +2952,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 +2970,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 +3008,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { default: UNREACHABLE(); } } + ast_context()->ReturnValue(Pop()); } @@ -3059,6 +3020,8 @@ 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)); @@ -3076,6 +3039,7 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { AddInstruction(new HStoreKeyedFastElement(elements, key, value)); AddSimulate(expr->GetIdForElement(i)); } + ast_context()->ReturnValue(Pop()); } @@ -3257,27 +3221,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->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()); } @@ -3333,14 +3299,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 +3320,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 +3344,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 +3370,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 +3382,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 +3402,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 +3416,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 +3442,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 +3462,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 +3476,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 +3513,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 +3525,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()); } @@ -3668,11 +3661,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 +3674,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 +3723,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 +3759,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 +3778,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 +3788,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()); + } } @@ -4061,6 +4063,7 @@ bool HGraphBuilder::TryInline(Call* expr) { function_return_ = saved_function_return; oracle_ = saved_oracle; graph()->info()->SetOsrAstId(saved_osr_ast_id); + return true; } @@ -4086,10 +4089,55 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { case kMathSqrt: 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; @@ -4134,8 +4182,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 +4213,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 +4239,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 +4259,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 +4307,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 +4342,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 +4358,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 +4368,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 +4382,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 +4408,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 +4423,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 +4431,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(), + !context->invert_false(), + !context->invert_true()); + } else { + 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); + ast_context()->ReturnValue(Pop()); + } } else if (op == Token::BIT_NOT || op == Token::SUB) { VISIT_FOR_VALUE(expr->expression()); HValue* value = Pop(); @@ -4398,11 +4487,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"); } @@ -4453,11 +4542,15 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } if (var->is_global()) { - HandleGlobalVariableAssignment(proxy, instr, expr->position()); + HandleGlobalVariableAssignment(var, + instr, + expr->position(), + expr->AssignmentId()); } else { ASSERT(var->IsStackAllocated()); Bind(var, instr); } + ast_context()->ReturnValue(Pop()); } else if (prop != NULL) { prop->RecordTypeFeedback(oracle()); @@ -4465,11 +4558,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 value 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 +4577,35 @@ 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 = 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()); - } + 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 +4618,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 +4722,47 @@ 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) { + VisitForControl(expr->left(), eval_right, context->if_false(), + false, context->invert_false()); + } else { + VisitForControl(expr->left(), context->if_true(), eval_right, + context->invert_true(), false); + } + if (HasStackOverflow()) return; + eval_right->SetJoinId(expr->left()->id()); + + // Translate right subexpression by visiting it in the same AST + // context as the entire expression. + eval_right->last_environment()->Pop(); + 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 +4770,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 +4810,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 +4825,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 +4862,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 +4872,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 +4899,305 @@ 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) { +void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsNonNegativeSmi"); } -void HGraphBuilder::GenerateIsObject(int argument_count) { +void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) { BAILOUT("inlined runtime function: IsObject"); } -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..5611d8da18 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -557,10 +557,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 +592,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 +604,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); }; @@ -598,6 +625,9 @@ class TestContext: public AstContext { invert_false_(invert_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); @@ -610,6 +640,10 @@ class TestContext: public AstContext { 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_; @@ -631,9 +665,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 +700,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) @@ -683,8 +728,6 @@ class HGraphBuilder: public AstVisitor { 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); } @@ -708,18 +751,15 @@ class HGraphBuilder: public AstVisitor { 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 +783,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 +793,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/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/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index 1f7095f578..e538ee4b9f 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -911,7 +911,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 +1480,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 +1523,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); @@ -1849,7 +1853,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()) { @@ -1897,6 +1901,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { break; } } + PrepareForBailoutForId(bailout_ast_id, TOS_REG); + context()->Plug(eax); } @@ -1969,8 +1975,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, } __ bind(&done); } - - context()->Plug(eax); } @@ -2007,10 +2011,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 +2052,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(eax); } + PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(eax); } @@ -3749,6 +3754,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 +3766,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 +3775,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 +3790,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()) { diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index dc0f5e90f9..b5c0289174 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -2174,6 +2174,67 @@ 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(); + Representation exponent_type = instr->hydrogen()->right()->representation(); + if (exponent_type.IsDouble()) { + // Pass two doubles as arguments on the stack. + __ PrepareCallCFunction(4, eax); + __ 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()) { + __ 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()); + __ PrepareCallCFunction(4, ebx); + __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); + Register right_reg = ToRegister(right); + Label non_smi; + Label done; + __ test(right_reg, Immediate(kSmiTagMask)); + __ j(not_zero, &non_smi); + __ SmiUntag(right_reg); + __ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right)); + __ CallCFunction(ExternalReference::power_double_int_function(), 4); + __ jmp(&done); + + __ bind(&non_smi); + __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , ebx); + DeoptimizeIf(not_equal, instr->environment()); + __ movdbl(xmm1, FieldOperand(right_reg, HeapNumber::kValueOffset)); + __ movdbl(Operand(esp, 1 * kDoubleSize), xmm1); + __ CallCFunction(ExternalReference::power_double_double_function(), 4); + + __ bind(&done); + } + + // 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(ToDoubleRegister(instr->result()), Operand(esp, 0)); + __ add(Operand(esp), Immediate(kDoubleSize)); +} + + void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { switch (instr->op()) { case kMathAbs: @@ -2188,6 +2249,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { case kMathSqrt: DoMathSqrt(instr); break; + case kMathPowHalf: + DoMathPowHalf(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..ca4e9b3fae 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -175,6 +175,7 @@ class LCodeGen BASE_EMBEDDED { void DoMathFloor(LUnaryMathOperation* instr); void DoMathRound(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr); + void DoMathPowHalf(LUnaryMathOperation* instr); // Support for recording safepoint and position information. void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index e3a3d7bcb0..1f9714357e 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -460,12 +460,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); } @@ -1372,6 +1366,8 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { return AssignEnvironment(DefineAsRegister(result)); case kMathSqrt: return DefineSameAsFirst(result); + case kMathPowHalf: + return AssignEnvironment(DefineSameAsFirst(result)); default: UNREACHABLE(); return NULL; @@ -1572,6 +1568,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, xmm1), instr, + CAN_DEOPTIMIZE_EAGERLY); +} + + LInstruction* LChunkBuilder::DoCompare(HCompare* instr) { Token::Value op = instr->token(); if (instr->left()->representation().IsInteger32()) { diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index af0d5604dd..c97859cfc9 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 @@ -229,6 +230,7 @@ class LGapNode; V(ObjectLiteral) \ V(OsrEntry) \ V(Parameter) \ + V(Power) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -1154,6 +1156,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 +1902,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..6c51d6859c 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -1216,25 +1216,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 +1283,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/json.js b/deps/v8/src/json.js index e8b732a52a..c0af9d0e46 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 = []; @@ -197,10 +185,9 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) { 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 +207,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)) { diff --git a/deps/v8/src/lithium-allocator.cc b/deps/v8/src/lithium-allocator.cc index db0bc8b72d..513a67c7c8 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,7 +290,7 @@ 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 @@ -625,7 +625,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 +642,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; @@ -1258,14 +1258,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 +1389,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 +1411,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 +1422,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 +1463,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 +1517,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 +1550,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 +1736,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 +1762,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 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 +1871,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]; + + 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; + } - current->set_assigned_register(max_reg, mode_ == XMM_REGISTERS); - SplitAndSpillIntersecting(current); + 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 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 +1942,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 +1955,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 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", + 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 +2021,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 +2074,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..b8e3f05a47 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(); } @@ -786,7 +724,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 +737,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 +749,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 +771,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 +782,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 +792,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 +808,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 +820,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 +851,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 +865,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 +904,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 +920,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 +1109,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 +1551,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; } @@ -1707,9 +1598,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/math.js b/deps/v8/src/math.js index fa1934da41..2e886d099a 100644 --- a/deps/v8/src/math.js +++ b/deps/v8/src/math.js @@ -264,6 +264,7 @@ function SetupMath() { %SetMathFunctionId($Math.round, 2); %SetMathFunctionId($Math.abs, 4); %SetMathFunctionId($Math.sqrt, 0xd); + %SetMathFunctionId($Math.pow, 0xe); // TODO(erikcorry): Set the id of the other functions so they can be // optimized. }; diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 1827ab0114..c3958e2396 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -3729,7 +3729,9 @@ enum MathFunctionId { kMathACos = 0xa, kMathATan = 0xb, kMathExp = 0xc, - kMathSqrt = 0xd + kMathSqrt = 0xd, + kMathPow = 0xe, + kMathPowHalf = 0xf }; @@ -4154,11 +4156,11 @@ class SharedFunctionInfo: public HeapObject { 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 kMathFunctionMask = 0x1f; + static const int kLiveObjectsMayExist = 8; + static const int kCodeAgeShift = 9; static const int kCodeAgeMask = 0x7; - static const int kOptimizationDisabled = 11; + static const int kOptimizationDisabled = 12; DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo); }; diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 056332b5b4..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; @@ -4409,10 +4449,25 @@ CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) { } +static const uc16 kNoCharClass = 0; + +// Adds range or pre-defined character class to character ranges. +// If char_class is not kInvalidClass, it's interpreted as a class +// escape (i.e., 's' means whitespace, from '\s'). +static inline void AddRangeOrEscape(ZoneList* ranges, + uc16 char_class, + CharacterRange range) { + if (char_class != kNoCharClass) { + CharacterRange::AddClassEscape(char_class, ranges); + } else { + ranges->Add(range); + } +} + + RegExpTree* RegExpParser::ParseCharacterClass() { static const char* kUnterminated = "Unterminated character class"; static const char* kRangeOutOfOrder = "Range out of order in character class"; - static const char* kInvalidRange = "Invalid character range"; ASSERT_EQ(current(), '['); Advance(); @@ -4421,30 +4476,10 @@ RegExpTree* RegExpParser::ParseCharacterClass() { is_negated = true; Advance(); } - // A CharacterClass is a sequence of single characters, character class - // escapes or ranges. Ranges are on the form "x-y" where x and y are - // single characters (and not character class escapes like \s). - // A "-" may occur at the start or end of the character class (just after - // "[" or "[^", or just before "]") without being considered part of a - // range. A "-" may also appear as the beginning or end of a range. - // I.e., [--+] is valid, so is [!--]. - ZoneList* ranges = new ZoneList(2); while (has_more() && current() != ']') { - uc16 char_class = 0; + uc16 char_class = kNoCharClass; CharacterRange first = ParseClassAtom(&char_class CHECK_FAILED); - if (char_class) { - CharacterRange::AddClassEscape(char_class, ranges); - if (current() == '-') { - Advance(); - ranges->Add(CharacterRange::Singleton('-')); - if (current() != ']') { - ReportError(CStrVector(kInvalidRange) CHECK_FAILED); - } - break; - } - continue; - } if (current() == '-') { Advance(); if (current() == kEndMarker) { @@ -4452,20 +4487,25 @@ RegExpTree* RegExpParser::ParseCharacterClass() { // following code report an error. break; } else if (current() == ']') { - ranges->Add(first); + AddRangeOrEscape(ranges, char_class, first); ranges->Add(CharacterRange::Singleton('-')); break; } - CharacterRange next = ParseClassAtom(&char_class CHECK_FAILED); - if (char_class) { - ReportError(CStrVector(kInvalidRange) CHECK_FAILED); + uc16 char_class_2 = kNoCharClass; + CharacterRange next = ParseClassAtom(&char_class_2 CHECK_FAILED); + if (char_class != kNoCharClass || char_class_2 != kNoCharClass) { + // Either end is an escaped character class. Treat the '-' verbatim. + AddRangeOrEscape(ranges, char_class, first); + ranges->Add(CharacterRange::Singleton('-')); + AddRangeOrEscape(ranges, char_class_2, next); + continue; } if (first.from() > next.to()) { return ReportError(CStrVector(kRangeOutOfOrder) CHECK_FAILED); } ranges->Add(CharacterRange::Range(first.from(), next.to())); } else { - ranges->Add(first); + AddRangeOrEscape(ranges, char_class, first); } } if (!has_more()) { @@ -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