1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/codegen/handler-table.h" 6 7#include <algorithm> 8#include <iomanip> 9 10#include "src/base/iterator.h" 11#include "src/codegen/assembler-inl.h" 12#include "src/objects/code-inl.h" 13#include "src/objects/objects-inl.h" 14 15#if V8_ENABLE_WEBASSEMBLY 16#include "src/wasm/wasm-code-manager.h" 17#endif // V8_ENABLE_WEBASSEMBLY 18 19namespace v8 { 20namespace internal { 21 22HandlerTable::HandlerTable(Code code) 23 : HandlerTable(code.HandlerTableAddress(), code.handler_table_size(), 24 kReturnAddressBasedEncoding) {} 25 26#if V8_ENABLE_WEBASSEMBLY 27HandlerTable::HandlerTable(const wasm::WasmCode* code) 28 : HandlerTable(code->handler_table(), code->handler_table_size(), 29 kReturnAddressBasedEncoding) {} 30#endif // V8_ENABLE_WEBASSEMBLY 31 32HandlerTable::HandlerTable(BytecodeArray bytecode_array) 33 : HandlerTable(bytecode_array.handler_table()) {} 34 35HandlerTable::HandlerTable(ByteArray byte_array) 36 : HandlerTable(reinterpret_cast<Address>(byte_array.GetDataStartAddress()), 37 byte_array.length(), kRangeBasedEncoding) {} 38 39HandlerTable::HandlerTable(Address handler_table, int handler_table_size, 40 EncodingMode encoding_mode) 41 : number_of_entries_(handler_table_size / EntrySizeFromMode(encoding_mode) / 42 sizeof(int32_t)), 43#ifdef DEBUG 44 mode_(encoding_mode), 45#endif 46 raw_encoded_data_(handler_table) { 47 // Check padding. 48 static_assert(4 < kReturnEntrySize * sizeof(int32_t), "allowed padding"); 49 // For return address encoding, maximum padding is 4; otherwise, there should 50 // be no padding. 51 DCHECK_GE(kReturnAddressBasedEncoding == encoding_mode ? 4 : 0, 52 handler_table_size % 53 (EntrySizeFromMode(encoding_mode) * sizeof(int32_t))); 54} 55 56// static 57int HandlerTable::EntrySizeFromMode(EncodingMode mode) { 58 switch (mode) { 59 case kReturnAddressBasedEncoding: 60 return kReturnEntrySize; 61 case kRangeBasedEncoding: 62 return kRangeEntrySize; 63 } 64 UNREACHABLE(); 65} 66 67int HandlerTable::GetRangeStart(int index) const { 68 DCHECK_EQ(kRangeBasedEncoding, mode_); 69 DCHECK_LT(index, NumberOfRangeEntries()); 70 int offset = index * kRangeEntrySize + kRangeStartIndex; 71 return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); 72} 73 74int HandlerTable::GetRangeEnd(int index) const { 75 DCHECK_EQ(kRangeBasedEncoding, mode_); 76 DCHECK_LT(index, NumberOfRangeEntries()); 77 int offset = index * kRangeEntrySize + kRangeEndIndex; 78 return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); 79} 80 81int HandlerTable::GetRangeHandler(int index) const { 82 DCHECK_EQ(kRangeBasedEncoding, mode_); 83 DCHECK_LT(index, NumberOfRangeEntries()); 84 int offset = index * kRangeEntrySize + kRangeHandlerIndex; 85 return HandlerOffsetField::decode( 86 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); 87} 88 89int HandlerTable::GetRangeData(int index) const { 90 DCHECK_EQ(kRangeBasedEncoding, mode_); 91 DCHECK_LT(index, NumberOfRangeEntries()); 92 int offset = index * kRangeEntrySize + kRangeDataIndex; 93 return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); 94} 95 96HandlerTable::CatchPrediction HandlerTable::GetRangePrediction( 97 int index) const { 98 DCHECK_EQ(kRangeBasedEncoding, mode_); 99 DCHECK_LT(index, NumberOfRangeEntries()); 100 int offset = index * kRangeEntrySize + kRangeHandlerIndex; 101 return HandlerPredictionField::decode( 102 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); 103} 104 105int HandlerTable::GetReturnOffset(int index) const { 106 DCHECK_EQ(kReturnAddressBasedEncoding, mode_); 107 DCHECK_LT(index, NumberOfReturnEntries()); 108 int offset = index * kReturnEntrySize + kReturnOffsetIndex; 109 return Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)); 110} 111 112int HandlerTable::GetReturnHandler(int index) const { 113 DCHECK_EQ(kReturnAddressBasedEncoding, mode_); 114 DCHECK_LT(index, NumberOfReturnEntries()); 115 int offset = index * kReturnEntrySize + kReturnHandlerIndex; 116 return HandlerOffsetField::decode( 117 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t))); 118} 119 120void HandlerTable::SetRangeStart(int index, int value) { 121 int offset = index * kRangeEntrySize + kRangeStartIndex; 122 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; 123} 124 125void HandlerTable::SetRangeEnd(int index, int value) { 126 int offset = index * kRangeEntrySize + kRangeEndIndex; 127 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; 128} 129 130void HandlerTable::SetRangeHandler(int index, int handler_offset, 131 CatchPrediction prediction) { 132 int value = HandlerOffsetField::encode(handler_offset) | 133 HandlerPredictionField::encode(prediction); 134 int offset = index * kRangeEntrySize + kRangeHandlerIndex; 135 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; 136} 137 138void HandlerTable::SetRangeData(int index, int value) { 139 int offset = index * kRangeEntrySize + kRangeDataIndex; 140 Memory<int32_t>(raw_encoded_data_ + offset * sizeof(int32_t)) = value; 141} 142 143// static 144int HandlerTable::LengthForRange(int entries) { 145 return entries * kRangeEntrySize * sizeof(int32_t); 146} 147 148// static 149int HandlerTable::EmitReturnTableStart(Assembler* masm) { 150 masm->DataAlign(Code::kMetadataAlignment); 151 masm->RecordComment(";;; Exception handler table."); 152 int table_start = masm->pc_offset(); 153 return table_start; 154} 155 156// static 157void HandlerTable::EmitReturnEntry(Assembler* masm, int offset, int handler) { 158 masm->dd(offset); 159 masm->dd(HandlerOffsetField::encode(handler)); 160} 161 162int HandlerTable::NumberOfRangeEntries() const { 163 DCHECK_EQ(kRangeBasedEncoding, mode_); 164 return number_of_entries_; 165} 166 167int HandlerTable::NumberOfReturnEntries() const { 168 DCHECK_EQ(kReturnAddressBasedEncoding, mode_); 169 return number_of_entries_; 170} 171 172int HandlerTable::LookupRange(int pc_offset, int* data_out, 173 CatchPrediction* prediction_out) { 174 int innermost_handler = -1; 175#ifdef DEBUG 176 // Assuming that ranges are well nested, we don't need to track the innermost 177 // offsets. This is just to verify that the table is actually well nested. 178 int innermost_start = std::numeric_limits<int>::min(); 179 int innermost_end = std::numeric_limits<int>::max(); 180#endif 181 for (int i = 0; i < NumberOfRangeEntries(); ++i) { 182 int start_offset = GetRangeStart(i); 183 int end_offset = GetRangeEnd(i); 184 int handler_offset = GetRangeHandler(i); 185 int handler_data = GetRangeData(i); 186 CatchPrediction prediction = GetRangePrediction(i); 187 if (pc_offset >= start_offset && pc_offset < end_offset) { 188 DCHECK_GE(start_offset, innermost_start); 189 DCHECK_LT(end_offset, innermost_end); 190 innermost_handler = handler_offset; 191#ifdef DEBUG 192 innermost_start = start_offset; 193 innermost_end = end_offset; 194#endif 195 if (data_out) *data_out = handler_data; 196 if (prediction_out) *prediction_out = prediction; 197 } 198 } 199 return innermost_handler; 200} 201 202int HandlerTable::LookupReturn(int pc_offset) { 203 // We only implement the methods needed by the standard libraries we care 204 // about. This is not technically a full random access iterator by the spec. 205 struct Iterator : base::iterator<std::random_access_iterator_tag, int> { 206 Iterator(HandlerTable* tbl, int idx) : table(tbl), index(idx) {} 207 value_type operator*() const { return table->GetReturnOffset(index); } 208 bool operator!=(const Iterator& other) const { return !(*this == other); } 209 bool operator==(const Iterator& other) const { 210 return index == other.index; 211 } 212 // GLIBCXX_DEBUG checks uses the <= comparator. 213 bool operator<=(const Iterator& other) { return index <= other.index; } 214 Iterator& operator++() { 215 index++; 216 return *this; 217 } 218 Iterator& operator--() { 219 index--; 220 return *this; 221 } 222 Iterator& operator+=(difference_type offset) { 223 index += offset; 224 return *this; 225 } 226 difference_type operator-(const Iterator& other) const { 227 return index - other.index; 228 } 229 HandlerTable* table; 230 int index; 231 }; 232 Iterator begin{this, 0}, end{this, NumberOfReturnEntries()}; 233 SLOW_DCHECK(std::is_sorted(begin, end)); // Must be sorted. 234 Iterator result = std::lower_bound(begin, end, pc_offset); 235 bool exact_match = result != end && *result == pc_offset; 236 return exact_match ? GetReturnHandler(result.index) : -1; 237} 238 239#ifdef ENABLE_DISASSEMBLER 240 241void HandlerTable::HandlerTableRangePrint(std::ostream& os) { 242 os << " from to hdlr (prediction, data)\n"; 243 for (int i = 0; i < NumberOfRangeEntries(); ++i) { 244 int pc_start = GetRangeStart(i); 245 int pc_end = GetRangeEnd(i); 246 int handler_offset = GetRangeHandler(i); 247 int handler_data = GetRangeData(i); 248 CatchPrediction prediction = GetRangePrediction(i); 249 os << " (" << std::setw(4) << pc_start << "," << std::setw(4) << pc_end 250 << ") -> " << std::setw(4) << handler_offset 251 << " (prediction=" << prediction << ", data=" << handler_data << ")\n"; 252 } 253} 254 255void HandlerTable::HandlerTableReturnPrint(std::ostream& os) { 256 os << " offset handler\n"; 257 for (int i = 0; i < NumberOfReturnEntries(); ++i) { 258 int pc_offset = GetReturnOffset(i); 259 int handler_offset = GetReturnHandler(i); 260 os << std::hex << " " << std::setw(4) << pc_offset << " -> " 261 << std::setw(4) << handler_offset << std::dec << "\n"; 262 } 263} 264 265#endif // ENABLE_DISASSEMBLER 266 267} // namespace internal 268} // namespace v8 269