Version 3.2.2
Fixed a number of crash and correctness bugs.
Improved Crankshaft performance on all platforms.
Fixed Crankshaft on Solaris/Illumos.
git-svn-id: http://v8.googlecode.com/svn/trunk@7162 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 32a58b2..2c330b3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2011-03-14: Version 3.2.2
+
+ Fixed a number of crash and correctness bugs.
+
+ Improved Crankshaft performance on all platforms.
+
+ Fixed Crankshaft on Solaris/Illumos.
+
+
2011-03-10: Version 3.2.1
Fixed a number of crash bugs.
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 675a229..d466d5e 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -245,14 +245,15 @@
class V8EXPORT HeapGraphNode {
public:
enum Type {
- kHidden = 0, // Hidden node, may be filtered when shown to user.
- kArray = 1, // An array of elements.
- kString = 2, // A string.
- kObject = 3, // A JS object (except for arrays and strings).
- kCode = 4, // Compiled code.
- kClosure = 5, // Function closure.
- kRegExp = 6, // RegExp.
- kHeapNumber = 7 // Number stored in the heap.
+ kHidden = 0, // Hidden node, may be filtered when shown to user.
+ kArray = 1, // An array of elements.
+ kString = 2, // A string.
+ kObject = 3, // A JS object (except for arrays and strings).
+ kCode = 4, // Compiled code.
+ kClosure = 5, // Function closure.
+ kRegExp = 6, // RegExp.
+ kHeapNumber = 7, // Number stored in the heap.
+ kNative = 8 // Native object (not from V8 heap).
};
/** Returns node type (see HeapGraphNode::Type). */
@@ -392,11 +393,22 @@
};
+class RetainedObjectInfo;
+
/**
* Interface for controlling heap profiling.
*/
class V8EXPORT HeapProfiler {
public:
+ /**
+ * Callback function invoked for obtaining RetainedObjectInfo for
+ * the given JavaScript wrapper object. It is prohibited to enter V8
+ * while the callback is running: only getters on the handle and
+ * GetPointerFromInternalField on the objects are allowed.
+ */
+ typedef RetainedObjectInfo* (*WrapperInfoCallback)
+ (uint16_t class_id, Handle<Value> wrapper);
+
/** Returns the number of snapshots taken. */
static int GetSnapshotsCount();
@@ -414,6 +426,81 @@
Handle<String> title,
HeapSnapshot::Type type = HeapSnapshot::kFull,
ActivityControl* control = NULL);
+
+ /** Binds a callback to embedder's class ID. */
+ static void DefineWrapperClass(
+ uint16_t class_id,
+ WrapperInfoCallback callback);
+
+ /**
+ * Default value of persistent handle class ID. Must not be used to
+ * define a class. Can be used to reset a class of a persistent
+ * handle.
+ */
+ static const uint16_t kPersistentHandleNoClassId = 0;
+};
+
+
+/**
+ * Interface for providing information about embedder's objects
+ * held by global handles. This information is reported in two ways:
+ *
+ * 1. When calling AddObjectGroup, an embedder may pass
+ * RetainedObjectInfo instance describing the group. To collect
+ * this information while taking a heap snapshot, V8 calls GC
+ * prologue and epilogue callbacks.
+ *
+ * 2. When a heap snapshot is collected, V8 additionally
+ * requests RetainedObjectInfos for persistent handles that
+ * were not previously reported via AddObjectGroup.
+ *
+ * Thus, if an embedder wants to provide information about native
+ * objects for heap snapshots, he can do it in a GC prologue
+ * handler, and / or by assigning wrapper class ids in the following way:
+ *
+ * 1. Bind a callback to class id by calling DefineWrapperClass.
+ * 2. Call SetWrapperClassId on certain persistent handles.
+ *
+ * V8 takes ownership of RetainedObjectInfo instances passed to it and
+ * keeps them alive only during snapshot collection. Afterwards, they
+ * are freed by calling the Dispose class function.
+ */
+class V8EXPORT RetainedObjectInfo { // NOLINT
+ public:
+ /** Called by V8 when it no longer needs an instance. */
+ virtual void Dispose() = 0;
+
+ /** Returns whether two instances are equivalent. */
+ virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
+
+ /**
+ * Returns hash value for the instance. Equivalent instances
+ * must have the same hash value.
+ */
+ virtual intptr_t GetHash() = 0;
+
+ /**
+ * Returns human-readable label. It must be a NUL-terminated UTF-8
+ * encoded string. V8 copies its contents during a call to GetLabel.
+ */
+ virtual const char* GetLabel() = 0;
+
+ /**
+ * Returns element count in case if a global handle retains
+ * a subgraph by holding one of its nodes.
+ */
+ virtual intptr_t GetElementCount() { return -1; }
+
+ /** Returns embedder's object size in bytes. */
+ virtual intptr_t GetSizeInBytes() { return -1; }
+
+ protected:
+ RetainedObjectInfo() {}
+ virtual ~RetainedObjectInfo() {}
+
+ private:
+ RetainedObjectInfo(const RetainedObjectInfo&);
+ RetainedObjectInfo& operator=(const RetainedObjectInfo&);
};
diff --git a/include/v8-testing.h b/include/v8-testing.h
index 4db30a4..245f74d 100644
--- a/include/v8-testing.h
+++ b/include/v8-testing.h
@@ -87,6 +87,11 @@
* should be between 0 and one less than the result from GetStressRuns()
*/
static void PrepareStressRun(int run);
+
+ /**
+ * Force deoptimization of all functions.
+ */
+ static void DeoptimizeAll();
};
diff --git a/include/v8.h b/include/v8.h
index 7b3d3f9..ddbd6af 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -396,6 +396,12 @@
*/
inline bool IsWeak() const;
+ /**
+ * Assigns a wrapper class ID to the handle. See RetainedObjectInfo
+ * interface description in v8-profiler.h for details.
+ */
+ inline void SetWrapperClassId(uint16_t class_id);
+
private:
friend class ImplementationUtilities;
friend class ObjectTemplate;
@@ -2534,6 +2540,8 @@
};
+class RetainedObjectInfo;
+
/**
* Container class for static utility functions.
*/
@@ -2703,8 +2711,11 @@
* intended to be used in the before-garbage-collection callback
* function, for instance to simulate DOM tree connections among JS
* wrapper objects.
+ * See v8-profiler.h for RetainedObjectInfo interface description.
*/
- static void AddObjectGroup(Persistent<Value>* objects, size_t length);
+ static void AddObjectGroup(Persistent<Value>* objects,
+ size_t length,
+ RetainedObjectInfo* info = NULL);
/**
* Initializes from snapshot if possible. Otherwise, attempts to
@@ -2913,6 +2924,8 @@
static void ClearWeak(internal::Object** global_handle);
static bool IsGlobalNearDeath(internal::Object** global_handle);
static bool IsGlobalWeak(internal::Object** global_handle);
+ static void SetWrapperClassId(internal::Object** global_handle,
+ uint16_t class_id);
template <class T> friend class Handle;
template <class T> friend class Local;
@@ -3561,6 +3574,10 @@
V8::ClearWeak(reinterpret_cast<internal::Object**>(**this));
}
+template <class T>
+void Persistent<T>::SetWrapperClassId(uint16_t class_id) {
+ V8::SetWrapperClassId(reinterpret_cast<internal::Object**>(**this), class_id);
+}
Arguments::Arguments(internal::Object** implicit_args,
internal::Object** values, int length,
diff --git a/samples/shell.cc b/samples/shell.cc
index 64f78f0..fc3d76c 100644
--- a/samples/shell.cc
+++ b/samples/shell.cc
@@ -65,6 +65,11 @@
// Create a new execution environment containing the built-in
// functions
v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+ if (context.IsEmpty()) {
+ printf("Error creating context\n");
+ return 1;
+ }
+
bool run_shell = (argc == 1);
for (int i = 1; i < argc; i++) {
// Enter the execution environment before evaluating any code.
@@ -139,6 +144,8 @@
v8::Testing::PrepareStressRun(i);
result = RunMain(argc, argv);
}
+ printf("======== Full Deoptimization =======\n");
+ v8::Testing::DeoptimizeAll();
} else {
result = RunMain(argc, argv);
}
diff --git a/src/api.cc b/src/api.cc
index 87793ae..26d2246 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3580,6 +3580,11 @@
}
+void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) {
+ i::GlobalHandles::SetWrapperClassId(global_handle, class_id);
+}
+
+
Local<v8::Object> ObjectTemplate::NewInstance() {
ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
LOG_API("ObjectTemplate::NewInstance");
@@ -4116,10 +4121,13 @@
}
-void V8::AddObjectGroup(Persistent<Value>* objects, size_t length) {
+void V8::AddObjectGroup(Persistent<Value>* objects,
+ size_t length,
+ RetainedObjectInfo* info) {
if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
- i::GlobalHandles::AddGroup(reinterpret_cast<i::Object***>(objects), length);
+ i::GlobalHandles::AddGroup(
+ reinterpret_cast<i::Object***>(objects), length, info);
}
@@ -5065,6 +5073,12 @@
*Utils::OpenHandle(*title), internal_type, control));
}
+
+void HeapProfiler::DefineWrapperClass(uint16_t class_id,
+ WrapperInfoCallback callback) {
+ i::HeapProfiler::DefineWrapperClass(class_id, callback);
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING
@@ -5129,6 +5143,11 @@
}
+void Testing::DeoptimizeAll() {
+ internal::Deoptimizer::DeoptimizeAll();
+}
+
+
namespace internal {
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index c91d4ba..48b91a6 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -767,11 +767,35 @@
}
-bool Operand::is_single_instruction() const {
+bool Operand::is_single_instruction(Instr instr) const {
if (rm_.is_valid()) return true;
- if (must_use_constant_pool()) return false;
uint32_t dummy1, dummy2;
- return fits_shifter(imm32_, &dummy1, &dummy2, NULL);
+ if (must_use_constant_pool() ||
+ !fits_shifter(imm32_, &dummy1, &dummy2, &instr)) {
+ // The immediate operand cannot be encoded as a shifter operand, or use of
+ // constant pool is required. For a mov instruction not setting the
+ // condition code additional instruction conventions can be used.
+ if ((instr & ~kCondMask) == 13*B21) { // mov, S not set
+ if (must_use_constant_pool() || !CpuFeatures::IsSupported(ARMv7)) {
+ // mov instruction will be an ldr from constant pool (one instruction).
+ return true;
+ } else {
+ // mov instruction will be a mov or movw followed by movt (two
+ // instructions).
+ return false;
+ }
+ } else {
+ // If this is not a mov or mvn instruction there will always an additional
+ // instructions - either mov or ldr. The mov might actually be two
+ // instructions mov or movw followed by movt so including the actual
+ // instruction two or three instructions will be generated.
+ return false;
+ }
+ } else {
+ // No use of constant pool and the immediate operand can be encoded as a
+ // shifter operand.
+ return true;
+ }
}
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index f5eb507..4594ecb 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -389,8 +389,11 @@
INLINE(bool is_reg() const);
// Return true if this operand fits in one instruction so that no
- // 2-instruction solution with a load into the ip register is necessary.
- bool is_single_instruction() const;
+ // 2-instruction solution with a load into the ip register is necessary. If
+ // the instruction this operand is used for is a MOV or MVN instruction the
+ // actual instruction to use is required for this calculation. For other
+ // instructions instr is ignored.
+ bool is_single_instruction(Instr instr = 0) const;
bool must_use_constant_pool() const;
inline int32_t immediate() const {
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index bce0c51..31a7288 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -397,20 +397,18 @@
Register scratch2,
Label* not_number);
- // Loads the number from object into dst as a 32-bit integer if possible. If
- // the object cannot be converted to a 32-bit integer control continues at
- // the label not_int32. If VFP is supported double_scratch is used
- // but not scratch2.
- // Floating point value in the 32-bit integer range will be rounded
- // to an integer.
- static void LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- DwVfpRegister double_scratch,
- Label* not_int32);
+ // Convert the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ static void ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32);
// Load the number from object into double_dst in the double format.
// Control will jump to not_int32 if the value cannot be exactly represented
@@ -606,27 +604,97 @@
}
-void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- DwVfpRegister double_scratch,
- Label* not_int32) {
+void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_number) {
if (FLAG_debug_code) {
__ AbortIfNotRootValue(heap_number_map,
Heap::kHeapNumberMapRootIndex,
"HeapNumberMap register clobbered.");
}
- Label is_smi, done;
+ Label is_smi;
+ Label done;
+ Label not_in_int32_range;
+
__ JumpIfSmi(object, &is_smi);
__ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
__ cmp(scratch1, heap_number_map);
- __ b(ne, not_int32);
- __ ConvertToInt32(
- object, dst, scratch1, scratch2, double_scratch, not_int32);
+ __ b(ne, not_number);
+ __ ConvertToInt32(object,
+ dst,
+ scratch1,
+ scratch2,
+ double_scratch,
+ ¬_in_int32_range);
__ jmp(&done);
+
+ __ bind(¬_in_int32_range);
+ __ ldr(scratch2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ // Register scratch1 contains mantissa word, scratch2 contains
+ // sign, exponent and mantissa. Extract biased exponent into dst.
+ __ Ubfx(dst,
+ scratch2,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // Express exponent as delta to 31.
+ __ sub(dst, dst, Operand(HeapNumber::kExponentBias + 31));
+
+ Label normal_exponent;
+ // If the delta is larger than kMantissaBits plus one, all bits
+ // would be shifted away, which means that we can return 0.
+ __ cmp(dst, Operand(HeapNumber::kMantissaBits + 1));
+ __ b(&normal_exponent, lt);
+ __ mov(dst, Operand(0));
+ __ jmp(&done);
+
+ __ bind(&normal_exponent);
+ const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
+ // Calculate shift.
+ __ add(scratch3, dst, Operand(kShiftBase));
+
+ // Put implicit 1 before the mantissa part in scratch2.
+ __ orr(scratch2,
+ scratch2,
+ Operand(1 << HeapNumber::kMantissaBitsInTopWord));
+
+ // Save sign.
+ Register sign = dst;
+ __ and_(sign, scratch2, Operand(HeapNumber::kSignMask));
+
+ // Shift mantisssa bits the correct position in high word.
+ __ mov(scratch2, Operand(scratch2, LSL, scratch3));
+
+ // Replace the shifted bits with bits from the lower mantissa word.
+ Label pos_shift, shift_done;
+ __ rsb(scratch3, scratch3, Operand(32), SetCC);
+ __ b(&pos_shift, ge);
+
+ // Negate scratch3.
+ __ rsb(scratch3, scratch3, Operand(0));
+ __ mov(scratch1, Operand(scratch1, LSL, scratch3));
+ __ jmp(&shift_done);
+
+ __ bind(&pos_shift);
+ __ mov(scratch1, Operand(scratch1, LSR, scratch3));
+
+ __ bind(&shift_done);
+ __ orr(scratch2, scratch2, Operand(scratch1));
+
+ // Restore sign if necessary.
+ __ cmp(sign, Operand(0));
+ __ rsb(dst, scratch2, Operand(0), LeaveCC, ne);
+ __ mov(dst, scratch2, LeaveCC, eq);
+ __ jmp(&done);
+
__ bind(&is_smi);
__ SmiUntag(dst, object);
__ bind(&done);
@@ -3024,6 +3092,7 @@
Register right = r0;
Register scratch1 = r7;
Register scratch2 = r9;
+ Register scratch3 = r4;
ASSERT(smi_operands || (not_numbers != NULL));
if (smi_operands && FLAG_debug_code) {
@@ -3111,22 +3180,24 @@
__ SmiUntag(r2, right);
} else {
// Convert operands to 32-bit integers. Right in r2 and left in r3.
- FloatingPointHelper::LoadNumberAsInteger(masm,
- left,
- r3,
- heap_number_map,
- scratch1,
- scratch2,
- d0,
- not_numbers);
- FloatingPointHelper::LoadNumberAsInteger(masm,
- right,
- r2,
- heap_number_map,
- scratch1,
- scratch2,
- d0,
- not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ left,
+ r3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ right,
+ r2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ not_numbers);
}
Label result_not_a_smi;
@@ -3572,13 +3643,10 @@
void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
- Label not_numbers, call_runtime;
+ Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::HEAP_NUMBER);
- GenerateFPOperation(masm, false, ¬_numbers, &call_runtime);
-
- __ bind(¬_numbers);
- GenerateTypeTransition(masm);
+ GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
__ bind(&call_runtime);
GenerateCallRuntime(masm);
diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc
index 9a5aa90..40e0fc1 100644
--- a/src/arm/deoptimizer-arm.cc
+++ b/src/arm/deoptimizer-arm.cc
@@ -46,6 +46,7 @@
void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+ HandleScope scope;
AssertNoAllocation no_allocation;
if (!function->IsOptimized()) return;
@@ -69,8 +70,6 @@
int deoptimization_index = safepoint_entry.deoptimization_index();
int gap_code_size = safepoint_entry.gap_code_size();
// Check that we did not shoot past next safepoint.
- // TODO(srdjan): How do we guarantee that safepoint code does not
- // overlap other safepoint patching code?
CHECK(pc_offset >= last_pc_offset);
#ifdef DEBUG
// Destroy the code which is not supposed to be run again.
@@ -117,6 +116,11 @@
PrintF("[forced deoptimization: ");
function->PrintName();
PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
+#ifdef DEBUG
+ if (FLAG_print_code) {
+ code->PrintLn();
+ }
+#endif
}
}
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index c5b0c1d..a9b33a0 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1350,10 +1350,21 @@
// when we provide a native implementation.
ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
- LOperand* value = UseFixed(instr->left(), r0);
- LOperand* divisor = UseFixed(instr->right(), r1);
- LInstruction* result = DefineFixed(new LModI(value, divisor), r0);
- result = AssignEnvironment(AssignPointerMap(result));
+
+ LInstruction* result;
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LModI* mod = new LModI(value, UseOrConstant(instr->right()));
+ result = DefineSameAsFirst(mod);
+ result = AssignEnvironment(result);
+ } else {
+ LOperand* value = UseFixed(instr->left(), r0);
+ LOperand* divisor = UseFixed(instr->right(), r1);
+ result = DefineFixed(new LModI(value, divisor), r0);
+ result = AssignEnvironment(AssignPointerMap(result));
+ }
+
return result;
} else if (instr->representation().IsTagged()) {
return DoArithmeticT(Token::MOD, instr);
@@ -1893,6 +1904,13 @@
}
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
LOperand* string = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LStringLength(string));
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 00ca024..54faafd 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -149,6 +149,7 @@
V(StoreNamedGeneric) \
V(StorePixelArrayElement) \
V(StringCharCodeAt) \
+ V(StringCharFromCode) \
V(StringLength) \
V(SubI) \
V(TaggedToI) \
@@ -1635,6 +1636,19 @@
};
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+ LOperand* char_code() { return inputs_[0]; }
+};
+
+
class LStringLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LStringLength(LOperand* string) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index b5046ec..f06e7a0 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -34,7 +34,7 @@
namespace internal {
-class SafepointGenerator : public PostCallGenerator {
+class SafepointGenerator : public CallWrapper {
public:
SafepointGenerator(LCodeGen* codegen,
LPointerMap* pointers,
@@ -44,7 +44,24 @@
deoptimization_index_(deoptimization_index) { }
virtual ~SafepointGenerator() { }
- virtual void Generate() {
+ virtual void BeforeCall(int call_size) {
+ ASSERT(call_size >= 0);
+ // Ensure that we have enough space after the previous safepoint position
+ // for the generated code there.
+ int call_end = codegen_->masm()->pc_offset() + call_size;
+ int prev_jump_end =
+ codegen_->LastSafepointEnd() + Deoptimizer::patch_size();
+ if (call_end < prev_jump_end) {
+ int padding_size = prev_jump_end - call_end;
+ ASSERT_EQ(0, padding_size % Assembler::kInstrSize);
+ while (padding_size > 0) {
+ codegen_->masm()->nop();
+ padding_size -= Assembler::kInstrSize;
+ }
+ }
+ }
+
+ virtual void AfterCall() {
codegen_->RecordSafepoint(pointers_, deoptimization_index_);
}
@@ -779,6 +796,30 @@
void LCodeGen::DoModI(LModI* instr) {
+ if (instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->InputAt(0));
+
+ int32_t divisor =
+ HConstant::cast(instr->hydrogen()->right())->Integer32Value();
+
+ if (divisor < 0) divisor = -divisor;
+
+ Label positive_dividend, done;
+ __ tst(dividend, Operand(dividend));
+ __ b(pl, &positive_dividend);
+ __ rsb(dividend, dividend, Operand(0));
+ __ and_(dividend, dividend, Operand(divisor - 1));
+ __ rsb(dividend, dividend, Operand(0), SetCC);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ b(ne, &done);
+ DeoptimizeIf(al, instr->environment());
+ }
+ __ bind(&positive_dividend);
+ __ and_(dividend, dividend, Operand(divisor - 1));
+ __ bind(&done);
+ return;
+ }
+
class DeferredModI: public LDeferredCode {
public:
DeferredModI(LCodeGen* codegen, LModI* instr)
@@ -839,6 +880,7 @@
__ JumpIfNotPowerOfTwoOrZero(right, scratch, &call_stub);
// Perform modulo operation (scratch contains right - 1).
__ and_(result, scratch, Operand(left));
+ __ b(&done);
__ bind(&call_stub);
// Call the stub. The numbers in r0 and r1 have
@@ -3081,6 +3123,56 @@
}
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+ class DeferredStringCharFromCode: public LDeferredCode {
+ public:
+ DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+ private:
+ LStringCharFromCode* instr_;
+ };
+
+ DeferredStringCharFromCode* deferred =
+ new DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmp(char_code, Operand(String::kMaxAsciiCharCode));
+ __ b(hi, deferred->entry());
+ __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+ __ add(result, result, Operand(char_code, LSL, kPointerSizeLog2));
+ __ ldr(result, FieldMemOperand(result, FixedArray::kHeaderSize));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(result, ip);
+ __ b(eq, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+
+ // TODO(3095996): Get rid of this. For now, we need to make the
+ // result register contain a valid pointer because it is already
+ // contained in the register pointer map.
+ __ mov(result, Operand(0));
+
+ __ PushSafepointRegisters();
+ __ SmiTag(char_code);
+ __ push(char_code);
+ __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ __ StoreToSafepointRegisterSlot(r0, result);
+ __ PopSafepointRegisters();
+}
+
+
void LCodeGen::DoStringLength(LStringLength* instr) {
Register string = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index b0ef268..e2b572e 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -105,6 +105,7 @@
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check);
@@ -229,6 +230,9 @@
int arguments,
int deoptimization_index);
void RecordPosition(int position);
+ int LastSafepointEnd() {
+ return static_cast<int>(safepoints_.GetPcAfterGap());
+ }
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
void EmitGoto(int block, LDeferredCode* deferred_stack_check = NULL);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index d431f6a..916110a 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -103,7 +103,22 @@
}
+int MacroAssembler::CallSize(Register target, Condition cond) {
+#if USE_BLX
+ return kInstrSize;
+#else
+ return 2 * kInstrSize;
+#endif
+}
+
+
void MacroAssembler::Call(Register target, Condition cond) {
+ // Block constant pool for the call instruction sequence.
+ BlockConstPoolScope block_const_pool(this);
+#ifdef DEBUG
+ int pre_position = pc_offset();
+#endif
+
#if USE_BLX
blx(target, cond);
#else
@@ -111,29 +126,46 @@
mov(lr, Operand(pc), LeaveCC, cond);
mov(pc, Operand(target), LeaveCC, cond);
#endif
+
+#ifdef DEBUG
+ int post_position = pc_offset();
+ CHECK_EQ(pre_position + CallSize(target, cond), post_position);
+#endif
}
-void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode,
- Condition cond) {
+int MacroAssembler::CallSize(
+ intptr_t target, RelocInfo::Mode rmode, Condition cond) {
+ int size = 2 * kInstrSize;
+ Instr mov_instr = cond | MOV | LeaveCC;
+ if (!Operand(target, rmode).is_single_instruction(mov_instr)) {
+ size += kInstrSize;
+ }
+ return size;
+}
+
+
+void MacroAssembler::Call(
+ intptr_t target, RelocInfo::Mode rmode, Condition cond) {
+ // Block constant pool for the call instruction sequence.
+ BlockConstPoolScope block_const_pool(this);
+#ifdef DEBUG
+ int pre_position = pc_offset();
+#endif
+
#if USE_BLX
// On ARMv5 and after the recommended call sequence is:
// ldr ip, [pc, #...]
// blx ip
- // The two instructions (ldr and blx) could be separated by a constant
- // pool and the code would still work. The issue comes from the
- // patching code which expect the ldr to be just above the blx.
- { BlockConstPoolScope block_const_pool(this);
- // Statement positions are expected to be recorded when the target
- // address is loaded. The mov method will automatically record
- // positions when pc is the target, since this is not the case here
- // we have to do it explicitly.
- positions_recorder()->WriteRecordedPositions();
+ // Statement positions are expected to be recorded when the target
+ // address is loaded. The mov method will automatically record
+ // positions when pc is the target, since this is not the case here
+ // we have to do it explicitly.
+ positions_recorder()->WriteRecordedPositions();
- mov(ip, Operand(target, rmode), LeaveCC, cond);
- blx(ip, cond);
- }
+ mov(ip, Operand(target, rmode), LeaveCC, cond);
+ blx(ip, cond);
ASSERT(kCallTargetAddressOffset == 2 * kInstrSize);
#else
@@ -141,24 +173,58 @@
mov(lr, Operand(pc), LeaveCC, cond);
// Emit a ldr<cond> pc, [pc + offset of target in constant pool].
mov(pc, Operand(target, rmode), LeaveCC, cond);
-
ASSERT(kCallTargetAddressOffset == kInstrSize);
#endif
+
+#ifdef DEBUG
+ int post_position = pc_offset();
+ CHECK_EQ(pre_position + CallSize(target, rmode, cond), post_position);
+#endif
}
-void MacroAssembler::Call(byte* target, RelocInfo::Mode rmode,
- Condition cond) {
- ASSERT(!RelocInfo::IsCodeTarget(rmode));
- Call(reinterpret_cast<intptr_t>(target), rmode, cond);
+int MacroAssembler::CallSize(
+ byte* target, RelocInfo::Mode rmode, Condition cond) {
+ return CallSize(reinterpret_cast<intptr_t>(target), rmode);
}
-void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
- Condition cond) {
+void MacroAssembler::Call(
+ byte* target, RelocInfo::Mode rmode, Condition cond) {
+#ifdef DEBUG
+ int pre_position = pc_offset();
+#endif
+
+ ASSERT(!RelocInfo::IsCodeTarget(rmode));
+ Call(reinterpret_cast<intptr_t>(target), rmode, cond);
+
+#ifdef DEBUG
+ int post_position = pc_offset();
+ CHECK_EQ(pre_position + CallSize(target, rmode, cond), post_position);
+#endif
+}
+
+
+int MacroAssembler::CallSize(
+ Handle<Code> code, RelocInfo::Mode rmode, Condition cond) {
+ return CallSize(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+}
+
+
+void MacroAssembler::Call(
+ Handle<Code> code, RelocInfo::Mode rmode, Condition cond) {
+#ifdef DEBUG
+ int pre_position = pc_offset();
+#endif
+
ASSERT(RelocInfo::IsCodeTarget(rmode));
// 'code' is always generated ARM code, never THUMB code
Call(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+
+#ifdef DEBUG
+ int post_position = pc_offset();
+ CHECK_EQ(pre_position + CallSize(code, rmode, cond), post_position);
+#endif
}
@@ -784,7 +850,7 @@
Register code_reg,
Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ CallWrapper* call_wrapper) {
bool definitely_matches = false;
Label regular_invoke;
@@ -839,8 +905,11 @@
Handle<Code> adaptor =
Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
if (flag == CALL_FUNCTION) {
+ if (call_wrapper != NULL) {
+ call_wrapper->BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+ }
Call(adaptor, RelocInfo::CODE_TARGET);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ if (call_wrapper != NULL) call_wrapper->AfterCall();
b(done);
} else {
Jump(adaptor, RelocInfo::CODE_TARGET);
@@ -854,14 +923,15 @@
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ CallWrapper* call_wrapper) {
Label done;
InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
- post_call_generator);
+ call_wrapper);
if (flag == CALL_FUNCTION) {
+ if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
Call(code);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ if (call_wrapper != NULL) call_wrapper->AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
Jump(code);
@@ -896,7 +966,7 @@
void MacroAssembler::InvokeFunction(Register fun,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ CallWrapper* call_wrapper) {
// Contract with called JS functions requires that function is passed in r1.
ASSERT(fun.is(r1));
@@ -913,7 +983,7 @@
FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
- InvokeCode(code_reg, expected, actual, flag, post_call_generator);
+ InvokeCode(code_reg, expected, actual, flag, call_wrapper);
}
@@ -2083,11 +2153,12 @@
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
InvokeJSFlags flags,
- PostCallGenerator* post_call_generator) {
+ CallWrapper* call_wrapper) {
GetBuiltinEntry(r2, id);
if (flags == CALL_JS) {
+ if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(r2));
Call(r2);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ if (call_wrapper != NULL) call_wrapper->AfterCall();
} else {
ASSERT(flags == JUMP_JS);
Jump(r2);
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index aaf4458..d79edd7 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -34,7 +34,7 @@
namespace internal {
// Forward declaration.
-class PostCallGenerator;
+class CallWrapper;
// ----------------------------------------------------------------------------
// Static helper functions
@@ -96,8 +96,11 @@
void Jump(Register target, Condition cond = al);
void Jump(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
+ int CallSize(Register target, Condition cond = al);
void Call(Register target, Condition cond = al);
+ int CallSize(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
+ int CallSize(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
void Ret(Condition cond = al);
@@ -343,7 +346,7 @@
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ CallWrapper* call_wrapper = NULL);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
@@ -356,7 +359,7 @@
void InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ CallWrapper* call_wrapper = NULL);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
@@ -748,7 +751,7 @@
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id,
InvokeJSFlags flags,
- PostCallGenerator* post_call_generator = NULL);
+ CallWrapper* call_wrapper = NULL);
// Store the code object for the given builtin in the target register and
// setup the function in r1.
@@ -911,6 +914,7 @@
private:
void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
+ int CallSize(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
void Call(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
// Helper functions for generating invokes.
@@ -920,7 +924,7 @@
Register code_reg,
Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ CallWrapper* call_wrapper = NULL);
// Activation support.
void EnterFrame(StackFrame::Type type);
@@ -984,11 +988,15 @@
// Helper class for generating code or data associated with the code
// right after a call instruction. As an example this can be used to
// generate safepoint data after calls for crankshaft.
-class PostCallGenerator {
+class CallWrapper {
public:
- PostCallGenerator() { }
- virtual ~PostCallGenerator() { }
- virtual void Generate() = 0;
+ CallWrapper() { }
+ virtual ~CallWrapper() { }
+ // Called just before emitting a call. Argument is the size of the generated
+ // call code.
+ virtual void BeforeCall(int call_size) = 0;
+ // Called just after emitting a call, i.e., at the return site for the call.
+ virtual void AfterCall() = 0;
};
diff --git a/src/ast.cc b/src/ast.cc
index 7cfc3da..184aaa5 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -540,6 +540,8 @@
ZoneMapList* types = oracle->LoadReceiverTypes(this, name);
receiver_types_ = types;
}
+ } else if (oracle->LoadIsBuiltin(this, Builtins::KeyedLoadIC_String)) {
+ is_string_access_ = true;
} else if (is_monomorphic_) {
monomorphic_receiver_type_ = oracle->LoadMonomorphicReceiverType(this);
if (monomorphic_receiver_type_->has_external_array_elements()) {
diff --git a/src/ast.h b/src/ast.h
index ec02772..b00612d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1214,6 +1214,7 @@
is_monomorphic_(false),
is_array_length_(false),
is_string_length_(false),
+ is_string_access_(false),
is_function_prototype_(false),
is_arguments_access_(false) { }
@@ -1228,6 +1229,7 @@
bool is_synthetic() const { return type_ == SYNTHETIC; }
bool IsStringLength() const { return is_string_length_; }
+ bool IsStringAccess() const { return is_string_access_; }
bool IsFunctionPrototype() const { return is_function_prototype_; }
// Marks that this is actually an argument rewritten to a keyed property
@@ -1265,6 +1267,7 @@
bool is_monomorphic_ : 1;
bool is_array_length_ : 1;
bool is_string_length_ : 1;
+ bool is_string_access_ : 1;
bool is_function_prototype_ : 1;
bool is_arguments_access_ : 1;
Handle<Map> monomorphic_receiver_type_;
diff --git a/src/compiler.cc b/src/compiler.cc
index 667432f..553f486 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -172,6 +172,7 @@
ASSERT(code->kind() == Code::FUNCTION);
code->set_optimizable(false);
info->SetCode(code);
+ CompilationCache::MarkForLazyOptimizing(info->closure());
if (FLAG_trace_opt) {
PrintF("[disabled optimization for: ");
info->closure()->PrintName();
@@ -248,7 +249,7 @@
// performance of the hydrogen-based compiler.
int64_t start = OS::Ticks();
bool should_recompile = !info->shared_info()->has_deoptimization_support();
- if (should_recompile || FLAG_time_hydrogen) {
+ if (should_recompile || FLAG_hydrogen_stats) {
HPhase phase(HPhase::kFullCodeGen);
CompilationInfo unoptimized(info->shared_info());
// Note that we use the same AST that we will use for generating the
diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h
index 440dedc..a7fffe0 100644
--- a/src/cpu-profiler-inl.h
+++ b/src/cpu-profiler-inl.h
@@ -41,8 +41,8 @@
void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->AddCode(start, entry, size);
- if (sfi_address != NULL) {
- entry->set_shared_id(code_map->GetSFITag(sfi_address));
+ if (shared != NULL) {
+ entry->set_shared_id(code_map->GetSharedId(shared));
}
}
@@ -57,7 +57,7 @@
}
-void SFIMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
+void SharedFunctionInfoMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->MoveCode(from, to);
}
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index ad04a00..f82d8ee 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -69,7 +69,7 @@
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, prefix, name);
rec->size = 1;
- rec->sfi_address = NULL;
+ rec->shared = NULL;
events_buffer_.Enqueue(evt_rec);
}
@@ -80,7 +80,7 @@
int line_number,
Address start,
unsigned size,
- Address sfi_address) {
+ Address shared) {
if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
@@ -89,7 +89,7 @@
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
rec->size = size;
- rec->sfi_address = sfi_address;
+ rec->shared = shared;
events_buffer_.Enqueue(evt_rec);
}
@@ -106,7 +106,7 @@
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name);
rec->size = size;
- rec->sfi_address = NULL;
+ rec->shared = NULL;
events_buffer_.Enqueue(evt_rec);
}
@@ -123,7 +123,7 @@
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, args_count);
rec->size = size;
- rec->sfi_address = NULL;
+ rec->shared = NULL;
events_buffer_.Enqueue(evt_rec);
}
@@ -149,10 +149,12 @@
}
-void ProfilerEventsProcessor::SFIMoveEvent(Address from, Address to) {
+void ProfilerEventsProcessor::SharedFunctionInfoMoveEvent(Address from,
+ Address to) {
CodeEventsContainer evt_rec;
- SFIMoveEventRecord* rec = &evt_rec.SFIMoveEventRecord_;
- rec->type = CodeEventRecord::SFI_MOVE;
+ SharedFunctionInfoMoveEventRecord* rec =
+ &evt_rec.SharedFunctionInfoMoveEventRecord_;
+ rec->type = CodeEventRecord::SHARED_FUNC_MOVE;
rec->order = ++enqueue_order_;
rec->from = from;
rec->to = to;
@@ -403,8 +405,8 @@
}
-void CpuProfiler::SFIMoveEvent(Address from, Address to) {
- singleton_->processor_->SFIMoveEvent(from, to);
+void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
+ singleton_->processor_->SharedFunctionInfoMoveEvent(from, to);
}
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
index 1ebbfeb..fc4dfb7 100644
--- a/src/cpu-profiler.h
+++ b/src/cpu-profiler.h
@@ -46,11 +46,11 @@
class ProfileGenerator;
class TokenEnumerator;
-#define CODE_EVENTS_TYPE_LIST(V) \
- V(CODE_CREATION, CodeCreateEventRecord) \
- V(CODE_MOVE, CodeMoveEventRecord) \
- V(CODE_DELETE, CodeDeleteEventRecord) \
- V(SFI_MOVE, SFIMoveEventRecord)
+#define CODE_EVENTS_TYPE_LIST(V) \
+ V(CODE_CREATION, CodeCreateEventRecord) \
+ V(CODE_MOVE, CodeMoveEventRecord) \
+ V(CODE_DELETE, CodeDeleteEventRecord) \
+ V(SHARED_FUNC_MOVE, SharedFunctionInfoMoveEventRecord)
class CodeEventRecord {
@@ -73,7 +73,7 @@
Address start;
CodeEntry* entry;
unsigned size;
- Address sfi_address;
+ Address shared;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
@@ -96,7 +96,7 @@
};
-class SFIMoveEventRecord : public CodeEventRecord {
+class SharedFunctionInfoMoveEventRecord : public CodeEventRecord {
public:
Address from;
Address to;
@@ -149,7 +149,7 @@
String* name,
String* resource_name, int line_number,
Address start, unsigned size,
- Address sfi_address);
+ Address shared);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name,
Address start, unsigned size);
@@ -158,7 +158,7 @@
Address start, unsigned size);
void CodeMoveEvent(Address from, Address to);
void CodeDeleteEvent(Address from);
- void SFIMoveEvent(Address from, Address to);
+ void SharedFunctionInfoMoveEvent(Address from, Address to);
void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag,
const char* prefix, String* name,
Address start, unsigned size);
@@ -251,7 +251,7 @@
static void GetterCallbackEvent(String* name, Address entry_point);
static void RegExpCodeCreateEvent(Code* code, String* source);
static void SetterCallbackEvent(String* name, Address entry_point);
- static void SFIMoveEvent(Address from, Address to);
+ static void SharedFunctionInfoMoveEvent(Address from, Address to);
static INLINE(bool is_profiling()) {
return NoBarrier_Load(&is_profiling_);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 2566766..cdad211 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -111,7 +111,7 @@
DEFINE_bool(limit_inlining, true, "limit code size growth from inlining")
DEFINE_bool(eliminate_empty_blocks, true, "eliminate empty blocks")
DEFINE_bool(loop_invariant_code_motion, true, "loop invariant code motion")
-DEFINE_bool(time_hydrogen, false, "timing for hydrogen")
+DEFINE_bool(hydrogen_stats, false, "print statistics for hydrogen")
DEFINE_bool(trace_hydrogen, false, "trace generated hydrogen to file")
DEFINE_bool(trace_inlining, false, "trace inlining decisions")
DEFINE_bool(trace_alloc, false, "trace register allocator")
diff --git a/src/global-handles.cc b/src/global-handles.cc
index 18cdc5a..6cc0cfb 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -41,6 +41,7 @@
void Initialize(Object* object) {
// Set the initial value of the handle.
object_ = object;
+ class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
state_ = NORMAL;
parameter_or_next_free_.parameter = NULL;
callback_ = NULL;
@@ -137,6 +138,14 @@
return state_ == WEAK;
}
+ bool CanBeRetainer() {
+ return state_ != DESTROYED && state_ != NEAR_DEATH;
+ }
+
+ void SetWrapperClassId(uint16_t class_id) {
+ class_id_ = class_id;
+ }
+
// Returns the id for this weak handle.
void set_parameter(void* parameter) {
ASSERT(state_ != DESTROYED);
@@ -190,6 +199,8 @@
// Place the handle address first to avoid offset computation.
Object* object_; // Storage for object pointer.
+ uint16_t class_id_;
+
// Transition diagram:
// NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
enum State {
@@ -199,7 +210,7 @@
NEAR_DEATH, // Callback has informed the handle is near death.
DESTROYED
};
- State state_;
+ State state_ : 4; // Need one more bit for MSVC as it treats enums as signed.
private:
// Handle specific callback.
@@ -337,6 +348,11 @@
}
+void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
+ Node::FromLocation(location)->SetWrapperClassId(class_id);
+}
+
+
void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
// Traversal of GC roots in the global handle list that are marked as
// WEAK or PENDING.
@@ -435,6 +451,16 @@
}
+void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
+ current->CanBeRetainer()) {
+ v->VisitEmbedderReference(¤t->object_, current->class_id_);
+ }
+ }
+}
+
+
void GlobalHandles::TearDown() {
// Reset all the lists.
set_head(NULL);
@@ -515,8 +541,10 @@
return &groups;
}
-void GlobalHandles::AddGroup(Object*** handles, size_t length) {
- ObjectGroup* new_entry = new ObjectGroup(length);
+void GlobalHandles::AddGroup(Object*** handles,
+ size_t length,
+ v8::RetainedObjectInfo* info) {
+ ObjectGroup* new_entry = new ObjectGroup(length, info);
for (size_t i = 0; i < length; ++i)
new_entry->objects_.Add(handles[i]);
ObjectGroups()->Add(new_entry);
diff --git a/src/global-handles.h b/src/global-handles.h
index 37b2b44..3bab4b8 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -48,10 +48,16 @@
class ObjectGroup : public Malloced {
public:
ObjectGroup() : objects_(4) {}
- explicit ObjectGroup(size_t capacity)
- : objects_(static_cast<int>(capacity)) { }
+ ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
+ : objects_(static_cast<int>(capacity)),
+ info_(info) { }
+ ~ObjectGroup() { if (info_ != NULL) info_->Dispose(); }
List<Object**> objects_;
+ v8::RetainedObjectInfo* info_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
};
@@ -75,6 +81,8 @@
void* parameter,
WeakReferenceCallback callback);
+ static void SetWrapperClassId(Object** location, uint16_t class_id);
+
// Returns the current number of weak handles.
static int NumberOfWeakHandles() { return number_of_weak_handles_; }
@@ -105,6 +113,9 @@
// Iterates over all handles.
static void IterateAllRoots(ObjectVisitor* v);
+ // Iterates over all handles that have embedder-assigned class ID.
+ static void IterateAllRootsWithClassIds(ObjectVisitor* v);
+
// Iterates over all weak roots in heap.
static void IterateWeakRoots(ObjectVisitor* v);
@@ -119,7 +130,9 @@
// Add an object group.
// Should only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
- static void AddGroup(Object*** handles, size_t length);
+ static void AddGroup(Object*** handles,
+ size_t length,
+ v8::RetainedObjectInfo* info);
// Returns the object groups.
static List<ObjectGroup*>* ObjectGroups();
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index 07b631f..97d10c9 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -364,6 +364,27 @@
}
+void HeapProfiler::DefineWrapperClass(
+ uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
+ ASSERT(singleton_ != NULL);
+ ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
+ if (singleton_->wrapper_callbacks_.length() <= class_id) {
+ singleton_->wrapper_callbacks_.AddBlock(
+ NULL, class_id - singleton_->wrapper_callbacks_.length() + 1);
+ }
+ singleton_->wrapper_callbacks_[class_id] = callback;
+}
+
+
+v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
+ uint16_t class_id, Object** wrapper) {
+ ASSERT(singleton_ != NULL);
+ if (singleton_->wrapper_callbacks_.length() <= class_id) return NULL;
+ return singleton_->wrapper_callbacks_[class_id](
+ class_id, Utils::ToLocal(Handle<Object>(wrapper)));
+}
+
+
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
int type,
v8::ActivityControl* control) {
@@ -401,7 +422,7 @@
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
int type,
v8::ActivityControl* control) {
- return TakeSnapshotImpl(snapshots_->GetName(name), type, control);
+ return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
}
@@ -872,7 +893,8 @@
const NumberAndSizeInfo& number_and_size) {
const char* name = cluster.GetSpecialCaseName();
if (name == NULL) {
- name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+ name = snapshot_->collection()->names()->GetFunctionName(
+ cluster.constructor());
}
AddEntryFromAggregatedSnapshot(snapshot_,
root_child_index_,
@@ -1013,7 +1035,8 @@
JSObjectsCluster cluster = HeapObjectAsCluster(obj);
const char* name = cluster.GetSpecialCaseName();
if (name == NULL) {
- name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+ name = snapshot_->collection()->names()->GetFunctionName(
+ cluster.constructor());
}
return AddEntryFromAggregatedSnapshot(
snapshot_, root_child_index_, HeapEntry::kObject, name,
diff --git a/src/heap-profiler.h b/src/heap-profiler.h
index 20ba457..c5a9ab4 100644
--- a/src/heap-profiler.h
+++ b/src/heap-profiler.h
@@ -68,6 +68,11 @@
static void ObjectMoveEvent(Address from, Address to);
+ static void DefineWrapperClass(
+ uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
+ static v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id,
+ Object** wrapper);
+
static INLINE(bool is_profiling()) {
return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
}
@@ -88,6 +93,7 @@
HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_;
+ List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
static HeapProfiler* singleton_;
#endif // ENABLE_LOGGING_AND_PROFILING
diff --git a/src/heap.cc b/src/heap.cc
index 199ee60..dd4d733 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1341,7 +1341,8 @@
#if defined(ENABLE_LOGGING_AND_PROFILING)
if (Logger::is_logging() || CpuProfiler::is_profiling()) {
if (target->IsSharedFunctionInfo()) {
- PROFILE(SFIMoveEvent(source->address(), target->address()));
+ PROFILE(SharedFunctionInfoMoveEvent(
+ source->address(), target->address()));
}
}
#endif
diff --git a/src/heap.h b/src/heap.h
index 61a41a4..0127933 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -1138,6 +1138,14 @@
static GCTracer* tracer() { return tracer_; }
+ static void CallGlobalGCPrologueCallback() {
+ if (global_gc_prologue_callback_ != NULL) global_gc_prologue_callback_();
+ }
+
+ static void CallGlobalGCEpilogueCallback() {
+ if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_();
+ }
+
private:
static int reserved_semispace_size_;
static int max_semispace_size_;
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index 6c516f2..fc376bd 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -151,6 +151,7 @@
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringCharCodeAt) \
+ V(StringCharFromCode) \
V(StringLength) \
V(Sub) \
V(Test) \
@@ -2567,6 +2568,16 @@
SetFlag(kCanBeDivByZero);
}
+ bool HasPowerOf2Divisor() {
+ if (right()->IsConstant() &&
+ HConstant::cast(right())->HasInteger32Value()) {
+ int32_t value = HConstant::cast(right())->Integer32Value();
+ return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
+ }
+
+ return false;
+ }
+
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
DECLARE_CONCRETE_INSTRUCTION(Mod, "mod")
@@ -3258,6 +3269,23 @@
};
+class HStringCharFromCode: public HUnaryOperation {
+ public:
+ explicit HStringCharFromCode(HValue* char_code) : HUnaryOperation(char_code) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Integer32();
+ }
+
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string_char_from_code")
+};
+
+
class HStringLength: public HUnaryOperation {
public:
explicit HStringLength(HValue* string) : HUnaryOperation(string) {
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index dbd9adf..b50399d 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -2136,6 +2136,8 @@
HGraph* HGraphBuilder::CreateGraph() {
graph_ = new HGraph(info());
+ if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
+
{
HPhase phase("Block building");
current_block_ = graph()->entry_block();
@@ -3701,6 +3703,13 @@
FIRST_STRING_TYPE,
LAST_STRING_TYPE));
instr = new HStringLength(string);
+ } else if (expr->IsStringAccess()) {
+ VISIT_FOR_VALUE(expr->key());
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+ AddInstruction(char_code);
+ instr = new HStringCharFromCode(char_code);
} else if (expr->IsFunctionPrototype()) {
HValue* function = Pop();
@@ -4081,6 +4090,7 @@
int argument_count = expr->arguments()->length() + 1; // Plus receiver.
switch (id) {
case kStringCharCodeAt:
+ case kStringCharAt:
if (argument_count == 2 && check_type == STRING_CHECK) {
HValue* index = Pop();
HValue* string = Pop();
@@ -4088,7 +4098,13 @@
AddInstruction(new HCheckPrototypeMaps(
oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
expr->holder()));
- HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
+ HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+ if (id == kStringCharCodeAt) {
+ ast_context()->ReturnInstruction(char_code, expr->id());
+ return true;
+ }
+ AddInstruction(char_code);
+ HStringCharFromCode* result = new HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, expr->id());
return true;
}
@@ -4265,10 +4281,12 @@
}
if (HasCustomCallGenerator(expr->target()) ||
+ CallOptimization(*expr->target()).is_simple_api_call() ||
expr->check_type() != RECEIVER_MAP_CHECK) {
// When the target has a custom call IC generator, use the IC,
- // because it is likely to generate better code. Also use the
- // IC when a primitive receiver check is required.
+ // because it is likely to generate better code. Similarly, we
+ // generate better call stubs for some API functions.
+ // Also use the IC when a primitive receiver check is required.
HContext* context = new HContext;
AddInstruction(context);
call = PreProcessCall(new HCallNamed(context, name, argument_count));
@@ -5174,19 +5192,24 @@
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
- BAILOUT("inlined runtime function: StringCharFromCode");
+ ASSERT(call->arguments()->length() == 1);
+ VISIT_FOR_VALUE(call->arguments()->at(0));
+ HValue* char_code = Pop();
+ HStringCharFromCode* result = new HStringCharFromCode(char_code);
+ ast_context()->ReturnInstruction(result, call->id());
}
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
- ASSERT_EQ(2, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
- AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::StringCharAt, 2);
- Drop(2);
+ ASSERT(call->arguments()->length() == 2);
+ VISIT_FOR_VALUE(call->arguments()->at(0));
+ VISIT_FOR_VALUE(call->arguments()->at(1));
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+ AddInstruction(char_code);
+ HStringCharFromCode* result = new HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5736,12 +5759,12 @@
Tag tag(this, "intervals");
PrintStringProperty("name", name);
- const ZoneList<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
+ const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
for (int i = 0; i < fixed_d->length(); ++i) {
TraceLiveRange(fixed_d->at(i), "fixed");
}
- const ZoneList<LiveRange*>* fixed = allocator->fixed_live_ranges();
+ const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
for (int i = 0; i < fixed->length(); ++i) {
TraceLiveRange(fixed->at(i), "fixed");
}
@@ -5812,6 +5835,11 @@
}
+void HStatistics::Initialize(CompilationInfo* info) {
+ source_size_ += info->shared_info()->SourceSize();
+}
+
+
void HStatistics::Print() {
PrintF("Timing results:\n");
int64_t sum = 0;
@@ -5829,9 +5857,10 @@
double size_percent = static_cast<double>(size) * 100 / total_size_;
PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
}
- PrintF("%30s - %7.3f ms %8u bytes\n", "Sum",
- static_cast<double>(sum) / 1000,
- total_size_);
+ double source_size_in_kb = static_cast<double>(source_size_) / 1024;
+ PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
+ (static_cast<double>(sum) / 1000) / source_size_in_kb,
+ total_size_ / source_size_in_kb);
PrintF("---------------------------------------------------------------\n");
PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
"Total",
@@ -5876,13 +5905,13 @@
if (allocator != NULL && chunk_ == NULL) {
chunk_ = allocator->chunk();
}
- if (FLAG_time_hydrogen) start_ = OS::Ticks();
+ if (FLAG_hydrogen_stats) start_ = OS::Ticks();
start_allocation_size_ = Zone::allocation_size_;
}
void HPhase::End() const {
- if (FLAG_time_hydrogen) {
+ if (FLAG_hydrogen_stats) {
int64_t end = OS::Ticks();
unsigned size = Zone::allocation_size_ - start_allocation_size_;
HStatistics::Instance()->SaveTiming(name_, end - start_, size);
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 21f3677..235b669 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -939,6 +939,7 @@
class HStatistics: public Malloced {
public:
+ void Initialize(CompilationInfo* info);
void Print();
void SaveTiming(const char* name, int64_t ticks, unsigned size);
static HStatistics* Instance() {
@@ -957,7 +958,8 @@
sizes_(5),
total_(0),
total_size_(0),
- full_code_gen_(0) { }
+ full_code_gen_(0),
+ source_size_(0) { }
List<int64_t> timing_;
List<const char*> names_;
@@ -965,6 +967,7 @@
int64_t total_;
unsigned total_size_;
int64_t full_code_gen_;
+ double source_size_;
};
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index 5f4d944..49308bf 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -56,6 +56,7 @@
void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+ HandleScope scope;
AssertNoAllocation no_allocation;
if (!function->IsOptimized()) return;
@@ -132,6 +133,11 @@
PrintF("[forced deoptimization: ");
function->PrintName();
PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
+#ifdef DEBUG
+ if (FLAG_print_code) {
+ code->PrintLn();
+ }
+#endif
}
}
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 28f4679..269591e 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -781,41 +781,64 @@
void LCodeGen::DoModI(LModI* instr) {
- LOperand* right = instr->InputAt(1);
- ASSERT(ToRegister(instr->result()).is(edx));
- ASSERT(ToRegister(instr->InputAt(0)).is(eax));
- ASSERT(!ToRegister(instr->InputAt(1)).is(eax));
- ASSERT(!ToRegister(instr->InputAt(1)).is(edx));
+ if (instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->InputAt(0));
- Register right_reg = ToRegister(right);
+ int32_t divisor =
+ HConstant::cast(instr->hydrogen()->right())->Integer32Value();
- // Check for x % 0.
- if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
- __ test(right_reg, ToOperand(right));
- DeoptimizeIf(zero, instr->environment());
- }
+ if (divisor < 0) divisor = -divisor;
- // Sign extend to edx.
- __ cdq();
-
- // Check for (0 % -x) that will produce negative zero.
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel positive_left;
- NearLabel done;
- __ test(eax, Operand(eax));
- __ j(not_sign, &positive_left);
- __ idiv(right_reg);
-
- // Test the remainder for 0, because then the result would be -0.
- __ test(edx, Operand(edx));
- __ j(not_zero, &done);
-
- DeoptimizeIf(no_condition, instr->environment());
- __ bind(&positive_left);
- __ idiv(right_reg);
+ NearLabel positive_dividend, done;
+ __ test(dividend, Operand(dividend));
+ __ j(not_sign, &positive_dividend);
+ __ neg(dividend);
+ __ and_(dividend, divisor - 1);
+ __ neg(dividend);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ j(not_zero, &done);
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ __ bind(&positive_dividend);
+ __ and_(dividend, divisor - 1);
__ bind(&done);
} else {
- __ idiv(right_reg);
+ LOperand* right = instr->InputAt(1);
+ ASSERT(ToRegister(instr->InputAt(0)).is(eax));
+ ASSERT(ToRegister(instr->result()).is(edx));
+
+ Register right_reg = ToRegister(right);
+ ASSERT(!right_reg.is(eax));
+ ASSERT(!right_reg.is(edx));
+
+ // Check for x % 0.
+ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ test(right_reg, ToOperand(right));
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Sign extend to edx.
+ __ cdq();
+
+ // Check for (0 % -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ NearLabel positive_left;
+ NearLabel done;
+ __ test(eax, Operand(eax));
+ __ j(not_sign, &positive_left);
+ __ idiv(right_reg);
+
+ // Test the remainder for 0, because then the result would be -0.
+ __ test(edx, Operand(edx));
+ __ j(not_zero, &done);
+
+ DeoptimizeIf(no_condition, instr->environment());
+ __ bind(&positive_left);
+ __ idiv(right_reg);
+ __ bind(&done);
+ } else {
+ __ idiv(right_reg);
+ }
}
}
@@ -3007,6 +3030,56 @@
}
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+ class DeferredStringCharFromCode: public LDeferredCode {
+ public:
+ DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+ private:
+ LStringCharFromCode* instr_;
+ };
+
+ DeferredStringCharFromCode* deferred =
+ new DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmp(char_code, String::kMaxAsciiCharCode);
+ __ j(above, deferred->entry());
+ __ Set(result, Immediate(Factory::single_character_string_cache()));
+ __ mov(result, FieldOperand(result,
+ char_code, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result, Factory::undefined_value());
+ __ j(equal, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+
+ // TODO(3095996): Get rid of this. For now, we need to make the
+ // result register contain a valid pointer because it is already
+ // contained in the register pointer map.
+ __ Set(result, Immediate(0));
+
+ __ PushSafepointRegisters();
+ __ SmiTag(char_code);
+ __ push(char_code);
+ __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ __ StoreToSafepointRegisterSlot(result, eax);
+ __ PopSafepointRegisters();
+}
+
+
void LCodeGen::DoStringLength(LStringLength* instr) {
Register string = ToRegister(instr->string());
Register result = ToRegister(instr->result());
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index ecd6caa..c5c76c1 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -95,6 +95,7 @@
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check);
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 0ade55f..3148ae2 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1366,13 +1366,23 @@
if (instr->representation().IsInteger32()) {
ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
- // The temporary operand is necessary to ensure that right is not allocated
- // into edx.
- LOperand* temp = FixedTemp(edx);
- LOperand* value = UseFixed(instr->left(), eax);
- LOperand* divisor = UseRegister(instr->right());
- LModI* mod = new LModI(value, divisor, temp);
- LInstruction* result = DefineFixed(mod, edx);
+
+ LInstruction* result;
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL);
+ result = DefineSameAsFirst(mod);
+ } else {
+ // The temporary operand is necessary to ensure that right is
+ // not allocated into edx.
+ LOperand* temp = FixedTemp(edx);
+ LOperand* value = UseFixed(instr->left(), eax);
+ LOperand* divisor = UseRegister(instr->right());
+ LModI* mod = new LModI(value, divisor, temp);
+ result = DefineFixed(mod, edx);
+ }
+
return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
instr->CheckFlag(HValue::kCanBeDivByZero))
? AssignEnvironment(result)
@@ -1933,6 +1943,13 @@
}
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
LOperand* string = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LStringLength(string));
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index e832be5..9a07c6f 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -151,6 +151,7 @@
V(StoreNamedGeneric) \
V(StorePixelArrayElement) \
V(StringCharCodeAt) \
+ V(StringCharFromCode) \
V(StringLength) \
V(SubI) \
V(TaggedToI) \
@@ -1715,6 +1716,19 @@
};
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+ LOperand* char_code() { return inputs_[0]; }
+};
+
+
class LStringLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LStringLength(LOperand* string) {
diff --git a/src/lithium-allocator.cc b/src/lithium-allocator.cc
index 1e79c00..1983390 100644
--- a/src/lithium-allocator.cc
+++ b/src/lithium-allocator.cc
@@ -525,6 +525,24 @@
}
+LAllocator::LAllocator(int num_values, HGraph* graph)
+ : chunk_(NULL),
+ live_in_sets_(graph->blocks()->length()),
+ live_ranges_(num_values * 2),
+ fixed_live_ranges_(NULL),
+ fixed_double_live_ranges_(NULL),
+ unhandled_live_ranges_(num_values * 2),
+ active_live_ranges_(8),
+ inactive_live_ranges_(8),
+ reusable_slots_(8),
+ next_virtual_register_(num_values),
+ first_artificial_register_(num_values),
+ mode_(NONE),
+ num_registers_(-1),
+ graph_(graph),
+ has_osr_entry_(false) {}
+
+
void LAllocator::InitializeLivenessAnalysis() {
// Initialize the live_in sets for each block to NULL.
int block_count = graph_->blocks()->length();
@@ -618,11 +636,7 @@
LiveRange* LAllocator::FixedLiveRangeFor(int index) {
- if (index >= fixed_live_ranges_.length()) {
- fixed_live_ranges_.AddBlock(NULL,
- index - fixed_live_ranges_.length() + 1);
- }
-
+ ASSERT(index < Register::kNumAllocatableRegisters);
LiveRange* result = fixed_live_ranges_[index];
if (result == NULL) {
result = new LiveRange(FixedLiveRangeID(index));
@@ -635,11 +649,7 @@
LiveRange* LAllocator::FixedDoubleLiveRangeFor(int index) {
- if (index >= fixed_double_live_ranges_.length()) {
- fixed_double_live_ranges_.AddBlock(NULL,
- index - fixed_double_live_ranges_.length() + 1);
- }
-
+ ASSERT(index < DoubleRegister::kNumAllocatableRegisters);
LiveRange* result = fixed_double_live_ranges_[index];
if (result == NULL) {
result = new LiveRange(FixedDoubleLiveRangeID(index));
@@ -650,6 +660,7 @@
return result;
}
+
LiveRange* LAllocator::LiveRangeFor(int index) {
if (index >= live_ranges_.length()) {
live_ranges_.AddBlock(NULL, index - live_ranges_.length() + 1);
diff --git a/src/lithium-allocator.h b/src/lithium-allocator.h
index d53ea78..14a0201 100644
--- a/src/lithium-allocator.h
+++ b/src/lithium-allocator.h
@@ -428,22 +428,7 @@
class LAllocator BASE_EMBEDDED {
public:
- explicit LAllocator(int first_virtual_register, HGraph* graph)
- : chunk_(NULL),
- live_in_sets_(0),
- live_ranges_(16),
- fixed_live_ranges_(8),
- fixed_double_live_ranges_(8),
- unhandled_live_ranges_(8),
- active_live_ranges_(8),
- inactive_live_ranges_(8),
- reusable_slots_(8),
- next_virtual_register_(first_virtual_register),
- first_artificial_register_(first_virtual_register),
- mode_(NONE),
- num_registers_(-1),
- graph_(graph),
- has_osr_entry_(false) {}
+ LAllocator(int first_virtual_register, HGraph* graph);
static void Setup();
static void TraceAlloc(const char* msg, ...);
@@ -468,10 +453,10 @@
void Allocate(LChunk* chunk);
const ZoneList<LiveRange*>* live_ranges() const { return &live_ranges_; }
- const ZoneList<LiveRange*>* fixed_live_ranges() const {
+ const Vector<LiveRange*>* fixed_live_ranges() const {
return &fixed_live_ranges_;
}
- const ZoneList<LiveRange*>* fixed_double_live_ranges() const {
+ const Vector<LiveRange*>* fixed_double_live_ranges() const {
return &fixed_double_live_ranges_;
}
@@ -616,8 +601,10 @@
ZoneList<LiveRange*> live_ranges_;
// Lists of live ranges
- ZoneList<LiveRange*> fixed_live_ranges_;
- ZoneList<LiveRange*> fixed_double_live_ranges_;
+ EmbeddedVector<LiveRange*, Register::kNumAllocatableRegisters>
+ fixed_live_ranges_;
+ EmbeddedVector<LiveRange*, DoubleRegister::kNumAllocatableRegisters>
+ fixed_double_live_ranges_;
ZoneList<LiveRange*> unhandled_live_ranges_;
ZoneList<LiveRange*> active_live_ranges_;
ZoneList<LiveRange*> inactive_live_ranges_;
diff --git a/src/log.cc b/src/log.cc
index bfef643..b43b685 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -881,9 +881,9 @@
}
-void Logger::SFIMoveEvent(Address from, Address to) {
+void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- MoveEventInternal(SFI_MOVE_EVENT, from, to);
+ MoveEventInternal(SHARED_FUNC_MOVE_EVENT, from, to);
#endif
}
diff --git a/src/log.h b/src/log.h
index 5470fd6..6e1736d 100644
--- a/src/log.h
+++ b/src/log.h
@@ -91,7 +91,7 @@
V(CODE_MOVE_EVENT, "code-move") \
V(CODE_DELETE_EVENT, "code-delete") \
V(CODE_MOVING_GC, "code-moving-gc") \
- V(SFI_MOVE_EVENT, "sfi-move") \
+ V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
V(TICK_EVENT, "tick") \
V(REPEAT_META_EVENT, "repeat") \
@@ -224,7 +224,7 @@
// Emits a code delete event.
static void CodeDeleteEvent(Address from);
- static void SFIMoveEvent(Address from, Address to);
+ static void SharedFunctionInfoMoveEvent(Address from, Address to);
static void SnapshotPositionEvent(Address addr, int pos);
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index a4c782c..9df9af1 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -2827,7 +2827,7 @@
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsSharedFunctionInfo()) {
- PROFILE(SFIMoveEvent(old_addr, new_addr));
+ PROFILE(SharedFunctionInfoMoveEvent(old_addr, new_addr));
}
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
@@ -2919,7 +2919,7 @@
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsSharedFunctionInfo()) {
- PROFILE(SFIMoveEvent(old_addr, new_addr));
+ PROFILE(SharedFunctionInfoMoveEvent(old_addr, new_addr));
}
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
diff --git a/src/objects-inl.h b/src/objects-inl.h
index eac311b..01073ca 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -3668,6 +3668,22 @@
}
+template <typename schar>
+uint32_t HashSequentialString(const schar* chars, int length) {
+ StringHasher hasher(length);
+ if (!hasher.has_trivial_hash()) {
+ int i;
+ for (i = 0; hasher.is_array_index() && (i < length); i++) {
+ hasher.AddCharacter(chars[i]);
+ }
+ for (; i < length; i++) {
+ hasher.AddCharacterNoIndex(chars[i]);
+ }
+ }
+ return hasher.GetHashField();
+}
+
+
bool String::AsArrayIndex(uint32_t* index) {
uint32_t field = hash_field();
if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
diff --git a/src/objects.cc b/src/objects.cc
index bde9998..6e47946 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -5238,22 +5238,6 @@
}
-template <typename schar>
-static inline uint32_t HashSequentialString(const schar* chars, int length) {
- StringHasher hasher(length);
- if (!hasher.has_trivial_hash()) {
- int i;
- for (i = 0; hasher.is_array_index() && (i < length); i++) {
- hasher.AddCharacter(chars[i]);
- }
- for (; i < length; i++) {
- hasher.AddCharacterNoIndex(chars[i]);
- }
- }
- return hasher.GetHashField();
-}
-
-
uint32_t String::ComputeAndSetHash() {
// Should only be called if hash code has not yet been computed.
ASSERT(!HasHashCode());
diff --git a/src/objects.h b/src/objects.h
index 9f558a4..3d88568 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -5154,6 +5154,11 @@
};
+// Calculates string hash.
+template <typename schar>
+inline uint32_t HashSequentialString(const schar* chars, int length);
+
+
// The characteristics of a string are stored in its map. Retrieving these
// few bits of information is moderately expensive, involving two memory
// loads where the second is dependent on the first. To improve efficiency
@@ -6535,6 +6540,9 @@
VisitExternalReferences(p, p + 1);
}
+ // Visits a handle that has an embedder-assigned class ID.
+ virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}
+
#ifdef DEBUG
// Intended for serialization/deserialization checking: insert, or
// check for the presence of, a tag at this position in the stream.
diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc
index ebe0475..794fe07 100644
--- a/src/platform-solaris.cc
+++ b/src/platform-solaris.cc
@@ -45,7 +45,7 @@
#include <errno.h>
#include <ieeefp.h> // finite()
#include <signal.h> // sigemptyset(), etc
-#include <sys/kdi_regs.h>
+#include <sys/regset.h>
#undef MAP_TYPE
@@ -612,11 +612,16 @@
static pthread_t vm_tid_ = 0;
+static pthread_t GetThreadID() {
+ return pthread_self();
+}
+
+
static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
USE(info);
if (signal != SIGPROF) return;
if (active_sampler_ == NULL || !active_sampler_->IsActive()) return;
- if (vm_tid_ != pthread_self()) return;
+ if (vm_tid_ != GetThreadID()) return;
TickSample sample_obj;
TickSample* sample = CpuProfiler::TickSampleEvent();
@@ -627,17 +632,10 @@
mcontext_t& mcontext = ucontext->uc_mcontext;
sample->state = Top::current_vm_state();
-#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EBP]);
-#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RBP]);
-#else
- UNIMPLEMENTED();
-#endif
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
+
active_sampler_->SampleStack(sample);
active_sampler_->Tick(sample);
}
@@ -645,26 +643,86 @@
class Sampler::PlatformData : public Malloced {
public:
- PlatformData() {
- signal_handler_installed_ = false;
+ enum SleepInterval {
+ FULL_INTERVAL,
+ HALF_INTERVAL
+ };
+
+ explicit PlatformData(Sampler* sampler)
+ : sampler_(sampler),
+ signal_handler_installed_(false),
+ vm_tgid_(getpid()),
+ signal_sender_launched_(false) {
}
+ void SignalSender() {
+ while (sampler_->IsActive()) {
+ if (rate_limiter_.SuspendIfNecessary()) continue;
+ if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) {
+ SendProfilingSignal();
+ Sleep(HALF_INTERVAL);
+ RuntimeProfiler::NotifyTick();
+ Sleep(HALF_INTERVAL);
+ } else {
+ if (sampler_->IsProfiling()) SendProfilingSignal();
+ if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick();
+ Sleep(FULL_INTERVAL);
+ }
+ }
+ }
+
+ void SendProfilingSignal() {
+ if (!signal_handler_installed_) return;
+ pthread_kill(vm_tid_, SIGPROF);
+ }
+
+ void Sleep(SleepInterval full_or_half) {
+ // Convert ms to us and subtract 100 us to compensate delays
+ // occuring during signal delivery.
+ useconds_t interval = sampler_->interval_ * 1000 - 100;
+ if (full_or_half == HALF_INTERVAL) interval /= 2;
+ int result = usleep(interval);
+#ifdef DEBUG
+ if (result != 0 && errno != EINTR) {
+ fprintf(stderr,
+ "SignalSender usleep error; interval = %u, errno = %d\n",
+ interval,
+ errno);
+ ASSERT(result == 0 || errno == EINTR);
+ }
+#endif
+ USE(result);
+ }
+
+ Sampler* sampler_;
bool signal_handler_installed_;
struct sigaction old_signal_handler_;
- struct itimerval old_timer_value_;
+ int vm_tgid_;
+ bool signal_sender_launched_;
+ pthread_t signal_sender_thread_;
+ RuntimeProfilerRateLimiter rate_limiter_;
};
+static void* SenderEntry(void* arg) {
+ Sampler::PlatformData* data =
+ reinterpret_cast<Sampler::PlatformData*>(arg);
+ data->SignalSender();
+ return 0;
+}
+
+
Sampler::Sampler(int interval)
: interval_(interval),
profiling_(false),
active_(false),
samples_taken_(0) {
- data_ = new PlatformData();
+ data_ = new PlatformData(this);
}
Sampler::~Sampler() {
+ ASSERT(!data_->signal_sender_launched_);
delete data_;
}
@@ -672,41 +730,50 @@
void Sampler::Start() {
// There can only be one active sampler at the time on POSIX
// platforms.
- if (active_sampler_ != NULL) return;
+ ASSERT(!IsActive());
+ vm_tid_ = GetThreadID();
// Request profiling signals.
struct sigaction sa;
sa.sa_sigaction = ProfilerSignalHandler;
sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_SIGINFO;
- if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
- data_->signal_handler_installed_ = true;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ data_->signal_handler_installed_ =
+ sigaction(SIGPROF, &sa, &data_->old_signal_handler_) == 0;
- // Set the itimer to generate a tick for each interval.
- itimerval itimer;
- itimer.it_interval.tv_sec = interval_ / 1000;
- itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
- itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
- itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
- setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
+ // Start a thread that sends SIGPROF signal to VM thread.
+ // Sending the signal ourselves instead of relying on itimer provides
+ // much better accuracy.
+ SetActive(true);
+ if (pthread_create(
+ &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
+ data_->signal_sender_launched_ = true;
+ }
// Set this sampler as the active sampler.
active_sampler_ = this;
- active_ = true;
}
void Sampler::Stop() {
+ SetActive(false);
+
+ // Wait for signal sender termination (it will exit after setting
+ // active_ to false).
+ if (data_->signal_sender_launched_) {
+ Top::WakeUpRuntimeProfilerThreadBeforeShutdown();
+ pthread_join(data_->signal_sender_thread_, NULL);
+ data_->signal_sender_launched_ = false;
+ }
+
// Restore old signal handler
if (data_->signal_handler_installed_) {
- setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
sigaction(SIGPROF, &data_->old_signal_handler_, 0);
data_->signal_handler_installed_ = false;
}
// This sampler is no longer the active sampler.
active_sampler_ = NULL;
- active_ = false;
}
#endif // ENABLE_LOGGING_AND_PROFILING
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 7612eab..099b9df 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -29,6 +29,7 @@
#include "v8.h"
#include "global-handles.h"
+#include "heap-profiler.h"
#include "scopeinfo.h"
#include "top.h"
#include "unicode.h"
@@ -94,51 +95,70 @@
}
-static void DeleteIndexName(char** name_ptr) {
- DeleteArray(*name_ptr);
-}
-
-
StringsStorage::~StringsStorage() {
for (HashMap::Entry* p = names_.Start();
p != NULL;
p = names_.Next(p)) {
DeleteArray(reinterpret_cast<const char*>(p->value));
}
- index_names_.Iterate(DeleteIndexName);
+}
+
+
+const char* StringsStorage::GetCopy(const char* src) {
+ int len = static_cast<int>(strlen(src));
+ Vector<char> dst = Vector<char>::New(len + 1);
+ OS::StrNCpy(dst, src, len);
+ dst[len] = '\0';
+ uint32_t hash = HashSequentialString(dst.start(), len);
+ return AddOrDisposeString(dst.start(), hash);
+}
+
+
+const char* StringsStorage::GetFormatted(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ const char* result = GetVFormatted(format, args);
+ va_end(args);
+ return result;
+}
+
+
+const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
+ HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
+ if (cache_entry->value == NULL) {
+ // New entry added.
+ cache_entry->value = str;
+ } else {
+ DeleteArray(str);
+ }
+ return reinterpret_cast<const char*>(cache_entry->value);
+}
+
+
+const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
+ Vector<char> str = Vector<char>::New(1024);
+ int len = OS::VSNPrintF(str, format, args);
+ if (len == -1) {
+ DeleteArray(str.start());
+ return format;
+ }
+ uint32_t hash = HashSequentialString(str.start(), len);
+ return AddOrDisposeString(str.start(), hash);
}
const char* StringsStorage::GetName(String* name) {
if (name->IsString()) {
- char* c_name =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
- HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true);
- if (cache_entry->value == NULL) {
- // New entry added.
- cache_entry->value = c_name;
- } else {
- DeleteArray(c_name);
- }
- return reinterpret_cast<const char*>(cache_entry->value);
+ return AddOrDisposeString(
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(),
+ name->Hash());
}
return "";
}
const char* StringsStorage::GetName(int index) {
- ASSERT(index >= 0);
- if (index_names_.length() <= index) {
- index_names_.AddBlock(
- NULL, index - index_names_.length() + 1);
- }
- if (index_names_[index] == NULL) {
- const int kMaximumNameLength = 32;
- char* name = NewArray<char>(kMaximumNameLength);
- OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", index);
- index_names_[index] = name;
- }
- return index_names_[index];
+ return GetFormatted("%d", index);
}
@@ -465,7 +485,7 @@
}
-CodeEntry* const CodeMap::kSfiCodeEntry = NULL;
+CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
CodeMap::CodeEntryInfo(NULL, 0);
@@ -483,18 +503,18 @@
}
-int CodeMap::GetSFITag(Address addr) {
+int CodeMap::GetSharedId(Address addr) {
CodeTree::Locator locator;
- // For SFI entries, 'size' field is used to store their IDs.
+ // For shared function entries, 'size' field is used to store their IDs.
if (tree_.Find(addr, &locator)) {
const CodeEntryInfo& entry = locator.value();
- ASSERT(entry.entry == kSfiCodeEntry);
+ ASSERT(entry.entry == kSharedFunctionCodeEntry);
return entry.size;
} else {
tree_.Insert(addr, &locator);
- int tag = next_sfi_tag_++;
- locator.set_value(CodeEntryInfo(kSfiCodeEntry, tag));
- return tag;
+ int id = next_shared_id_++;
+ locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
+ return id;
}
}
@@ -1009,6 +1029,7 @@
case kArray: return "/array/";
case kRegExp: return "/regexp/";
case kHeapNumber: return "/number/";
+ case kNative: return "/native/";
default: return "???";
}
}
@@ -1205,6 +1226,7 @@
uid_(uid),
root_entry_(NULL),
gc_roots_entry_(NULL),
+ natives_root_entry_(NULL),
raw_entries_(NULL),
entries_sorted_(false),
retaining_paths_(HeapEntry::Match) {
@@ -1279,6 +1301,19 @@
}
+HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
+ int retainers_count) {
+ ASSERT(natives_root_entry_ == NULL);
+ return (natives_root_entry_ = AddEntry(
+ HeapEntry::kObject,
+ "(Native objects)",
+ HeapObjectsMap::kNativesRootObjectId,
+ 0,
+ children_count,
+ retainers_count));
+}
+
+
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name,
uint64_t id,
@@ -1372,10 +1407,13 @@
}
-const uint64_t HeapObjectsMap::kInternalRootObjectId = 0;
-const uint64_t HeapObjectsMap::kGcRootsObjectId = 1;
+// We split IDs on evens for embedder objects (see
+// HeapObjectsMap::GenerateId) and odds for native objects.
+const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
+const uint64_t HeapObjectsMap::kGcRootsObjectId = 3;
+const uint64_t HeapObjectsMap::kNativesRootObjectId = 5;
// Increase kFirstAvailableObjectId if new 'special' objects appear.
-const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2;
+const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
HeapObjectsMap::HeapObjectsMap()
: initial_fill_mode_(true),
@@ -1400,7 +1438,8 @@
uint64_t existing = FindEntry(addr);
if (existing != 0) return existing;
}
- uint64_t id = next_id_++;
+ uint64_t id = next_id_;
+ next_id_ += 2;
AddEntry(addr, id);
return id;
}
@@ -1468,6 +1507,17 @@
}
+uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+ uint64_t id = static_cast<uint64_t>(info->GetHash());
+ const char* label = info->GetLabel();
+ id ^= HashSequentialString(label, static_cast<int>(strlen(label)));
+ intptr_t element_count = info->GetElementCount();
+ if (element_count != -1)
+ id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count));
+ return id << 1;
+}
+
+
HeapSnapshotsCollection::HeapSnapshotsCollection()
: is_tracking_objects_(false),
snapshots_uids_(HeapSnapshotsMatch),
@@ -1551,6 +1601,8 @@
p->key,
entry_info->children_count,
entry_info->retainers_count);
+ ASSERT(entry_info->entry != NULL);
+ ASSERT(entry_info->entry != kHeapEntryPlaceholder);
entry_info->children_count = 0;
entry_info->retainers_count = 0;
}
@@ -1630,9 +1682,11 @@
HeapObject *const V8HeapExplorer::kInternalRootObject =
- reinterpret_cast<HeapObject*>(1);
+ reinterpret_cast<HeapObject*>(
+ static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
HeapObject *const V8HeapExplorer::kGcRootsObject =
- reinterpret_cast<HeapObject*>(2);
+ reinterpret_cast<HeapObject*>(
+ static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
V8HeapExplorer::V8HeapExplorer(
@@ -1669,27 +1723,28 @@
SharedFunctionInfo* shared = func->shared();
return AddEntry(object,
HeapEntry::kClosure,
- collection_->GetName(String::cast(shared->name())),
+ collection_->names()->GetName(String::cast(shared->name())),
children_count,
retainers_count);
} else if (object->IsJSRegExp()) {
JSRegExp* re = JSRegExp::cast(object);
return AddEntry(object,
HeapEntry::kRegExp,
- collection_->GetName(re->Pattern()),
+ collection_->names()->GetName(re->Pattern()),
children_count,
retainers_count);
} else if (object->IsJSObject()) {
return AddEntry(object,
HeapEntry::kObject,
- collection_->GetName(GetConstructorNameForHeapProfile(
- JSObject::cast(object))),
+ collection_->names()->GetName(
+ GetConstructorNameForHeapProfile(
+ JSObject::cast(object))),
children_count,
retainers_count);
} else if (object->IsString()) {
return AddEntry(object,
HeapEntry::kString,
- collection_->GetName(String::cast(object)),
+ collection_->names()->GetName(String::cast(object)),
children_count,
retainers_count);
} else if (object->IsCode()) {
@@ -1702,7 +1757,7 @@
SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
return AddEntry(object,
HeapEntry::kCode,
- collection_->GetName(String::cast(shared->name())),
+ collection_->names()->GetName(String::cast(shared->name())),
children_count,
retainers_count);
} else if (object->IsScript()) {
@@ -1710,7 +1765,9 @@
return AddEntry(object,
HeapEntry::kCode,
script->name()->IsString() ?
- collection_->GetName(String::cast(script->name())) : "",
+ collection_->names()->GetName(
+ String::cast(script->name()))
+ : "",
children_count,
retainers_count);
} else if (object->IsFixedArray()) {
@@ -1749,8 +1806,8 @@
void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
- filler->AddEntry(kInternalRootObject);
- filler->AddEntry(kGcRootsObject);
+ filler->AddEntry(kInternalRootObject, this);
+ filler->AddEntry(kGcRootsObject, this);
}
@@ -1940,7 +1997,7 @@
HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
if (!obj->IsHeapObject()) return NULL;
- return filler_->FindOrAddEntry(obj);
+ return filler_->FindOrAddEntry(obj, this);
}
@@ -1992,7 +2049,7 @@
filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
parent_obj,
parent_entry,
- collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
child_obj,
child_entry);
known_references_.Insert(child_obj);
@@ -2043,7 +2100,7 @@
filler_->SetNamedReference(HeapGraphEdge::kInternal,
parent_obj,
parent_entry,
- collection_->GetName(index),
+ collection_->names()->GetName(index),
child_obj,
child_entry);
known_references_.Insert(child_obj);
@@ -2078,7 +2135,7 @@
filler_->SetNamedReference(type,
parent_obj,
parent_entry,
- collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
child_obj,
child_entry);
known_references_.Insert(child_obj);
@@ -2096,7 +2153,7 @@
filler_->SetNamedReference(HeapGraphEdge::kShortcut,
parent_obj,
parent_entry,
- collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
child_obj,
child_entry);
}
@@ -2132,25 +2189,215 @@
}
+class GlobalHandlesExtractor : public ObjectVisitor {
+ public:
+ explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
+ : explorer_(explorer) {}
+ virtual ~GlobalHandlesExtractor() {}
+ virtual void VisitPointers(Object** start, Object** end) {
+ UNREACHABLE();
+ }
+ virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
+ explorer_->VisitSubtreeWrapper(p, class_id);
+ }
+ private:
+ NativeObjectsExplorer* explorer_;
+};
+
+HeapThing const NativeObjectsExplorer::kNativesRootObject =
+ reinterpret_cast<HeapThing>(
+ static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId));
+
+
+NativeObjectsExplorer::NativeObjectsExplorer(
+ HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
+ : snapshot_(snapshot),
+ collection_(snapshot_->collection()),
+ progress_(progress),
+ embedder_queried_(false),
+ objects_by_info_(RetainedInfosMatch),
+ filler_(NULL) {
+}
+
+
+NativeObjectsExplorer::~NativeObjectsExplorer() {
+ for (HashMap::Entry* p = objects_by_info_.Start();
+ p != NULL;
+ p = objects_by_info_.Next(p)) {
+ v8::RetainedObjectInfo* info =
+ reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+ info->Dispose();
+ List<HeapObject*>* objects =
+ reinterpret_cast<List<HeapObject*>* >(p->value);
+ delete objects;
+ }
+}
+
+
+HeapEntry* NativeObjectsExplorer::AllocateEntry(
+ HeapThing ptr, int children_count, int retainers_count) {
+ if (ptr == kNativesRootObject) {
+ return snapshot_->AddNativesRootEntry(children_count, retainers_count);
+ } else {
+ v8::RetainedObjectInfo* info =
+ reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
+ intptr_t elements = info->GetElementCount();
+ intptr_t size = info->GetSizeInBytes();
+ return snapshot_->AddEntry(
+ HeapEntry::kNative,
+ elements != -1 ?
+ collection_->names()->GetFormatted(
+ "%s / %" V8_PTR_PREFIX "d entries",
+ info->GetLabel(),
+ info->GetElementCount()) :
+ collection_->names()->GetCopy(info->GetLabel()),
+ HeapObjectsMap::GenerateId(info),
+ size != -1 ? static_cast<int>(size) : 0,
+ children_count,
+ retainers_count);
+ }
+}
+
+
+void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
+ if (EstimateObjectsCount() <= 0) return;
+ filler->AddEntry(kNativesRootObject, this);
+}
+
+
+int NativeObjectsExplorer::EstimateObjectsCount() {
+ FillRetainedObjects();
+ return objects_by_info_.occupancy();
+}
+
+
+void NativeObjectsExplorer::FillRetainedObjects() {
+ if (embedder_queried_) return;
+ // Record objects that are joined into ObjectGroups.
+ Heap::CallGlobalGCPrologueCallback();
+ List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups();
+ for (int i = 0; i < groups->length(); ++i) {
+ ObjectGroup* group = groups->at(i);
+ if (group->info_ == NULL) continue;
+ List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
+ for (int j = 0; j < group->objects_.length(); ++j) {
+ HeapObject* obj = HeapObject::cast(*group->objects_[j]);
+ list->Add(obj);
+ in_groups_.Insert(obj);
+ }
+ group->info_ = NULL; // Acquire info object ownership.
+ }
+ GlobalHandles::RemoveObjectGroups();
+ Heap::CallGlobalGCEpilogueCallback();
+ // Record objects that are not in ObjectGroups, but have class ID.
+ GlobalHandlesExtractor extractor(this);
+ GlobalHandles::IterateAllRootsWithClassIds(&extractor);
+ embedder_queried_ = true;
+}
+
+
+List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
+ v8::RetainedObjectInfo* info) {
+ HashMap::Entry* entry =
+ objects_by_info_.Lookup(info, InfoHash(info), true);
+ if (entry->value != NULL) {
+ info->Dispose();
+ } else {
+ entry->value = new List<HeapObject*>(4);
+ }
+ return reinterpret_cast<List<HeapObject*>* >(entry->value);
+}
+
+
+bool NativeObjectsExplorer::IterateAndExtractReferences(
+ SnapshotFillerInterface* filler) {
+ if (EstimateObjectsCount() <= 0) return true;
+ filler_ = filler;
+ FillRetainedObjects();
+ for (HashMap::Entry* p = objects_by_info_.Start();
+ p != NULL;
+ p = objects_by_info_.Next(p)) {
+ v8::RetainedObjectInfo* info =
+ reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+ SetNativeRootReference(info);
+ List<HeapObject*>* objects =
+ reinterpret_cast<List<HeapObject*>* >(p->value);
+ for (int i = 0; i < objects->length(); ++i) {
+ SetWrapperNativeReferences(objects->at(i), info);
+ }
+ }
+ SetRootNativesRootReference();
+ filler_ = NULL;
+ return true;
+}
+
+
+void NativeObjectsExplorer::SetNativeRootReference(
+ v8::RetainedObjectInfo* info) {
+ HeapEntry* child_entry = filler_->FindOrAddEntry(info, this);
+ ASSERT(child_entry != NULL);
+ filler_->SetIndexedAutoIndexReference(
+ HeapGraphEdge::kElement,
+ kNativesRootObject, snapshot_->natives_root(),
+ info, child_entry);
+}
+
+
+void NativeObjectsExplorer::SetWrapperNativeReferences(
+ HeapObject* wrapper, v8::RetainedObjectInfo* info) {
+ HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
+ ASSERT(wrapper_entry != NULL);
+ HeapEntry* info_entry = filler_->FindOrAddEntry(info, this);
+ ASSERT(info_entry != NULL);
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ wrapper, wrapper_entry,
+ "Native",
+ info, info_entry);
+ filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
+ info, info_entry,
+ wrapper, wrapper_entry);
+}
+
+
+void NativeObjectsExplorer::SetRootNativesRootReference() {
+ filler_->SetIndexedAutoIndexReference(
+ HeapGraphEdge::kElement,
+ V8HeapExplorer::kInternalRootObject, snapshot_->root(),
+ kNativesRootObject, snapshot_->natives_root());
+}
+
+
+void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
+ if (in_groups_.Contains(*p)) return;
+ v8::RetainedObjectInfo* info =
+ HeapProfiler::ExecuteWrapperClassCallback(class_id, p);
+ if (info == NULL) return;
+ GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
+}
+
+
HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
v8::ActivityControl* control)
: snapshot_(snapshot),
control_(control),
- v8_heap_explorer_(snapshot_, this) {
+ v8_heap_explorer_(snapshot_, this),
+ dom_explorer_(snapshot_, this) {
}
class SnapshotCounter : public SnapshotFillerInterface {
public:
- SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries)
- : allocator_(allocator), entries_(entries) { }
- HeapEntry* AddEntry(HeapThing ptr) {
- entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder);
+ explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
+ HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
return HeapEntriesMap::kHeapEntryPlaceholder;
}
- HeapEntry* FindOrAddEntry(HeapThing ptr) {
- HeapEntry* entry = entries_->Map(ptr);
- return entry != NULL ? entry : AddEntry(ptr);
+ HeapEntry* FindEntry(HeapThing ptr) {
+ return entries_->Map(ptr);
+ }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = FindEntry(ptr);
+ return entry != NULL ? entry : AddEntry(ptr, allocator);
}
void SetIndexedReference(HeapGraphEdge::Type,
HeapThing parent_ptr,
@@ -2183,7 +2430,6 @@
entries_->CountReference(parent_ptr, child_ptr);
}
private:
- HeapEntriesAllocator* allocator_;
HeapEntriesMap* entries_;
};
@@ -2194,13 +2440,16 @@
: snapshot_(snapshot),
collection_(snapshot->collection()),
entries_(entries) { }
- HeapEntry* AddEntry(HeapThing ptr) {
+ HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
UNREACHABLE();
return NULL;
}
- HeapEntry* FindOrAddEntry(HeapThing ptr) {
- HeapEntry* entry = entries_->Map(ptr);
- return entry != NULL ? entry : AddEntry(ptr);
+ HeapEntry* FindEntry(HeapThing ptr) {
+ return entries_->Map(ptr);
+ }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = FindEntry(ptr);
+ return entry != NULL ? entry : AddEntry(ptr, allocator);
}
void SetIndexedReference(HeapGraphEdge::Type type,
HeapThing parent_ptr,
@@ -2247,7 +2496,7 @@
parent_ptr, child_ptr, &child_index, &retainer_index);
parent_entry->SetNamedReference(type,
child_index,
- collection_->GetName(child_index + 1),
+ collection_->names()->GetName(child_index + 1),
child_entry,
retainer_index);
}
@@ -2303,21 +2552,28 @@
void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
if (control_ == NULL) return;
- progress_total_ = v8_heap_explorer_.EstimateObjectsCount() * iterations_count;
+ progress_total_ = (
+ v8_heap_explorer_.EstimateObjectsCount() +
+ dom_explorer_.EstimateObjectsCount()) * iterations_count;
progress_counter_ = 0;
}
bool HeapSnapshotGenerator::CountEntriesAndReferences() {
- SnapshotCounter counter(&v8_heap_explorer_, &entries_);
+ SnapshotCounter counter(&entries_);
v8_heap_explorer_.AddRootEntries(&counter);
- return v8_heap_explorer_.IterateAndExtractReferences(&counter);
+ dom_explorer_.AddRootEntries(&counter);
+ return
+ v8_heap_explorer_.IterateAndExtractReferences(&counter) &&
+ dom_explorer_.IterateAndExtractReferences(&counter);
}
bool HeapSnapshotGenerator::FillReferences() {
SnapshotFiller filler(snapshot_, &entries_);
- return v8_heap_explorer_.IterateAndExtractReferences(&filler);
+ return
+ v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
+ dom_explorer_.IterateAndExtractReferences(&filler);
}
@@ -2735,7 +2991,8 @@
"," JSON_S("code")
"," JSON_S("closure")
"," JSON_S("regexp")
- "," JSON_S("number"))
+ "," JSON_S("number")
+ "," JSON_S("native"))
"," JSON_S("string")
"," JSON_S("number")
"," JSON_S("number")
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 4762eb6..559ffc6 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -66,6 +66,9 @@
StringsStorage();
~StringsStorage();
+ const char* GetCopy(const char* src);
+ const char* GetFormatted(const char* format, ...);
+ const char* GetVFormatted(const char* format, va_list args);
const char* GetName(String* name);
const char* GetName(int index);
inline const char* GetFunctionName(String* name);
@@ -76,11 +79,10 @@
return strcmp(reinterpret_cast<char*>(key1),
reinterpret_cast<char*>(key2)) == 0;
}
+ const char* AddOrDisposeString(char* str, uint32_t hash);
// Mapping of strings by String::Hash to const char* strings.
HashMap names_;
- // Mapping from ints to char* strings.
- List<char*> index_names_;
DISALLOW_COPY_AND_ASSIGN(StringsStorage);
};
@@ -236,12 +238,12 @@
class CodeMap {
public:
- CodeMap() : next_sfi_tag_(1) { }
+ CodeMap() : next_shared_id_(1) { }
INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size));
INLINE(void MoveCode(Address from, Address to));
INLINE(void DeleteCode(Address addr));
CodeEntry* FindEntry(Address addr);
- int GetSFITag(Address addr);
+ int GetSharedId(Address addr);
void Print();
@@ -269,11 +271,11 @@
void Call(const Address& key, const CodeEntryInfo& value);
};
- // Fake CodeEntry pointer to distinguish SFI entries.
- static CodeEntry* const kSfiCodeEntry;
+ // Fake CodeEntry pointer to distinguish shared function entries.
+ static CodeEntry* const kSharedFunctionCodeEntry;
CodeTree tree_;
- int next_sfi_tag_;
+ int next_shared_id_;
DISALLOW_COPY_AND_ASSIGN(CodeMap);
};
@@ -517,7 +519,8 @@
kCode = v8::HeapGraphNode::kCode,
kClosure = v8::HeapGraphNode::kClosure,
kRegExp = v8::HeapGraphNode::kRegExp,
- kHeapNumber = v8::HeapGraphNode::kHeapNumber
+ kHeapNumber = v8::HeapGraphNode::kHeapNumber,
+ kNative = v8::HeapGraphNode::kNative
};
HeapEntry() { }
@@ -604,8 +607,8 @@
const char* TypeAsString();
unsigned painted_: 2;
- unsigned type_: 3;
- int children_count_: 27;
+ unsigned type_: 4;
+ int children_count_: 26;
int retainers_count_;
int self_size_;
union {
@@ -677,6 +680,7 @@
unsigned uid() { return uid_; }
HeapEntry* root() { return root_entry_; }
HeapEntry* gc_roots() { return gc_roots_entry_; }
+ HeapEntry* natives_root() { return natives_root_entry_; }
List<HeapEntry*>* entries() { return &entries_; }
void AllocateEntries(
@@ -689,6 +693,7 @@
int retainers_count);
HeapEntry* AddRootEntry(int children_count);
HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
+ HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
HeapEntry* GetEntryById(uint64_t id);
@@ -710,6 +715,7 @@
unsigned uid_;
HeapEntry* root_entry_;
HeapEntry* gc_roots_entry_;
+ HeapEntry* natives_root_entry_;
char* raw_entries_;
List<HeapEntry*> entries_;
bool entries_sorted_;
@@ -733,8 +739,11 @@
uint64_t FindObject(Address addr);
void MoveObject(Address from, Address to);
+ static uint64_t GenerateId(v8::RetainedObjectInfo* info);
+
static const uint64_t kInternalRootObjectId;
static const uint64_t kGcRootsObjectId;
+ static const uint64_t kNativesRootObjectId;
static const uint64_t kFirstAvailableObjectId;
private:
@@ -832,12 +841,7 @@
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid);
- const char* GetName(String* name) { return names_.GetName(name); }
- const char* GetName(int index) { return names_.GetName(index); }
- const char* GetFunctionName(String* name) {
- return names_.GetFunctionName(name);
- }
-
+ StringsStorage* names() { return &names_; }
TokenEnumerator* token_enumerator() { return token_enumerator_; }
uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
@@ -950,8 +954,11 @@
class SnapshotFillerInterface {
public:
virtual ~SnapshotFillerInterface() { }
- virtual HeapEntry* AddEntry(HeapThing ptr) = 0;
- virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0;
+ virtual HeapEntry* AddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) = 0;
+ virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
+ virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) = 0;
virtual void SetIndexedReference(HeapGraphEdge::Type type,
HeapThing parent_ptr,
HeapEntry* parent_entry,
@@ -990,13 +997,15 @@
public:
V8HeapExplorer(HeapSnapshot* snapshot,
SnapshottingProgressReportingInterface* progress);
- ~V8HeapExplorer();
+ virtual ~V8HeapExplorer();
virtual HeapEntry* AllocateEntry(
HeapThing ptr, int children_count, int retainers_count);
void AddRootEntries(SnapshotFillerInterface* filler);
int EstimateObjectsCount();
bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
+ static HeapObject* const kInternalRootObject;
+
private:
HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count);
@@ -1052,7 +1061,6 @@
HeapObjectsSet known_references_;
SnapshotFillerInterface* filler_;
- static HeapObject* const kInternalRootObject;
static HeapObject* const kGcRootsObject;
friend class IndexedReferencesExtractor;
@@ -1062,6 +1070,54 @@
};
+// An implementation of retained native objects extractor.
+class NativeObjectsExplorer : public HeapEntriesAllocator {
+ public:
+ NativeObjectsExplorer(HeapSnapshot* snapshot,
+ SnapshottingProgressReportingInterface* progress);
+ virtual ~NativeObjectsExplorer();
+ virtual HeapEntry* AllocateEntry(
+ HeapThing ptr, int children_count, int retainers_count);
+ void AddRootEntries(SnapshotFillerInterface* filler);
+ int EstimateObjectsCount();
+ bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
+
+ private:
+ void FillRetainedObjects();
+ List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info);
+ void SetNativeRootReference(v8::RetainedObjectInfo* info);
+ void SetRootNativesRootReference();
+ void SetWrapperNativeReferences(HeapObject* wrapper,
+ v8::RetainedObjectInfo* info);
+ void VisitSubtreeWrapper(Object** p, uint16_t class_id);
+
+ static uint32_t InfoHash(v8::RetainedObjectInfo* info) {
+ return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()));
+ }
+ static bool RetainedInfosMatch(void* key1, void* key2) {
+ return key1 == key2 ||
+ (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent(
+ reinterpret_cast<v8::RetainedObjectInfo*>(key2));
+ }
+
+ HeapSnapshot* snapshot_;
+ HeapSnapshotsCollection* collection_;
+ SnapshottingProgressReportingInterface* progress_;
+ bool embedder_queried_;
+ HeapObjectsSet in_groups_;
+ // RetainedObjectInfo* -> List<HeapObject*>*
+ HashMap objects_by_info_;
+ // Used during references extraction.
+ SnapshotFillerInterface* filler_;
+
+ static HeapThing const kNativesRootObject;
+
+ friend class GlobalHandlesExtractor;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeObjectsExplorer);
+};
+
+
class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
public:
HeapSnapshotGenerator(HeapSnapshot* snapshot,
@@ -1083,6 +1139,7 @@
HeapSnapshot* snapshot_;
v8::ActivityControl* control_;
V8HeapExplorer v8_heap_explorer_;
+ NativeObjectsExplorer dom_explorer_;
// Mapping from HeapThing pointers to HeapEntry* pointers.
HeapEntriesMap entries_;
// Used during snapshot generation.
diff --git a/src/v8.cc b/src/v8.cc
index 945043d..27648b1 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -158,7 +158,7 @@
void V8::TearDown() {
if (!has_been_setup_ || has_been_disposed_) return;
- if (FLAG_time_hydrogen) HStatistics::Instance()->Print();
+ if (FLAG_hydrogen_stats) HStatistics::Instance()->Print();
// We must stop the logger before we tear down other components.
Logger::EnsureTickerStopped();
diff --git a/src/v8globals.h b/src/v8globals.h
index d11bc38..e44b9e7 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -472,7 +472,11 @@
// The Strict Mode (ECMA-262 5th edition, 4.2.2).
enum StrictModeFlag {
kNonStrictMode,
- kStrictMode
+ kStrictMode,
+ // This value is never used, but is needed to prevent GCC 4.5 from failing
+ // to compile when we assert that a flag is either kNonStrictMode or
+ // kStrictMode.
+ kInvalidStrictFlag
};
} } // namespace v8::internal
diff --git a/src/v8natives.js b/src/v8natives.js
index 563de73..52ff8d2 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -495,7 +495,7 @@
// property descriptor. For a description of the array layout please
// see the runtime.cc file.
function ConvertDescriptorArrayToDescriptor(desc_array) {
- if (desc_array == false) {
+ if (desc_array === false) {
throw 'Internal error: invalid desc_array';
}
@@ -544,7 +544,7 @@
var props = %GetOwnProperty(ToObject(obj), ToString(p));
// A false value here means that access checks failed.
- if (props == false) return void 0;
+ if (props === false) return void 0;
return ConvertDescriptorArrayToDescriptor(props);
}
@@ -554,7 +554,7 @@
function DefineOwnProperty(obj, p, desc, should_throw) {
var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p));
// A false value here means that access checks failed.
- if (current_or_access == false) return void 0;
+ if (current_or_access === false) return void 0;
var current = ConvertDescriptorArrayToDescriptor(current_or_access);
var extensible = %IsExtensible(ToObject(obj));
diff --git a/src/version.cc b/src/version.cc
index 024adca..c1eab1a 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,8 +34,8 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 2
-#define BUILD_NUMBER 1
-#define PATCH_LEVEL 1
+#define BUILD_NUMBER 2
+#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
// Define SONAME to have the SCons build the put a specific SONAME into the
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 41111a7..f879ae7 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -191,12 +191,12 @@
// Register constants.
const int Register::kRegisterCodeByAllocationIndex[kNumAllocatableRegisters] = {
- // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12
- 0, 3, 2, 1, 7, 8, 9, 11, 14, 12
+ // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r15
+ 0, 3, 2, 1, 7, 8, 9, 11, 14, 15
};
const int Register::kAllocationIndexByRegisterCode[kNumRegisters] = {
- 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1
+ 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, -1, -1, 8, 9
};
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index ab26d74..0b7bdc0 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -93,8 +93,8 @@
// rbp - frame pointer
// rsi - context register
// r10 - fixed scratch register
+ // r12 - smi constant register
// r13 - root register
- // r15 - smi constant register
static const int kNumRegisters = 16;
static const int kNumAllocatableRegisters = 10;
@@ -120,7 +120,7 @@
"r9",
"r11",
"r14",
- "r12"
+ "r15"
};
return names[index];
}
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 9999a1c..b11072c 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -46,8 +46,8 @@
__ Ret();
__ bind(&check_heap_number);
- __ Move(rbx, Factory::heap_number_map());
- __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
__ j(not_equal, &call_builtin);
__ Ret();
@@ -104,7 +104,7 @@
__ pop(rdx);
__ push(rsi);
__ push(rdx);
- __ Push(Factory::false_value());
+ __ PushRoot(Heap::kFalseValueRootIndex);
__ push(rcx); // Restore return address.
__ TailCallRuntime(Runtime::kNewClosure, 3, 1);
}
@@ -2493,7 +2493,8 @@
// Check that the JSArray is in fast case.
__ movq(rbx, FieldOperand(rdi, JSArray::kElementsOffset));
__ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
- __ Cmp(rdi, Factory::fixed_array_map());
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
__ j(not_equal, &runtime);
// Check that the last match info has space for the capture registers and the
// additional information. Ensure no overflow in add.
@@ -2528,8 +2529,8 @@
__ testb(rbx, Immediate(kIsNotStringMask | kExternalStringTag));
__ j(not_zero, &runtime);
// String is a cons string.
- __ movq(rdx, FieldOperand(rdi, ConsString::kSecondOffset));
- __ Cmp(rdx, Factory::empty_string());
+ __ CompareRoot(FieldOperand(rdi, ConsString::kSecondOffset),
+ Heap::kEmptyStringRootIndex);
__ j(not_equal, &runtime);
__ movq(rdi, FieldOperand(rdi, ConsString::kFirstOffset));
__ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
@@ -2793,8 +2794,8 @@
__ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
// Set empty properties FixedArray.
- __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
- Factory::empty_fixed_array());
+ __ LoadRoot(kScratchRegister, Heap::kEmptyFixedArrayRootIndex);
+ __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), kScratchRegister);
// Set elements to point to FixedArray allocated right after the JSArray.
__ lea(rcx, Operand(rax, JSRegExpResult::kSize));
@@ -2814,13 +2815,13 @@
// rbx: Number of elements in array as int32.
// Set map.
- __ Move(FieldOperand(rcx, HeapObject::kMapOffset),
- Factory::fixed_array_map());
+ __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
+ __ movq(FieldOperand(rcx, HeapObject::kMapOffset), kScratchRegister);
// Set length.
__ Integer32ToSmi(rdx, rbx);
__ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx);
// Fill contents of fixed-array with the-hole.
- __ Move(rdx, Factory::the_hole_value());
+ __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex);
__ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
// Fill fixed array elements with hole.
// rax: JSArray.
@@ -3321,7 +3322,7 @@
// rbp: frame pointer (restored after C call).
// rsp: stack pointer (restored after C call).
// r14: number of arguments including receiver (C callee-saved).
- // r12: pointer to the first argument (C callee-saved).
+ // r15: pointer to the first argument (C callee-saved).
// This pointer is reused in LeaveExitFrame(), so it is stored in a
// callee-saved register.
@@ -3362,7 +3363,7 @@
// Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
// Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
__ movq(StackSpaceOperand(0), r14); // argc.
- __ movq(StackSpaceOperand(1), r12); // argv.
+ __ movq(StackSpaceOperand(1), r15); // argv.
if (result_size_ < 2) {
// Pass a pointer to the Arguments object as the first argument.
// Return result in single register (rax).
@@ -3378,7 +3379,7 @@
#else // _WIN64
// GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
__ movq(rdi, r14); // argc.
- __ movq(rsi, r12); // argv.
+ __ movq(rsi, r15); // argv.
#endif
__ call(rbx);
// Result is in rax - do not destroy this register!
@@ -3482,7 +3483,7 @@
// rbp: frame pointer of exit frame (restored after C call).
// rsp: stack pointer (restored after C call).
// r14: number of arguments including receiver (C callee-saved).
- // r12: argv pointer (C callee-saved).
+ // r15: argv pointer (C callee-saved).
Label throw_normal_exception;
Label throw_termination_exception;
@@ -4173,7 +4174,7 @@
// just allocate a new one.
Label make_two_character_string, make_flat_ascii_string;
StringHelper::GenerateTwoCharacterSymbolTableProbe(
- masm, rbx, rcx, r14, r11, rdi, r12, &make_two_character_string);
+ masm, rbx, rcx, r14, r11, rdi, r15, &make_two_character_string);
__ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
@@ -4945,7 +4946,7 @@
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// the empty string.
- __ Move(result, Factory::empty_string());
+ __ LoadRoot(result, Heap::kEmptyStringRootIndex);
__ jmp(&done);
__ bind(&need_conversion);
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index e3ab7c2..3c054cd 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -7961,7 +7961,7 @@
&& (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
&& (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
&& (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
- && (allocator()->count(r12) == (frame()->is_used(r12) ? 1 : 0));
+ && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0));
}
#endif
diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc
index daa9128..1771570 100644
--- a/src/x64/deoptimizer-x64.cc
+++ b/src/x64/deoptimizer-x64.cc
@@ -102,6 +102,7 @@
void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+ HandleScope scope;
AssertNoAllocation no_allocation;
if (!function->IsOptimized()) return;
@@ -196,6 +197,11 @@
PrintF("[forced deoptimization: ");
function->PrintName();
PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
+#ifdef DEBUG
+ if (FLAG_print_code) {
+ code->PrintLn();
+ }
+#endif
}
}
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index cca99a1..dbadcac 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -752,41 +752,65 @@
void LCodeGen::DoModI(LModI* instr) {
- LOperand* right = instr->InputAt(1);
- ASSERT(ToRegister(instr->result()).is(rdx));
- ASSERT(ToRegister(instr->InputAt(0)).is(rax));
- ASSERT(!ToRegister(instr->InputAt(1)).is(rax));
- ASSERT(!ToRegister(instr->InputAt(1)).is(rdx));
+ if (instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->InputAt(0));
- Register right_reg = ToRegister(right);
+ int32_t divisor =
+ HConstant::cast(instr->hydrogen()->right())->Integer32Value();
- // Check for x % 0.
- if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
- __ testl(right_reg, right_reg);
- DeoptimizeIf(zero, instr->environment());
- }
+ if (divisor < 0) divisor = -divisor;
- // Sign extend eax to edx. (We are using only the low 32 bits of the values.)
- __ cdq();
-
- // Check for (0 % -x) that will produce negative zero.
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel positive_left;
- NearLabel done;
- __ testl(rax, rax);
- __ j(not_sign, &positive_left);
- __ idivl(right_reg);
-
- // Test the remainder for 0, because then the result would be -0.
- __ testl(rdx, rdx);
- __ j(not_zero, &done);
-
- DeoptimizeIf(no_condition, instr->environment());
- __ bind(&positive_left);
- __ idivl(right_reg);
+ NearLabel positive_dividend, done;
+ __ testl(dividend, dividend);
+ __ j(not_sign, &positive_dividend);
+ __ negl(dividend);
+ __ andl(dividend, Immediate(divisor - 1));
+ __ negl(dividend);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ j(not_zero, &done);
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ __ bind(&positive_dividend);
+ __ andl(dividend, Immediate(divisor - 1));
__ bind(&done);
} else {
- __ idivl(right_reg);
+ LOperand* right = instr->InputAt(1);
+ Register right_reg = ToRegister(right);
+
+ ASSERT(ToRegister(instr->result()).is(rdx));
+ ASSERT(ToRegister(instr->InputAt(0)).is(rax));
+ ASSERT(!right_reg.is(rax));
+ ASSERT(!right_reg.is(rdx));
+
+ // Check for x % 0.
+ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ testl(right_reg, right_reg);
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Sign extend eax to edx.
+ // (We are using only the low 32 bits of the values.)
+ __ cdq();
+
+ // Check for (0 % -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ NearLabel positive_left;
+ NearLabel done;
+ __ testl(rax, rax);
+ __ j(not_sign, &positive_left);
+ __ idivl(right_reg);
+
+ // Test the remainder for 0, because then the result would be -0.
+ __ testl(rdx, rdx);
+ __ j(not_zero, &done);
+
+ DeoptimizeIf(no_condition, instr->environment());
+ __ bind(&positive_left);
+ __ idivl(right_reg);
+ __ bind(&done);
+ } else {
+ __ idivl(right_reg);
+ }
}
}
@@ -1059,7 +1083,7 @@
void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) {
Register result = ToRegister(instr->result());
Register array = ToRegister(instr->InputAt(0));
- __ movq(result, FieldOperand(array, ExternalPixelArray::kLengthOffset));
+ __ movl(result, FieldOperand(array, ExternalPixelArray::kLengthOffset));
}
@@ -1210,7 +1234,7 @@
Register reg = ToRegister(instr->InputAt(0));
HType type = instr->hydrogen()->type();
if (type.IsBoolean()) {
- __ Cmp(reg, Factory::true_value());
+ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
EmitBranch(true_block, false_block, equal);
} else if (type.IsSmi()) {
__ SmiCompare(reg, Smi::FromInt(0));
@@ -1477,14 +1501,14 @@
int true_block = chunk_->LookupDestination(instr->true_block_id());
- __ Cmp(reg, Factory::null_value());
+ __ CompareRoot(reg, Heap::kNullValueRootIndex);
if (instr->is_strict()) {
EmitBranch(true_block, false_block, equal);
} else {
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
__ j(equal, true_label);
- __ Cmp(reg, Factory::undefined_value());
+ __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
__ j(equal, true_label);
__ JumpIfSmi(reg, false_label);
// Check for undetectable objects by looking in the bit field in
@@ -2092,14 +2116,14 @@
__ movq(result, FieldOperand(input, JSObject::kElementsOffset));
if (FLAG_debug_code) {
NearLabel done;
- __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
- Factory::fixed_array_map());
+ __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
__ j(equal, &done);
- __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
- Factory::external_pixel_array_map());
+ __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+ Heap::kExternalPixelArrayMapRootIndex);
__ j(equal, &done);
- __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
- Factory::fixed_cow_array_map());
+ __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+ Heap::kFixedCOWArrayMapRootIndex);
__ Check(equal, "Check for fast elements failed.");
__ bind(&done);
}
@@ -2146,7 +2170,7 @@
FixedArray::kHeaderSize));
// Check for the hole value.
- __ Cmp(result, Factory::the_hole_value());
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
DeoptimizeIf(equal, instr->environment());
}
@@ -2978,6 +3002,56 @@
}
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+ class DeferredStringCharFromCode: public LDeferredCode {
+ public:
+ DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+ private:
+ LStringCharFromCode* instr_;
+ };
+
+ DeferredStringCharFromCode* deferred =
+ new DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmpl(char_code, Immediate(String::kMaxAsciiCharCode));
+ __ j(above, deferred->entry());
+ __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+ __ movq(result, FieldOperand(result,
+ char_code, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
+ __ j(equal, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+
+ // TODO(3095996): Get rid of this. For now, we need to make the
+ // result register contain a valid pointer because it is already
+ // contained in the register pointer map.
+ __ Set(result, 0);
+
+ __ PushSafepointRegisters();
+ __ Integer32ToSmi(char_code, char_code);
+ __ push(char_code);
+ __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ __ StoreToSafepointRegisterSlot(result, rax);
+ __ PopSafepointRegisters();
+}
+
+
void LCodeGen::DoStringLength(LStringLength* instr) {
Register string = ToRegister(instr->string());
Register result = ToRegister(instr->result());
@@ -3434,7 +3508,9 @@
} else {
__ push(rsi);
__ Push(shared_info);
- __ Push(pretenure ? Factory::true_value() : Factory::false_value());
+ __ PushRoot(pretenure ?
+ Heap::kTrueValueRootIndex :
+ Heap::kFalseValueRootIndex);
CallRuntime(Runtime::kNewClosure, 3, instr);
}
}
@@ -3517,8 +3593,9 @@
Condition final_branch_condition = no_condition;
if (type_name->Equals(Heap::number_symbol())) {
__ JumpIfSmi(input, true_label);
- __ Cmp(FieldOperand(input, HeapObject::kMapOffset),
- Factory::heap_number_map());
+ __ CompareRoot(FieldOperand(input, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+
final_branch_condition = equal;
} else if (type_name->Equals(Heap::string_symbol())) {
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index 5592456..6b00335 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -92,6 +92,7 @@
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check);
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index e387cee..99a7ace 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1347,13 +1347,23 @@
if (instr->representation().IsInteger32()) {
ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
- // The temporary operand is necessary to ensure that right is not allocated
- // into edx.
- LOperand* temp = FixedTemp(rdx);
- LOperand* value = UseFixed(instr->left(), rax);
- LOperand* divisor = UseRegister(instr->right());
- LModI* mod = new LModI(value, divisor, temp);
- LInstruction* result = DefineFixed(mod, rdx);
+
+ LInstruction* result;
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL);
+ result = DefineSameAsFirst(mod);
+ } else {
+ // The temporary operand is necessary to ensure that right is not
+ // allocated into edx.
+ LOperand* temp = FixedTemp(rdx);
+ LOperand* value = UseFixed(instr->left(), rax);
+ LOperand* divisor = UseRegister(instr->right());
+ LModI* mod = new LModI(value, divisor, temp);
+ result = DefineFixed(mod, rdx);
+ }
+
return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
instr->CheckFlag(HValue::kCanBeDivByZero))
? AssignEnvironment(result)
@@ -1791,7 +1801,6 @@
LInstruction* LChunkBuilder::DoLoadPixelArrayElement(
HLoadPixelArrayElement* instr) {
- Abort("Pixel array loads in generated code cause segfaults (danno)");
ASSERT(instr->representation().IsInteger32());
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer =
@@ -1833,7 +1842,6 @@
LInstruction* LChunkBuilder::DoStorePixelArrayElement(
HStorePixelArrayElement* instr) {
- Abort("Pixel array stores in generated code sometimes segfaults (danno)");
ASSERT(instr->value()->representation().IsInteger32());
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
@@ -1897,6 +1905,13 @@
}
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
LOperand* string = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LStringLength(string));
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index a5a2046..926924b 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -149,6 +149,7 @@
V(StoreNamedGeneric) \
V(StorePixelArrayElement) \
V(StringCharCodeAt) \
+ V(StringCharFromCode) \
V(StringLength) \
V(SubI) \
V(TaggedToI) \
@@ -1631,6 +1632,19 @@
};
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+ LOperand* char_code() { return inputs_[0]; }
+};
+
+
class LStringLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LStringLength(LOperand* string) {
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 45fbd41..f4874ad 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -555,7 +555,7 @@
// Allocate HandleScope in callee-save registers.
Register prev_next_address_reg = r14;
Register prev_limit_reg = rbx;
- Register base_reg = r12;
+ Register base_reg = r15;
movq(base_reg, next_address);
movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
@@ -721,11 +721,11 @@
bind(&ok);
}
}
- if (source->value() == 0) {
+ int value = source->value();
+ if (value == 0) {
xorl(dst, dst);
return;
}
- int value = source->value();
bool negative = value < 0;
unsigned int uvalue = negative ? -value : value;
@@ -1474,10 +1474,10 @@
push(r9);
// r10 is kScratchRegister.
push(r11);
- push(r12);
+ // r12 is kSmiConstantRegister.
// r13 is kRootRegister.
push(r14);
- // r15 is kSmiConstantRegister
+ push(r15);
STATIC_ASSERT(11 == kNumSafepointSavedRegisters);
// Use lea for symmetry with Popad.
int sp_delta =
@@ -1491,8 +1491,8 @@
int sp_delta =
(kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
lea(rsp, Operand(rsp, sp_delta));
+ pop(r15);
pop(r14);
- pop(r12);
pop(r11);
pop(r9);
pop(r8);
@@ -1511,7 +1511,7 @@
// Order general registers are pushed by Pushad:
-// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14.
+// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
int MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
0,
1,
@@ -1525,10 +1525,10 @@
7,
-1,
8,
- 9,
-1,
- 10,
- -1
+ -1,
+ 9,
+ 10
};
@@ -2077,10 +2077,10 @@
void MacroAssembler::EnterExitFrame(int arg_stack_space, bool save_doubles) {
EnterExitFramePrologue(true);
- // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
+ // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
- lea(r12, Operand(rbp, r14, times_pointer_size, offset));
+ lea(r15, Operand(rbp, r14, times_pointer_size, offset));
EnterExitFrameEpilogue(arg_stack_space, save_doubles);
}
@@ -2094,7 +2094,7 @@
void MacroAssembler::LeaveExitFrame(bool save_doubles) {
// Registers:
- // r12 : argv
+ // r15 : argv
if (save_doubles) {
int offset = -2 * kPointerSize;
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
@@ -2108,7 +2108,7 @@
// Drop everything up to and including the arguments and the receiver
// from the caller stack.
- lea(rsp, Operand(r12, 1 * kPointerSize));
+ lea(rsp, Operand(r15, 1 * kPointerSize));
// Push the return address to get ready to return.
push(rcx);
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 5908cef..28f814b 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -48,7 +48,7 @@
// a spare register). The register isn't callee save, and not used by the
// function calling convention.
static const Register kScratchRegister = { 10 }; // r10.
-static const Register kSmiConstantRegister = { 15 }; // r15 (callee save).
+static const Register kSmiConstantRegister = { 12 }; // r12 (callee save).
static const Register kRootRegister = { 13 }; // r13 (callee save).
// Value of smi in kSmiConstantRegister.
static const int kSmiConstantRegisterValue = 1;
@@ -941,7 +941,7 @@
// Calls an API function. Allocates HandleScope, extracts
// returned value from handle and propagates exceptions.
- // Clobbers r12, r14, rbx and caller-save registers. Restores context.
+ // Clobbers r14, r15, rbx and caller-save registers. Restores context.
// On return removes stack_space * kPointerSize (GCed).
MUST_USE_RESULT MaybeObject* TryCallApiFunctionAndReturn(
ApiFunction* function, int stack_space);
@@ -1013,7 +1013,7 @@
private:
// Order general registers are pushed by Pushad.
- // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14.
+ // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
static int kSafepointPushRegisterIndices[Register::kNumRegisters];
static const int kNumSafepointSavedRegisters = 11;
diff --git a/src/x64/register-allocator-x64-inl.h b/src/x64/register-allocator-x64-inl.h
index c6bea3a..5df3d54 100644
--- a/src/x64/register-allocator-x64-inl.h
+++ b/src/x64/register-allocator-x64-inl.h
@@ -60,10 +60,10 @@
6, // r9
-1, // r10 Scratch register.
8, // r11
- 9, // r12
+ -1, // r12 Smi constant.
-1, // r13 Roots array. This is callee saved.
7, // r14
- -1 // r15 Smi constant register.
+ 9 // r15
};
return kNumbers[reg.code()];
}
@@ -72,7 +72,7 @@
Register RegisterAllocator::ToRegister(int num) {
ASSERT(num >= 0 && num < kNumRegisters);
const Register kRegisters[] =
- { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r12 };
+ { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r15 };
return kRegisters[num];
}
diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc
index 5d292df..1554bda 100644
--- a/test/cctest/test-assembler-x64.cc
+++ b/test/cctest/test-assembler-x64.cc
@@ -50,8 +50,8 @@
using v8::internal::rsp;
using v8::internal::r8;
using v8::internal::r9;
-using v8::internal::r12;
using v8::internal::r13;
+using v8::internal::r15;
using v8::internal::times_1;
using v8::internal::FUNCTION_CAST;
@@ -317,7 +317,7 @@
CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
- CHECK(!Operand(rsp, offset).AddressUsesRegister(r12));
+ CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
@@ -333,7 +333,7 @@
CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
- CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r12));
+ CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
}
}
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index ad242fe..f1e9fcd 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -1258,4 +1258,141 @@
CHECK_GT(control.total(), 0);
}
+
+namespace {
+
+class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
+ public:
+ TestRetainedObjectInfo(int hash,
+ const char* label,
+ intptr_t element_count = -1,
+ intptr_t size = -1)
+ : disposed_(false),
+ hash_(hash),
+ label_(label),
+ element_count_(element_count),
+ size_(size) {
+ instances.Add(this);
+ }
+ virtual ~TestRetainedObjectInfo() {}
+ virtual void Dispose() {
+ CHECK(!disposed_);
+ disposed_ = true;
+ }
+ virtual bool IsEquivalent(RetainedObjectInfo* other) {
+ return GetHash() == other->GetHash();
+ }
+ virtual intptr_t GetHash() { return hash_; }
+ virtual const char* GetLabel() { return label_; }
+ virtual intptr_t GetElementCount() { return element_count_; }
+ virtual intptr_t GetSizeInBytes() { return size_; }
+ bool disposed() { return disposed_; }
+
+ static v8::RetainedObjectInfo* WrapperInfoCallback(
+ uint16_t class_id, v8::Handle<v8::Value> wrapper) {
+ if (class_id == 1) {
+ if (wrapper->IsString()) {
+ v8::String::AsciiValue ascii(wrapper);
+ if (strcmp(*ascii, "AAA") == 0)
+ return new TestRetainedObjectInfo(1, "aaa", 100);
+ else if (strcmp(*ascii, "BBB") == 0)
+ return new TestRetainedObjectInfo(1, "aaa", 100);
+ }
+ } else if (class_id == 2) {
+ if (wrapper->IsString()) {
+ v8::String::AsciiValue ascii(wrapper);
+ if (strcmp(*ascii, "CCC") == 0)
+ return new TestRetainedObjectInfo(2, "ccc");
+ }
+ }
+ CHECK(false);
+ return NULL;
+ }
+
+ static i::List<TestRetainedObjectInfo*> instances;
+
+ private:
+ bool disposed_;
+ int category_;
+ int hash_;
+ const char* label_;
+ intptr_t element_count_;
+ intptr_t size_;
+};
+
+
+i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
+}
+
+
+static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
+ v8::HeapGraphNode::Type type,
+ const char* name) {
+ for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
+ if (node->GetType() == type && strcmp(name,
+ const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+
+TEST(HeapSnapshotRetainedObjectInfo) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ v8::HeapProfiler::DefineWrapperClass(
+ 1, TestRetainedObjectInfo::WrapperInfoCallback);
+ v8::HeapProfiler::DefineWrapperClass(
+ 2, TestRetainedObjectInfo::WrapperInfoCallback);
+ v8::Persistent<v8::String> p_AAA =
+ v8::Persistent<v8::String>::New(v8_str("AAA"));
+ p_AAA.SetWrapperClassId(1);
+ v8::Persistent<v8::String> p_BBB =
+ v8::Persistent<v8::String>::New(v8_str("BBB"));
+ p_BBB.SetWrapperClassId(1);
+ v8::Persistent<v8::String> p_CCC =
+ v8::Persistent<v8::String>::New(v8_str("CCC"));
+ p_CCC.SetWrapperClassId(2);
+ CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
+ const v8::HeapSnapshot* snapshot =
+ v8::HeapProfiler::TakeSnapshot(v8::String::New("retained"));
+
+ CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
+ for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
+ CHECK(TestRetainedObjectInfo::instances[i]->disposed());
+ delete TestRetainedObjectInfo::instances[i];
+ }
+
+ const v8::HeapGraphNode* natives = GetNode(
+ snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)");
+ CHECK_NE(NULL, natives);
+ CHECK_EQ(2, natives->GetChildrenCount());
+ const v8::HeapGraphNode* aaa = GetNode(
+ natives, v8::HeapGraphNode::kNative, "aaa / 100 entries");
+ CHECK_NE(NULL, aaa);
+ const v8::HeapGraphNode* ccc = GetNode(
+ natives, v8::HeapGraphNode::kNative, "ccc");
+ CHECK_NE(NULL, ccc);
+
+ CHECK_EQ(2, aaa->GetChildrenCount());
+ const v8::HeapGraphNode* n_AAA = GetNode(
+ aaa, v8::HeapGraphNode::kString, "AAA");
+ CHECK_NE(NULL, n_AAA);
+ const v8::HeapGraphNode* n_BBB = GetNode(
+ aaa, v8::HeapGraphNode::kString, "BBB");
+ CHECK_NE(NULL, n_BBB);
+ CHECK_EQ(1, ccc->GetChildrenCount());
+ const v8::HeapGraphNode* n_CCC = GetNode(
+ ccc, v8::HeapGraphNode::kString, "CCC");
+ CHECK_NE(NULL, n_CCC);
+
+ CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "Native"));
+ CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "Native"));
+ CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "Native"));
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING
diff --git a/test/cctest/test-macro-assembler-x64.cc b/test/cctest/test-macro-assembler-x64.cc
index 9b1fc46..8a7cf41 100755
--- a/test/cctest/test-macro-assembler-x64.cc
+++ b/test/cctest/test-macro-assembler-x64.cc
@@ -57,9 +57,9 @@
using v8::internal::r8;
using v8::internal::r9;
using v8::internal::r11;
-using v8::internal::r12;
using v8::internal::r13;
using v8::internal::r14;
+using v8::internal::r15;
using v8::internal::times_pointer_size;
using v8::internal::FUNCTION_CAST;
using v8::internal::CodeDesc;
@@ -1120,30 +1120,30 @@
if (!fraction && !overflow && !negative_zero && !division_by_zero) {
// Division succeeds
__ movq(rcx, r11);
- __ movq(r12, Immediate(id));
+ __ movq(r15, Immediate(id));
int result = x / y;
__ Move(r8, Smi::FromInt(result));
__ SmiDiv(r9, rcx, r14, exit);
// Might have destroyed rcx and r14.
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(r9, r8);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ movq(rcx, r11);
__ Move(r14, Smi::FromInt(y));
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiDiv(rcx, rcx, r14, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r8);
__ j(not_equal, exit);
} else {
// Division fails.
- __ movq(r12, Immediate(id + 8));
+ __ movq(r15, Immediate(id + 8));
Label fail_ok, fail_ok2;
__ movq(rcx, r11);
@@ -1151,16 +1151,16 @@
__ jmp(exit);
__ bind(&fail_ok);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiDiv(rcx, rcx, r14, &fail_ok2);
__ jmp(exit);
__ bind(&fail_ok2);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
}
@@ -1184,7 +1184,7 @@
Label exit;
__ push(r14);
- __ push(r12);
+ __ push(r15);
TestSmiDiv(masm, &exit, 0x10, 1, 1);
TestSmiDiv(masm, &exit, 0x20, 1, 0);
TestSmiDiv(masm, &exit, 0x30, -1, 0);
@@ -1206,10 +1206,10 @@
TestSmiDiv(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
TestSmiDiv(masm, &exit, 0x140, Smi::kMinValue, -1);
- __ xor_(r12, r12); // Success.
+ __ xor_(r15, r15); // Success.
__ bind(&exit);
- __ movq(rax, r12);
- __ pop(r12);
+ __ movq(rax, r15);
+ __ pop(r15);
__ pop(r14);
ExitCode(masm);
__ ret(0);
@@ -1232,44 +1232,44 @@
__ Move(r14, Smi::FromInt(y));
if (!division_overflow && !negative_zero && !division_by_zero) {
// Modulo succeeds
- __ movq(r12, Immediate(id));
+ __ movq(r15, Immediate(id));
int result = x % y;
__ Move(r8, Smi::FromInt(result));
__ SmiMod(r9, rcx, r14, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(r9, r8);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiMod(rcx, rcx, r14, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r8);
__ j(not_equal, exit);
} else {
// Modulo fails.
- __ movq(r12, Immediate(id + 8));
+ __ movq(r15, Immediate(id + 8));
Label fail_ok, fail_ok2;
__ SmiMod(r9, rcx, r14, &fail_ok);
__ jmp(exit);
__ bind(&fail_ok);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r12);
+ __ incq(r15);
__ SmiMod(rcx, rcx, r14, &fail_ok2);
__ jmp(exit);
__ bind(&fail_ok2);
- __ incq(r12);
+ __ incq(r15);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
}
@@ -1293,7 +1293,7 @@
Label exit;
__ push(r14);
- __ push(r12);
+ __ push(r15);
TestSmiMod(masm, &exit, 0x10, 1, 1);
TestSmiMod(masm, &exit, 0x20, 1, 0);
TestSmiMod(masm, &exit, 0x30, -1, 0);
@@ -1315,10 +1315,10 @@
TestSmiMod(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
TestSmiMod(masm, &exit, 0x140, Smi::kMinValue, -1);
- __ xor_(r12, r12); // Success.
+ __ xor_(r15, r15); // Success.
__ bind(&exit);
- __ movq(rax, r12);
- __ pop(r12);
+ __ movq(rax, r15);
+ __ pop(r15);
__ pop(r14);
ExitCode(masm);
__ ret(0);
@@ -2150,7 +2150,7 @@
__ push(Immediate(0x108));
__ push(Immediate(0x109)); // <-- rsp
// rbp = rsp[9]
- // r12 = rsp[3]
+ // r15 = rsp[3]
// rbx = rsp[5]
// r13 = rsp[7]
__ lea(r14, Operand(rsp, 3 * kPointerSize));
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index 3e3175e..54c12eb 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -339,8 +339,8 @@
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
- GlobalHandles::AddGroup(g1_objects, 2);
- GlobalHandles::AddGroup(g2_objects, 2);
+ GlobalHandles::AddGroup(g1_objects, 2, NULL);
+ GlobalHandles::AddGroup(g2_objects, 2, NULL);
}
// Do a full GC
Heap::CollectGarbage(OLD_POINTER_SPACE);
@@ -357,8 +357,8 @@
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
- GlobalHandles::AddGroup(g1_objects, 2);
- GlobalHandles::AddGroup(g2_objects, 2);
+ GlobalHandles::AddGroup(g1_objects, 2, NULL);
+ GlobalHandles::AddGroup(g2_objects, 2, NULL);
}
Heap::CollectGarbage(OLD_POINTER_SPACE);
diff --git a/test/mjsunit/regress/regress-1233.js b/test/mjsunit/regress/regress-1233.js
new file mode 100644
index 0000000..a09b715
--- /dev/null
+++ b/test/mjsunit/regress/regress-1233.js
@@ -0,0 +1,47 @@
+// Copyright 2011 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.
+
+// Test that Object.freeze and Object.getOwnPropertyDescriptor do not
+// call toString or valueOf on members of the object.
+
+// See http://code.google.com/p/v8/issues/detail?id=1233.
+
+
+var delicate = new Object();
+delicate.toString = function(){ throw Error("toString"); };
+delicate.valueOf = function(){ throw Error("valueOf"); };
+
+var x = { foo: delicate };
+
+var status = "fail";
+try {
+ Object.getOwnPropertyDescriptor(x, "foo");
+ Object.freeze(x);
+ status = "succeed";
+} catch (e) {}
+
+assertEquals("succeed", status);