1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef frontend_BytecodeSection_h
8 #define frontend_BytecodeSection_h
10 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS
11 #include "mozilla/Maybe.h" // mozilla::Maybe
12 #include "mozilla/Span.h" // mozilla::Span
14 #include <stddef.h> // ptrdiff_t, size_t
15 #include <stdint.h> // uint16_t, int32_t, uint32_t
17 #include "frontend/AbstractScopePtr.h" // AbstractScopePtr, ScopeIndex
18 #include "frontend/BytecodeOffset.h" // BytecodeOffset
19 #include "frontend/CompilationStencil.h" // CompilationStencil, CompilationGCOutput, CompilationAtomCache
20 #include "frontend/FrontendContext.h" // FrontendContext
21 #include "frontend/JumpList.h" // JumpTarget
22 #include "frontend/NameCollections.h" // AtomIndexMap, PooledMapPtr
23 #include "frontend/ParseNode.h" // BigIntLiteral
24 #include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex, ParserAtom
25 #include "frontend/SourceNotes.h" // SrcNote
26 #include "frontend/Stencil.h" // Stencils
27 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
28 #include "js/TypeDecls.h" // jsbytecode, JSContext
29 #include "js/Vector.h" // Vector
30 #include "vm/SharedStencil.h" // TryNote, ScopeNote, GCThingIndex
31 #include "vm/StencilEnums.h" // TryNoteKind
38 struct MOZ_STACK_CLASS GCThingList
{
39 // The BCE accumulates TaggedScriptThingIndex items so use a vector type. We
40 // reserve some stack slots to avoid allocating for most small scripts.
41 using ScriptThingsStackVector
= Vector
<TaggedScriptThingIndex
, 8>;
43 CompilationState
& compilationState
;
44 ScriptThingsStackVector vector
;
46 // Index of the first scope in the vector.
47 mozilla::Maybe
<GCThingIndex
> firstScopeIndex
;
49 explicit GCThingList(FrontendContext
* fc
, CompilationState
& compilationState
)
50 : compilationState(compilationState
), vector(fc
) {}
52 [[nodiscard
]] bool append(TaggedParserAtomIndex atom
,
53 ParserAtom::Atomize atomize
, GCThingIndex
* index
) {
54 *index
= GCThingIndex(vector
.length());
55 compilationState
.parserAtoms
.markUsedByStencil(atom
, atomize
);
56 if (!vector
.emplaceBack(atom
)) {
61 [[nodiscard
]] bool append(ScopeIndex scope
, GCThingIndex
* index
) {
62 *index
= GCThingIndex(vector
.length());
63 if (!vector
.emplaceBack(scope
)) {
66 if (!firstScopeIndex
) {
67 firstScopeIndex
.emplace(*index
);
71 [[nodiscard
]] bool append(BigIntLiteral
* literal
, GCThingIndex
* index
) {
72 *index
= GCThingIndex(vector
.length());
73 if (!vector
.emplaceBack(literal
->index())) {
78 [[nodiscard
]] bool append(RegExpLiteral
* literal
, GCThingIndex
* index
) {
79 *index
= GCThingIndex(vector
.length());
80 if (!vector
.emplaceBack(literal
->index())) {
85 [[nodiscard
]] bool append(ObjLiteralIndex objlit
, GCThingIndex
* index
) {
86 *index
= GCThingIndex(vector
.length());
87 if (!vector
.emplaceBack(objlit
)) {
92 [[nodiscard
]] bool append(FunctionBox
* funbox
, GCThingIndex
* index
);
94 [[nodiscard
]] bool appendEmptyGlobalScope(GCThingIndex
* index
) {
95 *index
= GCThingIndex(vector
.length());
96 EmptyGlobalScopeType emptyGlobalScope
;
97 if (!vector
.emplaceBack(emptyGlobalScope
)) {
100 if (!firstScopeIndex
) {
101 firstScopeIndex
.emplace(*index
);
106 uint32_t length() const { return vector
.length(); }
108 const ScriptThingsStackVector
& objects() { return vector
; }
110 AbstractScopePtr
getScope(size_t index
) const;
112 // Index of scope within CompilationStencil or Nothing is the scope is
113 // EmptyGlobalScopeType.
114 mozilla::Maybe
<ScopeIndex
> getScopeIndex(size_t index
) const;
116 TaggedParserAtomIndex
getAtom(size_t index
) const;
118 AbstractScopePtr
firstScope() const {
119 MOZ_ASSERT(firstScopeIndex
.isSome());
120 return getScope(*firstScopeIndex
);
124 [[nodiscard
]] bool EmitScriptThingsVector(
125 JSContext
* cx
, const CompilationAtomCache
& atomCache
,
126 const CompilationStencil
& stencil
, CompilationGCOutput
& gcOutput
,
127 mozilla::Span
<const TaggedScriptThingIndex
> things
,
128 mozilla::Span
<JS::GCCellPtr
> output
);
130 struct CGTryNoteList
{
131 Vector
<TryNote
, 0> list
;
132 explicit CGTryNoteList(FrontendContext
* fc
) : list(fc
) {}
134 [[nodiscard
]] bool append(TryNoteKind kind
, uint32_t stackDepth
,
135 BytecodeOffset start
, BytecodeOffset end
);
136 mozilla::Span
<const TryNote
> span() const {
137 return {list
.begin(), list
.length()};
139 size_t length() const { return list
.length(); }
142 struct CGScopeNoteList
{
143 Vector
<ScopeNote
, 0> list
;
144 explicit CGScopeNoteList(FrontendContext
* fc
) : list(fc
) {}
146 [[nodiscard
]] bool append(GCThingIndex scopeIndex
, BytecodeOffset offset
,
148 void recordEnd(uint32_t index
, BytecodeOffset offset
);
149 void recordEndFunctionBodyVar(uint32_t index
);
150 mozilla::Span
<const ScopeNote
> span() const {
151 return {list
.begin(), list
.length()};
153 size_t length() const { return list
.length(); }
156 void recordEndImpl(uint32_t index
, uint32_t offset
);
159 struct CGResumeOffsetList
{
160 Vector
<uint32_t, 0> list
;
161 explicit CGResumeOffsetList(FrontendContext
* fc
) : list(fc
) {}
163 [[nodiscard
]] bool append(uint32_t offset
) { return list
.append(offset
); }
164 mozilla::Span
<const uint32_t> span() const {
165 return {list
.begin(), list
.length()};
167 size_t length() const { return list
.length(); }
170 static constexpr size_t MaxBytecodeLength
= INT32_MAX
;
171 static constexpr size_t MaxSrcNotesLength
= INT32_MAX
;
173 // Have a few inline elements, so as to avoid heap allocation for tiny
174 // sequences. See bug 1390526.
175 typedef Vector
<jsbytecode
, 64> BytecodeVector
;
176 typedef Vector
<js::SrcNote
, 64> SrcNotesVector
;
178 // Bytecode and all data directly associated with specific opcode/index inside
179 // bytecode is stored in this class.
180 class BytecodeSection
{
182 BytecodeSection(FrontendContext
* fc
, uint32_t lineNum
,
183 JS::LimitedColumnNumberOneOrigin column
);
185 // ---- Bytecode ----
187 BytecodeVector
& code() { return code_
; }
188 const BytecodeVector
& code() const { return code_
; }
190 jsbytecode
* code(BytecodeOffset offset
) {
191 return code_
.begin() + offset
.value();
193 BytecodeOffset
offset() const {
194 return BytecodeOffset(code_
.end() - code_
.begin());
197 // ---- Source notes ----
199 SrcNotesVector
& notes() { return notes_
; }
200 const SrcNotesVector
& notes() const { return notes_
; }
202 BytecodeOffset
lastNoteOffset() const { return lastNoteOffset_
; }
203 void setLastNoteOffset(BytecodeOffset offset
) { lastNoteOffset_
= offset
; }
207 BytecodeOffset
lastTargetOffset() const { return lastTarget_
.offset
; }
208 void setLastTargetOffset(BytecodeOffset offset
) {
209 lastTarget_
.offset
= offset
;
214 int32_t stackDepth() const { return stackDepth_
; }
215 void setStackDepth(int32_t depth
) { stackDepth_
= depth
; }
217 uint32_t maxStackDepth() const { return maxStackDepth_
; }
219 void updateDepth(JSOp op
, BytecodeOffset target
);
221 // ---- Try notes ----
223 CGTryNoteList
& tryNoteList() { return tryNoteList_
; };
224 const CGTryNoteList
& tryNoteList() const { return tryNoteList_
; };
228 CGScopeNoteList
& scopeNoteList() { return scopeNoteList_
; };
229 const CGScopeNoteList
& scopeNoteList() const { return scopeNoteList_
; };
231 // ---- Generator ----
233 CGResumeOffsetList
& resumeOffsetList() { return resumeOffsetList_
; }
234 const CGResumeOffsetList
& resumeOffsetList() const {
235 return resumeOffsetList_
;
238 uint32_t numYields() const { return numYields_
; }
239 void addNumYields() { numYields_
++; }
241 // ---- Line and column ----
243 uint32_t currentLine() const { return currentLine_
; }
244 JS::LimitedColumnNumberOneOrigin
lastColumn() const { return lastColumn_
; }
245 void setCurrentLine(uint32_t line
, uint32_t sourceOffset
) {
247 lastColumn_
= JS::LimitedColumnNumberOneOrigin();
248 lastSourceOffset_
= sourceOffset
;
251 void setLastColumn(JS::LimitedColumnNumberOneOrigin column
, uint32_t offset
) {
252 lastColumn_
= column
;
253 lastSourceOffset_
= offset
;
256 void updateSeparatorPosition() {
257 lastSeparatorCodeOffset_
= code().length();
258 lastSeparatorSourceOffset_
= lastSourceOffset_
;
259 lastSeparatorLine_
= currentLine_
;
260 lastSeparatorColumn_
= lastColumn_
;
263 void updateSeparatorPositionIfPresent() {
264 if (lastSeparatorCodeOffset_
== code().length()) {
265 lastSeparatorSourceOffset_
= lastSourceOffset_
;
266 lastSeparatorLine_
= currentLine_
;
267 lastSeparatorColumn_
= lastColumn_
;
271 bool isDuplicateLocation() const {
272 return lastSeparatorLine_
== currentLine_
&&
273 lastSeparatorColumn_
== lastColumn_
;
276 bool atSeparator(uint32_t offset
) const {
277 return lastSeparatorSourceOffset_
== offset
;
282 uint32_t numICEntries() const { return numICEntries_
; }
283 void incrementNumICEntries() {
284 MOZ_ASSERT(numICEntries_
!= UINT32_MAX
, "Shouldn't overflow");
287 void setNumICEntries(uint32_t entries
) { numICEntries_
= entries
; }
290 // ---- Bytecode ----
293 BytecodeVector code_
;
295 // ---- Source notes ----
298 SrcNotesVector notes_
;
300 // Code offset for last source note
301 BytecodeOffset lastNoteOffset_
;
305 // Last jump target emitted.
306 JumpTarget lastTarget_
;
310 // Maximum number of expression stack slots so far.
311 uint32_t maxStackDepth_
= 0;
313 // Current stack depth in script frame.
314 int32_t stackDepth_
= 0;
316 // ---- Try notes ----
318 // List of emitted try notes.
319 CGTryNoteList tryNoteList_
;
323 // List of emitted block scope notes.
324 CGScopeNoteList scopeNoteList_
;
326 // ---- Generator ----
328 // Certain ops (yield, await) have an entry in the script's resumeOffsets
329 // list. This can be used to map from the op's resumeIndex to the bytecode
330 // offset of the next pc. This indirection makes it easy to resume in the JIT
331 // (because BaselineScript stores a resumeIndex => native code array).
332 CGResumeOffsetList resumeOffsetList_
;
334 // Number of yield instructions emitted. Does not include JSOp::Await.
335 uint32_t numYields_
= 0;
337 // ---- Line and column ----
339 // Line number for srcnotes.
341 // WARNING: If this becomes out of sync with already-emitted srcnotes,
342 // we can get undefined behavior.
343 uint32_t currentLine_
;
345 // Column index in UTF-16 code units on currentLine_ of last
346 // SrcNoteType::ColSpan-annotated opcode.
348 // WARNING: If this becomes out of sync with already-emitted srcnotes,
349 // we can get undefined behavior.
350 JS::LimitedColumnNumberOneOrigin lastColumn_
;
352 // The last code unit used for srcnotes.
353 uint32_t lastSourceOffset_
= 0;
355 // The offset, line and column numbers of the last opcode for the
356 // breakpoint for step execution.
357 uint32_t lastSeparatorCodeOffset_
= 0;
358 uint32_t lastSeparatorSourceOffset_
= 0;
359 uint32_t lastSeparatorLine_
= 0;
360 JS::LimitedColumnNumberOneOrigin lastSeparatorColumn_
;
364 // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
365 // and, if the script is a function, for |this| and each formal argument.
366 uint32_t numICEntries_
= 0;
369 // Data that is not directly associated with specific opcode/index inside
370 // bytecode, but referred from bytecode is stored in this class.
371 class PerScriptData
{
373 PerScriptData(FrontendContext
* fc
,
374 frontend::CompilationState
& compilationState
);
376 [[nodiscard
]] bool init(FrontendContext
* fc
);
378 GCThingList
& gcThingList() { return gcThingList_
; }
379 const GCThingList
& gcThingList() const { return gcThingList_
; }
381 PooledMapPtr
<AtomIndexMap
>& atomIndices() { return atomIndices_
; }
382 const PooledMapPtr
<AtomIndexMap
>& atomIndices() const { return atomIndices_
; }
385 // List of emitted scopes/objects/bigints.
386 GCThingList gcThingList_
;
388 // Map from atom to index.
389 PooledMapPtr
<AtomIndexMap
> atomIndices_
;
392 } /* namespace frontend */
395 #endif /* frontend_BytecodeSection_h */