| // Copyright 2018 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/handler-table.h" | |
| #include <iomanip> | |
| #include "src/assembler-inl.h" | |
| #include "src/objects-inl.h" | |
| #include "src/objects/code-inl.h" | |
| namespace v8 { | |
| namespace internal { | |
| HandlerTable::HandlerTable(Code* code) | |
| : HandlerTable(code->InstructionStart(), code->handler_table_offset()) {} | |
| HandlerTable::HandlerTable(BytecodeArray* bytecode_array) | |
| : HandlerTable(bytecode_array->handler_table()) {} | |
| HandlerTable::HandlerTable(ByteArray* byte_array) | |
| : number_of_entries_(byte_array->length() / kRangeEntrySize / | |
| sizeof(int32_t)), | |
| #ifdef DEBUG | |
| mode_(kRangeBasedEncoding), | |
| #endif | |
| raw_encoded_data_( | |
| reinterpret_cast<Address>(byte_array->GetDataStartAddress())) { | |
| } | |
| HandlerTable::HandlerTable(Address instruction_start, | |
| size_t handler_table_offset) | |
| : number_of_entries_(0), | |
| #ifdef DEBUG | |
| mode_(kReturnAddressBasedEncoding), | |
| #endif | |
| raw_encoded_data_(instruction_start + handler_table_offset) { | |
| if (handler_table_offset > 0) { | |
| number_of_entries_ = Memory<int32_t>(raw_encoded_data_); | |
| raw_encoded_data_ += sizeof(int32_t); | |
| } | |
| } | |
| int HandlerTable::GetRangeStart(int index) const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfRangeEntries()); | |
| int offset = index * kRangeEntrySize + kRangeStartIndex; | |
| return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); | |
| } | |
| int HandlerTable::GetRangeEnd(int index) const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfRangeEntries()); | |
| int offset = index * kRangeEntrySize + kRangeEndIndex; | |
| return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); | |
| } | |
| int HandlerTable::GetRangeHandler(int index) const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfRangeEntries()); | |
| int offset = index * kRangeEntrySize + kRangeHandlerIndex; | |
| return HandlerOffsetField::decode( | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); | |
| } | |
| int HandlerTable::GetRangeData(int index) const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfRangeEntries()); | |
| int offset = index * kRangeEntrySize + kRangeDataIndex; | |
| return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); | |
| } | |
| HandlerTable::CatchPrediction HandlerTable::GetRangePrediction( | |
| int index) const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfRangeEntries()); | |
| int offset = index * kRangeEntrySize + kRangeHandlerIndex; | |
| return HandlerPredictionField::decode( | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); | |
| } | |
| int HandlerTable::GetReturnOffset(int index) const { | |
| DCHECK_EQ(kReturnAddressBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfReturnEntries()); | |
| int offset = index * kReturnEntrySize + kReturnOffsetIndex; | |
| return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); | |
| } | |
| int HandlerTable::GetReturnHandler(int index) const { | |
| DCHECK_EQ(kReturnAddressBasedEncoding, mode_); | |
| DCHECK_LT(index, NumberOfReturnEntries()); | |
| int offset = index * kReturnEntrySize + kReturnHandlerIndex; | |
| return HandlerOffsetField::decode( | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); | |
| } | |
| void HandlerTable::SetRangeStart(int index, int value) { | |
| int offset = index * kRangeEntrySize + kRangeStartIndex; | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; | |
| } | |
| void HandlerTable::SetRangeEnd(int index, int value) { | |
| int offset = index * kRangeEntrySize + kRangeEndIndex; | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; | |
| } | |
| void HandlerTable::SetRangeHandler(int index, int handler_offset, | |
| CatchPrediction prediction) { | |
| int value = HandlerOffsetField::encode(handler_offset) | | |
| HandlerPredictionField::encode(prediction); | |
| int offset = index * kRangeEntrySize + kRangeHandlerIndex; | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; | |
| } | |
| void HandlerTable::SetRangeData(int index, int value) { | |
| int offset = index * kRangeEntrySize + kRangeDataIndex; | |
| Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; | |
| } | |
| // static | |
| int HandlerTable::LengthForRange(int entries) { | |
| return entries * kRangeEntrySize * sizeof(int32_t); | |
| } | |
| // static | |
| int HandlerTable::EmitReturnTableStart(Assembler* masm, int entries) { | |
| masm->DataAlign(sizeof(int32_t)); // Make sure entries are aligned. | |
| masm->RecordComment(";;; Exception handler table."); | |
| int table_start = masm->pc_offset(); | |
| masm->dd(entries); | |
| return table_start; | |
| } | |
| // static | |
| void HandlerTable::EmitReturnEntry(Assembler* masm, int offset, int handler) { | |
| masm->dd(offset); | |
| masm->dd(HandlerOffsetField::encode(handler)); | |
| } | |
| int HandlerTable::NumberOfRangeEntries() const { | |
| DCHECK_EQ(kRangeBasedEncoding, mode_); | |
| return number_of_entries_; | |
| } | |
| int HandlerTable::NumberOfReturnEntries() const { | |
| DCHECK_EQ(kReturnAddressBasedEncoding, mode_); | |
| return number_of_entries_; | |
| } | |
| int HandlerTable::LookupRange(int pc_offset, int* data_out, | |
| CatchPrediction* prediction_out) { | |
| int innermost_handler = -1; | |
| #ifdef DEBUG | |
| // Assuming that ranges are well nested, we don't need to track the innermost | |
| // offsets. This is just to verify that the table is actually well nested. | |
| int innermost_start = std::numeric_limits<int>::min(); | |
| int innermost_end = std::numeric_limits<int>::max(); | |
| #endif | |
| for (int i = 0; i < NumberOfRangeEntries(); ++i) { | |
| int start_offset = GetRangeStart(i); | |
| int end_offset = GetRangeEnd(i); | |
| int handler_offset = GetRangeHandler(i); | |
| int handler_data = GetRangeData(i); | |
| CatchPrediction prediction = GetRangePrediction(i); | |
| if (pc_offset >= start_offset && pc_offset < end_offset) { | |
| DCHECK_GE(start_offset, innermost_start); | |
| DCHECK_LT(end_offset, innermost_end); | |
| innermost_handler = handler_offset; | |
| #ifdef DEBUG | |
| innermost_start = start_offset; | |
| innermost_end = end_offset; | |
| #endif | |
| if (data_out) *data_out = handler_data; | |
| if (prediction_out) *prediction_out = prediction; | |
| } | |
| } | |
| return innermost_handler; | |
| } | |
| // TODO(turbofan): Make sure table is sorted and use binary search. | |
| int HandlerTable::LookupReturn(int pc_offset) { | |
| for (int i = 0; i < NumberOfReturnEntries(); ++i) { | |
| int return_offset = GetReturnOffset(i); | |
| if (pc_offset == return_offset) { | |
| return GetReturnHandler(i); | |
| } | |
| } | |
| return -1; | |
| } | |
| #ifdef ENABLE_DISASSEMBLER | |
| void HandlerTable::HandlerTableRangePrint(std::ostream& os) { | |
| os << " from to hdlr (prediction, data)\n"; | |
| for (int i = 0; i < NumberOfRangeEntries(); ++i) { | |
| int pc_start = GetRangeStart(i); | |
| int pc_end = GetRangeEnd(i); | |
| int handler_offset = GetRangeHandler(i); | |
| int handler_data = GetRangeData(i); | |
| CatchPrediction prediction = GetRangePrediction(i); | |
| os << " (" << std::setw(4) << pc_start << "," << std::setw(4) << pc_end | |
| << ") -> " << std::setw(4) << handler_offset | |
| << " (prediction=" << prediction << ", data=" << handler_data << ")\n"; | |
| } | |
| } | |
| void HandlerTable::HandlerTableReturnPrint(std::ostream& os) { | |
| os << " offset handler\n"; | |
| for (int i = 0; i < NumberOfReturnEntries(); ++i) { | |
| int pc_offset = GetReturnOffset(i); | |
| int handler_offset = GetReturnHandler(i); | |
| os << std::hex << " " << std::setw(4) << pc_offset << " -> " | |
| << std::setw(4) << handler_offset << std::dec << "\n"; | |
| } | |
| } | |
| #endif // ENABLE_DISASSEMBLER | |
| } // namespace internal | |
| } // namespace v8 |