Repository navigation
Expand file tree
/
Copy pathExpressionInfo.h
More file actions
356 lines (278 loc) · 14.8 KB
/
Copy pathExpressionInfo.h
File metadata and controls
356 lines (278 loc) · 14.8 KB
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
/*
* Copyright (C) 2024 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
*/
#pragma once
#include <JavaScriptCore/LineColumn.h>
#include <wtf/HashMap.h>
#include <wtf/HashTraits.h>
#include <wtf/IterationStatus.h>
#include <wtf/MallocPtr.h>
#include <wtf/PrintStream.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
namespace JSC {
class SourceProvider;
class ConcurrentJSLocker;
// See comment at the top of ExpressionInfo.cpp on how ExpressionInfo works.
class ExpressionInfo {
WTF_MAKE_NONCOPYABLE(ExpressionInfo);
WTF_DEPRECATED_MAKE_FAST_ALLOCATED(ExpressionInfo);
public:
enum class FieldID : uint8_t { InstPC, Divot, Start, End };
class Decoder;
using InstPC = unsigned; // aka bytecode PC.
static constexpr InstPC maxInstPC = std::numeric_limits<unsigned>::max();
struct Chapter {
InstPC startInstPC;
unsigned startEncodedInfoIndex;
};
struct Entry {
void LEGACY_NODELETE reset()
{
instPC = 0;
divot = 0;
startOffset = 0;
endOffset = 0;
}
InstPC instPC { 0 };
unsigned divot { 0 };
unsigned startOffset { 0 }; // This value is relative to divot.
unsigned endOffset { 0 }; // This value is relative to divot.
};
struct EncodedInfo {
bool LEGACY_NODELETE isAbsInstPC() const;
bool LEGACY_NODELETE isExtension() const;
unsigned value;
};
struct Diff;
class Encoder {
public:
void encode(InstPC, unsigned divot, unsigned startOffset, unsigned endOffset);
template<typename RemapFunc>
void remap(Vector<unsigned>&& adjustments, RemapFunc);
// Re-encode all entries with instruction PCs mapped through a monotonic (non-decreasing) function.
// Entries that end up sharing a PC with a later entry are dropped (the later one wins on lookup anyway).
template<typename MapFunc>
void rebuild(const MapFunc&);
Entry entry() const { return m_entry; }
std::unique_ptr<ExpressionInfo> createExpressionInfo();
void dumpEncodedInfo() // For debugging use only.
{
ExpressionInfo::dumpEncodedInfo(m_expressionInfoEncodedInfo.begin(), m_expressionInfoEncodedInfo.end());
}
private:
struct Wide {
enum class SortOrder : uint8_t { Single, Duo, Multi };
unsigned value;
FieldID fieldID;
SortOrder order { SortOrder::Multi };
};
static EncodedInfo LEGACY_NODELETE encodeAbsInstPC(InstPC absInstPC);
static EncodedInfo LEGACY_NODELETE encodeExtension(unsigned offset);
static constexpr EncodedInfo LEGACY_NODELETE encodeExtensionEnd();
static EncodedInfo LEGACY_NODELETE encodeSingle(FieldID, unsigned);
static EncodedInfo LEGACY_NODELETE encodeDuo(FieldID, unsigned value1, FieldID, unsigned value2);
static EncodedInfo LEGACY_NODELETE encodeMultiHeader(unsigned numWides, Wide*);
static EncodedInfo LEGACY_NODELETE encodeBasic(const Diff&);
void adjustInstPC(unsigned infoIndex, unsigned instPCDelta);
template<unsigned bitCount> bool LEGACY_NODELETE fits(Wide);
template<typename T, unsigned bitCount> bool fits(T);
Entry m_entry;
unsigned m_currentChapterStartIndex { 0 };
unsigned m_numberOfEncodedInfoExtensions { 0 };
Vector<ExpressionInfo::Chapter> m_expressionInfoChapters;
Vector<ExpressionInfo::EncodedInfo> m_expressionInfoEncodedInfo;
};
class Decoder {
public:
Decoder() = default;
Decoder(const ExpressionInfo&);
Decoder(Vector<ExpressionInfo::EncodedInfo>&);
IterationStatus LEGACY_NODELETE decode(std::optional<InstPC> targetInstPC = std::nullopt);
void LEGACY_NODELETE recacheInfo(Vector<ExpressionInfo::EncodedInfo>&);
EncodedInfo* currentInfo() const { return m_currentInfo; }
// This is meant to be used to jump to the start of a chapter, where the encoder
// always start over with no historical Entry values to compute diffs from.
// If you use this to point to any EncodedInfo other than the start of a chapter,
// then you're responsible for setting up the initial conditions of m_entry correctly
// apriori.
void setNextInfo(EncodedInfo* info) { m_nextInfo = info; }
Entry entry() const { return m_entry; }
void setEntry(Entry entry) { m_entry = entry; } // For debugging use only.
InstPC instPC() const { return m_entry.instPC; }
unsigned divot() const { return m_entry.divot; }
unsigned startOffset() const { return m_entry.startOffset; }
unsigned endOffset() const { return m_entry.endOffset; }
private:
struct Wide {
FieldID fieldID;
unsigned value;
};
void appendWide(FieldID id, unsigned value)
{
m_wides[m_numWides++] = { id, value };
}
Entry m_entry;
EncodedInfo* m_startInfo { nullptr };
EncodedInfo* m_endInfo { nullptr };
EncodedInfo* m_endExtensionInfo { nullptr };
EncodedInfo* m_currentInfo { nullptr };
EncodedInfo* m_nextInfo { nullptr };
bool m_hasDecodedFirstEntry { false };
unsigned m_numWides { 0 };
std::array<Wide, 6> m_wides;
};
~ExpressionInfo() = default;
// The caller holds the owning UnlinkedCodeBlock's lock, because compiler threads also look up
// entries (useSourceCodeDump) and lookups fill a cache.
Entry entryForInstPC(const ConcurrentJSLocker&, InstPC);
// Decodes without touching the cache: takes no lock and does not allocate (UnlinkedCodeBlock::expressionInfoIfDecoded).
Entry LEGACY_NODELETE decodeEntryForInstPC(InstPC);
// For tests: how many EncodedInfo words decodeEntryForInstPC() steps over, from the start of the chapter to the
// entry behind the one that it returns.
unsigned LEGACY_NODELETE numberOfEncodedInfoDecodedForInstPC(InstPC);
// The zero-based line and column of the instruction's divot in the text of its source, where this code starts at
// sourceOffset. Decoding an entry starts from the start of its chapter, and stack traces ask for the
// same instructions again, so this keeps each answer. Sources share unlinked code when the CodeCache finds their text
// equal (a precompiled program is for the source it was compiled from), and everyone who asks it for global code
// gives it all of a source. So the text and sourceOffset, and with them the answer, are the same for every source
// that shares this. Where a source says its text starts is not in the answer.
// Not for a thread that runs beside the mutator: nothing guards the map.
LineColumn lineColumnInTextForInstPC(InstPC, SourceProvider&, unsigned sourceOffset);
bool isEmpty() const { return !m_numberOfEncodedInfo; };
unsigned numberOfEncodedInfo() const { return m_numberOfEncodedInfo; }
unsigned numberOfChapters() const { return m_numberOfChapters; }
size_t LEGACY_NODELETE byteSize() const; // owned by this object
size_t LEGACY_NODELETE byteSizeForGCPacing() const; // what a generated (non-borrowed) one this size would own
template<unsigned bitCount>
static void print(PrintStream&, FieldID, unsigned value);
static void dumpEncodedInfo(EncodedInfo* start, EncodedInfo* end); // For debugging use only.
private:
ExpressionInfo(unsigned numberOfChapters, unsigned numberOfEncodedInfo, unsigned numberOfEncodedInfoExtensions);
ExpressionInfo(Vector<Chapter>&&, Vector<EncodedInfo>&&, unsigned numberOfEncodedInfoExtensions);
template<typename T, unsigned bitCount> static T LEGACY_NODELETE cast(unsigned);
static bool LEGACY_NODELETE isSpecial(unsigned);
static bool LEGACY_NODELETE isWideOrSpecial(unsigned);
static size_t payloadSizeInBytes(size_t numChapters, size_t numberOfEncodedInfo, size_t numberOfEncodedInfoExtensions)
{
size_t size = numChapters * sizeof(Chapter) + (numberOfEncodedInfo + numberOfEncodedInfoExtensions) * sizeof(EncodedInfo);
return roundUpToMultipleOf<sizeof(unsigned)>(size);
}
static size_t totalSizeInBytes(size_t numChapters, size_t numberOfEncodedInfo, size_t numberOfEncodedInfoExtensions)
{
return sizeof(ExpressionInfo) + payloadSizeInBytes(numChapters, numberOfEncodedInfo, numberOfEncodedInfoExtensions);
}
EncodedInfo* LEGACY_NODELETE findChapterEncodedInfoJustBelow(InstPC) const;
Chapter* chapters() const
{
return std::bit_cast<Chapter*>(payload());
}
EncodedInfo* encodedInfo() const
{
return std::bit_cast<EncodedInfo*>(&chapters()[m_numberOfChapters]);
}
EncodedInfo* endEncodedInfo() const
{
return &encodedInfo()[m_numberOfEncodedInfo];
}
EncodedInfo* endExtensionEncodedInfo() const
{
return &encodedInfo()[m_numberOfEncodedInfo + m_numberOfEncodedInfoExtensions];
}
size_t payloadSize() const
{
return payloadSizeInBytes(m_numberOfChapters, m_numberOfEncodedInfo, m_numberOfEncodedInfoExtensions) / sizeof(unsigned);
}
unsigned* payload() const
{
if (m_borrowedPayload) [[unlikely]]
return m_borrowedPayload;
return std::bit_cast<unsigned*>(this + 1);
}
static std::unique_ptr<ExpressionInfo> createUninitialized(unsigned numberOfChapters, unsigned numberOfEncodedInfo, unsigned numberOfEncodedInfoExtensions);
// Header-only object whose (immutable) payload lives elsewhere, e.g. inside an mmap'd bytecode cache.
static std::unique_ptr<ExpressionInfo> createBorrowed(unsigned numberOfChapters, unsigned numberOfEncodedInfo, unsigned numberOfEncodedInfoExtensions, const unsigned* payload);
static constexpr unsigned bitsPerWord = sizeof(unsigned) * CHAR_BIT;
// Number of bits of each field in Basic encoding.
static constexpr unsigned instPCBits = 5;
static constexpr unsigned divotBits = 11;
static constexpr unsigned startBits = 8;
static constexpr unsigned endBits = 8;
static_assert(instPCBits + divotBits + startBits + endBits == bitsPerWord);
// Bias values used for the signed diff values which make it easier to do range checks on these.
static constexpr unsigned divotBias = (1 << divotBits) / 2;
static constexpr unsigned instPCShift = bitsPerWord - instPCBits;
static constexpr unsigned divotShift = instPCShift - divotBits;
static constexpr unsigned startShift = divotShift - startBits;
static constexpr unsigned endShift = startShift - endBits;
static constexpr unsigned specialHeader = (1 << instPCBits) - 1;
static constexpr unsigned wideHeader = specialHeader - 1;
static constexpr unsigned maxInstPCValue = wideHeader - 1;
static constexpr unsigned maxBiasedDivotValue = (1 << divotBits) - 1;
static constexpr unsigned maxStartValue = (1 << startBits) - 1;
static constexpr unsigned maxEndValue = (1 << endBits) - 1;
// Number of bits in Wide / Special encodings.
static constexpr unsigned specialValueBits = 26;
static constexpr unsigned singleValueBits = 23;
static constexpr unsigned duoValueBits = 10;
static constexpr unsigned fullValueBits = 32;
static constexpr unsigned multiSizeBits = 5;
static constexpr unsigned fieldIDBits = 3;
static constexpr unsigned maxSpecialValue = (1 << specialValueBits) - 1;
static constexpr unsigned maxSingleValue = (1 << singleValueBits) - 1;
static constexpr unsigned maxDuoValue = (1 << duoValueBits) - 1;
static constexpr unsigned invalidFieldID = (1 << fieldIDBits) - 1;
static constexpr unsigned multiSizeMask = (1 << multiSizeBits) - 1;
static constexpr unsigned fieldIDMask = (1 << fieldIDBits) - 1;
static constexpr unsigned headerShift = bitsPerWord - instPCBits;
static constexpr unsigned multiBitShift = headerShift - 1;
static_assert(headerShift == 27);
static_assert(multiBitShift == 26);
static constexpr unsigned specialValueShift = multiBitShift - specialValueBits;
static_assert(!specialValueShift);
static constexpr unsigned firstFieldIDShift = multiBitShift - fieldIDBits;
static constexpr unsigned singleValueShift = firstFieldIDShift - singleValueBits;
static_assert(!singleValueShift);
static constexpr unsigned duoFirstValueShift = firstFieldIDShift - duoValueBits;
static constexpr unsigned duoSecondFieldIDShift = duoFirstValueShift - fieldIDBits;
static constexpr unsigned duoSecondValueShift = duoSecondFieldIDShift - duoValueBits;
static_assert(!duoSecondValueShift);
static constexpr unsigned multiSizeShift = firstFieldIDShift - multiSizeBits;
static constexpr unsigned multiFirstFieldShift = multiSizeShift - fieldIDBits;
using LineColumnMap = UncheckedKeyHashMap<InstPC, LineColumn, WTF::IntHash<InstPC>, WTF::UnsignedWithZeroKeyHashTraits<InstPC>>;
UncheckedKeyHashMap<InstPC, Entry, WTF::IntHash<InstPC>, WTF::UnsignedWithZeroKeyHashTraits<InstPC>> m_cachedEntries;
LineColumnMap m_cachedLineColumns;
unsigned m_numberOfChapters;
unsigned m_numberOfEncodedInfo;
unsigned m_numberOfEncodedInfoExtensions;
unsigned* m_borrowedPayload { nullptr };
// Followed by the following which are allocated but are dynamically sized.
// Chapter chapters[numberOfChapters];
// EncodedInfo encodedInfo[numberOfEncodedInfo + numberOfEncodedInfoExtensions];
friend class CachedExpressionInfo;
};
static_assert(roundUpToMultipleOf<sizeof(unsigned)>(sizeof(ExpressionInfo)) == sizeof(ExpressionInfo), "CachedExpressionInfo relies on this invariant");
} // namespace JSC
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END