Update V8 to r6101 as required by WebKit r74534
Change-Id: I7f84af8dd732f11898fd644b2c2b1538914cb78d
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc
new file mode 100644
index 0000000..dd70baa
--- /dev/null
+++ b/src/deoptimizer.cc
@@ -0,0 +1,1147 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "disasm.h"
+#include "full-codegen.h"
+#include "global-handles.h"
+#include "macro-assembler.h"
+#include "prettyprinter.h"
+
+
+namespace v8 {
+namespace internal {
+
+LargeObjectChunk* Deoptimizer::eager_deoptimization_entry_code_ = NULL;
+LargeObjectChunk* Deoptimizer::lazy_deoptimization_entry_code_ = NULL;
+Deoptimizer* Deoptimizer::current_ = NULL;
+DeoptimizingCodeListNode* Deoptimizer::deoptimizing_code_list_ = NULL;
+
+
+Deoptimizer* Deoptimizer::New(JSFunction* function,
+ BailoutType type,
+ unsigned bailout_id,
+ Address from,
+ int fp_to_sp_delta) {
+ Deoptimizer* deoptimizer =
+ new Deoptimizer(function, type, bailout_id, from, fp_to_sp_delta);
+ ASSERT(current_ == NULL);
+ current_ = deoptimizer;
+ return deoptimizer;
+}
+
+
+Deoptimizer* Deoptimizer::Grab() {
+ Deoptimizer* result = current_;
+ ASSERT(result != NULL);
+ result->DeleteFrameDescriptions();
+ current_ = NULL;
+ return result;
+}
+
+
+void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
+ int count,
+ BailoutType type) {
+ TableEntryGenerator generator(masm, type, count);
+ generator.Generate();
+}
+
+
+class DeoptimizingVisitor : public OptimizedFunctionVisitor {
+ public:
+ virtual void EnterContext(Context* context) {
+ if (FLAG_trace_deopt) {
+ PrintF("[deoptimize context: %" V8PRIxPTR "]\n",
+ reinterpret_cast<intptr_t>(context));
+ }
+ }
+
+ virtual void VisitFunction(JSFunction* function) {
+ Deoptimizer::DeoptimizeFunction(function);
+ }
+
+ virtual void LeaveContext(Context* context) {
+ context->ClearOptimizedFunctions();
+ }
+};
+
+
+void Deoptimizer::DeoptimizeAll() {
+ AssertNoAllocation no_allocation;
+
+ if (FLAG_trace_deopt) {
+ PrintF("[deoptimize all contexts]\n");
+ }
+
+ DeoptimizingVisitor visitor;
+ VisitAllOptimizedFunctions(&visitor);
+}
+
+
+void Deoptimizer::DeoptimizeGlobalObject(JSObject* object) {
+ AssertNoAllocation no_allocation;
+
+ DeoptimizingVisitor visitor;
+ VisitAllOptimizedFunctionsForGlobalObject(object, &visitor);
+}
+
+
+void Deoptimizer::VisitAllOptimizedFunctionsForContext(
+ Context* context, OptimizedFunctionVisitor* visitor) {
+ AssertNoAllocation no_allocation;
+
+ ASSERT(context->IsGlobalContext());
+
+ visitor->EnterContext(context);
+ // Run through the list of optimized functions and deoptimize them.
+ Object* element = context->OptimizedFunctionsListHead();
+ while (!element->IsUndefined()) {
+ JSFunction* element_function = JSFunction::cast(element);
+ // Get the next link before deoptimizing as deoptimizing will clear the
+ // next link.
+ element = element_function->next_function_link();
+ visitor->VisitFunction(element_function);
+ }
+ visitor->LeaveContext(context);
+}
+
+
+void Deoptimizer::VisitAllOptimizedFunctionsForGlobalObject(
+ JSObject* object, OptimizedFunctionVisitor* visitor) {
+ AssertNoAllocation no_allocation;
+
+ if (object->IsJSGlobalProxy()) {
+ Object* proto = object->GetPrototype();
+ ASSERT(proto->IsJSGlobalObject());
+ VisitAllOptimizedFunctionsForContext(
+ GlobalObject::cast(proto)->global_context(), visitor);
+ } else if (object->IsGlobalObject()) {
+ VisitAllOptimizedFunctionsForContext(
+ GlobalObject::cast(object)->global_context(), visitor);
+ }
+}
+
+
+void Deoptimizer::VisitAllOptimizedFunctions(
+ OptimizedFunctionVisitor* visitor) {
+ AssertNoAllocation no_allocation;
+
+ // Run through the list of all global contexts and deoptimize.
+ Object* global = Heap::global_contexts_list();
+ while (!global->IsUndefined()) {
+ VisitAllOptimizedFunctionsForGlobalObject(Context::cast(global)->global(),
+ visitor);
+ global = Context::cast(global)->get(Context::NEXT_CONTEXT_LINK);
+ }
+}
+
+
+void Deoptimizer::HandleWeakDeoptimizedCode(
+ v8::Persistent<v8::Value> obj, void* data) {
+ DeoptimizingCodeListNode* node =
+ reinterpret_cast<DeoptimizingCodeListNode*>(data);
+ RemoveDeoptimizingCode(*node->code());
+#ifdef DEBUG
+ node = Deoptimizer::deoptimizing_code_list_;
+ while (node != NULL) {
+ ASSERT(node != reinterpret_cast<DeoptimizingCodeListNode*>(data));
+ node = node->next();
+ }
+#endif
+}
+
+
+void Deoptimizer::ComputeOutputFrames(Deoptimizer* deoptimizer) {
+ deoptimizer->DoComputeOutputFrames();
+}
+
+
+Deoptimizer::Deoptimizer(JSFunction* function,
+ BailoutType type,
+ unsigned bailout_id,
+ Address from,
+ int fp_to_sp_delta)
+ : function_(function),
+ bailout_id_(bailout_id),
+ bailout_type_(type),
+ from_(from),
+ fp_to_sp_delta_(fp_to_sp_delta),
+ output_count_(0),
+ output_(NULL),
+ integer32_values_(NULL),
+ double_values_(NULL) {
+ if (FLAG_trace_deopt && type != OSR) {
+ PrintF("**** DEOPT: ");
+ function->PrintName();
+ PrintF(" at bailout #%u, address 0x%" V8PRIxPTR ", frame size %d\n",
+ bailout_id,
+ reinterpret_cast<intptr_t>(from),
+ fp_to_sp_delta - (2 * kPointerSize));
+ } else if (FLAG_trace_osr && type == OSR) {
+ PrintF("**** OSR: ");
+ function->PrintName();
+ PrintF(" at ast id #%u, address 0x%" V8PRIxPTR ", frame size %d\n",
+ bailout_id,
+ reinterpret_cast<intptr_t>(from),
+ fp_to_sp_delta - (2 * kPointerSize));
+ }
+ // Find the optimized code.
+ if (type == EAGER) {
+ ASSERT(from == NULL);
+ optimized_code_ = function_->code();
+ } else if (type == LAZY) {
+ optimized_code_ = FindDeoptimizingCodeFromAddress(from);
+ ASSERT(optimized_code_ != NULL);
+ } else if (type == OSR) {
+ // The function has already been optimized and we're transitioning
+ // from the unoptimized shared version to the optimized one in the
+ // function. The return address (from) points to unoptimized code.
+ optimized_code_ = function_->code();
+ ASSERT(optimized_code_->kind() == Code::OPTIMIZED_FUNCTION);
+ ASSERT(!optimized_code_->contains(from));
+ }
+ ASSERT(Heap::allow_allocation(false));
+ unsigned size = ComputeInputFrameSize();
+ input_ = new(size) FrameDescription(size, function);
+}
+
+
+Deoptimizer::~Deoptimizer() {
+ ASSERT(input_ == NULL && output_ == NULL);
+ delete[] integer32_values_;
+ delete[] double_values_;
+}
+
+
+void Deoptimizer::DeleteFrameDescriptions() {
+ delete input_;
+ for (int i = 0; i < output_count_; ++i) {
+ if (output_[i] != input_) delete output_[i];
+ }
+ delete[] output_;
+ input_ = NULL;
+ output_ = NULL;
+ ASSERT(!Heap::allow_allocation(true));
+}
+
+
+Address Deoptimizer::GetDeoptimizationEntry(int id, BailoutType type) {
+ ASSERT(id >= 0);
+ if (id >= kNumberOfEntries) return NULL;
+ LargeObjectChunk* base = NULL;
+ if (type == EAGER) {
+ if (eager_deoptimization_entry_code_ == NULL) {
+ eager_deoptimization_entry_code_ = CreateCode(type);
+ }
+ base = eager_deoptimization_entry_code_;
+ } else {
+ if (lazy_deoptimization_entry_code_ == NULL) {
+ lazy_deoptimization_entry_code_ = CreateCode(type);
+ }
+ base = lazy_deoptimization_entry_code_;
+ }
+ return
+ static_cast<Address>(base->GetStartAddress()) + (id * table_entry_size_);
+}
+
+
+int Deoptimizer::GetDeoptimizationId(Address addr, BailoutType type) {
+ LargeObjectChunk* base = NULL;
+ if (type == EAGER) {
+ base = eager_deoptimization_entry_code_;
+ } else {
+ base = lazy_deoptimization_entry_code_;
+ }
+ if (base == NULL ||
+ addr < base->GetStartAddress() ||
+ addr >= base->GetStartAddress() +
+ (kNumberOfEntries * table_entry_size_)) {
+ return kNotDeoptimizationEntry;
+ }
+ ASSERT_EQ(0,
+ static_cast<int>(addr - base->GetStartAddress()) % table_entry_size_);
+ return static_cast<int>(addr - base->GetStartAddress()) / table_entry_size_;
+}
+
+
+void Deoptimizer::Setup() {
+ // Do nothing yet.
+}
+
+
+void Deoptimizer::TearDown() {
+ if (eager_deoptimization_entry_code_ != NULL) {
+ eager_deoptimization_entry_code_->Free(EXECUTABLE);
+ eager_deoptimization_entry_code_ = NULL;
+ }
+ if (lazy_deoptimization_entry_code_ != NULL) {
+ lazy_deoptimization_entry_code_->Free(EXECUTABLE);
+ lazy_deoptimization_entry_code_ = NULL;
+ }
+}
+
+
+unsigned Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data,
+ unsigned id,
+ SharedFunctionInfo* shared) {
+ // TODO(kasperl): For now, we do a simple linear search for the PC
+ // offset associated with the given node id. This should probably be
+ // changed to a binary search.
+ int length = data->DeoptPoints();
+ Smi* smi_id = Smi::FromInt(id);
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == smi_id) {
+ return data->PcAndState(i)->value();
+ }
+ }
+ PrintF("[couldn't find pc offset for node=%u]\n", id);
+ PrintF("[method: %s]\n", *shared->DebugName()->ToCString());
+ // Print the source code if available.
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ shared->SourceCodePrint(&stream, -1);
+ PrintF("[source:\n%s\n]", *stream.ToCString());
+
+ UNREACHABLE();
+ return -1;
+}
+
+
+int Deoptimizer::GetDeoptimizedCodeCount() {
+ int length = 0;
+ DeoptimizingCodeListNode* node = Deoptimizer::deoptimizing_code_list_;
+ while (node != NULL) {
+ length++;
+ node = node->next();
+ }
+ return length;
+}
+
+
+void Deoptimizer::DoComputeOutputFrames() {
+ if (bailout_type_ == OSR) {
+ DoComputeOsrOutputFrame();
+ return;
+ }
+
+ // Print some helpful diagnostic information.
+ int64_t start = OS::Ticks();
+ if (FLAG_trace_deopt) {
+ PrintF("[deoptimizing%s: begin 0x%08" V8PRIxPTR " ",
+ (bailout_type_ == LAZY ? " (lazy)" : ""),
+ reinterpret_cast<intptr_t>(function_));
+ function_->PrintName();
+ PrintF(" @%d]\n", bailout_id_);
+ }
+
+ // Determine basic deoptimization information. The optimized frame is
+ // described by the input data.
+ DeoptimizationInputData* input_data =
+ DeoptimizationInputData::cast(optimized_code_->deoptimization_data());
+ unsigned node_id = input_data->AstId(bailout_id_)->value();
+ ByteArray* translations = input_data->TranslationByteArray();
+ unsigned translation_index =
+ input_data->TranslationIndex(bailout_id_)->value();
+
+ // Do the input frame to output frame(s) translation.
+ TranslationIterator iterator(translations, translation_index);
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator.Next());
+ ASSERT(Translation::BEGIN == opcode);
+ USE(opcode);
+ // Read the number of output frames and allocate an array for their
+ // descriptions.
+ int count = iterator.Next();
+ ASSERT(output_ == NULL);
+ output_ = new FrameDescription*[count];
+ // Per-frame lists of untagged and unboxed int32 and double values.
+ integer32_values_ = new List<ValueDescriptionInteger32>[count];
+ double_values_ = new List<ValueDescriptionDouble>[count];
+ for (int i = 0; i < count; ++i) {
+ output_[i] = NULL;
+ integer32_values_[i].Initialize(0);
+ double_values_[i].Initialize(0);
+ }
+ output_count_ = count;
+
+ // Translate each output frame.
+ for (int i = 0; i < count; ++i) {
+ DoComputeFrame(&iterator, i);
+ }
+
+ // Print some helpful diagnostic information.
+ if (FLAG_trace_deopt) {
+ double ms = static_cast<double>(OS::Ticks() - start) / 1000;
+ int index = output_count_ - 1; // Index of the topmost frame.
+ JSFunction* function = output_[index]->GetFunction();
+ PrintF("[deoptimizing: end 0x%08" V8PRIxPTR " ",
+ reinterpret_cast<intptr_t>(function));
+ function->PrintName();
+ PrintF(" => node=%u, pc=0x%08" V8PRIxPTR ", state=%s, took %0.3f ms]\n",
+ node_id,
+ output_[index]->GetPc(),
+ FullCodeGenerator::State2String(
+ static_cast<FullCodeGenerator::State>(
+ output_[index]->GetState()->value())),
+ ms);
+ }
+}
+
+
+void Deoptimizer::InsertHeapNumberValues(int index, JavaScriptFrame* frame) {
+ // We need to adjust the stack index by one for the top-most frame.
+ int extra_slot_count = (index == output_count() - 1) ? 1 : 0;
+ List<ValueDescriptionInteger32>* ints = &integer32_values_[index];
+ for (int i = 0; i < ints->length(); i++) {
+ ValueDescriptionInteger32 value = ints->at(i);
+ double val = static_cast<double>(value.int32_value());
+ InsertHeapNumberValue(frame, value.stack_index(), val, extra_slot_count);
+ }
+
+ // Iterate over double values and convert them to a heap number.
+ List<ValueDescriptionDouble>* doubles = &double_values_[index];
+ for (int i = 0; i < doubles->length(); ++i) {
+ ValueDescriptionDouble value = doubles->at(i);
+ InsertHeapNumberValue(frame, value.stack_index(), value.double_value(),
+ extra_slot_count);
+ }
+}
+
+
+void Deoptimizer::InsertHeapNumberValue(JavaScriptFrame* frame,
+ int stack_index,
+ double val,
+ int extra_slot_count) {
+ // Add one to the TOS index to take the 'state' pushed before jumping
+ // to the stub that calls Runtime::NotifyDeoptimized into account.
+ int tos_index = stack_index + extra_slot_count;
+ int index = (frame->ComputeExpressionsCount() - 1) - tos_index;
+ if (FLAG_trace_deopt) PrintF("Allocating a new heap number: %e\n", val);
+ Handle<Object> num = Factory::NewNumber(val);
+ frame->SetExpression(index, *num);
+}
+
+
+void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
+ int frame_index,
+ unsigned output_offset) {
+ disasm::NameConverter converter;
+ // A GC-safe temporary placeholder that we can put in the output frame.
+ const intptr_t kPlaceholder = reinterpret_cast<intptr_t>(Smi::FromInt(0));
+
+ // Ignore commands marked as duplicate and act on the first non-duplicate.
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+ while (opcode == Translation::DUPLICATE) {
+ opcode = static_cast<Translation::Opcode>(iterator->Next());
+ iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+ opcode = static_cast<Translation::Opcode>(iterator->Next());
+ }
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::FRAME:
+ case Translation::DUPLICATE:
+ UNREACHABLE();
+ return;
+
+ case Translation::REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t input_value = input_->GetRegister(input_reg);
+ if (FLAG_trace_deopt) {
+ PrintF(
+ " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" V8PRIxPTR " ; %s\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ input_value,
+ converter.NameOfCPURegister(input_reg));
+ }
+ output_[frame_index]->SetFrameSlot(output_offset, input_value);
+ return;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t value = input_->GetRegister(input_reg);
+ bool is_smi = Smi::IsValid(value);
+ unsigned output_index = output_offset / kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(
+ " 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIdPTR " ; %s (%s)\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ converter.NameOfCPURegister(input_reg),
+ is_smi ? "smi" : "heap number");
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddInteger32Value(frame_index,
+ output_index,
+ static_cast<int32_t>(value));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ int input_reg = iterator->Next();
+ double value = input_->GetDoubleRegister(input_reg);
+ unsigned output_index = output_offset / kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; %s\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ DoubleRegister::AllocationIndexToString(input_reg));
+ }
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddDoubleValue(frame_index, output_index, value);
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ return;
+ }
+
+ case Translation::STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset =
+ input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ intptr_t input_value = input_->GetFrameSlot(input_offset);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": ",
+ output_[frame_index]->GetTop() + output_offset);
+ PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d]\n",
+ output_offset,
+ input_value,
+ input_offset);
+ }
+ output_[frame_index]->SetFrameSlot(output_offset, input_value);
+ return;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset =
+ input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ intptr_t value = input_->GetFrameSlot(input_offset);
+ bool is_smi = Smi::IsValid(value);
+ unsigned output_index = output_offset / kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": ",
+ output_[frame_index]->GetTop() + output_offset);
+ PrintF("[top + %d] <- %" V8PRIdPTR " ; [esp + %d] (%s)\n",
+ output_offset,
+ value,
+ input_offset,
+ is_smi ? "smi" : "heap number");
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddInteger32Value(frame_index,
+ output_index,
+ static_cast<int32_t>(value));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset =
+ input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ double value = input_->GetDoubleFrameSlot(input_offset);
+ unsigned output_index = output_offset / kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ input_offset);
+ }
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddDoubleValue(frame_index, output_index, value);
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ return;
+ }
+
+ case Translation::LITERAL: {
+ Object* literal = ComputeLiteral(iterator->Next());
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset);
+ literal->ShortPrint();
+ PrintF(" ; literal\n");
+ }
+ intptr_t value = reinterpret_cast<intptr_t>(literal);
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ return;
+ }
+
+ case Translation::ARGUMENTS_OBJECT: {
+ // Use the hole value as a sentinel and fill in the arguments object
+ // after the deoptimized frame is built.
+ ASSERT(frame_index == 0); // Only supported for first frame.
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset);
+ Heap::the_hole_value()->ShortPrint();
+ PrintF(" ; arguments object\n");
+ }
+ intptr_t value = reinterpret_cast<intptr_t>(Heap::the_hole_value());
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ return;
+ }
+ }
+}
+
+
+bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
+ int* input_offset) {
+ disasm::NameConverter converter;
+ FrameDescription* output = output_[0];
+
+ // The input values are all part of the unoptimized frame so they
+ // are all tagged pointers.
+ uintptr_t input_value = input_->GetFrameSlot(*input_offset);
+ Object* input_object = reinterpret_cast<Object*>(input_value);
+
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+ bool duplicate = (opcode == Translation::DUPLICATE);
+ if (duplicate) {
+ opcode = static_cast<Translation::Opcode>(iterator->Next());
+ }
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::FRAME:
+ case Translation::DUPLICATE:
+ UNREACHABLE(); // Malformed input.
+ return false;
+
+ case Translation::REGISTER: {
+ int output_reg = iterator->Next();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- 0x%08" V8PRIxPTR " ; [esp + %d]\n",
+ converter.NameOfCPURegister(output_reg),
+ input_value,
+ *input_offset);
+ }
+ output->SetRegister(output_reg, input_value);
+ break;
+ }
+
+ case Translation::INT32_REGISTER: {
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_reg = iterator->Next();
+ int int32_value = input_object->IsSmi()
+ ? Smi::cast(input_object)->value()
+ : FastD2I(input_object->Number());
+ // Abort the translation if the conversion lost information.
+ if (!input_object->IsSmi() &&
+ FastI2D(int32_value) != input_object->Number()) {
+ if (FLAG_trace_osr) {
+ PrintF("**** %g could not be converted to int32 ****\n",
+ input_object->Number());
+ }
+ return false;
+ }
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- %d (int32) ; [esp + %d]\n",
+ converter.NameOfCPURegister(output_reg),
+ int32_value,
+ *input_offset);
+ }
+ output->SetRegister(output_reg, int32_value);
+ break;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_reg = iterator->Next();
+ double double_value = input_object->Number();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- %g (double) ; [esp + %d]\n",
+ DoubleRegister::AllocationIndexToString(output_reg),
+ double_value,
+ *input_offset);
+ }
+ output->SetDoubleRegister(output_reg, double_value);
+ break;
+ }
+
+ case Translation::STACK_SLOT: {
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(this, output_index);
+ if (FLAG_trace_osr) {
+ PrintF(" [esp + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d]\n",
+ output_offset,
+ input_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset, input_value);
+ break;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(this, output_index);
+ int int32_value = input_object->IsSmi()
+ ? Smi::cast(input_object)->value()
+ : DoubleToInt32(input_object->Number());
+ // Abort the translation if the conversion lost information.
+ if (!input_object->IsSmi() &&
+ FastI2D(int32_value) != input_object->Number()) {
+ if (FLAG_trace_osr) {
+ PrintF("**** %g could not be converted to int32 ****\n",
+ input_object->Number());
+ }
+ return false;
+ }
+ if (FLAG_trace_osr) {
+ PrintF(" [esp + %d] <- %d (int32) ; [esp + %d]\n",
+ output_offset,
+ int32_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset, int32_value);
+ break;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ static const int kLowerOffset = 0 * kPointerSize;
+ static const int kUpperOffset = 1 * kPointerSize;
+
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(this, output_index);
+ double double_value = input_object->Number();
+ uint64_t int_value = BitCast<uint64_t, double>(double_value);
+ int32_t lower = static_cast<int32_t>(int_value);
+ int32_t upper = static_cast<int32_t>(int_value >> kBitsPerInt);
+ if (FLAG_trace_osr) {
+ PrintF(" [esp + %d] <- 0x%08x (upper bits of %g) ; [esp + %d]\n",
+ output_offset + kUpperOffset,
+ upper,
+ double_value,
+ *input_offset);
+ PrintF(" [esp + %d] <- 0x%08x (lower bits of %g) ; [esp + %d]\n",
+ output_offset + kLowerOffset,
+ lower,
+ double_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset + kLowerOffset, lower);
+ output->SetFrameSlot(output_offset + kUpperOffset, upper);
+ break;
+ }
+
+ case Translation::LITERAL: {
+ // Just ignore non-materialized literals.
+ iterator->Next();
+ break;
+ }
+
+ case Translation::ARGUMENTS_OBJECT: {
+ // Optimized code assumes that the argument object has not been
+ // materialized and so bypasses it when doing arguments access.
+ // We should have bailed out before starting the frame
+ // translation.
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ if (!duplicate) *input_offset -= kPointerSize;
+ return true;
+}
+
+
+unsigned Deoptimizer::ComputeInputFrameSize() const {
+ unsigned fixed_size = ComputeFixedSize(function_);
+ // The fp-to-sp delta already takes the context and the function
+ // into account so we have to avoid double counting them (-2).
+ unsigned result = fixed_size + fp_to_sp_delta_ - (2 * kPointerSize);
+#ifdef DEBUG
+ if (bailout_type_ == OSR) {
+ // TODO(kasperl): It would be nice if we could verify that the
+ // size matches with the stack height we can compute based on the
+ // environment at the OSR entry. The code for that his built into
+ // the DoComputeOsrOutputFrame function for now.
+ } else {
+ unsigned stack_slots = optimized_code_->stack_slots();
+ unsigned outgoing_size = ComputeOutgoingArgumentSize();
+ ASSERT(result == fixed_size + (stack_slots * kPointerSize) + outgoing_size);
+ }
+#endif
+ return result;
+}
+
+
+unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const {
+ // The fixed part of the frame consists of the return address, frame
+ // pointer, function, context, and all the incoming arguments.
+ static const unsigned kFixedSlotSize = 4 * kPointerSize;
+ return ComputeIncomingArgumentSize(function) + kFixedSlotSize;
+}
+
+
+unsigned Deoptimizer::ComputeIncomingArgumentSize(JSFunction* function) const {
+ // The incoming arguments is the values for formal parameters and
+ // the receiver. Every slot contains a pointer.
+ unsigned arguments = function->shared()->formal_parameter_count() + 1;
+ return arguments * kPointerSize;
+}
+
+
+unsigned Deoptimizer::ComputeOutgoingArgumentSize() const {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ optimized_code_->deoptimization_data());
+ unsigned height = data->ArgumentsStackHeight(bailout_id_)->value();
+ return height * kPointerSize;
+}
+
+
+Object* Deoptimizer::ComputeLiteral(int index) const {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ optimized_code_->deoptimization_data());
+ FixedArray* literals = data->LiteralArray();
+ return literals->get(index);
+}
+
+
+void Deoptimizer::AddInteger32Value(int frame_index,
+ int slot_index,
+ int32_t value) {
+ ValueDescriptionInteger32 value_desc(slot_index, value);
+ integer32_values_[frame_index].Add(value_desc);
+}
+
+
+void Deoptimizer::AddDoubleValue(int frame_index,
+ int slot_index,
+ double value) {
+ ValueDescriptionDouble value_desc(slot_index, value);
+ double_values_[frame_index].Add(value_desc);
+}
+
+
+LargeObjectChunk* Deoptimizer::CreateCode(BailoutType type) {
+ // We cannot run this if the serializer is enabled because this will
+ // cause us to emit relocation information for the external
+ // references. This is fine because the deoptimizer's code section
+ // isn't meant to be serialized at all.
+ ASSERT(!Serializer::enabled());
+ bool old_debug_code = FLAG_debug_code;
+ FLAG_debug_code = false;
+
+ MacroAssembler masm(NULL, 16 * KB);
+ GenerateDeoptimizationEntries(&masm, kNumberOfEntries, type);
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(desc.reloc_size == 0);
+
+ LargeObjectChunk* chunk = LargeObjectChunk::New(desc.instr_size, EXECUTABLE);
+ memcpy(chunk->GetStartAddress(), desc.buffer, desc.instr_size);
+ CPU::FlushICache(chunk->GetStartAddress(), desc.instr_size);
+ FLAG_debug_code = old_debug_code;
+ return chunk;
+}
+
+
+Code* Deoptimizer::FindDeoptimizingCodeFromAddress(Address addr) {
+ DeoptimizingCodeListNode* node = Deoptimizer::deoptimizing_code_list_;
+ while (node != NULL) {
+ if (node->code()->contains(addr)) return *node->code();
+ node = node->next();
+ }
+ return NULL;
+}
+
+
+void Deoptimizer::RemoveDeoptimizingCode(Code* code) {
+ ASSERT(deoptimizing_code_list_ != NULL);
+ // Run through the code objects to find this one and remove it.
+ DeoptimizingCodeListNode* prev = NULL;
+ DeoptimizingCodeListNode* current = deoptimizing_code_list_;
+ while (current != NULL) {
+ if (*current->code() == code) {
+ // Unlink from list. If prev is NULL we are looking at the first element.
+ if (prev == NULL) {
+ deoptimizing_code_list_ = current->next();
+ } else {
+ prev->set_next(current->next());
+ }
+ delete current;
+ return;
+ }
+ // Move to next in list.
+ prev = current;
+ current = current->next();
+ }
+ // Deoptimizing code is removed through weak callback. Each object is expected
+ // to be removed once and only once.
+ UNREACHABLE();
+}
+
+
+FrameDescription::FrameDescription(uint32_t frame_size,
+ JSFunction* function)
+ : frame_size_(frame_size),
+ function_(function),
+ top_(kZapUint32),
+ pc_(kZapUint32),
+ fp_(kZapUint32) {
+ // Zap all the registers.
+ for (int r = 0; r < Register::kNumRegisters; r++) {
+ SetRegister(r, kZapUint32);
+ }
+
+ // Zap all the slots.
+ for (unsigned o = 0; o < frame_size; o += kPointerSize) {
+ SetFrameSlot(o, kZapUint32);
+ }
+}
+
+
+unsigned FrameDescription::GetOffsetFromSlotIndex(Deoptimizer* deoptimizer,
+ int slot_index) {
+ if (slot_index >= 0) {
+ // Local or spill slots. Skip the fixed part of the frame
+ // including all arguments.
+ unsigned base = static_cast<unsigned>(
+ GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction()));
+ return base - ((slot_index + 1) * kPointerSize);
+ } else {
+ // Incoming parameter.
+ unsigned base = static_cast<unsigned>(GetFrameSize() -
+ deoptimizer->ComputeIncomingArgumentSize(GetFunction()));
+ return base - ((slot_index + 1) * kPointerSize);
+ }
+}
+
+
+void TranslationBuffer::Add(int32_t value) {
+ // Encode the sign bit in the least significant bit.
+ bool is_negative = (value < 0);
+ uint32_t bits = ((is_negative ? -value : value) << 1) |
+ static_cast<int32_t>(is_negative);
+ // Encode the individual bytes using the least significant bit of
+ // each byte to indicate whether or not more bytes follow.
+ do {
+ uint32_t next = bits >> 7;
+ contents_.Add(((bits << 1) & 0xFF) | (next != 0));
+ bits = next;
+ } while (bits != 0);
+}
+
+
+int32_t TranslationIterator::Next() {
+ ASSERT(HasNext());
+ // Run through the bytes until we reach one with a least significant
+ // bit of zero (marks the end).
+ uint32_t bits = 0;
+ for (int i = 0; true; i += 7) {
+ uint8_t next = buffer_->get(index_++);
+ bits |= (next >> 1) << i;
+ if ((next & 1) == 0) break;
+ }
+ // The bits encode the sign in the least significant bit.
+ bool is_negative = (bits & 1) == 1;
+ int32_t result = bits >> 1;
+ return is_negative ? -result : result;
+}
+
+
+Handle<ByteArray> TranslationBuffer::CreateByteArray() {
+ int length = contents_.length();
+ Handle<ByteArray> result = Factory::NewByteArray(length, TENURED);
+ memcpy(result->GetDataStartAddress(), contents_.ToVector().start(), length);
+ return result;
+}
+
+
+void Translation::BeginFrame(int node_id, int literal_id, unsigned height) {
+ buffer_->Add(FRAME);
+ buffer_->Add(node_id);
+ buffer_->Add(literal_id);
+ buffer_->Add(height);
+}
+
+
+void Translation::StoreRegister(Register reg) {
+ buffer_->Add(REGISTER);
+ buffer_->Add(reg.code());
+}
+
+
+void Translation::StoreInt32Register(Register reg) {
+ buffer_->Add(INT32_REGISTER);
+ buffer_->Add(reg.code());
+}
+
+
+void Translation::StoreDoubleRegister(DoubleRegister reg) {
+ buffer_->Add(DOUBLE_REGISTER);
+ buffer_->Add(DoubleRegister::ToAllocationIndex(reg));
+}
+
+
+void Translation::StoreStackSlot(int index) {
+ buffer_->Add(STACK_SLOT);
+ buffer_->Add(index);
+}
+
+
+void Translation::StoreInt32StackSlot(int index) {
+ buffer_->Add(INT32_STACK_SLOT);
+ buffer_->Add(index);
+}
+
+
+void Translation::StoreDoubleStackSlot(int index) {
+ buffer_->Add(DOUBLE_STACK_SLOT);
+ buffer_->Add(index);
+}
+
+
+void Translation::StoreLiteral(int literal_id) {
+ buffer_->Add(LITERAL);
+ buffer_->Add(literal_id);
+}
+
+
+void Translation::StoreArgumentsObject() {
+ buffer_->Add(ARGUMENTS_OBJECT);
+}
+
+
+void Translation::MarkDuplicate() {
+ buffer_->Add(DUPLICATE);
+}
+
+
+int Translation::NumberOfOperandsFor(Opcode opcode) {
+ switch (opcode) {
+ case ARGUMENTS_OBJECT:
+ case DUPLICATE:
+ return 0;
+ case BEGIN:
+ case REGISTER:
+ case INT32_REGISTER:
+ case DOUBLE_REGISTER:
+ case STACK_SLOT:
+ case INT32_STACK_SLOT:
+ case DOUBLE_STACK_SLOT:
+ case LITERAL:
+ return 1;
+ case FRAME:
+ return 3;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+#ifdef OBJECT_PRINT
+
+const char* Translation::StringFor(Opcode opcode) {
+ switch (opcode) {
+ case BEGIN:
+ return "BEGIN";
+ case FRAME:
+ return "FRAME";
+ case REGISTER:
+ return "REGISTER";
+ case INT32_REGISTER:
+ return "INT32_REGISTER";
+ case DOUBLE_REGISTER:
+ return "DOUBLE_REGISTER";
+ case STACK_SLOT:
+ return "STACK_SLOT";
+ case INT32_STACK_SLOT:
+ return "INT32_STACK_SLOT";
+ case DOUBLE_STACK_SLOT:
+ return "DOUBLE_STACK_SLOT";
+ case LITERAL:
+ return "LITERAL";
+ case ARGUMENTS_OBJECT:
+ return "ARGUMENTS_OBJECT";
+ case DUPLICATE:
+ return "DUPLICATE";
+ }
+ UNREACHABLE();
+ return "";
+}
+
+#endif
+
+
+DeoptimizingCodeListNode::DeoptimizingCodeListNode(Code* code): next_(NULL) {
+ // Globalize the code object and make it weak.
+ code_ = Handle<Code>::cast((GlobalHandles::Create(code)));
+ GlobalHandles::MakeWeak(reinterpret_cast<Object**>(code_.location()),
+ this,
+ Deoptimizer::HandleWeakDeoptimizedCode);
+}
+
+
+DeoptimizingCodeListNode::~DeoptimizingCodeListNode() {
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(code_.location()));
+}
+
+
+} } // namespace v8::internal