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