-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathcode_descriptors.h
373 lines (308 loc) · 12.5 KB
/
code_descriptors.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_CODE_DESCRIPTORS_H_
#define RUNTIME_VM_CODE_DESCRIPTORS_H_
#include "vm/datastream.h"
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/log.h"
#include "vm/runtime_entry.h"
#include "vm/token_position.h"
namespace dart {
static constexpr intptr_t kInvalidTryIndex = -1;
class DescriptorList : public ZoneAllocated {
public:
explicit DescriptorList(
Zone* zone,
const GrowableArray<const Function*>* inline_id_to_function = nullptr);
~DescriptorList() {}
void AddDescriptor(UntaggedPcDescriptors::Kind kind,
intptr_t pc_offset,
intptr_t deopt_id,
TokenPosition token_pos,
intptr_t try_index,
intptr_t yield_index);
PcDescriptorsPtr FinalizePcDescriptors(uword entry_point);
private:
static constexpr intptr_t kInitialStreamSize = 64;
const Function& function_;
const Script& script_;
ZoneWriteStream encoded_data_;
intptr_t prev_pc_offset;
intptr_t prev_deopt_id;
int32_t prev_token_pos;
DISALLOW_COPY_AND_ASSIGN(DescriptorList);
};
class CompressedStackMapsBuilder : public ZoneAllocated {
public:
explicit CompressedStackMapsBuilder(Zone* zone)
: encoded_bytes_(zone, kInitialStreamSize) {}
void AddEntry(intptr_t pc_offset,
BitmapBuilder* bitmap,
intptr_t spill_slot_bit_count);
CompressedStackMapsPtr Finalize() const;
private:
static constexpr intptr_t kInitialStreamSize = 16;
ZoneWriteStream encoded_bytes_;
intptr_t last_pc_offset_ = 0;
DISALLOW_COPY_AND_ASSIGN(CompressedStackMapsBuilder);
};
class ExceptionHandlerList : public ZoneAllocated {
public:
struct HandlerDesc {
intptr_t outer_try_index; // Try block in which this try block is nested.
intptr_t pc_offset; // Handler PC offset value.
bool is_generated; // False if this is directly from Dart code.
const Array* handler_types; // Catch clause guards.
bool needs_stacktrace;
};
explicit ExceptionHandlerList(const Function& function)
: list_(),
has_async_handler_(
(function.IsAsyncFunction() || function.IsAsyncGenerator()) &&
!function.is_declared_in_bytecode()) {}
intptr_t Length() const { return list_.length(); }
void AddPlaceHolder() {
struct HandlerDesc data;
data.outer_try_index = -1;
data.pc_offset = ExceptionHandlers::kInvalidPcOffset;
data.is_generated = true;
data.handler_types = nullptr;
data.needs_stacktrace = false;
list_.Add(data);
}
void AddHandler(intptr_t try_index,
intptr_t outer_try_index,
intptr_t pc_offset,
bool is_generated,
const Array& handler_types,
bool needs_stacktrace) {
ASSERT(try_index >= 0);
while (Length() <= try_index) {
AddPlaceHolder();
}
list_[try_index].outer_try_index = outer_try_index;
ASSERT(list_[try_index].pc_offset == ExceptionHandlers::kInvalidPcOffset);
list_[try_index].pc_offset = pc_offset;
list_[try_index].is_generated = is_generated;
DEBUG_ASSERT(handler_types.IsNotTemporaryScopedHandle());
list_[try_index].handler_types = &handler_types;
list_[try_index].needs_stacktrace |= needs_stacktrace;
}
// Called by rethrows, to mark their enclosing handlers.
void SetNeedsStackTrace(intptr_t try_index) {
// Rethrows can be generated outside a try by the compiler.
if (try_index == kInvalidTryIndex) {
return;
}
ASSERT(try_index >= 0);
while (Length() <= try_index) {
AddPlaceHolder();
}
list_[try_index].needs_stacktrace = true;
}
static bool ContainsCatchAllType(const Array& array) {
auto& type = AbstractType::Handle();
for (intptr_t i = 0; i < array.Length(); i++) {
type ^= array.At(i);
if (type.IsCatchAllType()) {
return true;
}
}
return false;
}
ExceptionHandlersPtr FinalizeExceptionHandlers(uword entry_point) const;
private:
GrowableArray<struct HandlerDesc> list_;
const bool has_async_handler_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerList);
};
#if !defined(DART_PRECOMPILED_RUNTIME)
// Used to construct CatchEntryMoves for the AOT mode of compilation.
class CatchEntryMovesMapBuilder : public ZoneAllocated {
public:
CatchEntryMovesMapBuilder();
void NewMapping(intptr_t pc_offset);
void Append(const CatchEntryMove& move);
void EndMapping();
TypedDataPtr FinalizeCatchEntryMovesMap();
private:
class TrieNode;
Zone* zone_;
TrieNode* root_;
intptr_t current_pc_offset_;
GrowableArray<CatchEntryMove> moves_;
ZoneWriteStream stream_;
DISALLOW_COPY_AND_ASSIGN(CatchEntryMovesMapBuilder);
};
#endif // !defined(DART_PRECOMPILED_RUNTIME)
// Instructions have two pieces of information needed to get accurate source
// locations: the token position and the inlining id. The inlining id tells us
// which function, and thus which script, to use for this instruction and the
// token position, when real, tells us the position in the source for the
// script for the instruction.
//
// Thus, we bundle the two pieces of information in InstructionSource structs
// when copying or retrieving to lower the likelihood that the token position
// is used without the appropriate inlining id.
struct InstructionSource {
// Treat an instruction source without inlining id information as unset.
InstructionSource() : InstructionSource(TokenPosition::kNoSource) {}
explicit InstructionSource(TokenPosition pos) : InstructionSource(pos, -1) {}
InstructionSource(TokenPosition pos, intptr_t id)
: token_pos(pos), inlining_id(id) {}
const TokenPosition token_pos;
const intptr_t inlining_id;
DISALLOW_ALLOCATION();
};
struct CodeSourceMapOps : AllStatic {
static constexpr uint8_t kChangePosition = 0;
static constexpr uint8_t kAdvancePC = 1;
static constexpr uint8_t kPushFunction = 2;
static constexpr uint8_t kPopFunction = 3;
static constexpr uint8_t kNullCheck = 4;
static uint8_t Read(ReadStream* stream,
int32_t* arg1,
int32_t* arg2 = nullptr);
static void Write(BaseWriteStream* stream,
uint8_t op,
int32_t arg1 = 0,
int32_t arg2 = 0);
private:
static constexpr intptr_t kOpBits = 3;
using OpField = BitField<int32_t, uint8_t, 0, kOpBits>;
using ArgField = BitField<int32_t, int32_t, OpField::kNextBit>;
static constexpr int32_t kMaxArgValue =
Utils::NBitMask<int32_t>(ArgField::bitsize() - 1);
static constexpr int32_t kMinArgValue = ~kMaxArgValue;
static constexpr int32_t kSignBits = static_cast<uint32_t>(kMinArgValue) << 1;
};
// A CodeSourceMap maps from pc offsets to a stack of inlined functions and
// their positions. This is encoded as a little bytecode that pushes and pops
// functions and changes the top function's position as the PC advances.
// Decoding happens by running this bytecode until we reach the desired PC.
//
// The implementation keeps track of two sets of state: one written to the byte
// stream and one that is buffered. On the JIT, this buffering effectively gives
// us a peephole optimization that merges adjacent advance PC bytecodes. On AOT,
// this allows to skip encoding our position until we reach a PC where we might
// throw.
class CodeSourceMapBuilder : public ZoneAllocated {
public:
CodeSourceMapBuilder(
Zone* zone,
bool stack_traces_only,
const GrowableArray<intptr_t>& caller_inline_id,
const GrowableArray<TokenPosition>& inline_id_to_token_pos,
const GrowableArray<const Function*>& inline_id_to_function);
// The position at which a function implicitly starts, for both the root and
// after a push bytecode. We use the classifying position kDartCodePrologue
// since it is the most common.
static const TokenPosition& kInitialPosition;
void BeginCodeSourceRange(int32_t pc_offset, const InstructionSource& source);
void EndCodeSourceRange(int32_t pc_offset, const InstructionSource& source);
void NoteDescriptor(UntaggedPcDescriptors::Kind kind,
int32_t pc_offset,
const InstructionSource& source);
void NoteNullCheck(int32_t pc_offset,
const InstructionSource& source,
intptr_t name_index);
void WriteFunctionEntrySourcePosition(const InstructionSource& source);
// If source is from an inlined call, returns the token position of the
// original call in the root function, otherwise the source's token position.
TokenPosition RootPosition(const InstructionSource& source);
ArrayPtr InliningIdToFunction();
CodeSourceMapPtr Finalize();
const GrowableArray<const Function*>& inline_id_to_function() const {
return inline_id_to_function_;
}
private:
intptr_t GetFunctionId(intptr_t inline_id);
void StartInliningInterval(int32_t pc_offset,
const InstructionSource& source);
void BufferChangePosition(TokenPosition pos);
void WriteChangePosition(TokenPosition pos);
void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; }
void WriteAdvancePC(int32_t distance) {
CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kAdvancePC, distance);
written_pc_offset_ += distance;
}
void BufferPush(intptr_t inline_id) {
buffered_inline_id_stack_.Add(inline_id);
buffered_token_pos_stack_.Add(kInitialPosition);
}
void WritePush(intptr_t inline_id) {
CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kPushFunction,
GetFunctionId(inline_id));
written_inline_id_stack_.Add(inline_id);
written_token_pos_stack_.Add(kInitialPosition);
}
void BufferPop() {
buffered_inline_id_stack_.RemoveLast();
buffered_token_pos_stack_.RemoveLast();
}
void WritePop() {
CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kPopFunction);
written_inline_id_stack_.RemoveLast();
written_token_pos_stack_.RemoveLast();
}
void WriteNullCheck(int32_t name_index) {
CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kNullCheck, name_index);
}
void FlushBuffer();
bool IsOnBufferedStack(intptr_t inline_id) {
for (intptr_t i = 0; i < buffered_inline_id_stack_.length(); i++) {
if (buffered_inline_id_stack_[i] == inline_id) return true;
}
return false;
}
Zone* const zone_;
intptr_t buffered_pc_offset_;
GrowableArray<intptr_t> buffered_inline_id_stack_;
GrowableArray<TokenPosition> buffered_token_pos_stack_;
intptr_t written_pc_offset_;
GrowableArray<intptr_t> written_inline_id_stack_;
GrowableArray<TokenPosition> written_token_pos_stack_;
const GrowableArray<intptr_t>& caller_inline_id_;
const GrowableArray<TokenPosition>& inline_id_to_token_pos_;
const GrowableArray<const Function*>& inline_id_to_function_;
const GrowableObjectArray& inlined_functions_;
Script& script_;
ZoneWriteStream stream_;
const bool stack_traces_only_;
DISALLOW_COPY_AND_ASSIGN(CodeSourceMapBuilder);
};
class CodeSourceMapReader : public ValueObject {
public:
CodeSourceMapReader(const CodeSourceMap& map,
const Array& functions,
const Function& root)
: map_(map), functions_(functions), root_(root) {}
void GetInlinedFunctionsAt(int32_t pc_offset,
GrowableArray<const Function*>* function_stack,
GrowableArray<TokenPosition>* token_positions);
NOT_IN_PRODUCT(void PrintJSONInlineIntervals(JSONObject* jsobj));
void DumpInlineIntervals(uword start);
void DumpSourcePositions(uword start);
intptr_t GetNullCheckNameIndexAt(int32_t pc_offset);
private:
static const TokenPosition& InitialPosition() {
if (FLAG_precompiled_mode) {
// In precompiled mode, the CodeSourceMap stores lines instead of
// real token positions and uses kNoSourcePos for no line information.
return TokenPosition::kNoSource;
} else {
return CodeSourceMapBuilder::kInitialPosition;
}
}
// Reads a TokenPosition value from a CSM, handling the different encoding for
// when non-symbolic stack traces are enabled.
static TokenPosition ReadPosition(ReadStream* stream);
const CodeSourceMap& map_;
const Array& functions_;
const Function& root_;
DISALLOW_COPY_AND_ASSIGN(CodeSourceMapReader);
};
} // namespace dart
#endif // RUNTIME_VM_CODE_DESCRIPTORS_H_