BytecodeSection.h (12628B)
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/. */ 6 7 #ifndef frontend_BytecodeSection_h 8 #define frontend_BytecodeSection_h 9 10 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS 11 #include "mozilla/Maybe.h" // mozilla::Maybe 12 #include "mozilla/Span.h" // mozilla::Span 13 14 #include <stddef.h> // ptrdiff_t, size_t 15 #include <stdint.h> // uint16_t, int32_t, uint32_t 16 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 32 33 namespace js { 34 namespace frontend { 35 36 class FunctionBox; 37 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>; 42 43 CompilationState& compilationState; 44 ScriptThingsStackVector vector; 45 46 // Index of the first scope in the vector. 47 mozilla::Maybe<GCThingIndex> firstScopeIndex; 48 49 explicit GCThingList(FrontendContext* fc, CompilationState& compilationState) 50 : compilationState(compilationState), vector(fc) {} 51 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)) { 57 return false; 58 } 59 return true; 60 } 61 [[nodiscard]] bool append(ScopeIndex scope, GCThingIndex* index) { 62 *index = GCThingIndex(vector.length()); 63 if (!vector.emplaceBack(scope)) { 64 return false; 65 } 66 if (!firstScopeIndex) { 67 firstScopeIndex.emplace(*index); 68 } 69 return true; 70 } 71 [[nodiscard]] bool append(BigIntLiteral* literal, GCThingIndex* index) { 72 *index = GCThingIndex(vector.length()); 73 if (!vector.emplaceBack(literal->index())) { 74 return false; 75 } 76 return true; 77 } 78 [[nodiscard]] bool append(RegExpLiteral* literal, GCThingIndex* index) { 79 *index = GCThingIndex(vector.length()); 80 if (!vector.emplaceBack(literal->index())) { 81 return false; 82 } 83 return true; 84 } 85 [[nodiscard]] bool append(ObjLiteralIndex objlit, GCThingIndex* index) { 86 *index = GCThingIndex(vector.length()); 87 if (!vector.emplaceBack(objlit)) { 88 return false; 89 } 90 return true; 91 } 92 [[nodiscard]] bool append(FunctionBox* funbox, GCThingIndex* index); 93 94 [[nodiscard]] bool appendEmptyGlobalScope(GCThingIndex* index) { 95 *index = GCThingIndex(vector.length()); 96 EmptyGlobalScopeType emptyGlobalScope; 97 if (!vector.emplaceBack(emptyGlobalScope)) { 98 return false; 99 } 100 if (!firstScopeIndex) { 101 firstScopeIndex.emplace(*index); 102 } 103 return true; 104 } 105 106 uint32_t length() const { return vector.length(); } 107 108 const ScriptThingsStackVector& objects() const { return vector; } 109 110 AbstractScopePtr getScope(size_t index) const; 111 112 // Index of scope within CompilationStencil or Nothing is the scope is 113 // EmptyGlobalScopeType. 114 mozilla::Maybe<ScopeIndex> getScopeIndex(size_t index) const; 115 116 TaggedParserAtomIndex getAtom(size_t index) const; 117 118 AbstractScopePtr firstScope() const { 119 MOZ_ASSERT(firstScopeIndex.isSome()); 120 return getScope(*firstScopeIndex); 121 } 122 }; 123 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); 129 130 struct CGTryNoteList { 131 Vector<TryNote, 0> list; 132 explicit CGTryNoteList(FrontendContext* fc) : list(fc) {} 133 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()}; 138 } 139 size_t length() const { return list.length(); } 140 }; 141 142 struct CGScopeNoteList { 143 Vector<ScopeNote, 0> list; 144 explicit CGScopeNoteList(FrontendContext* fc) : list(fc) {} 145 146 [[nodiscard]] bool append(GCThingIndex scopeIndex, BytecodeOffset offset, 147 uint32_t parent); 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()}; 152 } 153 size_t length() const { return list.length(); } 154 155 private: 156 void recordEndImpl(uint32_t index, uint32_t offset); 157 }; 158 159 struct CGResumeOffsetList { 160 Vector<uint32_t, 0> list; 161 explicit CGResumeOffsetList(FrontendContext* fc) : list(fc) {} 162 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()}; 166 } 167 size_t length() const { return list.length(); } 168 }; 169 170 static constexpr size_t MaxBytecodeLength = INT32_MAX; 171 static constexpr size_t MaxSrcNotesLength = INT32_MAX; 172 173 // Have a few inline elements, so as to avoid heap allocation for tiny 174 // sequences. See bug 1390526. 175 using BytecodeVector = Vector<jsbytecode, 64>; 176 using SrcNotesVector = Vector<js::SrcNote, 64>; 177 178 // Bytecode and all data directly associated with specific opcode/index inside 179 // bytecode is stored in this class. 180 class BytecodeSection { 181 public: 182 BytecodeSection(FrontendContext* fc, uint32_t lineNum, 183 JS::LimitedColumnNumberOneOrigin column); 184 185 // ---- Bytecode ---- 186 187 BytecodeVector& code() { return code_; } 188 const BytecodeVector& code() const { return code_; } 189 190 jsbytecode* code(BytecodeOffset offset) { 191 return code_.begin() + offset.value(); 192 } 193 BytecodeOffset offset() const { 194 return BytecodeOffset(code_.end() - code_.begin()); 195 } 196 197 // ---- Source notes ---- 198 199 SrcNotesVector& notes() { return notes_; } 200 const SrcNotesVector& notes() const { return notes_; } 201 202 BytecodeOffset lastNoteOffset() const { return lastNoteOffset_; } 203 void setLastNoteOffset(BytecodeOffset offset) { lastNoteOffset_ = offset; } 204 205 // ---- Jump ---- 206 207 BytecodeOffset lastTargetOffset() const { return lastTarget_.offset; } 208 void setLastTargetOffset(BytecodeOffset offset) { 209 lastTarget_.offset = offset; 210 } 211 212 // ---- Stack ---- 213 214 int32_t stackDepth() const { return stackDepth_; } 215 void setStackDepth(int32_t depth) { stackDepth_ = depth; } 216 217 uint32_t maxStackDepth() const { return maxStackDepth_; } 218 219 void updateDepth(JSOp op, BytecodeOffset target); 220 221 // ---- Try notes ---- 222 223 CGTryNoteList& tryNoteList() { return tryNoteList_; }; 224 const CGTryNoteList& tryNoteList() const { return tryNoteList_; }; 225 226 // ---- Scope ---- 227 228 CGScopeNoteList& scopeNoteList() { return scopeNoteList_; }; 229 const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; }; 230 231 // ---- Generator ---- 232 233 CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; } 234 const CGResumeOffsetList& resumeOffsetList() const { 235 return resumeOffsetList_; 236 } 237 238 uint32_t numYields() const { return numYields_; } 239 void addNumYields() { numYields_++; } 240 241 // ---- Line and column ---- 242 243 uint32_t currentLine() const { return currentLine_; } 244 JS::LimitedColumnNumberOneOrigin lastColumn() const { return lastColumn_; } 245 void setCurrentLine(uint32_t line, uint32_t sourceOffset) { 246 currentLine_ = line; 247 lastColumn_ = JS::LimitedColumnNumberOneOrigin(); 248 lastSourceOffset_ = sourceOffset; 249 } 250 251 void setLastColumn(JS::LimitedColumnNumberOneOrigin column, uint32_t offset) { 252 lastColumn_ = column; 253 lastSourceOffset_ = offset; 254 } 255 256 void updateSeparatorPosition() { 257 lastSeparatorCodeOffset_ = code().length(); 258 lastSeparatorSourceOffset_ = lastSourceOffset_; 259 lastSeparatorLine_ = currentLine_; 260 lastSeparatorColumn_ = lastColumn_; 261 } 262 263 void updateSeparatorPositionIfPresent() { 264 if (lastSeparatorCodeOffset_ == code().length()) { 265 lastSeparatorSourceOffset_ = lastSourceOffset_; 266 lastSeparatorLine_ = currentLine_; 267 lastSeparatorColumn_ = lastColumn_; 268 } 269 } 270 271 bool isDuplicateLocation() const { 272 return lastSeparatorLine_ == currentLine_ && 273 lastSeparatorColumn_ == lastColumn_; 274 } 275 276 bool atSeparator(uint32_t offset) const { 277 return lastSeparatorSourceOffset_ == offset; 278 } 279 280 // ---- JIT ---- 281 282 uint32_t numICEntries() const { return numICEntries_; } 283 void incrementNumICEntries() { 284 MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow"); 285 numICEntries_++; 286 } 287 void setNumICEntries(uint32_t entries) { numICEntries_ = entries; } 288 289 private: 290 // ---- Bytecode ---- 291 292 // Bytecode. 293 BytecodeVector code_; 294 295 // ---- Source notes ---- 296 297 // Source notes 298 SrcNotesVector notes_; 299 300 // Code offset for last source note 301 BytecodeOffset lastNoteOffset_; 302 303 // ---- Jump ---- 304 305 // Last jump target emitted. 306 JumpTarget lastTarget_; 307 308 // ---- Stack ---- 309 310 // Maximum number of expression stack slots so far. 311 uint32_t maxStackDepth_ = 0; 312 313 // Current stack depth in script frame. 314 int32_t stackDepth_ = 0; 315 316 // ---- Try notes ---- 317 318 // List of emitted try notes. 319 CGTryNoteList tryNoteList_; 320 321 // ---- Scope ---- 322 323 // List of emitted block scope notes. 324 CGScopeNoteList scopeNoteList_; 325 326 // ---- Generator ---- 327 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_; 333 334 // Number of yield instructions emitted. Does not include JSOp::Await. 335 uint32_t numYields_ = 0; 336 337 // ---- Line and column ---- 338 339 // Line number for srcnotes. 340 // 341 // WARNING: If this becomes out of sync with already-emitted srcnotes, 342 // we can get undefined behavior. 343 uint32_t currentLine_; 344 345 // Column index in UTF-16 code units on currentLine_ of last 346 // SrcNoteType::ColSpan-annotated opcode. 347 // 348 // WARNING: If this becomes out of sync with already-emitted srcnotes, 349 // we can get undefined behavior. 350 JS::LimitedColumnNumberOneOrigin lastColumn_; 351 352 // The last code unit used for srcnotes. 353 uint32_t lastSourceOffset_ = 0; 354 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_; 361 362 // ---- JIT ---- 363 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; 367 }; 368 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 { 372 public: 373 PerScriptData(FrontendContext* fc, 374 frontend::CompilationState& compilationState); 375 376 [[nodiscard]] bool init(FrontendContext* fc); 377 378 GCThingList& gcThingList() { return gcThingList_; } 379 const GCThingList& gcThingList() const { return gcThingList_; } 380 381 PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; } 382 const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; } 383 384 private: 385 // List of emitted scopes/objects/bigints. 386 GCThingList gcThingList_; 387 388 // Map from atom to index. 389 PooledMapPtr<AtomIndexMap> atomIndices_; 390 }; 391 392 } /* namespace frontend */ 393 } /* namespace js */ 394 395 #endif /* frontend_BytecodeSection_h */