]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
81345200 | 2 | * Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014 Apple Inc. All rights reserved. |
9dae56ea A |
3 | * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
81345200 | 14 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
9dae56ea A |
15 | * its contributors may be used to endorse or promote products derived |
16 | * from this software without specific prior written permission. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | #include "config.h" | |
31 | #include "CodeBlock.h" | |
32 | ||
14957cd0 | 33 | #include "BytecodeGenerator.h" |
81345200 | 34 | #include "BytecodeUseDef.h" |
93a37866 | 35 | #include "CallLinkStatus.h" |
6fe7ccc8 | 36 | #include "DFGCapabilities.h" |
93a37866 | 37 | #include "DFGCommon.h" |
81345200 A |
38 | #include "DFGDriver.h" |
39 | #include "DFGJITCode.h" | |
40 | #include "DFGWorklist.h" | |
14957cd0 | 41 | #include "Debugger.h" |
9dae56ea | 42 | #include "Interpreter.h" |
14957cd0 | 43 | #include "JIT.h" |
6fe7ccc8 | 44 | #include "JITStubs.h" |
14957cd0 | 45 | #include "JSActivation.h" |
93a37866 | 46 | #include "JSCJSValue.h" |
f9bf01c6 | 47 | #include "JSFunction.h" |
93a37866 | 48 | #include "JSNameScope.h" |
81345200 | 49 | #include "LLIntEntrypoint.h" |
6fe7ccc8 | 50 | #include "LowLevelInterpreter.h" |
81345200 A |
51 | #include "JSCInlines.h" |
52 | #include "PolymorphicGetByIdList.h" | |
53 | #include "PolymorphicPutByIdList.h" | |
54 | #include "ProfilerDatabase.h" | |
93a37866 | 55 | #include "ReduceWhitespace.h" |
81345200 | 56 | #include "Repatch.h" |
14957cd0 | 57 | #include "RepatchBuffer.h" |
93a37866 | 58 | #include "SlotVisitorInlines.h" |
81345200 A |
59 | #include "UnlinkedInstructionStream.h" |
60 | #include <wtf/BagToHashMap.h> | |
93a37866 | 61 | #include <wtf/CommaPrinter.h> |
9dae56ea | 62 | #include <wtf/StringExtras.h> |
93a37866 | 63 | #include <wtf/StringPrintStream.h> |
9dae56ea | 64 | |
6fe7ccc8 A |
65 | #if ENABLE(DFG_JIT) |
66 | #include "DFGOperations.h" | |
67 | #endif | |
68 | ||
81345200 A |
69 | #if ENABLE(FTL_JIT) |
70 | #include "FTLJITCode.h" | |
71 | #endif | |
9dae56ea A |
72 | |
73 | namespace JSC { | |
74 | ||
81345200 | 75 | CString CodeBlock::inferredName() const |
9dae56ea | 76 | { |
93a37866 A |
77 | switch (codeType()) { |
78 | case GlobalCode: | |
79 | return "<global>"; | |
80 | case EvalCode: | |
81 | return "<eval>"; | |
82 | case FunctionCode: | |
81345200 | 83 | return jsCast<FunctionExecutable*>(ownerExecutable())->inferredName().utf8(); |
93a37866 A |
84 | default: |
85 | CRASH(); | |
81345200 | 86 | return CString("", 0); |
93a37866 A |
87 | } |
88 | } | |
89 | ||
81345200 A |
90 | bool CodeBlock::hasHash() const |
91 | { | |
92 | return !!m_hash; | |
93 | } | |
94 | ||
95 | bool CodeBlock::isSafeToComputeHash() const | |
96 | { | |
97 | return !isCompilationThread(); | |
98 | } | |
99 | ||
93a37866 A |
100 | CodeBlockHash CodeBlock::hash() const |
101 | { | |
81345200 A |
102 | if (!m_hash) { |
103 | RELEASE_ASSERT(isSafeToComputeHash()); | |
104 | m_hash = CodeBlockHash(ownerExecutable()->source(), specializationKind()); | |
105 | } | |
106 | return m_hash; | |
93a37866 A |
107 | } |
108 | ||
81345200 | 109 | CString CodeBlock::sourceCodeForTools() const |
93a37866 A |
110 | { |
111 | if (codeType() != FunctionCode) | |
81345200 | 112 | return ownerExecutable()->source().toUTF8(); |
93a37866 A |
113 | |
114 | SourceProvider* provider = source(); | |
115 | FunctionExecutable* executable = jsCast<FunctionExecutable*>(ownerExecutable()); | |
116 | UnlinkedFunctionExecutable* unlinked = executable->unlinkedExecutable(); | |
117 | unsigned unlinkedStartOffset = unlinked->startOffset(); | |
118 | unsigned linkedStartOffset = executable->source().startOffset(); | |
119 | int delta = linkedStartOffset - unlinkedStartOffset; | |
81345200 A |
120 | unsigned rangeStart = delta + unlinked->unlinkedFunctionNameStart(); |
121 | unsigned rangeEnd = delta + unlinked->startOffset() + unlinked->sourceLength(); | |
122 | return toCString( | |
123 | "function ", | |
124 | provider->source().impl()->utf8ForRange(rangeStart, rangeEnd - rangeStart)); | |
93a37866 A |
125 | } |
126 | ||
81345200 | 127 | CString CodeBlock::sourceCodeOnOneLine() const |
93a37866 A |
128 | { |
129 | return reduceWhitespace(sourceCodeForTools()); | |
130 | } | |
131 | ||
81345200 A |
132 | CString CodeBlock::hashAsStringIfPossible() const |
133 | { | |
134 | if (hasHash() || isSafeToComputeHash()) | |
135 | return toCString(hash()); | |
136 | return "<no-hash>"; | |
137 | } | |
138 | ||
93a37866 A |
139 | void CodeBlock::dumpAssumingJITType(PrintStream& out, JITCode::JITType jitType) const |
140 | { | |
81345200 A |
141 | out.print(inferredName(), "#", hashAsStringIfPossible()); |
142 | out.print(":[", RawPointer(this), "->"); | |
143 | if (!!m_alternative) | |
144 | out.print(RawPointer(m_alternative.get()), "->"); | |
145 | out.print(RawPointer(ownerExecutable()), ", ", jitType, codeType()); | |
146 | ||
93a37866 A |
147 | if (codeType() == FunctionCode) |
148 | out.print(specializationKind()); | |
81345200 A |
149 | out.print(", ", instructionCount()); |
150 | if (this->jitType() == JITCode::BaselineJIT && m_shouldAlwaysBeInlined) | |
151 | out.print(" (SABI)"); | |
152 | if (ownerExecutable()->neverInline()) | |
153 | out.print(" (NeverInline)"); | |
154 | if (ownerExecutable()->isStrictMode()) | |
155 | out.print(" (StrictMode)"); | |
156 | if (this->jitType() == JITCode::BaselineJIT && m_didFailFTLCompilation) | |
157 | out.print(" (FTLFail)"); | |
158 | if (this->jitType() == JITCode::BaselineJIT && m_hasBeenCompiledWithFTL) | |
159 | out.print(" (HadFTLReplacement)"); | |
93a37866 A |
160 | out.print("]"); |
161 | } | |
162 | ||
163 | void CodeBlock::dump(PrintStream& out) const | |
164 | { | |
81345200 | 165 | dumpAssumingJITType(out, jitType()); |
93a37866 A |
166 | } |
167 | ||
81345200 | 168 | static CString constantName(int k, JSValue value) |
93a37866 | 169 | { |
81345200 | 170 | return toCString(value, "(@k", k - FirstConstantRegisterIndex, ")"); |
9dae56ea A |
171 | } |
172 | ||
f9bf01c6 | 173 | static CString idName(int id0, const Identifier& ident) |
9dae56ea | 174 | { |
81345200 | 175 | return toCString(ident.impl(), "(@id", id0, ")"); |
9dae56ea A |
176 | } |
177 | ||
81345200 | 178 | CString CodeBlock::registerName(int r) const |
9dae56ea | 179 | { |
f9bf01c6 A |
180 | if (r == missingThisObjectMarker()) |
181 | return "<null>"; | |
182 | ||
183 | if (isConstantRegisterIndex(r)) | |
81345200 A |
184 | return constantName(r, getConstant(r)); |
185 | ||
186 | if (operandIsArgument(r)) { | |
187 | if (!VirtualRegister(r).toArgument()) | |
188 | return "this"; | |
189 | return toCString("arg", VirtualRegister(r).toArgument()); | |
190 | } | |
f9bf01c6 | 191 | |
81345200 | 192 | return toCString("loc", VirtualRegister(r).toLocal()); |
9dae56ea A |
193 | } |
194 | ||
81345200 | 195 | static CString regexpToSourceString(RegExp* regExp) |
9dae56ea | 196 | { |
f9bf01c6 A |
197 | char postfix[5] = { '/', 0, 0, 0, 0 }; |
198 | int index = 1; | |
9dae56ea | 199 | if (regExp->global()) |
f9bf01c6 | 200 | postfix[index++] = 'g'; |
9dae56ea | 201 | if (regExp->ignoreCase()) |
f9bf01c6 | 202 | postfix[index++] = 'i'; |
9dae56ea | 203 | if (regExp->multiline()) |
f9bf01c6 | 204 | postfix[index] = 'm'; |
9dae56ea | 205 | |
81345200 | 206 | return toCString("/", regExp->pattern().impl(), postfix); |
9dae56ea A |
207 | } |
208 | ||
209 | static CString regexpName(int re, RegExp* regexp) | |
210 | { | |
81345200 | 211 | return toCString(regexpToSourceString(regexp), "(@re", re, ")"); |
9dae56ea A |
212 | } |
213 | ||
214 | NEVER_INLINE static const char* debugHookName(int debugHookID) | |
215 | { | |
216 | switch (static_cast<DebugHookID>(debugHookID)) { | |
217 | case DidEnterCallFrame: | |
218 | return "didEnterCallFrame"; | |
219 | case WillLeaveCallFrame: | |
220 | return "willLeaveCallFrame"; | |
221 | case WillExecuteStatement: | |
222 | return "willExecuteStatement"; | |
223 | case WillExecuteProgram: | |
224 | return "willExecuteProgram"; | |
225 | case DidExecuteProgram: | |
226 | return "didExecuteProgram"; | |
227 | case DidReachBreakpoint: | |
228 | return "didReachBreakpoint"; | |
229 | } | |
230 | ||
93a37866 | 231 | RELEASE_ASSERT_NOT_REACHED(); |
9dae56ea A |
232 | return ""; |
233 | } | |
234 | ||
93a37866 | 235 | void CodeBlock::printUnaryOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op) |
9dae56ea A |
236 | { |
237 | int r0 = (++it)->u.operand; | |
238 | int r1 = (++it)->u.operand; | |
239 | ||
81345200 A |
240 | printLocationAndOp(out, exec, location, it, op); |
241 | out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); | |
9dae56ea A |
242 | } |
243 | ||
93a37866 | 244 | void CodeBlock::printBinaryOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op) |
9dae56ea A |
245 | { |
246 | int r0 = (++it)->u.operand; | |
247 | int r1 = (++it)->u.operand; | |
248 | int r2 = (++it)->u.operand; | |
81345200 A |
249 | printLocationAndOp(out, exec, location, it, op); |
250 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
9dae56ea A |
251 | } |
252 | ||
93a37866 | 253 | void CodeBlock::printConditionalJump(PrintStream& out, ExecState* exec, const Instruction*, const Instruction*& it, int location, const char* op) |
9dae56ea A |
254 | { |
255 | int r0 = (++it)->u.operand; | |
256 | int offset = (++it)->u.operand; | |
81345200 A |
257 | printLocationAndOp(out, exec, location, it, op); |
258 | out.printf("%s, %d(->%d)", registerName(r0).data(), offset, location + offset); | |
9dae56ea A |
259 | } |
260 | ||
93a37866 | 261 | void CodeBlock::printGetByIdOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it) |
9dae56ea | 262 | { |
93a37866 A |
263 | const char* op; |
264 | switch (exec->interpreter()->getOpcodeID(it->u.opcode)) { | |
265 | case op_get_by_id: | |
266 | op = "get_by_id"; | |
267 | break; | |
268 | case op_get_by_id_out_of_line: | |
269 | op = "get_by_id_out_of_line"; | |
270 | break; | |
93a37866 A |
271 | case op_get_array_length: |
272 | op = "array_length"; | |
273 | break; | |
93a37866 A |
274 | default: |
275 | RELEASE_ASSERT_NOT_REACHED(); | |
276 | op = 0; | |
277 | } | |
9dae56ea A |
278 | int r0 = (++it)->u.operand; |
279 | int r1 = (++it)->u.operand; | |
280 | int id0 = (++it)->u.operand; | |
81345200 A |
281 | printLocationAndOp(out, exec, location, it, op); |
282 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data()); | |
93a37866 | 283 | it += 4; // Increment up to the value profiler. |
6fe7ccc8 A |
284 | } |
285 | ||
81345200 | 286 | static void dumpStructure(PrintStream& out, const char* name, ExecState* exec, Structure* structure, const Identifier& ident) |
6fe7ccc8 | 287 | { |
93a37866 A |
288 | if (!structure) |
289 | return; | |
290 | ||
291 | out.printf("%s = %p", name, structure); | |
292 | ||
81345200 | 293 | PropertyOffset offset = structure->getConcurrently(exec->vm(), ident.impl()); |
93a37866 A |
294 | if (offset != invalidOffset) |
295 | out.printf(" (offset = %d)", offset); | |
9dae56ea A |
296 | } |
297 | ||
93a37866 | 298 | #if ENABLE(JIT) // unused when not ENABLE(JIT), leading to silly warnings |
81345200 | 299 | static void dumpChain(PrintStream& out, ExecState* exec, StructureChain* chain, const Identifier& ident) |
9dae56ea | 300 | { |
93a37866 A |
301 | out.printf("chain = %p: [", chain); |
302 | bool first = true; | |
303 | for (WriteBarrier<Structure>* currentStructure = chain->head(); | |
304 | *currentStructure; | |
305 | ++currentStructure) { | |
306 | if (first) | |
307 | first = false; | |
308 | else | |
309 | out.printf(", "); | |
310 | dumpStructure(out, "struct", exec, currentStructure->get(), ident); | |
311 | } | |
312 | out.printf("]"); | |
9dae56ea | 313 | } |
93a37866 | 314 | #endif |
9dae56ea | 315 | |
81345200 | 316 | void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int location, const StubInfoMap& map) |
9dae56ea | 317 | { |
93a37866 | 318 | Instruction* instruction = instructions().begin() + location; |
9dae56ea | 319 | |
81345200 | 320 | const Identifier& ident = identifier(instruction[3].u.operand); |
93a37866 A |
321 | |
322 | UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations. | |
323 | ||
93a37866 A |
324 | if (exec->interpreter()->getOpcodeID(instruction[0].u.opcode) == op_get_array_length) |
325 | out.printf(" llint(array_length)"); | |
326 | else if (Structure* structure = instruction[4].u.structure.get()) { | |
327 | out.printf(" llint("); | |
328 | dumpStructure(out, "struct", exec, structure, ident); | |
329 | out.printf(")"); | |
9dae56ea | 330 | } |
9dae56ea | 331 | |
93a37866 | 332 | #if ENABLE(JIT) |
81345200 A |
333 | if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) { |
334 | StructureStubInfo& stubInfo = *stubPtr; | |
335 | if (stubInfo.resetByGC) | |
336 | out.print(" (Reset By GC)"); | |
337 | ||
93a37866 A |
338 | if (stubInfo.seen) { |
339 | out.printf(" jit("); | |
340 | ||
341 | Structure* baseStructure = 0; | |
342 | Structure* prototypeStructure = 0; | |
343 | StructureChain* chain = 0; | |
81345200 | 344 | PolymorphicGetByIdList* list = 0; |
93a37866 A |
345 | |
346 | switch (stubInfo.accessType) { | |
347 | case access_get_by_id_self: | |
348 | out.printf("self"); | |
349 | baseStructure = stubInfo.u.getByIdSelf.baseObjectStructure.get(); | |
350 | break; | |
93a37866 A |
351 | case access_get_by_id_chain: |
352 | out.printf("chain"); | |
353 | baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get(); | |
354 | chain = stubInfo.u.getByIdChain.chain.get(); | |
355 | break; | |
81345200 A |
356 | case access_get_by_id_list: |
357 | out.printf("list"); | |
358 | list = stubInfo.u.getByIdList.list; | |
93a37866 A |
359 | break; |
360 | case access_unset: | |
361 | out.printf("unset"); | |
362 | break; | |
93a37866 A |
363 | default: |
364 | RELEASE_ASSERT_NOT_REACHED(); | |
365 | break; | |
366 | } | |
367 | ||
368 | if (baseStructure) { | |
369 | out.printf(", "); | |
370 | dumpStructure(out, "struct", exec, baseStructure, ident); | |
371 | } | |
372 | ||
373 | if (prototypeStructure) { | |
374 | out.printf(", "); | |
375 | dumpStructure(out, "prototypeStruct", exec, baseStructure, ident); | |
376 | } | |
377 | ||
378 | if (chain) { | |
379 | out.printf(", "); | |
380 | dumpChain(out, exec, chain, ident); | |
381 | } | |
382 | ||
81345200 A |
383 | if (list) { |
384 | out.printf(", list = %p: [", list); | |
385 | for (unsigned i = 0; i < list->size(); ++i) { | |
93a37866 A |
386 | if (i) |
387 | out.printf(", "); | |
388 | out.printf("("); | |
81345200 A |
389 | dumpStructure(out, "base", exec, list->at(i).structure(), ident); |
390 | if (list->at(i).chain()) { | |
391 | out.printf(", "); | |
392 | dumpChain(out, exec, list->at(i).chain(), ident); | |
93a37866 A |
393 | } |
394 | out.printf(")"); | |
395 | } | |
396 | out.printf("]"); | |
397 | } | |
398 | out.printf(")"); | |
9dae56ea | 399 | } |
9dae56ea | 400 | } |
81345200 A |
401 | #else |
402 | UNUSED_PARAM(map); | |
93a37866 | 403 | #endif |
9dae56ea A |
404 | } |
405 | ||
81345200 | 406 | void CodeBlock::printCallOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap& map) |
9dae56ea | 407 | { |
81345200 | 408 | int dst = (++it)->u.operand; |
93a37866 A |
409 | int func = (++it)->u.operand; |
410 | int argCount = (++it)->u.operand; | |
411 | int registerOffset = (++it)->u.operand; | |
81345200 A |
412 | printLocationAndOp(out, exec, location, it, op); |
413 | out.printf("%s, %s, %d, %d", registerName(dst).data(), registerName(func).data(), argCount, registerOffset); | |
93a37866 | 414 | if (cacheDumpMode == DumpCaches) { |
93a37866 A |
415 | LLIntCallLinkInfo* callLinkInfo = it[1].u.callLinkInfo; |
416 | if (callLinkInfo->lastSeenCallee) { | |
417 | out.printf( | |
418 | " llint(%p, exec %p)", | |
419 | callLinkInfo->lastSeenCallee.get(), | |
420 | callLinkInfo->lastSeenCallee->executable()); | |
421 | } | |
93a37866 | 422 | #if ENABLE(JIT) |
81345200 A |
423 | if (CallLinkInfo* info = map.get(CodeOrigin(location))) { |
424 | JSFunction* target = info->lastSeenCallee.get(); | |
93a37866 A |
425 | if (target) |
426 | out.printf(" jit(%p, exec %p)", target, target->executable()); | |
427 | } | |
81345200 A |
428 | out.print(" status(", CallLinkStatus::computeFor(this, location, map), ")"); |
429 | #else | |
430 | UNUSED_PARAM(map); | |
93a37866 | 431 | #endif |
93a37866 | 432 | } |
81345200 A |
433 | ++it; |
434 | ++it; | |
435 | dumpArrayProfiling(out, it, hasPrintedProfiling); | |
436 | dumpValueProfiling(out, it, hasPrintedProfiling); | |
9dae56ea A |
437 | } |
438 | ||
93a37866 | 439 | void CodeBlock::printPutByIdOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op) |
9dae56ea | 440 | { |
93a37866 A |
441 | int r0 = (++it)->u.operand; |
442 | int id0 = (++it)->u.operand; | |
443 | int r1 = (++it)->u.operand; | |
81345200 A |
444 | printLocationAndOp(out, exec, location, it, op); |
445 | out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data()); | |
93a37866 | 446 | it += 5; |
9dae56ea | 447 | } |
9dae56ea | 448 | |
93a37866 | 449 | void CodeBlock::dumpBytecode(PrintStream& out) |
9dae56ea | 450 | { |
93a37866 A |
451 | // We only use the ExecState* for things that don't actually lead to JS execution, |
452 | // like converting a JSString to a String. Hence the globalExec is appropriate. | |
453 | ExecState* exec = m_globalObject->globalExec(); | |
454 | ||
9dae56ea A |
455 | size_t instructionCount = 0; |
456 | ||
6fe7ccc8 | 457 | for (size_t i = 0; i < instructions().size(); i += opcodeLengths[exec->interpreter()->getOpcodeID(instructions()[i].u.opcode)]) |
9dae56ea A |
458 | ++instructionCount; |
459 | ||
93a37866 A |
460 | out.print(*this); |
461 | out.printf( | |
462 | ": %lu m_instructions; %lu bytes; %d parameter(s); %d callee register(s); %d variable(s)", | |
6fe7ccc8 A |
463 | static_cast<unsigned long>(instructions().size()), |
464 | static_cast<unsigned long>(instructions().size() * sizeof(Instruction)), | |
93a37866 A |
465 | m_numParameters, m_numCalleeRegisters, m_numVars); |
466 | if (symbolTable() && symbolTable()->captureCount()) { | |
467 | out.printf( | |
468 | "; %d captured var(s) (from r%d to r%d, inclusive)", | |
81345200 | 469 | symbolTable()->captureCount(), symbolTable()->captureStart(), symbolTable()->captureEnd() + 1); |
93a37866 A |
470 | } |
471 | if (usesArguments()) { | |
472 | out.printf( | |
473 | "; uses arguments, in r%d, r%d", | |
81345200 A |
474 | argumentsRegister().offset(), |
475 | unmodifiedArgumentsRegister(argumentsRegister()).offset()); | |
93a37866 | 476 | } |
81345200 A |
477 | if (needsActivation() && codeType() == FunctionCode) |
478 | out.printf("; activation in r%d", activationRegister().offset()); | |
479 | out.printf("\n"); | |
480 | ||
481 | StubInfoMap stubInfos; | |
482 | CallLinkInfoMap callLinkInfos; | |
483 | getStubInfoMap(stubInfos); | |
484 | getCallLinkInfoMap(callLinkInfos); | |
485 | ||
93a37866 A |
486 | const Instruction* begin = instructions().begin(); |
487 | const Instruction* end = instructions().end(); | |
488 | for (const Instruction* it = begin; it != end; ++it) | |
81345200 A |
489 | dumpBytecode(out, exec, begin, it, stubInfos, callLinkInfos); |
490 | ||
491 | if (numberOfIdentifiers()) { | |
93a37866 | 492 | out.printf("\nIdentifiers:\n"); |
9dae56ea A |
493 | size_t i = 0; |
494 | do { | |
81345200 | 495 | out.printf(" id%u = %s\n", static_cast<unsigned>(i), identifier(i).string().utf8().data()); |
9dae56ea | 496 | ++i; |
81345200 | 497 | } while (i != numberOfIdentifiers()); |
9dae56ea A |
498 | } |
499 | ||
500 | if (!m_constantRegisters.isEmpty()) { | |
93a37866 | 501 | out.printf("\nConstants:\n"); |
9dae56ea A |
502 | size_t i = 0; |
503 | do { | |
81345200 | 504 | out.printf(" k%u = %s\n", static_cast<unsigned>(i), toCString(m_constantRegisters[i].get()).data()); |
9dae56ea | 505 | ++i; |
9dae56ea A |
506 | } while (i < m_constantRegisters.size()); |
507 | } | |
508 | ||
93a37866 A |
509 | if (size_t count = m_unlinkedCode->numberOfRegExps()) { |
510 | out.printf("\nm_regexps:\n"); | |
9dae56ea A |
511 | size_t i = 0; |
512 | do { | |
81345200 | 513 | out.printf(" re%u = %s\n", static_cast<unsigned>(i), regexpToSourceString(m_unlinkedCode->regexp(i)).data()); |
9dae56ea | 514 | ++i; |
93a37866 | 515 | } while (i < count); |
9dae56ea A |
516 | } |
517 | ||
9dae56ea | 518 | if (m_rareData && !m_rareData->m_exceptionHandlers.isEmpty()) { |
93a37866 | 519 | out.printf("\nException Handlers:\n"); |
9dae56ea A |
520 | unsigned i = 0; |
521 | do { | |
93a37866 | 522 | out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] depth: [%4d] }\n", i + 1, m_rareData->m_exceptionHandlers[i].start, m_rareData->m_exceptionHandlers[i].end, m_rareData->m_exceptionHandlers[i].target, m_rareData->m_exceptionHandlers[i].scopeDepth); |
9dae56ea A |
523 | ++i; |
524 | } while (i < m_rareData->m_exceptionHandlers.size()); | |
525 | } | |
526 | ||
81345200 A |
527 | if (m_rareData && !m_rareData->m_switchJumpTables.isEmpty()) { |
528 | out.printf("Switch Jump Tables:\n"); | |
9dae56ea A |
529 | unsigned i = 0; |
530 | do { | |
93a37866 | 531 | out.printf(" %1d = {\n", i); |
9dae56ea | 532 | int entry = 0; |
81345200 A |
533 | Vector<int32_t>::const_iterator end = m_rareData->m_switchJumpTables[i].branchOffsets.end(); |
534 | for (Vector<int32_t>::const_iterator iter = m_rareData->m_switchJumpTables[i].branchOffsets.begin(); iter != end; ++iter, ++entry) { | |
9dae56ea A |
535 | if (!*iter) |
536 | continue; | |
81345200 | 537 | out.printf("\t\t%4d => %04d\n", entry + m_rareData->m_switchJumpTables[i].min, *iter); |
93a37866 A |
538 | } |
539 | out.printf(" }\n"); | |
9dae56ea | 540 | ++i; |
81345200 | 541 | } while (i < m_rareData->m_switchJumpTables.size()); |
9dae56ea A |
542 | } |
543 | ||
544 | if (m_rareData && !m_rareData->m_stringSwitchJumpTables.isEmpty()) { | |
93a37866 | 545 | out.printf("\nString Switch Jump Tables:\n"); |
9dae56ea A |
546 | unsigned i = 0; |
547 | do { | |
93a37866 | 548 | out.printf(" %1d = {\n", i); |
9dae56ea A |
549 | StringJumpTable::StringOffsetTable::const_iterator end = m_rareData->m_stringSwitchJumpTables[i].offsetTable.end(); |
550 | for (StringJumpTable::StringOffsetTable::const_iterator iter = m_rareData->m_stringSwitchJumpTables[i].offsetTable.begin(); iter != end; ++iter) | |
81345200 | 551 | out.printf("\t\t\"%s\" => %04d\n", iter->key->utf8().data(), iter->value.branchOffset); |
93a37866 | 552 | out.printf(" }\n"); |
9dae56ea A |
553 | ++i; |
554 | } while (i < m_rareData->m_stringSwitchJumpTables.size()); | |
555 | } | |
556 | ||
93a37866 A |
557 | out.printf("\n"); |
558 | } | |
559 | ||
560 | void CodeBlock::beginDumpProfiling(PrintStream& out, bool& hasPrintedProfiling) | |
561 | { | |
562 | if (hasPrintedProfiling) { | |
563 | out.print("; "); | |
564 | return; | |
565 | } | |
566 | ||
567 | out.print(" "); | |
568 | hasPrintedProfiling = true; | |
569 | } | |
570 | ||
571 | void CodeBlock::dumpValueProfiling(PrintStream& out, const Instruction*& it, bool& hasPrintedProfiling) | |
572 | { | |
81345200 A |
573 | ConcurrentJITLocker locker(m_lock); |
574 | ||
93a37866 | 575 | ++it; |
81345200 | 576 | CString description = it->u.profile->briefDescription(locker); |
93a37866 A |
577 | if (!description.length()) |
578 | return; | |
579 | beginDumpProfiling(out, hasPrintedProfiling); | |
580 | out.print(description); | |
93a37866 A |
581 | } |
582 | ||
583 | void CodeBlock::dumpArrayProfiling(PrintStream& out, const Instruction*& it, bool& hasPrintedProfiling) | |
584 | { | |
81345200 A |
585 | ConcurrentJITLocker locker(m_lock); |
586 | ||
93a37866 | 587 | ++it; |
81345200 A |
588 | if (!it->u.arrayProfile) |
589 | return; | |
590 | CString description = it->u.arrayProfile->briefDescription(locker, this); | |
93a37866 A |
591 | if (!description.length()) |
592 | return; | |
593 | beginDumpProfiling(out, hasPrintedProfiling); | |
594 | out.print(description); | |
93a37866 A |
595 | } |
596 | ||
93a37866 A |
597 | void CodeBlock::dumpRareCaseProfile(PrintStream& out, const char* name, RareCaseProfile* profile, bool& hasPrintedProfiling) |
598 | { | |
599 | if (!profile || !profile->m_counter) | |
600 | return; | |
601 | ||
602 | beginDumpProfiling(out, hasPrintedProfiling); | |
603 | out.print(name, profile->m_counter); | |
9dae56ea A |
604 | } |
605 | ||
81345200 A |
606 | void CodeBlock::printLocationAndOp(PrintStream& out, ExecState*, int location, const Instruction*&, const char* op) |
607 | { | |
608 | out.printf("[%4d] %-17s ", location, op); | |
609 | } | |
610 | ||
611 | void CodeBlock::printLocationOpAndRegisterOperand(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, int operand) | |
612 | { | |
613 | printLocationAndOp(out, exec, location, it, op); | |
614 | out.printf("%s", registerName(operand).data()); | |
615 | } | |
616 | ||
617 | void CodeBlock::dumpBytecode( | |
618 | PrintStream& out, ExecState* exec, const Instruction* begin, const Instruction*& it, | |
619 | const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos) | |
9dae56ea A |
620 | { |
621 | int location = it - begin; | |
93a37866 | 622 | bool hasPrintedProfiling = false; |
81345200 A |
623 | OpcodeID opcode = exec->interpreter()->getOpcodeID(it->u.opcode); |
624 | switch (opcode) { | |
9dae56ea | 625 | case op_enter: { |
81345200 A |
626 | printLocationAndOp(out, exec, location, it, "enter"); |
627 | break; | |
628 | } | |
629 | case op_touch_entry: { | |
630 | printLocationAndOp(out, exec, location, it, "touch_entry"); | |
9dae56ea A |
631 | break; |
632 | } | |
14957cd0 | 633 | case op_create_activation: { |
9dae56ea | 634 | int r0 = (++it)->u.operand; |
81345200 | 635 | printLocationOpAndRegisterOperand(out, exec, location, it, "create_activation", r0); |
9dae56ea A |
636 | break; |
637 | } | |
638 | case op_create_arguments: { | |
14957cd0 | 639 | int r0 = (++it)->u.operand; |
81345200 | 640 | printLocationOpAndRegisterOperand(out, exec, location, it, "create_arguments", r0); |
9dae56ea A |
641 | break; |
642 | } | |
14957cd0 A |
643 | case op_init_lazy_reg: { |
644 | int r0 = (++it)->u.operand; | |
81345200 | 645 | printLocationOpAndRegisterOperand(out, exec, location, it, "init_lazy_reg", r0); |
14957cd0 A |
646 | break; |
647 | } | |
648 | case op_get_callee: { | |
649 | int r0 = (++it)->u.operand; | |
81345200 | 650 | printLocationOpAndRegisterOperand(out, exec, location, it, "get_callee", r0); |
93a37866 | 651 | ++it; |
14957cd0 A |
652 | break; |
653 | } | |
654 | case op_create_this: { | |
655 | int r0 = (++it)->u.operand; | |
656 | int r1 = (++it)->u.operand; | |
93a37866 | 657 | unsigned inferredInlineCapacity = (++it)->u.operand; |
81345200 A |
658 | printLocationAndOp(out, exec, location, it, "create_this"); |
659 | out.printf("%s, %s, %u", registerName(r0).data(), registerName(r1).data(), inferredInlineCapacity); | |
9dae56ea A |
660 | break; |
661 | } | |
81345200 | 662 | case op_to_this: { |
9dae56ea | 663 | int r0 = (++it)->u.operand; |
81345200 A |
664 | printLocationOpAndRegisterOperand(out, exec, location, it, "to_this", r0); |
665 | Structure* structure = (++it)->u.structure.get(); | |
666 | if (structure) | |
667 | out.print(" cache(struct = ", RawPointer(structure), ")"); | |
14957cd0 A |
668 | break; |
669 | } | |
9dae56ea A |
670 | case op_new_object: { |
671 | int r0 = (++it)->u.operand; | |
93a37866 | 672 | unsigned inferredInlineCapacity = (++it)->u.operand; |
81345200 A |
673 | printLocationAndOp(out, exec, location, it, "new_object"); |
674 | out.printf("%s, %u", registerName(r0).data(), inferredInlineCapacity); | |
93a37866 | 675 | ++it; // Skip object allocation profile. |
9dae56ea A |
676 | break; |
677 | } | |
678 | case op_new_array: { | |
679 | int dst = (++it)->u.operand; | |
680 | int argv = (++it)->u.operand; | |
681 | int argc = (++it)->u.operand; | |
81345200 A |
682 | printLocationAndOp(out, exec, location, it, "new_array"); |
683 | out.printf("%s, %s, %d", registerName(dst).data(), registerName(argv).data(), argc); | |
93a37866 A |
684 | ++it; // Skip array allocation profile. |
685 | break; | |
686 | } | |
687 | case op_new_array_with_size: { | |
688 | int dst = (++it)->u.operand; | |
689 | int length = (++it)->u.operand; | |
81345200 A |
690 | printLocationAndOp(out, exec, location, it, "new_array_with_size"); |
691 | out.printf("%s, %s", registerName(dst).data(), registerName(length).data()); | |
93a37866 | 692 | ++it; // Skip array allocation profile. |
9dae56ea A |
693 | break; |
694 | } | |
14957cd0 A |
695 | case op_new_array_buffer: { |
696 | int dst = (++it)->u.operand; | |
697 | int argv = (++it)->u.operand; | |
698 | int argc = (++it)->u.operand; | |
81345200 A |
699 | printLocationAndOp(out, exec, location, it, "new_array_buffer"); |
700 | out.printf("%s, %d, %d", registerName(dst).data(), argv, argc); | |
93a37866 | 701 | ++it; // Skip array allocation profile. |
14957cd0 A |
702 | break; |
703 | } | |
9dae56ea A |
704 | case op_new_regexp: { |
705 | int r0 = (++it)->u.operand; | |
706 | int re0 = (++it)->u.operand; | |
81345200 A |
707 | printLocationAndOp(out, exec, location, it, "new_regexp"); |
708 | out.printf("%s, ", registerName(r0).data()); | |
93a37866 A |
709 | if (r0 >=0 && r0 < (int)m_unlinkedCode->numberOfRegExps()) |
710 | out.printf("%s", regexpName(re0, regexp(re0)).data()); | |
6fe7ccc8 | 711 | else |
93a37866 | 712 | out.printf("bad_regexp(%d)", re0); |
9dae56ea A |
713 | break; |
714 | } | |
715 | case op_mov: { | |
716 | int r0 = (++it)->u.operand; | |
717 | int r1 = (++it)->u.operand; | |
81345200 A |
718 | printLocationAndOp(out, exec, location, it, "mov"); |
719 | out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); | |
720 | break; | |
721 | } | |
722 | case op_captured_mov: { | |
723 | int r0 = (++it)->u.operand; | |
724 | int r1 = (++it)->u.operand; | |
725 | printLocationAndOp(out, exec, location, it, "captured_mov"); | |
726 | out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); | |
727 | ++it; | |
9dae56ea A |
728 | break; |
729 | } | |
730 | case op_not: { | |
93a37866 | 731 | printUnaryOp(out, exec, location, it, "not"); |
9dae56ea A |
732 | break; |
733 | } | |
734 | case op_eq: { | |
93a37866 | 735 | printBinaryOp(out, exec, location, it, "eq"); |
9dae56ea A |
736 | break; |
737 | } | |
738 | case op_eq_null: { | |
93a37866 | 739 | printUnaryOp(out, exec, location, it, "eq_null"); |
9dae56ea A |
740 | break; |
741 | } | |
742 | case op_neq: { | |
93a37866 | 743 | printBinaryOp(out, exec, location, it, "neq"); |
9dae56ea A |
744 | break; |
745 | } | |
746 | case op_neq_null: { | |
93a37866 | 747 | printUnaryOp(out, exec, location, it, "neq_null"); |
9dae56ea A |
748 | break; |
749 | } | |
750 | case op_stricteq: { | |
93a37866 | 751 | printBinaryOp(out, exec, location, it, "stricteq"); |
9dae56ea A |
752 | break; |
753 | } | |
754 | case op_nstricteq: { | |
93a37866 | 755 | printBinaryOp(out, exec, location, it, "nstricteq"); |
9dae56ea A |
756 | break; |
757 | } | |
758 | case op_less: { | |
93a37866 | 759 | printBinaryOp(out, exec, location, it, "less"); |
9dae56ea A |
760 | break; |
761 | } | |
762 | case op_lesseq: { | |
93a37866 | 763 | printBinaryOp(out, exec, location, it, "lesseq"); |
9dae56ea A |
764 | break; |
765 | } | |
6fe7ccc8 | 766 | case op_greater: { |
93a37866 | 767 | printBinaryOp(out, exec, location, it, "greater"); |
6fe7ccc8 A |
768 | break; |
769 | } | |
770 | case op_greatereq: { | |
93a37866 | 771 | printBinaryOp(out, exec, location, it, "greatereq"); |
6fe7ccc8 A |
772 | break; |
773 | } | |
93a37866 | 774 | case op_inc: { |
9dae56ea | 775 | int r0 = (++it)->u.operand; |
81345200 | 776 | printLocationOpAndRegisterOperand(out, exec, location, it, "inc", r0); |
9dae56ea A |
777 | break; |
778 | } | |
93a37866 | 779 | case op_dec: { |
9dae56ea | 780 | int r0 = (++it)->u.operand; |
81345200 | 781 | printLocationOpAndRegisterOperand(out, exec, location, it, "dec", r0); |
9dae56ea A |
782 | break; |
783 | } | |
93a37866 A |
784 | case op_to_number: { |
785 | printUnaryOp(out, exec, location, it, "to_number"); | |
9dae56ea A |
786 | break; |
787 | } | |
788 | case op_negate: { | |
93a37866 | 789 | printUnaryOp(out, exec, location, it, "negate"); |
9dae56ea A |
790 | break; |
791 | } | |
792 | case op_add: { | |
93a37866 | 793 | printBinaryOp(out, exec, location, it, "add"); |
9dae56ea A |
794 | ++it; |
795 | break; | |
796 | } | |
797 | case op_mul: { | |
93a37866 | 798 | printBinaryOp(out, exec, location, it, "mul"); |
9dae56ea A |
799 | ++it; |
800 | break; | |
801 | } | |
802 | case op_div: { | |
93a37866 | 803 | printBinaryOp(out, exec, location, it, "div"); |
ba379fdc | 804 | ++it; |
9dae56ea A |
805 | break; |
806 | } | |
807 | case op_mod: { | |
93a37866 | 808 | printBinaryOp(out, exec, location, it, "mod"); |
9dae56ea A |
809 | break; |
810 | } | |
811 | case op_sub: { | |
93a37866 | 812 | printBinaryOp(out, exec, location, it, "sub"); |
9dae56ea A |
813 | ++it; |
814 | break; | |
815 | } | |
816 | case op_lshift: { | |
93a37866 | 817 | printBinaryOp(out, exec, location, it, "lshift"); |
9dae56ea A |
818 | break; |
819 | } | |
820 | case op_rshift: { | |
93a37866 | 821 | printBinaryOp(out, exec, location, it, "rshift"); |
9dae56ea A |
822 | break; |
823 | } | |
824 | case op_urshift: { | |
93a37866 | 825 | printBinaryOp(out, exec, location, it, "urshift"); |
9dae56ea A |
826 | break; |
827 | } | |
828 | case op_bitand: { | |
93a37866 | 829 | printBinaryOp(out, exec, location, it, "bitand"); |
9dae56ea A |
830 | ++it; |
831 | break; | |
832 | } | |
833 | case op_bitxor: { | |
93a37866 | 834 | printBinaryOp(out, exec, location, it, "bitxor"); |
9dae56ea A |
835 | ++it; |
836 | break; | |
837 | } | |
838 | case op_bitor: { | |
93a37866 | 839 | printBinaryOp(out, exec, location, it, "bitor"); |
9dae56ea A |
840 | ++it; |
841 | break; | |
842 | } | |
14957cd0 | 843 | case op_check_has_instance: { |
93a37866 A |
844 | int r0 = (++it)->u.operand; |
845 | int r1 = (++it)->u.operand; | |
846 | int r2 = (++it)->u.operand; | |
847 | int offset = (++it)->u.operand; | |
81345200 A |
848 | printLocationAndOp(out, exec, location, it, "check_has_instance"); |
849 | out.printf("%s, %s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), offset, location + offset); | |
14957cd0 A |
850 | break; |
851 | } | |
9dae56ea A |
852 | case op_instanceof: { |
853 | int r0 = (++it)->u.operand; | |
854 | int r1 = (++it)->u.operand; | |
855 | int r2 = (++it)->u.operand; | |
81345200 A |
856 | printLocationAndOp(out, exec, location, it, "instanceof"); |
857 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
858 | break; | |
859 | } | |
860 | case op_unsigned: { | |
861 | printUnaryOp(out, exec, location, it, "unsigned"); | |
9dae56ea A |
862 | break; |
863 | } | |
864 | case op_typeof: { | |
93a37866 | 865 | printUnaryOp(out, exec, location, it, "typeof"); |
9dae56ea A |
866 | break; |
867 | } | |
868 | case op_is_undefined: { | |
93a37866 | 869 | printUnaryOp(out, exec, location, it, "is_undefined"); |
9dae56ea A |
870 | break; |
871 | } | |
872 | case op_is_boolean: { | |
93a37866 | 873 | printUnaryOp(out, exec, location, it, "is_boolean"); |
9dae56ea A |
874 | break; |
875 | } | |
876 | case op_is_number: { | |
93a37866 | 877 | printUnaryOp(out, exec, location, it, "is_number"); |
9dae56ea A |
878 | break; |
879 | } | |
880 | case op_is_string: { | |
93a37866 | 881 | printUnaryOp(out, exec, location, it, "is_string"); |
9dae56ea A |
882 | break; |
883 | } | |
884 | case op_is_object: { | |
93a37866 | 885 | printUnaryOp(out, exec, location, it, "is_object"); |
9dae56ea A |
886 | break; |
887 | } | |
888 | case op_is_function: { | |
93a37866 | 889 | printUnaryOp(out, exec, location, it, "is_function"); |
9dae56ea A |
890 | break; |
891 | } | |
892 | case op_in: { | |
93a37866 | 893 | printBinaryOp(out, exec, location, it, "in"); |
9dae56ea A |
894 | break; |
895 | } | |
93a37866 | 896 | case op_init_global_const_nop: { |
81345200 | 897 | printLocationAndOp(out, exec, location, it, "init_global_const_nop"); |
93a37866 A |
898 | it++; |
899 | it++; | |
900 | it++; | |
6fe7ccc8 | 901 | it++; |
9dae56ea A |
902 | break; |
903 | } | |
93a37866 A |
904 | case op_init_global_const: { |
905 | WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer; | |
9dae56ea | 906 | int r0 = (++it)->u.operand; |
81345200 A |
907 | printLocationAndOp(out, exec, location, it, "init_global_const"); |
908 | out.printf("g%d(%p), %s", m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(r0).data()); | |
93a37866 | 909 | it++; |
6fe7ccc8 | 910 | it++; |
14957cd0 A |
911 | break; |
912 | } | |
93a37866 A |
913 | case op_get_by_id: |
914 | case op_get_by_id_out_of_line: | |
81345200 | 915 | case op_get_array_length: { |
93a37866 | 916 | printGetByIdOp(out, exec, location, it); |
81345200 | 917 | printGetByIdCacheStatus(out, exec, location, stubInfos); |
93a37866 | 918 | dumpValueProfiling(out, it, hasPrintedProfiling); |
9dae56ea A |
919 | break; |
920 | } | |
14957cd0 | 921 | case op_get_arguments_length: { |
93a37866 | 922 | printUnaryOp(out, exec, location, it, "get_arguments_length"); |
14957cd0 A |
923 | it++; |
924 | break; | |
925 | } | |
9dae56ea | 926 | case op_put_by_id: { |
93a37866 A |
927 | printPutByIdOp(out, exec, location, it, "put_by_id"); |
928 | break; | |
929 | } | |
930 | case op_put_by_id_out_of_line: { | |
931 | printPutByIdOp(out, exec, location, it, "put_by_id_out_of_line"); | |
9dae56ea A |
932 | break; |
933 | } | |
6fe7ccc8 | 934 | case op_put_by_id_transition_direct: { |
93a37866 A |
935 | printPutByIdOp(out, exec, location, it, "put_by_id_transition_direct"); |
936 | break; | |
937 | } | |
938 | case op_put_by_id_transition_direct_out_of_line: { | |
939 | printPutByIdOp(out, exec, location, it, "put_by_id_transition_direct_out_of_line"); | |
9dae56ea A |
940 | break; |
941 | } | |
6fe7ccc8 | 942 | case op_put_by_id_transition_normal: { |
93a37866 A |
943 | printPutByIdOp(out, exec, location, it, "put_by_id_transition_normal"); |
944 | break; | |
945 | } | |
946 | case op_put_by_id_transition_normal_out_of_line: { | |
947 | printPutByIdOp(out, exec, location, it, "put_by_id_transition_normal_out_of_line"); | |
6fe7ccc8 A |
948 | break; |
949 | } | |
6fe7ccc8 | 950 | case op_put_getter_setter: { |
9dae56ea A |
951 | int r0 = (++it)->u.operand; |
952 | int id0 = (++it)->u.operand; | |
953 | int r1 = (++it)->u.operand; | |
6fe7ccc8 | 954 | int r2 = (++it)->u.operand; |
81345200 A |
955 | printLocationAndOp(out, exec, location, it, "put_getter_setter"); |
956 | out.printf("%s, %s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data(), registerName(r2).data()); | |
ba379fdc A |
957 | break; |
958 | } | |
9dae56ea A |
959 | case op_del_by_id: { |
960 | int r0 = (++it)->u.operand; | |
961 | int r1 = (++it)->u.operand; | |
962 | int id0 = (++it)->u.operand; | |
81345200 A |
963 | printLocationAndOp(out, exec, location, it, "del_by_id"); |
964 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data()); | |
9dae56ea A |
965 | break; |
966 | } | |
967 | case op_get_by_val: { | |
968 | int r0 = (++it)->u.operand; | |
969 | int r1 = (++it)->u.operand; | |
970 | int r2 = (++it)->u.operand; | |
81345200 A |
971 | printLocationAndOp(out, exec, location, it, "get_by_val"); |
972 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
93a37866 A |
973 | dumpArrayProfiling(out, it, hasPrintedProfiling); |
974 | dumpValueProfiling(out, it, hasPrintedProfiling); | |
f9bf01c6 A |
975 | break; |
976 | } | |
14957cd0 A |
977 | case op_get_argument_by_val: { |
978 | int r0 = (++it)->u.operand; | |
979 | int r1 = (++it)->u.operand; | |
980 | int r2 = (++it)->u.operand; | |
81345200 A |
981 | printLocationAndOp(out, exec, location, it, "get_argument_by_val"); |
982 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
6fe7ccc8 | 983 | ++it; |
93a37866 | 984 | dumpValueProfiling(out, it, hasPrintedProfiling); |
14957cd0 A |
985 | break; |
986 | } | |
f9bf01c6 A |
987 | case op_get_by_pname: { |
988 | int r0 = (++it)->u.operand; | |
989 | int r1 = (++it)->u.operand; | |
990 | int r2 = (++it)->u.operand; | |
991 | int r3 = (++it)->u.operand; | |
992 | int r4 = (++it)->u.operand; | |
993 | int r5 = (++it)->u.operand; | |
81345200 A |
994 | printLocationAndOp(out, exec, location, it, "get_by_pname"); |
995 | out.printf("%s, %s, %s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data(), registerName(r4).data(), registerName(r5).data()); | |
9dae56ea A |
996 | break; |
997 | } | |
998 | case op_put_by_val: { | |
999 | int r0 = (++it)->u.operand; | |
1000 | int r1 = (++it)->u.operand; | |
1001 | int r2 = (++it)->u.operand; | |
81345200 A |
1002 | printLocationAndOp(out, exec, location, it, "put_by_val"); |
1003 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
1004 | dumpArrayProfiling(out, it, hasPrintedProfiling); | |
1005 | break; | |
1006 | } | |
1007 | case op_put_by_val_direct: { | |
1008 | int r0 = (++it)->u.operand; | |
1009 | int r1 = (++it)->u.operand; | |
1010 | int r2 = (++it)->u.operand; | |
1011 | printLocationAndOp(out, exec, location, it, "put_by_val_direct"); | |
1012 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
93a37866 | 1013 | dumpArrayProfiling(out, it, hasPrintedProfiling); |
9dae56ea A |
1014 | break; |
1015 | } | |
1016 | case op_del_by_val: { | |
1017 | int r0 = (++it)->u.operand; | |
1018 | int r1 = (++it)->u.operand; | |
1019 | int r2 = (++it)->u.operand; | |
81345200 A |
1020 | printLocationAndOp(out, exec, location, it, "del_by_val"); |
1021 | out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); | |
9dae56ea A |
1022 | break; |
1023 | } | |
1024 | case op_put_by_index: { | |
1025 | int r0 = (++it)->u.operand; | |
1026 | unsigned n0 = (++it)->u.operand; | |
1027 | int r1 = (++it)->u.operand; | |
81345200 A |
1028 | printLocationAndOp(out, exec, location, it, "put_by_index"); |
1029 | out.printf("%s, %u, %s", registerName(r0).data(), n0, registerName(r1).data()); | |
9dae56ea A |
1030 | break; |
1031 | } | |
1032 | case op_jmp: { | |
1033 | int offset = (++it)->u.operand; | |
81345200 A |
1034 | printLocationAndOp(out, exec, location, it, "jmp"); |
1035 | out.printf("%d(->%d)", offset, location + offset); | |
9dae56ea A |
1036 | break; |
1037 | } | |
1038 | case op_jtrue: { | |
93a37866 | 1039 | printConditionalJump(out, exec, begin, it, location, "jtrue"); |
9dae56ea A |
1040 | break; |
1041 | } | |
93a37866 A |
1042 | case op_jfalse: { |
1043 | printConditionalJump(out, exec, begin, it, location, "jfalse"); | |
f9bf01c6 A |
1044 | break; |
1045 | } | |
93a37866 A |
1046 | case op_jeq_null: { |
1047 | printConditionalJump(out, exec, begin, it, location, "jeq_null"); | |
9dae56ea A |
1048 | break; |
1049 | } | |
1050 | case op_jneq_null: { | |
93a37866 | 1051 | printConditionalJump(out, exec, begin, it, location, "jneq_null"); |
9dae56ea A |
1052 | break; |
1053 | } | |
ba379fdc A |
1054 | case op_jneq_ptr: { |
1055 | int r0 = (++it)->u.operand; | |
93a37866 | 1056 | Special::Pointer pointer = (++it)->u.specialPointer; |
ba379fdc | 1057 | int offset = (++it)->u.operand; |
81345200 A |
1058 | printLocationAndOp(out, exec, location, it, "jneq_ptr"); |
1059 | out.printf("%s, %d (%p), %d(->%d)", registerName(r0).data(), pointer, m_globalObject->actualPointerFor(pointer), offset, location + offset); | |
6fe7ccc8 A |
1060 | break; |
1061 | } | |
1062 | case op_jless: { | |
1063 | int r0 = (++it)->u.operand; | |
1064 | int r1 = (++it)->u.operand; | |
1065 | int offset = (++it)->u.operand; | |
81345200 A |
1066 | printLocationAndOp(out, exec, location, it, "jless"); |
1067 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
6fe7ccc8 A |
1068 | break; |
1069 | } | |
1070 | case op_jlesseq: { | |
1071 | int r0 = (++it)->u.operand; | |
1072 | int r1 = (++it)->u.operand; | |
1073 | int offset = (++it)->u.operand; | |
81345200 A |
1074 | printLocationAndOp(out, exec, location, it, "jlesseq"); |
1075 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
6fe7ccc8 A |
1076 | break; |
1077 | } | |
1078 | case op_jgreater: { | |
1079 | int r0 = (++it)->u.operand; | |
1080 | int r1 = (++it)->u.operand; | |
1081 | int offset = (++it)->u.operand; | |
81345200 A |
1082 | printLocationAndOp(out, exec, location, it, "jgreater"); |
1083 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
6fe7ccc8 A |
1084 | break; |
1085 | } | |
1086 | case op_jgreatereq: { | |
1087 | int r0 = (++it)->u.operand; | |
1088 | int r1 = (++it)->u.operand; | |
1089 | int offset = (++it)->u.operand; | |
81345200 A |
1090 | printLocationAndOp(out, exec, location, it, "jgreatereq"); |
1091 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
ba379fdc A |
1092 | break; |
1093 | } | |
9dae56ea A |
1094 | case op_jnless: { |
1095 | int r0 = (++it)->u.operand; | |
1096 | int r1 = (++it)->u.operand; | |
1097 | int offset = (++it)->u.operand; | |
81345200 A |
1098 | printLocationAndOp(out, exec, location, it, "jnless"); |
1099 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
9dae56ea A |
1100 | break; |
1101 | } | |
ba379fdc A |
1102 | case op_jnlesseq: { |
1103 | int r0 = (++it)->u.operand; | |
1104 | int r1 = (++it)->u.operand; | |
1105 | int offset = (++it)->u.operand; | |
81345200 A |
1106 | printLocationAndOp(out, exec, location, it, "jnlesseq"); |
1107 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
ba379fdc A |
1108 | break; |
1109 | } | |
6fe7ccc8 | 1110 | case op_jngreater: { |
9dae56ea A |
1111 | int r0 = (++it)->u.operand; |
1112 | int r1 = (++it)->u.operand; | |
1113 | int offset = (++it)->u.operand; | |
81345200 A |
1114 | printLocationAndOp(out, exec, location, it, "jngreater"); |
1115 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
f9bf01c6 A |
1116 | break; |
1117 | } | |
6fe7ccc8 | 1118 | case op_jngreatereq: { |
f9bf01c6 A |
1119 | int r0 = (++it)->u.operand; |
1120 | int r1 = (++it)->u.operand; | |
1121 | int offset = (++it)->u.operand; | |
81345200 A |
1122 | printLocationAndOp(out, exec, location, it, "jngreatereq"); |
1123 | out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset); | |
6fe7ccc8 A |
1124 | break; |
1125 | } | |
1126 | case op_loop_hint: { | |
81345200 | 1127 | printLocationAndOp(out, exec, location, it, "loop_hint"); |
9dae56ea A |
1128 | break; |
1129 | } | |
1130 | case op_switch_imm: { | |
1131 | int tableIndex = (++it)->u.operand; | |
1132 | int defaultTarget = (++it)->u.operand; | |
1133 | int scrutineeRegister = (++it)->u.operand; | |
81345200 A |
1134 | printLocationAndOp(out, exec, location, it, "switch_imm"); |
1135 | out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data()); | |
9dae56ea A |
1136 | break; |
1137 | } | |
1138 | case op_switch_char: { | |
1139 | int tableIndex = (++it)->u.operand; | |
1140 | int defaultTarget = (++it)->u.operand; | |
1141 | int scrutineeRegister = (++it)->u.operand; | |
81345200 A |
1142 | printLocationAndOp(out, exec, location, it, "switch_char"); |
1143 | out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data()); | |
9dae56ea A |
1144 | break; |
1145 | } | |
1146 | case op_switch_string: { | |
1147 | int tableIndex = (++it)->u.operand; | |
1148 | int defaultTarget = (++it)->u.operand; | |
1149 | int scrutineeRegister = (++it)->u.operand; | |
81345200 A |
1150 | printLocationAndOp(out, exec, location, it, "switch_string"); |
1151 | out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data()); | |
9dae56ea A |
1152 | break; |
1153 | } | |
1154 | case op_new_func: { | |
1155 | int r0 = (++it)->u.operand; | |
1156 | int f0 = (++it)->u.operand; | |
14957cd0 | 1157 | int shouldCheck = (++it)->u.operand; |
81345200 A |
1158 | printLocationAndOp(out, exec, location, it, "new_func"); |
1159 | out.printf("%s, f%d, %s", registerName(r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>"); | |
1160 | break; | |
1161 | } | |
1162 | case op_new_captured_func: { | |
1163 | int r0 = (++it)->u.operand; | |
1164 | int f0 = (++it)->u.operand; | |
1165 | printLocationAndOp(out, exec, location, it, "new_captured_func"); | |
1166 | out.printf("%s, f%d", registerName(r0).data(), f0); | |
1167 | ++it; | |
9dae56ea A |
1168 | break; |
1169 | } | |
1170 | case op_new_func_exp: { | |
1171 | int r0 = (++it)->u.operand; | |
1172 | int f0 = (++it)->u.operand; | |
81345200 A |
1173 | printLocationAndOp(out, exec, location, it, "new_func_exp"); |
1174 | out.printf("%s, f%d", registerName(r0).data(), f0); | |
9dae56ea A |
1175 | break; |
1176 | } | |
1177 | case op_call: { | |
81345200 | 1178 | printCallOp(out, exec, location, it, "call", DumpCaches, hasPrintedProfiling, callLinkInfos); |
9dae56ea A |
1179 | break; |
1180 | } | |
1181 | case op_call_eval: { | |
81345200 | 1182 | printCallOp(out, exec, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling, callLinkInfos); |
9dae56ea A |
1183 | break; |
1184 | } | |
81345200 A |
1185 | |
1186 | case op_construct_varargs: | |
ba379fdc | 1187 | case op_call_varargs: { |
81345200 | 1188 | int result = (++it)->u.operand; |
6fe7ccc8 A |
1189 | int callee = (++it)->u.operand; |
1190 | int thisValue = (++it)->u.operand; | |
1191 | int arguments = (++it)->u.operand; | |
1192 | int firstFreeRegister = (++it)->u.operand; | |
81345200 A |
1193 | int varArgOffset = (++it)->u.operand; |
1194 | ++it; | |
1195 | printLocationAndOp(out, exec, location, it, opcode == op_call_varargs ? "call_varargs" : "construct_varargs"); | |
1196 | out.printf("%s, %s, %s, %s, %d, %d", registerName(result).data(), registerName(callee).data(), registerName(thisValue).data(), registerName(arguments).data(), firstFreeRegister, varArgOffset); | |
1197 | dumpValueProfiling(out, it, hasPrintedProfiling); | |
ba379fdc A |
1198 | break; |
1199 | } | |
81345200 | 1200 | |
9dae56ea A |
1201 | case op_tear_off_activation: { |
1202 | int r0 = (++it)->u.operand; | |
81345200 | 1203 | printLocationOpAndRegisterOperand(out, exec, location, it, "tear_off_activation", r0); |
9dae56ea A |
1204 | break; |
1205 | } | |
1206 | case op_tear_off_arguments: { | |
14957cd0 | 1207 | int r0 = (++it)->u.operand; |
93a37866 | 1208 | int r1 = (++it)->u.operand; |
81345200 A |
1209 | printLocationAndOp(out, exec, location, it, "tear_off_arguments"); |
1210 | out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); | |
9dae56ea A |
1211 | break; |
1212 | } | |
1213 | case op_ret: { | |
1214 | int r0 = (++it)->u.operand; | |
81345200 | 1215 | printLocationOpAndRegisterOperand(out, exec, location, it, "ret", r0); |
9dae56ea A |
1216 | break; |
1217 | } | |
14957cd0 | 1218 | case op_ret_object_or_this: { |
9dae56ea A |
1219 | int r0 = (++it)->u.operand; |
1220 | int r1 = (++it)->u.operand; | |
81345200 A |
1221 | printLocationAndOp(out, exec, location, it, "constructor_ret"); |
1222 | out.printf("%s %s", registerName(r0).data(), registerName(r1).data()); | |
14957cd0 A |
1223 | break; |
1224 | } | |
1225 | case op_construct: { | |
81345200 | 1226 | printCallOp(out, exec, location, it, "construct", DumpCaches, hasPrintedProfiling, callLinkInfos); |
9dae56ea A |
1227 | break; |
1228 | } | |
ba379fdc A |
1229 | case op_strcat: { |
1230 | int r0 = (++it)->u.operand; | |
1231 | int r1 = (++it)->u.operand; | |
1232 | int count = (++it)->u.operand; | |
81345200 A |
1233 | printLocationAndOp(out, exec, location, it, "strcat"); |
1234 | out.printf("%s, %s, %d", registerName(r0).data(), registerName(r1).data(), count); | |
ba379fdc A |
1235 | break; |
1236 | } | |
1237 | case op_to_primitive: { | |
1238 | int r0 = (++it)->u.operand; | |
1239 | int r1 = (++it)->u.operand; | |
81345200 A |
1240 | printLocationAndOp(out, exec, location, it, "to_primitive"); |
1241 | out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); | |
ba379fdc A |
1242 | break; |
1243 | } | |
9dae56ea | 1244 | case op_get_pnames: { |
f9bf01c6 A |
1245 | int r0 = it[1].u.operand; |
1246 | int r1 = it[2].u.operand; | |
1247 | int r2 = it[3].u.operand; | |
1248 | int r3 = it[4].u.operand; | |
1249 | int offset = it[5].u.operand; | |
81345200 A |
1250 | printLocationAndOp(out, exec, location, it, "get_pnames"); |
1251 | out.printf("%s, %s, %s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data(), offset, location + offset); | |
f9bf01c6 | 1252 | it += OPCODE_LENGTH(op_get_pnames) - 1; |
9dae56ea A |
1253 | break; |
1254 | } | |
1255 | case op_next_pname: { | |
f9bf01c6 | 1256 | int dest = it[1].u.operand; |
14957cd0 A |
1257 | int base = it[2].u.operand; |
1258 | int i = it[3].u.operand; | |
1259 | int size = it[4].u.operand; | |
1260 | int iter = it[5].u.operand; | |
1261 | int offset = it[6].u.operand; | |
81345200 A |
1262 | printLocationAndOp(out, exec, location, it, "next_pname"); |
1263 | out.printf("%s, %s, %s, %s, %s, %d(->%d)", registerName(dest).data(), registerName(base).data(), registerName(i).data(), registerName(size).data(), registerName(iter).data(), offset, location + offset); | |
f9bf01c6 | 1264 | it += OPCODE_LENGTH(op_next_pname) - 1; |
9dae56ea A |
1265 | break; |
1266 | } | |
93a37866 | 1267 | case op_push_with_scope: { |
9dae56ea | 1268 | int r0 = (++it)->u.operand; |
81345200 | 1269 | printLocationOpAndRegisterOperand(out, exec, location, it, "push_with_scope", r0); |
9dae56ea A |
1270 | break; |
1271 | } | |
1272 | case op_pop_scope: { | |
81345200 | 1273 | printLocationAndOp(out, exec, location, it, "pop_scope"); |
9dae56ea A |
1274 | break; |
1275 | } | |
93a37866 | 1276 | case op_push_name_scope: { |
9dae56ea A |
1277 | int id0 = (++it)->u.operand; |
1278 | int r1 = (++it)->u.operand; | |
93a37866 | 1279 | unsigned attributes = (++it)->u.operand; |
81345200 A |
1280 | printLocationAndOp(out, exec, location, it, "push_name_scope"); |
1281 | out.printf("%s, %s, %u", idName(id0, identifier(id0)).data(), registerName(r1).data(), attributes); | |
9dae56ea A |
1282 | break; |
1283 | } | |
1284 | case op_catch: { | |
1285 | int r0 = (++it)->u.operand; | |
81345200 | 1286 | printLocationOpAndRegisterOperand(out, exec, location, it, "catch", r0); |
9dae56ea A |
1287 | break; |
1288 | } | |
1289 | case op_throw: { | |
1290 | int r0 = (++it)->u.operand; | |
81345200 | 1291 | printLocationOpAndRegisterOperand(out, exec, location, it, "throw", r0); |
9dae56ea A |
1292 | break; |
1293 | } | |
93a37866 | 1294 | case op_throw_static_error: { |
9dae56ea | 1295 | int k0 = (++it)->u.operand; |
93a37866 | 1296 | int k1 = (++it)->u.operand; |
81345200 A |
1297 | printLocationAndOp(out, exec, location, it, "throw_static_error"); |
1298 | out.printf("%s, %s", constantName(k0, getConstant(k0)).data(), k1 ? "true" : "false"); | |
9dae56ea A |
1299 | break; |
1300 | } | |
1301 | case op_debug: { | |
1302 | int debugHookID = (++it)->u.operand; | |
81345200 A |
1303 | int hasBreakpointFlag = (++it)->u.operand; |
1304 | printLocationAndOp(out, exec, location, it, "debug"); | |
1305 | out.printf("%s %d", debugHookName(debugHookID), hasBreakpointFlag); | |
9dae56ea A |
1306 | break; |
1307 | } | |
1308 | case op_profile_will_call: { | |
1309 | int function = (++it)->u.operand; | |
81345200 | 1310 | printLocationOpAndRegisterOperand(out, exec, location, it, "profile_will_call", function); |
9dae56ea A |
1311 | break; |
1312 | } | |
1313 | case op_profile_did_call: { | |
1314 | int function = (++it)->u.operand; | |
81345200 | 1315 | printLocationOpAndRegisterOperand(out, exec, location, it, "profile_did_call", function); |
9dae56ea A |
1316 | break; |
1317 | } | |
1318 | case op_end: { | |
1319 | int r0 = (++it)->u.operand; | |
81345200 A |
1320 | printLocationOpAndRegisterOperand(out, exec, location, it, "end", r0); |
1321 | break; | |
1322 | } | |
1323 | case op_resolve_scope: { | |
1324 | int r0 = (++it)->u.operand; | |
1325 | int id0 = (++it)->u.operand; | |
1326 | ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); | |
1327 | int depth = (++it)->u.operand; | |
1328 | printLocationAndOp(out, exec, location, it, "resolve_scope"); | |
1329 | out.printf("%s, %s, %u<%s|%s>, %d", registerName(r0).data(), idName(id0, identifier(id0)).data(), | |
1330 | modeAndType.operand(), resolveModeName(modeAndType.mode()), resolveTypeName(modeAndType.type()), | |
1331 | depth); | |
1332 | ++it; | |
1333 | break; | |
1334 | } | |
1335 | case op_get_from_scope: { | |
1336 | int r0 = (++it)->u.operand; | |
1337 | int r1 = (++it)->u.operand; | |
1338 | int id0 = (++it)->u.operand; | |
1339 | ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); | |
1340 | ++it; // Structure | |
1341 | int operand = (++it)->u.operand; // Operand | |
1342 | ++it; // Skip value profile. | |
1343 | printLocationAndOp(out, exec, location, it, "get_from_scope"); | |
1344 | out.printf("%s, %s, %s, %u<%s|%s>, <structure>, %d", | |
1345 | registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data(), | |
1346 | modeAndType.operand(), resolveModeName(modeAndType.mode()), resolveTypeName(modeAndType.type()), | |
1347 | operand); | |
1348 | break; | |
1349 | } | |
1350 | case op_put_to_scope: { | |
1351 | int r0 = (++it)->u.operand; | |
1352 | int id0 = (++it)->u.operand; | |
1353 | int r1 = (++it)->u.operand; | |
1354 | ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); | |
1355 | ++it; // Structure | |
1356 | int operand = (++it)->u.operand; // Operand | |
1357 | printLocationAndOp(out, exec, location, it, "put_to_scope"); | |
1358 | out.printf("%s, %s, %s, %u<%s|%s>, <structure>, %d", | |
1359 | registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data(), | |
1360 | modeAndType.operand(), resolveModeName(modeAndType.mode()), resolveTypeName(modeAndType.type()), | |
1361 | operand); | |
9dae56ea A |
1362 | break; |
1363 | } | |
93a37866 A |
1364 | default: |
1365 | RELEASE_ASSERT_NOT_REACHED(); | |
93a37866 A |
1366 | } |
1367 | ||
93a37866 A |
1368 | dumpRareCaseProfile(out, "rare case: ", rareCaseProfileForBytecodeOffset(location), hasPrintedProfiling); |
1369 | dumpRareCaseProfile(out, "special fast case: ", specialFastCaseProfileForBytecodeOffset(location), hasPrintedProfiling); | |
93a37866 A |
1370 | |
1371 | #if ENABLE(DFG_JIT) | |
81345200 | 1372 | Vector<DFG::FrequentExitSite> exitSites = exitProfile().exitSitesFor(location); |
93a37866 A |
1373 | if (!exitSites.isEmpty()) { |
1374 | out.print(" !! frequent exits: "); | |
1375 | CommaPrinter comma; | |
1376 | for (unsigned i = 0; i < exitSites.size(); ++i) | |
81345200 | 1377 | out.print(comma, exitSites[i].kind(), " ", exitSites[i].jitType()); |
9dae56ea | 1378 | } |
93a37866 A |
1379 | #else // ENABLE(DFG_JIT) |
1380 | UNUSED_PARAM(location); | |
1381 | #endif // ENABLE(DFG_JIT) | |
1382 | out.print("\n"); | |
1383 | } | |
1384 | ||
81345200 A |
1385 | void CodeBlock::dumpBytecode( |
1386 | PrintStream& out, unsigned bytecodeOffset, | |
1387 | const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos) | |
93a37866 A |
1388 | { |
1389 | ExecState* exec = m_globalObject->globalExec(); | |
1390 | const Instruction* it = instructions().begin() + bytecodeOffset; | |
81345200 | 1391 | dumpBytecode(out, exec, instructions().begin(), it, stubInfos, callLinkInfos); |
9dae56ea A |
1392 | } |
1393 | ||
9dae56ea A |
1394 | #define FOR_EACH_MEMBER_VECTOR(macro) \ |
1395 | macro(instructions) \ | |
9dae56ea A |
1396 | macro(callLinkInfos) \ |
1397 | macro(linkedCallerList) \ | |
1398 | macro(identifiers) \ | |
1399 | macro(functionExpressions) \ | |
1400 | macro(constantRegisters) | |
1401 | ||
1402 | #define FOR_EACH_MEMBER_VECTOR_RARE_DATA(macro) \ | |
1403 | macro(regexps) \ | |
1404 | macro(functions) \ | |
9dae56ea | 1405 | macro(exceptionHandlers) \ |
81345200 | 1406 | macro(switchJumpTables) \ |
9dae56ea | 1407 | macro(stringSwitchJumpTables) \ |
14957cd0 | 1408 | macro(evalCodeCache) \ |
9dae56ea A |
1409 | macro(expressionInfo) \ |
1410 | macro(lineInfo) \ | |
14957cd0 | 1411 | macro(callReturnIndexVector) |
9dae56ea A |
1412 | |
1413 | template<typename T> | |
1414 | static size_t sizeInBytes(const Vector<T>& vector) | |
1415 | { | |
1416 | return vector.capacity() * sizeof(T); | |
1417 | } | |
1418 | ||
93a37866 | 1419 | CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other) |
6fe7ccc8 A |
1420 | : m_globalObject(other.m_globalObject) |
1421 | , m_heap(other.m_heap) | |
1422 | , m_numCalleeRegisters(other.m_numCalleeRegisters) | |
1423 | , m_numVars(other.m_numVars) | |
6fe7ccc8 | 1424 | , m_isConstructor(other.m_isConstructor) |
81345200 A |
1425 | , m_shouldAlwaysBeInlined(true) |
1426 | , m_didFailFTLCompilation(false) | |
1427 | , m_hasBeenCompiledWithFTL(false) | |
93a37866 | 1428 | , m_unlinkedCode(*other.m_vm, other.m_ownerExecutable.get(), other.m_unlinkedCode.get()) |
81345200 A |
1429 | , m_hasDebuggerStatement(false) |
1430 | , m_steppingMode(SteppingModeDisabled) | |
1431 | , m_numBreakpoints(0) | |
93a37866 A |
1432 | , m_ownerExecutable(*other.m_vm, other.m_ownerExecutable.get(), other.m_ownerExecutable.get()) |
1433 | , m_vm(other.m_vm) | |
6fe7ccc8 A |
1434 | , m_instructions(other.m_instructions) |
1435 | , m_thisRegister(other.m_thisRegister) | |
1436 | , m_argumentsRegister(other.m_argumentsRegister) | |
1437 | , m_activationRegister(other.m_activationRegister) | |
6fe7ccc8 | 1438 | , m_isStrictMode(other.m_isStrictMode) |
93a37866 | 1439 | , m_needsActivation(other.m_needsActivation) |
81345200 A |
1440 | , m_mayBeExecuting(false) |
1441 | , m_visitAggregateHasBeenCalled(false) | |
6fe7ccc8 A |
1442 | , m_source(other.m_source) |
1443 | , m_sourceOffset(other.m_sourceOffset) | |
93a37866 A |
1444 | , m_firstLineColumnOffset(other.m_firstLineColumnOffset) |
1445 | , m_codeType(other.m_codeType) | |
6fe7ccc8 A |
1446 | , m_constantRegisters(other.m_constantRegisters) |
1447 | , m_functionDecls(other.m_functionDecls) | |
1448 | , m_functionExprs(other.m_functionExprs) | |
93a37866 | 1449 | , m_osrExitCounter(0) |
6fe7ccc8 A |
1450 | , m_optimizationDelayCounter(0) |
1451 | , m_reoptimizationRetryCounter(0) | |
81345200 | 1452 | , m_hash(other.m_hash) |
6fe7ccc8 | 1453 | #if ENABLE(JIT) |
81345200 | 1454 | , m_capabilityLevelState(DFG::CapabilityLevelNotSet) |
6fe7ccc8 A |
1455 | #endif |
1456 | { | |
81345200 A |
1457 | ASSERT(m_heap->isDeferred()); |
1458 | ||
1459 | if (SymbolTable* symbolTable = other.symbolTable()) | |
1460 | m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable); | |
1461 | ||
6fe7ccc8 A |
1462 | setNumParameters(other.numParameters()); |
1463 | optimizeAfterWarmUp(); | |
1464 | jitAfterWarmUp(); | |
93a37866 | 1465 | |
6fe7ccc8 A |
1466 | if (other.m_rareData) { |
1467 | createRareDataIfNecessary(); | |
1468 | ||
1469 | m_rareData->m_exceptionHandlers = other.m_rareData->m_exceptionHandlers; | |
6fe7ccc8 | 1470 | m_rareData->m_constantBuffers = other.m_rareData->m_constantBuffers; |
81345200 | 1471 | m_rareData->m_switchJumpTables = other.m_rareData->m_switchJumpTables; |
6fe7ccc8 | 1472 | m_rareData->m_stringSwitchJumpTables = other.m_rareData->m_stringSwitchJumpTables; |
93a37866 | 1473 | } |
81345200 A |
1474 | |
1475 | m_heap->m_codeBlocks.add(this); | |
1476 | m_heap->reportExtraMemoryCost(sizeof(CodeBlock)); | |
93a37866 A |
1477 | } |
1478 | ||
81345200 A |
1479 | CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlinkedCodeBlock, JSScope* scope, PassRefPtr<SourceProvider> sourceProvider, unsigned sourceOffset, unsigned firstLineColumnOffset) |
1480 | : m_globalObject(scope->globalObject()->vm(), ownerExecutable, scope->globalObject()) | |
93a37866 A |
1481 | , m_heap(&m_globalObject->vm().heap) |
1482 | , m_numCalleeRegisters(unlinkedCodeBlock->m_numCalleeRegisters) | |
1483 | , m_numVars(unlinkedCodeBlock->m_numVars) | |
1484 | , m_isConstructor(unlinkedCodeBlock->isConstructor()) | |
81345200 A |
1485 | , m_shouldAlwaysBeInlined(true) |
1486 | , m_didFailFTLCompilation(false) | |
1487 | , m_hasBeenCompiledWithFTL(false) | |
1488 | , m_unlinkedCode(m_globalObject->vm(), ownerExecutable, unlinkedCodeBlock) | |
1489 | , m_hasDebuggerStatement(false) | |
1490 | , m_steppingMode(SteppingModeDisabled) | |
1491 | , m_numBreakpoints(0) | |
1492 | , m_ownerExecutable(m_globalObject->vm(), ownerExecutable, ownerExecutable) | |
93a37866 A |
1493 | , m_vm(unlinkedCodeBlock->vm()) |
1494 | , m_thisRegister(unlinkedCodeBlock->thisRegister()) | |
1495 | , m_argumentsRegister(unlinkedCodeBlock->argumentsRegister()) | |
1496 | , m_activationRegister(unlinkedCodeBlock->activationRegister()) | |
1497 | , m_isStrictMode(unlinkedCodeBlock->isStrictMode()) | |
81345200 A |
1498 | , m_needsActivation(unlinkedCodeBlock->hasActivationRegister() && unlinkedCodeBlock->codeType() == FunctionCode) |
1499 | , m_mayBeExecuting(false) | |
1500 | , m_visitAggregateHasBeenCalled(false) | |
9dae56ea A |
1501 | , m_source(sourceProvider) |
1502 | , m_sourceOffset(sourceOffset) | |
93a37866 A |
1503 | , m_firstLineColumnOffset(firstLineColumnOffset) |
1504 | , m_codeType(unlinkedCodeBlock->codeType()) | |
93a37866 | 1505 | , m_osrExitCounter(0) |
6fe7ccc8 A |
1506 | , m_optimizationDelayCounter(0) |
1507 | , m_reoptimizationRetryCounter(0) | |
81345200 A |
1508 | #if ENABLE(JIT) |
1509 | , m_capabilityLevelState(DFG::CapabilityLevelNotSet) | |
1510 | #endif | |
9dae56ea | 1511 | { |
81345200 | 1512 | ASSERT(m_heap->isDeferred()); |
93a37866 | 1513 | |
81345200 A |
1514 | bool didCloneSymbolTable = false; |
1515 | ||
1516 | if (SymbolTable* symbolTable = unlinkedCodeBlock->symbolTable()) { | |
1517 | if (codeType() == FunctionCode && symbolTable->captureCount()) { | |
1518 | m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable->cloneCapturedNames(*m_vm)); | |
1519 | didCloneSymbolTable = true; | |
1520 | } else | |
1521 | m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable); | |
1522 | } | |
1523 | ||
9dae56ea | 1524 | ASSERT(m_source); |
93a37866 | 1525 | setNumParameters(unlinkedCodeBlock->numParameters()); |
9dae56ea | 1526 | |
93a37866 A |
1527 | setConstantRegisters(unlinkedCodeBlock->constantRegisters()); |
1528 | if (unlinkedCodeBlock->usesGlobalObject()) | |
81345200 A |
1529 | m_constantRegisters[unlinkedCodeBlock->globalObjectRegister().toConstantIndex()].set(*m_vm, ownerExecutable, m_globalObject.get()); |
1530 | m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls()); | |
93a37866 A |
1531 | for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) { |
1532 | UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i); | |
1533 | unsigned lineCount = unlinkedExecutable->lineCount(); | |
1534 | unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset(); | |
81345200 A |
1535 | bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset(); |
1536 | unsigned startColumn = unlinkedExecutable->unlinkedBodyStartColumn() + (startColumnIsOnOwnerStartLine ? ownerExecutable->startColumn() : 1); | |
1537 | bool endColumnIsOnStartLine = !lineCount; | |
1538 | unsigned endColumn = unlinkedExecutable->unlinkedBodyEndColumn() + (endColumnIsOnStartLine ? startColumn : 1); | |
93a37866 A |
1539 | unsigned startOffset = sourceOffset + unlinkedExecutable->startOffset(); |
1540 | unsigned sourceLength = unlinkedExecutable->sourceLength(); | |
1541 | SourceCode code(m_source, startOffset, startOffset + sourceLength, firstLine, startColumn); | |
81345200 | 1542 | FunctionExecutable* executable = FunctionExecutable::create(*m_vm, code, unlinkedExecutable, firstLine, firstLine + lineCount, startColumn, endColumn); |
93a37866 A |
1543 | m_functionDecls[i].set(*m_vm, ownerExecutable, executable); |
1544 | } | |
1545 | ||
81345200 | 1546 | m_functionExprs.resizeToFit(unlinkedCodeBlock->numberOfFunctionExprs()); |
93a37866 A |
1547 | for (size_t count = unlinkedCodeBlock->numberOfFunctionExprs(), i = 0; i < count; ++i) { |
1548 | UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionExpr(i); | |
1549 | unsigned lineCount = unlinkedExecutable->lineCount(); | |
1550 | unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset(); | |
81345200 A |
1551 | bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset(); |
1552 | unsigned startColumn = unlinkedExecutable->unlinkedBodyStartColumn() + (startColumnIsOnOwnerStartLine ? ownerExecutable->startColumn() : 1); | |
1553 | bool endColumnIsOnStartLine = !lineCount; | |
1554 | unsigned endColumn = unlinkedExecutable->unlinkedBodyEndColumn() + (endColumnIsOnStartLine ? startColumn : 1); | |
93a37866 A |
1555 | unsigned startOffset = sourceOffset + unlinkedExecutable->startOffset(); |
1556 | unsigned sourceLength = unlinkedExecutable->sourceLength(); | |
1557 | SourceCode code(m_source, startOffset, startOffset + sourceLength, firstLine, startColumn); | |
81345200 | 1558 | FunctionExecutable* executable = FunctionExecutable::create(*m_vm, code, unlinkedExecutable, firstLine, firstLine + lineCount, startColumn, endColumn); |
93a37866 A |
1559 | m_functionExprs[i].set(*m_vm, ownerExecutable, executable); |
1560 | } | |
1561 | ||
1562 | if (unlinkedCodeBlock->hasRareData()) { | |
1563 | createRareDataIfNecessary(); | |
1564 | if (size_t count = unlinkedCodeBlock->constantBufferCount()) { | |
1565 | m_rareData->m_constantBuffers.grow(count); | |
1566 | for (size_t i = 0; i < count; i++) { | |
1567 | const UnlinkedCodeBlock::ConstantBuffer& buffer = unlinkedCodeBlock->constantBuffer(i); | |
1568 | m_rareData->m_constantBuffers[i] = buffer; | |
1569 | } | |
1570 | } | |
1571 | if (size_t count = unlinkedCodeBlock->numberOfExceptionHandlers()) { | |
81345200 A |
1572 | m_rareData->m_exceptionHandlers.resizeToFit(count); |
1573 | size_t nonLocalScopeDepth = scope->depth(); | |
93a37866 A |
1574 | for (size_t i = 0; i < count; i++) { |
1575 | const UnlinkedHandlerInfo& handler = unlinkedCodeBlock->exceptionHandler(i); | |
1576 | m_rareData->m_exceptionHandlers[i].start = handler.start; | |
1577 | m_rareData->m_exceptionHandlers[i].end = handler.end; | |
1578 | m_rareData->m_exceptionHandlers[i].target = handler.target; | |
81345200 A |
1579 | m_rareData->m_exceptionHandlers[i].scopeDepth = nonLocalScopeDepth + handler.scopeDepth; |
1580 | #if ENABLE(JIT) | |
1581 | m_rareData->m_exceptionHandlers[i].nativeCode = CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(op_catch))); | |
93a37866 A |
1582 | #endif |
1583 | } | |
1584 | } | |
1585 | ||
1586 | if (size_t count = unlinkedCodeBlock->numberOfStringSwitchJumpTables()) { | |
1587 | m_rareData->m_stringSwitchJumpTables.grow(count); | |
1588 | for (size_t i = 0; i < count; i++) { | |
1589 | UnlinkedStringJumpTable::StringOffsetTable::iterator ptr = unlinkedCodeBlock->stringSwitchJumpTable(i).offsetTable.begin(); | |
1590 | UnlinkedStringJumpTable::StringOffsetTable::iterator end = unlinkedCodeBlock->stringSwitchJumpTable(i).offsetTable.end(); | |
1591 | for (; ptr != end; ++ptr) { | |
1592 | OffsetLocation offset; | |
1593 | offset.branchOffset = ptr->value; | |
1594 | m_rareData->m_stringSwitchJumpTables[i].offsetTable.add(ptr->key, offset); | |
1595 | } | |
1596 | } | |
1597 | } | |
1598 | ||
81345200 A |
1599 | if (size_t count = unlinkedCodeBlock->numberOfSwitchJumpTables()) { |
1600 | m_rareData->m_switchJumpTables.grow(count); | |
93a37866 | 1601 | for (size_t i = 0; i < count; i++) { |
81345200 A |
1602 | UnlinkedSimpleJumpTable& sourceTable = unlinkedCodeBlock->switchJumpTable(i); |
1603 | SimpleJumpTable& destTable = m_rareData->m_switchJumpTables[i]; | |
93a37866 A |
1604 | destTable.branchOffsets = sourceTable.branchOffsets; |
1605 | destTable.min = sourceTable.min; | |
1606 | } | |
1607 | } | |
81345200 | 1608 | } |
93a37866 A |
1609 | |
1610 | // Allocate metadata buffers for the bytecode | |
93a37866 | 1611 | if (size_t size = unlinkedCodeBlock->numberOfLLintCallLinkInfos()) |
81345200 | 1612 | m_llintCallLinkInfos.resizeToFit(size); |
93a37866 A |
1613 | if (size_t size = unlinkedCodeBlock->numberOfArrayProfiles()) |
1614 | m_arrayProfiles.grow(size); | |
1615 | if (size_t size = unlinkedCodeBlock->numberOfArrayAllocationProfiles()) | |
81345200 | 1616 | m_arrayAllocationProfiles.resizeToFit(size); |
93a37866 | 1617 | if (size_t size = unlinkedCodeBlock->numberOfValueProfiles()) |
81345200 | 1618 | m_valueProfiles.resizeToFit(size); |
93a37866 | 1619 | if (size_t size = unlinkedCodeBlock->numberOfObjectAllocationProfiles()) |
81345200 | 1620 | m_objectAllocationProfiles.resizeToFit(size); |
93a37866 A |
1621 | |
1622 | // Copy and translate the UnlinkedInstructions | |
81345200 A |
1623 | unsigned instructionCount = unlinkedCodeBlock->instructions().count(); |
1624 | UnlinkedInstructionStream::Reader instructionReader(unlinkedCodeBlock->instructions()); | |
1625 | ||
93a37866 | 1626 | Vector<Instruction, 0, UnsafeVectorOverflow> instructions(instructionCount); |
81345200 A |
1627 | for (unsigned i = 0; !instructionReader.atEnd(); ) { |
1628 | const UnlinkedInstruction* pc = instructionReader.next(); | |
1629 | ||
1630 | unsigned opLength = opcodeLength(pc[0].u.opcode); | |
1631 | ||
1632 | instructions[i] = vm()->interpreter->getOpcode(pc[0].u.opcode); | |
93a37866 A |
1633 | for (size_t j = 1; j < opLength; ++j) { |
1634 | if (sizeof(int32_t) != sizeof(intptr_t)) | |
1635 | instructions[i + j].u.pointer = 0; | |
81345200 | 1636 | instructions[i + j].u.operand = pc[j].u.operand; |
93a37866 | 1637 | } |
81345200 A |
1638 | switch (pc[0].u.opcode) { |
1639 | case op_call_varargs: | |
1640 | case op_construct_varargs: | |
93a37866 A |
1641 | case op_get_by_val: |
1642 | case op_get_argument_by_val: { | |
81345200 | 1643 | int arrayProfileIndex = pc[opLength - 2].u.operand; |
93a37866 A |
1644 | m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); |
1645 | ||
1646 | instructions[i + opLength - 2] = &m_arrayProfiles[arrayProfileIndex]; | |
81345200 | 1647 | FALLTHROUGH; |
93a37866 | 1648 | } |
81345200 A |
1649 | case op_get_by_id: { |
1650 | ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; | |
93a37866 A |
1651 | ASSERT(profile->m_bytecodeOffset == -1); |
1652 | profile->m_bytecodeOffset = i; | |
1653 | instructions[i + opLength - 1] = profile; | |
1654 | break; | |
1655 | } | |
1656 | case op_put_by_val: { | |
81345200 A |
1657 | int arrayProfileIndex = pc[opLength - 1].u.operand; |
1658 | m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); | |
1659 | instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex]; | |
1660 | break; | |
1661 | } | |
1662 | case op_put_by_val_direct: { | |
1663 | int arrayProfileIndex = pc[opLength - 1].u.operand; | |
93a37866 A |
1664 | m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); |
1665 | instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex]; | |
1666 | break; | |
1667 | } | |
1668 | ||
1669 | case op_new_array: | |
1670 | case op_new_array_buffer: | |
1671 | case op_new_array_with_size: { | |
81345200 | 1672 | int arrayAllocationProfileIndex = pc[opLength - 1].u.operand; |
93a37866 A |
1673 | instructions[i + opLength - 1] = &m_arrayAllocationProfiles[arrayAllocationProfileIndex]; |
1674 | break; | |
1675 | } | |
93a37866 | 1676 | case op_new_object: { |
81345200 | 1677 | int objectAllocationProfileIndex = pc[opLength - 1].u.operand; |
93a37866 | 1678 | ObjectAllocationProfile* objectAllocationProfile = &m_objectAllocationProfiles[objectAllocationProfileIndex]; |
81345200 | 1679 | int inferredInlineCapacity = pc[opLength - 2].u.operand; |
93a37866 A |
1680 | |
1681 | instructions[i + opLength - 1] = objectAllocationProfile; | |
1682 | objectAllocationProfile->initialize(*vm(), | |
1683 | m_ownerExecutable.get(), m_globalObject->objectPrototype(), inferredInlineCapacity); | |
1684 | break; | |
1685 | } | |
1686 | ||
81345200 A |
1687 | case op_call: |
1688 | case op_call_eval: { | |
1689 | ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; | |
93a37866 A |
1690 | ASSERT(profile->m_bytecodeOffset == -1); |
1691 | profile->m_bytecodeOffset = i; | |
1692 | instructions[i + opLength - 1] = profile; | |
81345200 | 1693 | int arrayProfileIndex = pc[opLength - 2].u.operand; |
93a37866 | 1694 | m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); |
81345200 A |
1695 | instructions[i + opLength - 2] = &m_arrayProfiles[arrayProfileIndex]; |
1696 | instructions[i + 5] = &m_llintCallLinkInfos[pc[5].u.operand]; | |
93a37866 A |
1697 | break; |
1698 | } | |
81345200 A |
1699 | case op_construct: { |
1700 | instructions[i + 5] = &m_llintCallLinkInfos[pc[5].u.operand]; | |
1701 | ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; | |
1702 | ASSERT(profile->m_bytecodeOffset == -1); | |
1703 | profile->m_bytecodeOffset = i; | |
1704 | instructions[i + opLength - 1] = profile; | |
93a37866 | 1705 | break; |
81345200 | 1706 | } |
93a37866 | 1707 | case op_get_by_id_out_of_line: |
93a37866 | 1708 | case op_get_array_length: |
93a37866 A |
1709 | CRASH(); |
1710 | ||
1711 | case op_init_global_const_nop: { | |
1712 | ASSERT(codeType() == GlobalCode); | |
81345200 A |
1713 | Identifier ident = identifier(pc[4].u.operand); |
1714 | SymbolTableEntry entry = m_globalObject->symbolTable()->get(ident.impl()); | |
93a37866 A |
1715 | if (entry.isNull()) |
1716 | break; | |
1717 | ||
81345200 A |
1718 | instructions[i + 0] = vm()->interpreter->getOpcode(op_init_global_const); |
1719 | instructions[i + 1] = &m_globalObject->registerAt(entry.getIndex()); | |
1720 | break; | |
1721 | } | |
1722 | ||
1723 | case op_resolve_scope: { | |
1724 | const Identifier& ident = identifier(pc[2].u.operand); | |
1725 | ResolveType type = static_cast<ResolveType>(pc[3].u.operand); | |
1726 | ||
1727 | ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Get, type); | |
1728 | instructions[i + 3].u.operand = op.type; | |
1729 | instructions[i + 4].u.operand = op.depth; | |
1730 | if (op.activation) | |
1731 | instructions[i + 5].u.activation.set(*vm(), ownerExecutable, op.activation); | |
1732 | break; | |
1733 | } | |
1734 | ||
1735 | case op_get_from_scope: { | |
1736 | ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; | |
1737 | ASSERT(profile->m_bytecodeOffset == -1); | |
1738 | profile->m_bytecodeOffset = i; | |
1739 | instructions[i + opLength - 1] = profile; | |
1740 | ||
1741 | // get_from_scope dst, scope, id, ResolveModeAndType, Structure, Operand | |
1742 | const Identifier& ident = identifier(pc[3].u.operand); | |
1743 | ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); | |
1744 | ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Get, modeAndType.type()); | |
1745 | ||
1746 | instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); | |
1747 | if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) | |
1748 | instructions[i + 5].u.watchpointSet = op.watchpointSet; | |
1749 | else if (op.structure) | |
1750 | instructions[i + 5].u.structure.set(*vm(), ownerExecutable, op.structure); | |
1751 | instructions[i + 6].u.pointer = reinterpret_cast<void*>(op.operand); | |
1752 | break; | |
1753 | } | |
1754 | ||
1755 | case op_put_to_scope: { | |
1756 | // put_to_scope scope, id, value, ResolveModeAndType, Structure, Operand | |
1757 | const Identifier& ident = identifier(pc[2].u.operand); | |
1758 | ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); | |
1759 | ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Put, modeAndType.type()); | |
1760 | ||
1761 | instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); | |
1762 | if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) | |
1763 | instructions[i + 5].u.watchpointSet = op.watchpointSet; | |
1764 | else if (op.type == ClosureVar || op.type == ClosureVarWithVarInjectionChecks) { | |
1765 | if (op.watchpointSet) | |
1766 | op.watchpointSet->invalidate(); | |
1767 | } else if (op.structure) | |
1768 | instructions[i + 5].u.structure.set(*vm(), ownerExecutable, op.structure); | |
1769 | instructions[i + 6].u.pointer = reinterpret_cast<void*>(op.operand); | |
1770 | break; | |
1771 | } | |
1772 | ||
1773 | case op_captured_mov: | |
1774 | case op_new_captured_func: { | |
1775 | if (pc[3].u.index == UINT_MAX) { | |
1776 | instructions[i + 3].u.watchpointSet = 0; | |
93a37866 A |
1777 | break; |
1778 | } | |
81345200 A |
1779 | StringImpl* uid = identifier(pc[3].u.index).impl(); |
1780 | RELEASE_ASSERT(didCloneSymbolTable); | |
1781 | ConcurrentJITLocker locker(m_symbolTable->m_lock); | |
1782 | SymbolTable::Map::iterator iter = m_symbolTable->find(locker, uid); | |
1783 | ASSERT(iter != m_symbolTable->end(locker)); | |
1784 | iter->value.prepareToWatch(symbolTable()); | |
1785 | instructions[i + 3].u.watchpointSet = iter->value.watchpointSet(); | |
93a37866 A |
1786 | break; |
1787 | } | |
1788 | ||
1789 | case op_debug: { | |
81345200 A |
1790 | if (pc[1].u.index == DidReachBreakpoint) |
1791 | m_hasDebuggerStatement = true; | |
93a37866 A |
1792 | break; |
1793 | } | |
1794 | ||
1795 | default: | |
1796 | break; | |
1797 | } | |
1798 | i += opLength; | |
1799 | } | |
1800 | m_instructions = WTF::RefCountedArray<Instruction>(instructions); | |
1801 | ||
1802 | // Set optimization thresholds only after m_instructions is initialized, since these | |
1803 | // rely on the instruction count (and are in theory permitted to also inspect the | |
1804 | // instruction stream to more accurate assess the cost of tier-up). | |
1805 | optimizeAfterWarmUp(); | |
1806 | jitAfterWarmUp(); | |
1807 | ||
81345200 A |
1808 | // If the concurrent thread will want the code block's hash, then compute it here |
1809 | // synchronously. | |
1810 | if (Options::alwaysComputeHash()) | |
1811 | hash(); | |
1812 | ||
93a37866 A |
1813 | if (Options::dumpGeneratedBytecodes()) |
1814 | dumpBytecode(); | |
81345200 A |
1815 | |
1816 | m_heap->m_codeBlocks.add(this); | |
1817 | m_heap->reportExtraMemoryCost(sizeof(CodeBlock) + m_instructions.size() * sizeof(Instruction)); | |
9dae56ea A |
1818 | } |
1819 | ||
1820 | CodeBlock::~CodeBlock() | |
1821 | { | |
93a37866 A |
1822 | if (m_vm->m_perBytecodeProfiler) |
1823 | m_vm->m_perBytecodeProfiler->notifyDestruction(this); | |
1824 | ||
6fe7ccc8 A |
1825 | #if ENABLE(VERBOSE_VALUE_PROFILE) |
1826 | dumpValueProfiles(); | |
1827 | #endif | |
6fe7ccc8 A |
1828 | while (m_incomingLLIntCalls.begin() != m_incomingLLIntCalls.end()) |
1829 | m_incomingLLIntCalls.begin()->remove(); | |
4e4e5a6f | 1830 | #if ENABLE(JIT) |
6fe7ccc8 A |
1831 | // We may be destroyed before any CodeBlocks that refer to us are destroyed. |
1832 | // Consider that two CodeBlocks become unreachable at the same time. There | |
1833 | // is no guarantee about the order in which the CodeBlocks are destroyed. | |
1834 | // So, if we don't remove incoming calls, and get destroyed before the | |
1835 | // CodeBlock(s) that have calls into us, then the CallLinkInfo vector's | |
1836 | // destructor will try to remove nodes from our (no longer valid) linked list. | |
1837 | while (m_incomingCalls.begin() != m_incomingCalls.end()) | |
1838 | m_incomingCalls.begin()->remove(); | |
1839 | ||
1840 | // Note that our outgoing calls will be removed from other CodeBlocks' | |
1841 | // m_incomingCalls linked lists through the execution of the ~CallLinkInfo | |
1842 | // destructors. | |
1843 | ||
81345200 A |
1844 | for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) |
1845 | (*iter)->deref(); | |
4e4e5a6f | 1846 | #endif // ENABLE(JIT) |
9dae56ea A |
1847 | } |
1848 | ||
6fe7ccc8 A |
1849 | void CodeBlock::setNumParameters(int newValue) |
1850 | { | |
1851 | m_numParameters = newValue; | |
1852 | ||
93a37866 | 1853 | m_argumentValueProfiles.resizeToFit(newValue); |
9dae56ea A |
1854 | } |
1855 | ||
14957cd0 | 1856 | void EvalCodeCache::visitAggregate(SlotVisitor& visitor) |
9dae56ea | 1857 | { |
14957cd0 A |
1858 | EvalCacheMap::iterator end = m_cacheMap.end(); |
1859 | for (EvalCacheMap::iterator ptr = m_cacheMap.begin(); ptr != end; ++ptr) | |
93a37866 | 1860 | visitor.append(&ptr->value); |
9dae56ea A |
1861 | } |
1862 | ||
81345200 A |
1863 | CodeBlock* CodeBlock::specialOSREntryBlockOrNull() |
1864 | { | |
1865 | #if ENABLE(FTL_JIT) | |
1866 | if (jitType() != JITCode::DFGJIT) | |
1867 | return 0; | |
1868 | DFG::JITCode* jitCode = m_jitCode->dfg(); | |
1869 | return jitCode->osrEntryBlock.get(); | |
1870 | #else // ENABLE(FTL_JIT) | |
1871 | return 0; | |
1872 | #endif // ENABLE(FTL_JIT) | |
1873 | } | |
1874 | ||
14957cd0 | 1875 | void CodeBlock::visitAggregate(SlotVisitor& visitor) |
6fe7ccc8 | 1876 | { |
81345200 A |
1877 | #if ENABLE(PARALLEL_GC) |
1878 | // I may be asked to scan myself more than once, and it may even happen concurrently. | |
1879 | // To this end, use a CAS loop to check if I've been called already. Only one thread | |
1880 | // may proceed past this point - whichever one wins the CAS race. | |
1881 | unsigned oldValue; | |
1882 | do { | |
1883 | oldValue = m_visitAggregateHasBeenCalled; | |
1884 | if (oldValue) { | |
1885 | // Looks like someone else won! Return immediately to ensure that we don't | |
1886 | // trace the same CodeBlock concurrently. Doing so is hazardous since we will | |
1887 | // be mutating the state of ValueProfiles, which contain JSValues, which can | |
1888 | // have word-tearing on 32-bit, leading to awesome timing-dependent crashes | |
1889 | // that are nearly impossible to track down. | |
1890 | ||
1891 | // Also note that it must be safe to return early as soon as we see the | |
1892 | // value true (well, (unsigned)1), since once a GC thread is in this method | |
1893 | // and has won the CAS race (i.e. was responsible for setting the value true) | |
1894 | // it will definitely complete the rest of this method before declaring | |
1895 | // termination. | |
1896 | return; | |
1897 | } | |
1898 | } while (!WTF::weakCompareAndSwap(&m_visitAggregateHasBeenCalled, 0, 1)); | |
1899 | #endif // ENABLE(PARALLEL_GC) | |
6fe7ccc8 A |
1900 | |
1901 | if (!!m_alternative) | |
1902 | m_alternative->visitAggregate(visitor); | |
81345200 A |
1903 | |
1904 | if (CodeBlock* otherBlock = specialOSREntryBlockOrNull()) | |
1905 | otherBlock->visitAggregate(visitor); | |
1906 | ||
1907 | visitor.reportExtraMemoryUsage(ownerExecutable(), sizeof(CodeBlock)); | |
1908 | if (m_jitCode) | |
1909 | visitor.reportExtraMemoryUsage(ownerExecutable(), m_jitCode->size()); | |
1910 | if (m_instructions.size()) { | |
1911 | // Divide by refCount() because m_instructions points to something that is shared | |
1912 | // by multiple CodeBlocks, and we only want to count it towards the heap size once. | |
1913 | // Having each CodeBlock report only its proportional share of the size is one way | |
1914 | // of accomplishing this. | |
1915 | visitor.reportExtraMemoryUsage(ownerExecutable(), m_instructions.size() * sizeof(Instruction) / m_instructions.refCount()); | |
1916 | } | |
6fe7ccc8 | 1917 | |
93a37866 A |
1918 | visitor.append(&m_unlinkedCode); |
1919 | ||
6fe7ccc8 A |
1920 | // There are three things that may use unconditional finalizers: lazy bytecode freeing, |
1921 | // inline cache clearing, and jettisoning. The probability of us wanting to do at | |
1922 | // least one of those things is probably quite close to 1. So we add one no matter what | |
1923 | // and when it runs, it figures out whether it has any work to do. | |
1924 | visitor.addUnconditionalFinalizer(this); | |
1925 | ||
81345200 A |
1926 | m_allTransitionsHaveBeenMarked = false; |
1927 | ||
6fe7ccc8 A |
1928 | if (shouldImmediatelyAssumeLivenessDuringScan()) { |
1929 | // This code block is live, so scan all references strongly and return. | |
1930 | stronglyVisitStrongReferences(visitor); | |
1931 | stronglyVisitWeakReferences(visitor); | |
81345200 | 1932 | propagateTransitions(visitor); |
6fe7ccc8 A |
1933 | return; |
1934 | } | |
1935 | ||
81345200 A |
1936 | // There are two things that we use weak reference harvesters for: DFG fixpoint for |
1937 | // jettisoning, and trying to find structures that would be live based on some | |
1938 | // inline cache. So it makes sense to register them regardless. | |
1939 | visitor.addWeakReferenceHarvester(this); | |
1940 | ||
6fe7ccc8 A |
1941 | #if ENABLE(DFG_JIT) |
1942 | // We get here if we're live in the sense that our owner executable is live, | |
1943 | // but we're not yet live for sure in another sense: we may yet decide that this | |
1944 | // code block should be jettisoned based on its outgoing weak references being | |
1945 | // stale. Set a flag to indicate that we're still assuming that we're dead, and | |
1946 | // perform one round of determining if we're live. The GC may determine, based on | |
1947 | // either us marking additional objects, or by other objects being marked for | |
1948 | // other reasons, that this iteration should run again; it will notify us of this | |
1949 | // decision by calling harvestWeakReferences(). | |
1950 | ||
81345200 | 1951 | m_jitCode->dfgCommon()->livenessHasBeenProved = false; |
6fe7ccc8 | 1952 | |
81345200 A |
1953 | propagateTransitions(visitor); |
1954 | determineLiveness(visitor); | |
6fe7ccc8 | 1955 | #else // ENABLE(DFG_JIT) |
93a37866 | 1956 | RELEASE_ASSERT_NOT_REACHED(); |
6fe7ccc8 A |
1957 | #endif // ENABLE(DFG_JIT) |
1958 | } | |
1959 | ||
81345200 A |
1960 | bool CodeBlock::shouldImmediatelyAssumeLivenessDuringScan() |
1961 | { | |
1962 | #if ENABLE(DFG_JIT) | |
1963 | // Interpreter and Baseline JIT CodeBlocks don't need to be jettisoned when | |
1964 | // their weak references go stale. So if a basline JIT CodeBlock gets | |
1965 | // scanned, we can assume that this means that it's live. | |
1966 | if (!JITCode::isOptimizingJIT(jitType())) | |
1967 | return true; | |
1968 | ||
1969 | // For simplicity, we don't attempt to jettison code blocks during GC if | |
1970 | // they are executing. Instead we strongly mark their weak references to | |
1971 | // allow them to continue to execute soundly. | |
1972 | if (m_mayBeExecuting) | |
1973 | return true; | |
1974 | ||
1975 | if (Options::forceDFGCodeBlockLiveness()) | |
1976 | return true; | |
1977 | ||
1978 | return false; | |
1979 | #else | |
1980 | return true; | |
1981 | #endif | |
1982 | } | |
1983 | ||
1984 | bool CodeBlock::isKnownToBeLiveDuringGC() | |
1985 | { | |
1986 | #if ENABLE(DFG_JIT) | |
1987 | // This should return true for: | |
1988 | // - Code blocks that behave like normal objects - i.e. if they are referenced then they | |
1989 | // are live. | |
1990 | // - Code blocks that were running on the stack. | |
1991 | // - Code blocks that survived the last GC if the current GC is an Eden GC. This is | |
1992 | // because either livenessHasBeenProved would have survived as true or m_mayBeExecuting | |
1993 | // would survive as true. | |
1994 | // - Code blocks that don't have any dead weak references. | |
1995 | ||
1996 | return shouldImmediatelyAssumeLivenessDuringScan() | |
1997 | || m_jitCode->dfgCommon()->livenessHasBeenProved; | |
1998 | #else | |
1999 | return true; | |
2000 | #endif | |
2001 | } | |
2002 | ||
2003 | void CodeBlock::propagateTransitions(SlotVisitor& visitor) | |
6fe7ccc8 A |
2004 | { |
2005 | UNUSED_PARAM(visitor); | |
81345200 A |
2006 | |
2007 | if (m_allTransitionsHaveBeenMarked) | |
2008 | return; | |
2009 | ||
2010 | bool allAreMarkedSoFar = true; | |
2011 | ||
2012 | Interpreter* interpreter = m_vm->interpreter; | |
2013 | if (jitType() == JITCode::InterpreterThunk) { | |
2014 | const Vector<unsigned>& propertyAccessInstructions = m_unlinkedCode->propertyAccessInstructions(); | |
2015 | for (size_t i = 0; i < propertyAccessInstructions.size(); ++i) { | |
2016 | Instruction* instruction = &instructions()[propertyAccessInstructions[i]]; | |
2017 | switch (interpreter->getOpcodeID(instruction[0].u.opcode)) { | |
2018 | case op_put_by_id_transition_direct: | |
2019 | case op_put_by_id_transition_normal: | |
2020 | case op_put_by_id_transition_direct_out_of_line: | |
2021 | case op_put_by_id_transition_normal_out_of_line: { | |
2022 | if (Heap::isMarked(instruction[4].u.structure.get())) | |
2023 | visitor.append(&instruction[6].u.structure); | |
2024 | else | |
2025 | allAreMarkedSoFar = false; | |
2026 | break; | |
2027 | } | |
2028 | default: | |
2029 | break; | |
2030 | } | |
2031 | } | |
2032 | } | |
2033 | ||
2034 | #if ENABLE(JIT) | |
2035 | if (JITCode::isJIT(jitType())) { | |
2036 | for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) { | |
2037 | StructureStubInfo& stubInfo = **iter; | |
2038 | switch (stubInfo.accessType) { | |
2039 | case access_put_by_id_transition_normal: | |
2040 | case access_put_by_id_transition_direct: { | |
2041 | JSCell* origin = stubInfo.codeOrigin.codeOriginOwner(); | |
2042 | if ((!origin || Heap::isMarked(origin)) | |
2043 | && Heap::isMarked(stubInfo.u.putByIdTransition.previousStructure.get())) | |
2044 | visitor.append(&stubInfo.u.putByIdTransition.structure); | |
2045 | else | |
2046 | allAreMarkedSoFar = false; | |
2047 | break; | |
2048 | } | |
2049 | ||
2050 | case access_put_by_id_list: { | |
2051 | PolymorphicPutByIdList* list = stubInfo.u.putByIdList.list; | |
2052 | JSCell* origin = stubInfo.codeOrigin.codeOriginOwner(); | |
2053 | if (origin && !Heap::isMarked(origin)) { | |
2054 | allAreMarkedSoFar = false; | |
2055 | break; | |
2056 | } | |
2057 | for (unsigned j = list->size(); j--;) { | |
2058 | PutByIdAccess& access = list->m_list[j]; | |
2059 | if (!access.isTransition()) | |
2060 | continue; | |
2061 | if (Heap::isMarked(access.oldStructure())) | |
2062 | visitor.append(&access.m_newStructure); | |
2063 | else | |
2064 | allAreMarkedSoFar = false; | |
2065 | } | |
2066 | break; | |
2067 | } | |
2068 | ||
2069 | default: | |
2070 | break; | |
2071 | } | |
2072 | } | |
2073 | } | |
2074 | #endif // ENABLE(JIT) | |
6fe7ccc8 A |
2075 | |
2076 | #if ENABLE(DFG_JIT) | |
81345200 A |
2077 | if (JITCode::isOptimizingJIT(jitType())) { |
2078 | DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); | |
2079 | for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { | |
2080 | if ((!dfgCommon->transitions[i].m_codeOrigin | |
2081 | || Heap::isMarked(dfgCommon->transitions[i].m_codeOrigin.get())) | |
2082 | && Heap::isMarked(dfgCommon->transitions[i].m_from.get())) { | |
6fe7ccc8 A |
2083 | // If the following three things are live, then the target of the |
2084 | // transition is also live: | |
2085 | // - This code block. We know it's live already because otherwise | |
2086 | // we wouldn't be scanning ourselves. | |
2087 | // - The code origin of the transition. Transitions may arise from | |
2088 | // code that was inlined. They are not relevant if the user's | |
2089 | // object that is required for the inlinee to run is no longer | |
2090 | // live. | |
2091 | // - The source of the transition. The transition checks if some | |
2092 | // heap location holds the source, and if so, stores the target. | |
2093 | // Hence the source must be live for the transition to be live. | |
81345200 | 2094 | visitor.append(&dfgCommon->transitions[i].m_to); |
6fe7ccc8 A |
2095 | } else |
2096 | allAreMarkedSoFar = false; | |
2097 | } | |
6fe7ccc8 | 2098 | } |
81345200 A |
2099 | #endif // ENABLE(DFG_JIT) |
2100 | ||
2101 | if (allAreMarkedSoFar) | |
2102 | m_allTransitionsHaveBeenMarked = true; | |
2103 | } | |
2104 | ||
2105 | void CodeBlock::determineLiveness(SlotVisitor& visitor) | |
2106 | { | |
2107 | UNUSED_PARAM(visitor); | |
6fe7ccc8 | 2108 | |
81345200 A |
2109 | if (shouldImmediatelyAssumeLivenessDuringScan()) |
2110 | return; | |
2111 | ||
2112 | #if ENABLE(DFG_JIT) | |
6fe7ccc8 | 2113 | // Check if we have any remaining work to do. |
81345200 A |
2114 | DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); |
2115 | if (dfgCommon->livenessHasBeenProved) | |
6fe7ccc8 A |
2116 | return; |
2117 | ||
2118 | // Now check all of our weak references. If all of them are live, then we | |
2119 | // have proved liveness and so we scan our strong references. If at end of | |
2120 | // GC we still have not proved liveness, then this code block is toast. | |
2121 | bool allAreLiveSoFar = true; | |
81345200 A |
2122 | for (unsigned i = 0; i < dfgCommon->weakReferences.size(); ++i) { |
2123 | if (!Heap::isMarked(dfgCommon->weakReferences[i].get())) { | |
6fe7ccc8 A |
2124 | allAreLiveSoFar = false; |
2125 | break; | |
2126 | } | |
2127 | } | |
2128 | ||
2129 | // If some weak references are dead, then this fixpoint iteration was | |
2130 | // unsuccessful. | |
2131 | if (!allAreLiveSoFar) | |
2132 | return; | |
2133 | ||
2134 | // All weak references are live. Record this information so we don't | |
2135 | // come back here again, and scan the strong references. | |
81345200 | 2136 | dfgCommon->livenessHasBeenProved = true; |
6fe7ccc8 A |
2137 | stronglyVisitStrongReferences(visitor); |
2138 | #endif // ENABLE(DFG_JIT) | |
2139 | } | |
2140 | ||
2141 | void CodeBlock::visitWeakReferences(SlotVisitor& visitor) | |
2142 | { | |
81345200 A |
2143 | propagateTransitions(visitor); |
2144 | determineLiveness(visitor); | |
6fe7ccc8 A |
2145 | } |
2146 | ||
93a37866 A |
2147 | void CodeBlock::finalizeUnconditionally() |
2148 | { | |
93a37866 | 2149 | Interpreter* interpreter = m_vm->interpreter; |
81345200 | 2150 | if (JITCode::couldBeInterpreted(jitType())) { |
93a37866 A |
2151 | const Vector<unsigned>& propertyAccessInstructions = m_unlinkedCode->propertyAccessInstructions(); |
2152 | for (size_t size = propertyAccessInstructions.size(), i = 0; i < size; ++i) { | |
2153 | Instruction* curInstruction = &instructions()[propertyAccessInstructions[i]]; | |
6fe7ccc8 A |
2154 | switch (interpreter->getOpcodeID(curInstruction[0].u.opcode)) { |
2155 | case op_get_by_id: | |
93a37866 | 2156 | case op_get_by_id_out_of_line: |
6fe7ccc8 | 2157 | case op_put_by_id: |
93a37866 | 2158 | case op_put_by_id_out_of_line: |
6fe7ccc8 A |
2159 | if (!curInstruction[4].u.structure || Heap::isMarked(curInstruction[4].u.structure.get())) |
2160 | break; | |
81345200 | 2161 | if (Options::verboseOSR()) |
93a37866 | 2162 | dataLogF("Clearing LLInt property access with structure %p.\n", curInstruction[4].u.structure.get()); |
6fe7ccc8 A |
2163 | curInstruction[4].u.structure.clear(); |
2164 | curInstruction[5].u.operand = 0; | |
2165 | break; | |
2166 | case op_put_by_id_transition_direct: | |
2167 | case op_put_by_id_transition_normal: | |
93a37866 A |
2168 | case op_put_by_id_transition_direct_out_of_line: |
2169 | case op_put_by_id_transition_normal_out_of_line: | |
6fe7ccc8 A |
2170 | if (Heap::isMarked(curInstruction[4].u.structure.get()) |
2171 | && Heap::isMarked(curInstruction[6].u.structure.get()) | |
2172 | && Heap::isMarked(curInstruction[7].u.structureChain.get())) | |
2173 | break; | |
81345200 | 2174 | if (Options::verboseOSR()) { |
93a37866 | 2175 | dataLogF("Clearing LLInt put transition with structures %p -> %p, chain %p.\n", |
6fe7ccc8 A |
2176 | curInstruction[4].u.structure.get(), |
2177 | curInstruction[6].u.structure.get(), | |
2178 | curInstruction[7].u.structureChain.get()); | |
2179 | } | |
2180 | curInstruction[4].u.structure.clear(); | |
2181 | curInstruction[6].u.structure.clear(); | |
2182 | curInstruction[7].u.structureChain.clear(); | |
2183 | curInstruction[0].u.opcode = interpreter->getOpcode(op_put_by_id); | |
2184 | break; | |
93a37866 A |
2185 | case op_get_array_length: |
2186 | break; | |
81345200 A |
2187 | case op_to_this: |
2188 | if (!curInstruction[2].u.structure || Heap::isMarked(curInstruction[2].u.structure.get())) | |
2189 | break; | |
2190 | if (Options::verboseOSR()) | |
2191 | dataLogF("Clearing LLInt to_this with structure %p.\n", curInstruction[2].u.structure.get()); | |
2192 | curInstruction[2].u.structure.clear(); | |
2193 | break; | |
2194 | case op_get_callee: | |
2195 | if (!curInstruction[2].u.jsCell || Heap::isMarked(curInstruction[2].u.jsCell.get())) | |
2196 | break; | |
2197 | if (Options::verboseOSR()) | |
2198 | dataLogF("Clearing LLInt get callee with function %p.\n", curInstruction[2].u.jsCell.get()); | |
2199 | curInstruction[2].u.jsCell.clear(); | |
2200 | break; | |
2201 | case op_resolve_scope: { | |
2202 | WriteBarrierBase<JSActivation>& activation = curInstruction[5].u.activation; | |
2203 | if (!activation || Heap::isMarked(activation.get())) | |
2204 | break; | |
2205 | if (Options::verboseOSR()) | |
2206 | dataLogF("Clearing dead activation %p.\n", activation.get()); | |
2207 | activation.clear(); | |
2208 | break; | |
2209 | } | |
2210 | case op_get_from_scope: | |
2211 | case op_put_to_scope: { | |
2212 | ResolveModeAndType modeAndType = | |
2213 | ResolveModeAndType(curInstruction[4].u.operand); | |
2214 | if (modeAndType.type() == GlobalVar || modeAndType.type() == GlobalVarWithVarInjectionChecks) | |
2215 | continue; | |
2216 | WriteBarrierBase<Structure>& structure = curInstruction[5].u.structure; | |
2217 | if (!structure || Heap::isMarked(structure.get())) | |
2218 | break; | |
2219 | if (Options::verboseOSR()) | |
2220 | dataLogF("Clearing scope access with structure %p.\n", structure.get()); | |
2221 | structure.clear(); | |
2222 | break; | |
2223 | } | |
6fe7ccc8 | 2224 | default: |
93a37866 | 2225 | RELEASE_ASSERT_NOT_REACHED(); |
6fe7ccc8 A |
2226 | } |
2227 | } | |
93a37866 | 2228 | |
6fe7ccc8 A |
2229 | for (unsigned i = 0; i < m_llintCallLinkInfos.size(); ++i) { |
2230 | if (m_llintCallLinkInfos[i].isLinked() && !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) { | |
81345200 | 2231 | if (Options::verboseOSR()) |
93a37866 | 2232 | dataLog("Clearing LLInt call from ", *this, "\n"); |
6fe7ccc8 A |
2233 | m_llintCallLinkInfos[i].unlink(); |
2234 | } | |
2235 | if (!!m_llintCallLinkInfos[i].lastSeenCallee && !Heap::isMarked(m_llintCallLinkInfos[i].lastSeenCallee.get())) | |
2236 | m_llintCallLinkInfos[i].lastSeenCallee.clear(); | |
2237 | } | |
2238 | } | |
6fe7ccc8 A |
2239 | |
2240 | #if ENABLE(DFG_JIT) | |
2241 | // Check if we're not live. If we are, then jettison. | |
81345200 A |
2242 | if (!isKnownToBeLiveDuringGC()) { |
2243 | if (Options::verboseOSR()) | |
93a37866 A |
2244 | dataLog(*this, " has dead weak references, jettisoning during GC.\n"); |
2245 | ||
2246 | if (DFG::shouldShowDisassembly()) { | |
2247 | dataLog(*this, " will be jettisoned because of the following dead references:\n"); | |
81345200 A |
2248 | DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); |
2249 | for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { | |
2250 | DFG::WeakReferenceTransition& transition = dfgCommon->transitions[i]; | |
93a37866 A |
2251 | JSCell* origin = transition.m_codeOrigin.get(); |
2252 | JSCell* from = transition.m_from.get(); | |
2253 | JSCell* to = transition.m_to.get(); | |
2254 | if ((!origin || Heap::isMarked(origin)) && Heap::isMarked(from)) | |
2255 | continue; | |
81345200 | 2256 | dataLog(" Transition under ", RawPointer(origin), ", ", RawPointer(from), " -> ", RawPointer(to), ".\n"); |
93a37866 | 2257 | } |
81345200 A |
2258 | for (unsigned i = 0; i < dfgCommon->weakReferences.size(); ++i) { |
2259 | JSCell* weak = dfgCommon->weakReferences[i].get(); | |
93a37866 A |
2260 | if (Heap::isMarked(weak)) |
2261 | continue; | |
81345200 | 2262 | dataLog(" Weak reference ", RawPointer(weak), ".\n"); |
93a37866 A |
2263 | } |
2264 | } | |
6fe7ccc8 | 2265 | |
81345200 | 2266 | jettison(Profiler::JettisonDueToWeakReference); |
6fe7ccc8 A |
2267 | return; |
2268 | } | |
2269 | #endif // ENABLE(DFG_JIT) | |
93a37866 | 2270 | |
6fe7ccc8 A |
2271 | #if ENABLE(JIT) |
2272 | // Handle inline caches. | |
81345200 | 2273 | if (!!jitCode()) { |
6fe7ccc8 | 2274 | RepatchBuffer repatchBuffer(this); |
81345200 A |
2275 | |
2276 | for (auto iter = callLinkInfosBegin(); !!iter; ++iter) | |
2277 | (*iter)->visitWeak(repatchBuffer); | |
2278 | ||
2279 | for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) { | |
2280 | StructureStubInfo& stubInfo = **iter; | |
6fe7ccc8 | 2281 | |
81345200 | 2282 | if (stubInfo.visitWeakReferences(repatchBuffer)) |
6fe7ccc8 A |
2283 | continue; |
2284 | ||
93a37866 | 2285 | resetStubDuringGCInternal(repatchBuffer, stubInfo); |
6fe7ccc8 | 2286 | } |
93a37866 A |
2287 | } |
2288 | #endif | |
2289 | } | |
6fe7ccc8 | 2290 | |
81345200 A |
2291 | void CodeBlock::getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result) |
2292 | { | |
2293 | #if ENABLE(JIT) | |
2294 | toHashMap(m_stubInfos, getStructureStubInfoCodeOrigin, result); | |
2295 | #else | |
2296 | UNUSED_PARAM(result); | |
2297 | #endif | |
2298 | } | |
2299 | ||
2300 | void CodeBlock::getStubInfoMap(StubInfoMap& result) | |
2301 | { | |
2302 | ConcurrentJITLocker locker(m_lock); | |
2303 | getStubInfoMap(locker, result); | |
2304 | } | |
2305 | ||
2306 | void CodeBlock::getCallLinkInfoMap(const ConcurrentJITLocker&, CallLinkInfoMap& result) | |
2307 | { | |
2308 | #if ENABLE(JIT) | |
2309 | toHashMap(m_callLinkInfos, getCallLinkInfoCodeOrigin, result); | |
2310 | #else | |
2311 | UNUSED_PARAM(result); | |
2312 | #endif | |
2313 | } | |
2314 | ||
2315 | void CodeBlock::getCallLinkInfoMap(CallLinkInfoMap& result) | |
2316 | { | |
2317 | ConcurrentJITLocker locker(m_lock); | |
2318 | getCallLinkInfoMap(locker, result); | |
2319 | } | |
2320 | ||
93a37866 | 2321 | #if ENABLE(JIT) |
81345200 A |
2322 | StructureStubInfo* CodeBlock::addStubInfo() |
2323 | { | |
2324 | ConcurrentJITLocker locker(m_lock); | |
2325 | return m_stubInfos.add(); | |
2326 | } | |
2327 | ||
2328 | CallLinkInfo* CodeBlock::addCallLinkInfo() | |
2329 | { | |
2330 | ConcurrentJITLocker locker(m_lock); | |
2331 | return m_callLinkInfos.add(); | |
2332 | } | |
2333 | ||
93a37866 A |
2334 | void CodeBlock::resetStub(StructureStubInfo& stubInfo) |
2335 | { | |
2336 | if (stubInfo.accessType == access_unset) | |
2337 | return; | |
2338 | ||
81345200 A |
2339 | ConcurrentJITLocker locker(m_lock); |
2340 | ||
93a37866 A |
2341 | RepatchBuffer repatchBuffer(this); |
2342 | resetStubInternal(repatchBuffer, stubInfo); | |
2343 | } | |
6fe7ccc8 | 2344 | |
93a37866 A |
2345 | void CodeBlock::resetStubInternal(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) |
2346 | { | |
2347 | AccessType accessType = static_cast<AccessType>(stubInfo.accessType); | |
2348 | ||
81345200 A |
2349 | if (Options::verboseOSR()) { |
2350 | // This can be called from GC destructor calls, so we don't try to do a full dump | |
2351 | // of the CodeBlock. | |
2352 | dataLog("Clearing structure cache (kind ", static_cast<int>(stubInfo.accessType), ") in ", RawPointer(this), ".\n"); | |
2353 | } | |
93a37866 | 2354 | |
81345200 A |
2355 | RELEASE_ASSERT(JITCode::isJIT(jitType())); |
2356 | ||
2357 | if (isGetByIdAccess(accessType)) | |
2358 | resetGetByID(repatchBuffer, stubInfo); | |
2359 | else if (isPutByIdAccess(accessType)) | |
2360 | resetPutByID(repatchBuffer, stubInfo); | |
2361 | else { | |
2362 | RELEASE_ASSERT(isInAccess(accessType)); | |
2363 | resetIn(repatchBuffer, stubInfo); | |
6fe7ccc8 | 2364 | } |
93a37866 A |
2365 | |
2366 | stubInfo.reset(); | |
2367 | } | |
2368 | ||
2369 | void CodeBlock::resetStubDuringGCInternal(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) | |
2370 | { | |
2371 | resetStubInternal(repatchBuffer, stubInfo); | |
2372 | stubInfo.resetByGC = true; | |
6fe7ccc8 | 2373 | } |
81345200 A |
2374 | |
2375 | CallLinkInfo* CodeBlock::getCallLinkInfoForBytecodeIndex(unsigned index) | |
2376 | { | |
2377 | for (auto iter = m_callLinkInfos.begin(); !!iter; ++iter) { | |
2378 | if ((*iter)->codeOrigin == CodeOrigin(index)) | |
2379 | return *iter; | |
2380 | } | |
2381 | return nullptr; | |
2382 | } | |
93a37866 | 2383 | #endif |
6fe7ccc8 A |
2384 | |
2385 | void CodeBlock::stronglyVisitStrongReferences(SlotVisitor& visitor) | |
9dae56ea | 2386 | { |
14957cd0 A |
2387 | visitor.append(&m_globalObject); |
2388 | visitor.append(&m_ownerExecutable); | |
81345200 | 2389 | visitor.append(&m_symbolTable); |
93a37866 A |
2390 | visitor.append(&m_unlinkedCode); |
2391 | if (m_rareData) | |
14957cd0 | 2392 | m_rareData->m_evalCodeCache.visitAggregate(visitor); |
14957cd0 | 2393 | visitor.appendValues(m_constantRegisters.data(), m_constantRegisters.size()); |
f9bf01c6 | 2394 | for (size_t i = 0; i < m_functionExprs.size(); ++i) |
14957cd0 | 2395 | visitor.append(&m_functionExprs[i]); |
f9bf01c6 | 2396 | for (size_t i = 0; i < m_functionDecls.size(); ++i) |
14957cd0 | 2397 | visitor.append(&m_functionDecls[i]); |
93a37866 A |
2398 | for (unsigned i = 0; i < m_objectAllocationProfiles.size(); ++i) |
2399 | m_objectAllocationProfiles[i].visitAggregate(visitor); | |
6fe7ccc8 | 2400 | |
81345200 A |
2401 | #if ENABLE(DFG_JIT) |
2402 | if (JITCode::isOptimizingJIT(jitType())) { | |
2403 | DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); | |
2404 | if (dfgCommon->inlineCallFrames.get()) | |
2405 | dfgCommon->inlineCallFrames->visitAggregate(visitor); | |
2406 | } | |
2407 | #endif | |
2408 | ||
2409 | updateAllPredictions(); | |
9dae56ea A |
2410 | } |
2411 | ||
6fe7ccc8 A |
2412 | void CodeBlock::stronglyVisitWeakReferences(SlotVisitor& visitor) |
2413 | { | |
2414 | UNUSED_PARAM(visitor); | |
2415 | ||
2416 | #if ENABLE(DFG_JIT) | |
81345200 | 2417 | if (!JITCode::isOptimizingJIT(jitType())) |
6fe7ccc8 | 2418 | return; |
81345200 A |
2419 | |
2420 | DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); | |
6fe7ccc8 | 2421 | |
81345200 A |
2422 | for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { |
2423 | if (!!dfgCommon->transitions[i].m_codeOrigin) | |
2424 | visitor.append(&dfgCommon->transitions[i].m_codeOrigin); // Almost certainly not necessary, since the code origin should also be a weak reference. Better to be safe, though. | |
2425 | visitor.append(&dfgCommon->transitions[i].m_from); | |
2426 | visitor.append(&dfgCommon->transitions[i].m_to); | |
6fe7ccc8 A |
2427 | } |
2428 | ||
81345200 A |
2429 | for (unsigned i = 0; i < dfgCommon->weakReferences.size(); ++i) |
2430 | visitor.append(&dfgCommon->weakReferences[i]); | |
6fe7ccc8 A |
2431 | #endif |
2432 | } | |
2433 | ||
81345200 A |
2434 | CodeBlock* CodeBlock::baselineAlternative() |
2435 | { | |
2436 | #if ENABLE(JIT) | |
2437 | CodeBlock* result = this; | |
2438 | while (result->alternative()) | |
2439 | result = result->alternative(); | |
2440 | RELEASE_ASSERT(result); | |
2441 | RELEASE_ASSERT(JITCode::isBaselineCode(result->jitType()) || result->jitType() == JITCode::None); | |
2442 | return result; | |
2443 | #else | |
2444 | return this; | |
2445 | #endif | |
2446 | } | |
2447 | ||
2448 | CodeBlock* CodeBlock::baselineVersion() | |
2449 | { | |
2450 | #if ENABLE(JIT) | |
2451 | if (JITCode::isBaselineCode(jitType())) | |
2452 | return this; | |
2453 | CodeBlock* result = replacement(); | |
2454 | if (!result) { | |
2455 | // This can happen if we're creating the original CodeBlock for an executable. | |
2456 | // Assume that we're the baseline CodeBlock. | |
2457 | RELEASE_ASSERT(jitType() == JITCode::None); | |
2458 | return this; | |
2459 | } | |
2460 | result = result->baselineAlternative(); | |
2461 | return result; | |
2462 | #else | |
2463 | return this; | |
2464 | #endif | |
2465 | } | |
2466 | ||
2467 | #if ENABLE(JIT) | |
2468 | bool CodeBlock::hasOptimizedReplacement(JITCode::JITType typeToReplace) | |
2469 | { | |
2470 | return JITCode::isHigherTier(replacement()->jitType(), typeToReplace); | |
2471 | } | |
2472 | ||
2473 | bool CodeBlock::hasOptimizedReplacement() | |
2474 | { | |
2475 | return hasOptimizedReplacement(jitType()); | |
2476 | } | |
2477 | #endif | |
2478 | ||
2479 | bool CodeBlock::isCaptured(VirtualRegister operand, InlineCallFrame* inlineCallFrame) const | |
2480 | { | |
2481 | if (operand.isArgument()) | |
2482 | return operand.toArgument() && usesArguments(); | |
2483 | ||
2484 | if (inlineCallFrame) | |
2485 | return inlineCallFrame->capturedVars.get(operand.toLocal()); | |
2486 | ||
2487 | // The activation object isn't in the captured region, but it's "captured" | |
2488 | // in the sense that stores to its location can be observed indirectly. | |
2489 | if (needsActivation() && operand == activationRegister()) | |
2490 | return true; | |
2491 | ||
2492 | // Ditto for the arguments object. | |
2493 | if (usesArguments() && operand == argumentsRegister()) | |
2494 | return true; | |
2495 | if (usesArguments() && operand == unmodifiedArgumentsRegister(argumentsRegister())) | |
2496 | return true; | |
2497 | ||
2498 | // We're in global code so there are no locals to capture | |
2499 | if (!symbolTable()) | |
2500 | return false; | |
2501 | ||
2502 | return symbolTable()->isCaptured(operand.offset()); | |
2503 | } | |
2504 | ||
2505 | int CodeBlock::framePointerOffsetToGetActivationRegisters(int machineCaptureStart) | |
2506 | { | |
2507 | // We'll be adding this to the stack pointer to get a registers pointer that looks | |
2508 | // like it would have looked in the baseline engine. For example, if bytecode would | |
2509 | // have put the first captured variable at offset -5 but we put it at offset -1, then | |
2510 | // we'll have an offset of 4. | |
2511 | int32_t offset = 0; | |
2512 | ||
2513 | // Compute where we put the captured variables. This offset will point the registers | |
2514 | // pointer directly at the first captured var. | |
2515 | offset += machineCaptureStart; | |
2516 | ||
2517 | // Now compute the offset needed to make the runtime see the captured variables at the | |
2518 | // same offset that the bytecode would have used. | |
2519 | offset -= symbolTable()->captureStart(); | |
2520 | ||
2521 | return offset; | |
2522 | } | |
2523 | ||
2524 | int CodeBlock::framePointerOffsetToGetActivationRegisters() | |
2525 | { | |
2526 | if (!JITCode::isOptimizingJIT(jitType())) | |
2527 | return 0; | |
2528 | #if ENABLE(DFG_JIT) | |
2529 | return framePointerOffsetToGetActivationRegisters(jitCode()->dfgCommon()->machineCaptureStart); | |
2530 | #else | |
2531 | RELEASE_ASSERT_NOT_REACHED(); | |
2532 | return 0; | |
2533 | #endif | |
2534 | } | |
2535 | ||
9dae56ea A |
2536 | HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset) |
2537 | { | |
93a37866 | 2538 | RELEASE_ASSERT(bytecodeOffset < instructions().size()); |
9dae56ea A |
2539 | |
2540 | if (!m_rareData) | |
2541 | return 0; | |
2542 | ||
2543 | Vector<HandlerInfo>& exceptionHandlers = m_rareData->m_exceptionHandlers; | |
2544 | for (size_t i = 0; i < exceptionHandlers.size(); ++i) { | |
2545 | // Handlers are ordered innermost first, so the first handler we encounter | |
2546 | // that contains the source address is the correct handler to use. | |
93a37866 | 2547 | if (exceptionHandlers[i].start <= bytecodeOffset && exceptionHandlers[i].end > bytecodeOffset) |
9dae56ea A |
2548 | return &exceptionHandlers[i]; |
2549 | } | |
2550 | ||
2551 | return 0; | |
2552 | } | |
2553 | ||
93a37866 | 2554 | unsigned CodeBlock::lineNumberForBytecodeOffset(unsigned bytecodeOffset) |
9dae56ea | 2555 | { |
93a37866 A |
2556 | RELEASE_ASSERT(bytecodeOffset < instructions().size()); |
2557 | return m_ownerExecutable->lineNo() + m_unlinkedCode->lineNumberForBytecodeOffset(bytecodeOffset); | |
9dae56ea A |
2558 | } |
2559 | ||
93a37866 | 2560 | unsigned CodeBlock::columnNumberForBytecodeOffset(unsigned bytecodeOffset) |
9dae56ea | 2561 | { |
93a37866 A |
2562 | int divot; |
2563 | int startOffset; | |
2564 | int endOffset; | |
2565 | unsigned line; | |
2566 | unsigned column; | |
2567 | expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); | |
2568 | return column; | |
9dae56ea | 2569 | } |
9dae56ea | 2570 | |
93a37866 | 2571 | void CodeBlock::expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) |
9dae56ea | 2572 | { |
93a37866 A |
2573 | m_unlinkedCode->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); |
2574 | divot += m_sourceOffset; | |
2575 | column += line ? 1 : firstLineColumnOffset(); | |
2576 | line += m_ownerExecutable->lineNo(); | |
9dae56ea | 2577 | } |
9dae56ea | 2578 | |
81345200 A |
2579 | bool CodeBlock::hasOpDebugForLineAndColumn(unsigned line, unsigned column) |
2580 | { | |
2581 | Interpreter* interpreter = vm()->interpreter; | |
2582 | const Instruction* begin = instructions().begin(); | |
2583 | const Instruction* end = instructions().end(); | |
2584 | for (const Instruction* it = begin; it != end;) { | |
2585 | OpcodeID opcodeID = interpreter->getOpcodeID(it->u.opcode); | |
2586 | if (opcodeID == op_debug) { | |
2587 | unsigned bytecodeOffset = it - begin; | |
2588 | int unused; | |
2589 | unsigned opDebugLine; | |
2590 | unsigned opDebugColumn; | |
2591 | expressionRangeForBytecodeOffset(bytecodeOffset, unused, unused, unused, opDebugLine, opDebugColumn); | |
2592 | if (line == opDebugLine && (column == Breakpoint::unspecifiedColumn || column == opDebugColumn)) | |
2593 | return true; | |
2594 | } | |
2595 | it += opcodeLengths[opcodeID]; | |
2596 | } | |
2597 | return false; | |
2598 | } | |
2599 | ||
93a37866 | 2600 | void CodeBlock::shrinkToFit(ShrinkMode shrinkMode) |
9dae56ea | 2601 | { |
93a37866 A |
2602 | m_rareCaseProfiles.shrinkToFit(); |
2603 | m_specialFastCaseProfiles.shrinkToFit(); | |
93a37866 A |
2604 | |
2605 | if (shrinkMode == EarlyShrink) { | |
93a37866 | 2606 | m_constantRegisters.shrinkToFit(); |
81345200 A |
2607 | |
2608 | if (m_rareData) { | |
2609 | m_rareData->m_switchJumpTables.shrinkToFit(); | |
2610 | m_rareData->m_stringSwitchJumpTables.shrinkToFit(); | |
2611 | } | |
93a37866 | 2612 | } // else don't shrink these, because we would have already pointed pointers into these tables. |
14957cd0 A |
2613 | } |
2614 | ||
81345200 | 2615 | unsigned CodeBlock::addOrFindConstant(JSValue v) |
14957cd0 | 2616 | { |
81345200 A |
2617 | unsigned result; |
2618 | if (findConstant(v, result)) | |
2619 | return result; | |
2620 | return addConstant(v); | |
14957cd0 | 2621 | } |
6fe7ccc8 | 2622 | |
81345200 | 2623 | bool CodeBlock::findConstant(JSValue v, unsigned& index) |
6fe7ccc8 A |
2624 | { |
2625 | unsigned numberOfConstants = numberOfConstantRegisters(); | |
2626 | for (unsigned i = 0; i < numberOfConstants; ++i) { | |
81345200 A |
2627 | if (getConstant(FirstConstantRegisterIndex + i) == v) { |
2628 | index = i; | |
2629 | return true; | |
2630 | } | |
6fe7ccc8 | 2631 | } |
81345200 A |
2632 | index = numberOfConstants; |
2633 | return false; | |
6fe7ccc8 A |
2634 | } |
2635 | ||
9dae56ea | 2636 | #if ENABLE(JIT) |
14957cd0 A |
2637 | void CodeBlock::unlinkCalls() |
2638 | { | |
6fe7ccc8 A |
2639 | if (!!m_alternative) |
2640 | m_alternative->unlinkCalls(); | |
6fe7ccc8 A |
2641 | for (size_t i = 0; i < m_llintCallLinkInfos.size(); ++i) { |
2642 | if (m_llintCallLinkInfos[i].isLinked()) | |
2643 | m_llintCallLinkInfos[i].unlink(); | |
2644 | } | |
81345200 | 2645 | if (m_callLinkInfos.isEmpty()) |
14957cd0 | 2646 | return; |
93a37866 | 2647 | if (!m_vm->canUseJIT()) |
14957cd0 A |
2648 | return; |
2649 | RepatchBuffer repatchBuffer(this); | |
81345200 A |
2650 | for (auto iter = m_callLinkInfos.begin(); !!iter; ++iter) { |
2651 | CallLinkInfo& info = **iter; | |
2652 | if (!info.isLinked()) | |
14957cd0 | 2653 | continue; |
81345200 | 2654 | info.unlink(repatchBuffer); |
9dae56ea A |
2655 | } |
2656 | } | |
6fe7ccc8 | 2657 | |
81345200 A |
2658 | void CodeBlock::linkIncomingCall(ExecState* callerFrame, CallLinkInfo* incoming) |
2659 | { | |
2660 | noticeIncomingCall(callerFrame); | |
2661 | m_incomingCalls.push(incoming); | |
2662 | } | |
2663 | #endif // ENABLE(JIT) | |
2664 | ||
6fe7ccc8 A |
2665 | void CodeBlock::unlinkIncomingCalls() |
2666 | { | |
6fe7ccc8 A |
2667 | while (m_incomingLLIntCalls.begin() != m_incomingLLIntCalls.end()) |
2668 | m_incomingLLIntCalls.begin()->unlink(); | |
81345200 | 2669 | #if ENABLE(JIT) |
6fe7ccc8 A |
2670 | if (m_incomingCalls.isEmpty()) |
2671 | return; | |
2672 | RepatchBuffer repatchBuffer(this); | |
2673 | while (m_incomingCalls.begin() != m_incomingCalls.end()) | |
81345200 A |
2674 | m_incomingCalls.begin()->unlink(repatchBuffer); |
2675 | #endif // ENABLE(JIT) | |
93a37866 A |
2676 | } |
2677 | ||
81345200 | 2678 | void CodeBlock::linkIncomingCall(ExecState* callerFrame, LLIntCallLinkInfo* incoming) |
93a37866 | 2679 | { |
81345200 A |
2680 | noticeIncomingCall(callerFrame); |
2681 | m_incomingLLIntCalls.push(incoming); | |
93a37866 | 2682 | } |
14957cd0 A |
2683 | |
2684 | void CodeBlock::clearEvalCache() | |
2685 | { | |
6fe7ccc8 A |
2686 | if (!!m_alternative) |
2687 | m_alternative->clearEvalCache(); | |
81345200 A |
2688 | if (CodeBlock* otherBlock = specialOSREntryBlockOrNull()) |
2689 | otherBlock->clearEvalCache(); | |
14957cd0 A |
2690 | if (!m_rareData) |
2691 | return; | |
2692 | m_rareData->m_evalCodeCache.clear(); | |
2693 | } | |
9dae56ea | 2694 | |
81345200 | 2695 | void CodeBlock::install() |
6fe7ccc8 | 2696 | { |
81345200 | 2697 | ownerExecutable()->installCode(this); |
6fe7ccc8 A |
2698 | } |
2699 | ||
81345200 | 2700 | PassRefPtr<CodeBlock> CodeBlock::newReplacement() |
6fe7ccc8 | 2701 | { |
81345200 | 2702 | return ownerExecutable()->newReplacementCodeBlockFor(specializationKind()); |
6fe7ccc8 A |
2703 | } |
2704 | ||
81345200 | 2705 | const SlowArgument* CodeBlock::machineSlowArguments() |
6fe7ccc8 | 2706 | { |
81345200 A |
2707 | if (!JITCode::isOptimizingJIT(jitType())) |
2708 | return symbolTable()->slowArguments(); | |
2709 | ||
2710 | #if ENABLE(DFG_JIT) | |
2711 | return jitCode()->dfgCommon()->slowArguments.get(); | |
2712 | #else // ENABLE(DFG_JIT) | |
2713 | return 0; | |
2714 | #endif // ENABLE(DFG_JIT) | |
6fe7ccc8 A |
2715 | } |
2716 | ||
2717 | #if ENABLE(JIT) | |
2718 | CodeBlock* ProgramCodeBlock::replacement() | |
2719 | { | |
81345200 | 2720 | return jsCast<ProgramExecutable*>(ownerExecutable())->codeBlock(); |
6fe7ccc8 A |
2721 | } |
2722 | ||
2723 | CodeBlock* EvalCodeBlock::replacement() | |
2724 | { | |
81345200 | 2725 | return jsCast<EvalExecutable*>(ownerExecutable())->codeBlock(); |
6fe7ccc8 A |
2726 | } |
2727 | ||
2728 | CodeBlock* FunctionCodeBlock::replacement() | |
2729 | { | |
81345200 | 2730 | return jsCast<FunctionExecutable*>(ownerExecutable())->codeBlockFor(m_isConstructor ? CodeForConstruct : CodeForCall); |
6fe7ccc8 A |
2731 | } |
2732 | ||
81345200 | 2733 | DFG::CapabilityLevel ProgramCodeBlock::capabilityLevelInternal() |
6fe7ccc8 | 2734 | { |
81345200 | 2735 | return DFG::programCapabilityLevel(this); |
6fe7ccc8 A |
2736 | } |
2737 | ||
81345200 | 2738 | DFG::CapabilityLevel EvalCodeBlock::capabilityLevelInternal() |
6fe7ccc8 | 2739 | { |
81345200 | 2740 | return DFG::evalCapabilityLevel(this); |
6fe7ccc8 A |
2741 | } |
2742 | ||
81345200 | 2743 | DFG::CapabilityLevel FunctionCodeBlock::capabilityLevelInternal() |
6fe7ccc8 A |
2744 | { |
2745 | if (m_isConstructor) | |
81345200 A |
2746 | return DFG::functionForConstructCapabilityLevel(this); |
2747 | return DFG::functionForCallCapabilityLevel(this); | |
6fe7ccc8 | 2748 | } |
81345200 | 2749 | #endif |
6fe7ccc8 | 2750 | |
81345200 | 2751 | void CodeBlock::jettison(Profiler::JettisonReason reason, ReoptimizationMode mode) |
6fe7ccc8 | 2752 | { |
81345200 A |
2753 | RELEASE_ASSERT(reason != Profiler::NotJettisoned); |
2754 | ||
2755 | #if ENABLE(DFG_JIT) | |
2756 | if (DFG::shouldShowDisassembly()) { | |
2757 | dataLog("Jettisoning ", *this); | |
2758 | if (mode == CountReoptimization) | |
2759 | dataLog(" and counting reoptimization"); | |
2760 | dataLog(" due to ", reason, ".\n"); | |
2761 | } | |
2762 | ||
2763 | DeferGCForAWhile deferGC(*m_heap); | |
2764 | RELEASE_ASSERT(JITCode::isOptimizingJIT(jitType())); | |
2765 | ||
2766 | if (Profiler::Compilation* compilation = jitCode()->dfgCommon()->compilation.get()) | |
2767 | compilation->setJettisonReason(reason); | |
2768 | ||
2769 | // We want to accomplish two things here: | |
2770 | // 1) Make sure that if this CodeBlock is on the stack right now, then if we return to it | |
2771 | // we should OSR exit at the top of the next bytecode instruction after the return. | |
2772 | // 2) Make sure that if we call the owner executable, then we shouldn't call this CodeBlock. | |
2773 | ||
2774 | // This accomplishes the OSR-exit-on-return part, and does its own book-keeping about | |
2775 | // whether the invalidation has already happened. | |
2776 | if (!jitCode()->dfgCommon()->invalidate()) { | |
2777 | // Nothing to do since we've already been invalidated. That means that we cannot be | |
2778 | // the optimized replacement. | |
2779 | RELEASE_ASSERT(this != replacement()); | |
2780 | return; | |
2781 | } | |
2782 | ||
2783 | if (DFG::shouldShowDisassembly()) | |
2784 | dataLog(" Did invalidate ", *this, "\n"); | |
2785 | ||
2786 | // Count the reoptimization if that's what the user wanted. | |
2787 | if (mode == CountReoptimization) { | |
2788 | // FIXME: Maybe this should call alternative(). | |
2789 | // https://bugs.webkit.org/show_bug.cgi?id=123677 | |
2790 | baselineAlternative()->countReoptimization(); | |
2791 | if (DFG::shouldShowDisassembly()) | |
2792 | dataLog(" Did count reoptimization for ", *this, "\n"); | |
2793 | } | |
2794 | ||
2795 | // Now take care of the entrypoint. | |
2796 | if (this != replacement()) { | |
2797 | // This means that we were never the entrypoint. This can happen for OSR entry code | |
2798 | // blocks. | |
2799 | return; | |
2800 | } | |
93a37866 A |
2801 | alternative()->optimizeAfterWarmUp(); |
2802 | tallyFrequentExitSites(); | |
81345200 | 2803 | alternative()->install(); |
93a37866 | 2804 | if (DFG::shouldShowDisassembly()) |
81345200 A |
2805 | dataLog(" Did install baseline version of ", *this, "\n"); |
2806 | #else // ENABLE(DFG_JIT) | |
2807 | UNUSED_PARAM(mode); | |
2808 | UNREACHABLE_FOR_PLATFORM(); | |
2809 | #endif // ENABLE(DFG_JIT) | |
6fe7ccc8 A |
2810 | } |
2811 | ||
81345200 | 2812 | JSGlobalObject* CodeBlock::globalObjectFor(CodeOrigin codeOrigin) |
6fe7ccc8 | 2813 | { |
81345200 A |
2814 | if (!codeOrigin.inlineCallFrame) |
2815 | return globalObject(); | |
2816 | return jsCast<FunctionExecutable*>(codeOrigin.inlineCallFrame->executable.get())->eitherCodeBlock()->globalObject(); | |
6fe7ccc8 A |
2817 | } |
2818 | ||
81345200 | 2819 | void CodeBlock::noticeIncomingCall(ExecState* callerFrame) |
6fe7ccc8 | 2820 | { |
81345200 A |
2821 | CodeBlock* callerCodeBlock = callerFrame->codeBlock(); |
2822 | ||
2823 | if (Options::verboseCallLink()) | |
2824 | dataLog("Noticing call link from ", *callerCodeBlock, " to ", *this, "\n"); | |
2825 | ||
2826 | if (!m_shouldAlwaysBeInlined) | |
2827 | return; | |
93a37866 | 2828 | |
81345200 A |
2829 | #if ENABLE(DFG_JIT) |
2830 | if (!hasBaselineJITProfiling()) | |
2831 | return; | |
6fe7ccc8 | 2832 | |
81345200 A |
2833 | if (!DFG::mightInlineFunction(this)) |
2834 | return; | |
6fe7ccc8 | 2835 | |
81345200 A |
2836 | if (!canInline(m_capabilityLevelState)) |
2837 | return; | |
2838 | ||
2839 | if (!DFG::isSmallEnoughToInlineCodeInto(callerCodeBlock)) { | |
2840 | m_shouldAlwaysBeInlined = false; | |
2841 | if (Options::verboseCallLink()) | |
2842 | dataLog(" Clearing SABI because caller is too large.\n"); | |
2843 | return; | |
2844 | } | |
6fe7ccc8 | 2845 | |
81345200 A |
2846 | if (callerCodeBlock->jitType() == JITCode::InterpreterThunk) { |
2847 | // If the caller is still in the interpreter, then we can't expect inlining to | |
2848 | // happen anytime soon. Assume it's profitable to optimize it separately. This | |
2849 | // ensures that a function is SABI only if it is called no more frequently than | |
2850 | // any of its callers. | |
2851 | m_shouldAlwaysBeInlined = false; | |
2852 | if (Options::verboseCallLink()) | |
2853 | dataLog(" Clearing SABI because caller is in LLInt.\n"); | |
2854 | return; | |
2855 | } | |
2856 | ||
2857 | if (callerCodeBlock->codeType() != FunctionCode) { | |
2858 | // If the caller is either eval or global code, assume that that won't be | |
2859 | // optimized anytime soon. For eval code this is particularly true since we | |
2860 | // delay eval optimization by a *lot*. | |
2861 | m_shouldAlwaysBeInlined = false; | |
2862 | if (Options::verboseCallLink()) | |
2863 | dataLog(" Clearing SABI because caller is not a function.\n"); | |
2864 | return; | |
2865 | } | |
2866 | ||
2867 | ExecState* frame = callerFrame; | |
2868 | for (unsigned i = Options::maximumInliningDepth(); i--; frame = frame->callerFrame()) { | |
2869 | if (frame->isVMEntrySentinel()) | |
2870 | break; | |
2871 | if (frame->codeBlock() == this) { | |
2872 | // Recursive calls won't be inlined. | |
2873 | if (Options::verboseCallLink()) | |
2874 | dataLog(" Clearing SABI because recursion was detected.\n"); | |
2875 | m_shouldAlwaysBeInlined = false; | |
2876 | return; | |
2877 | } | |
2878 | } | |
2879 | ||
2880 | RELEASE_ASSERT(callerCodeBlock->m_capabilityLevelState != DFG::CapabilityLevelNotSet); | |
2881 | ||
2882 | if (canCompile(callerCodeBlock->m_capabilityLevelState)) | |
2883 | return; | |
2884 | ||
2885 | if (Options::verboseCallLink()) | |
2886 | dataLog(" Clearing SABI because the caller is not a DFG candidate.\n"); | |
2887 | ||
2888 | m_shouldAlwaysBeInlined = false; | |
6fe7ccc8 | 2889 | #endif |
93a37866 A |
2890 | } |
2891 | ||
2892 | unsigned CodeBlock::reoptimizationRetryCounter() const | |
2893 | { | |
81345200 | 2894 | #if ENABLE(JIT) |
93a37866 A |
2895 | ASSERT(m_reoptimizationRetryCounter <= Options::reoptimizationRetryCounterMax()); |
2896 | return m_reoptimizationRetryCounter; | |
81345200 A |
2897 | #else |
2898 | return 0; | |
2899 | #endif // ENABLE(JIT) | |
93a37866 A |
2900 | } |
2901 | ||
81345200 | 2902 | #if ENABLE(JIT) |
93a37866 A |
2903 | void CodeBlock::countReoptimization() |
2904 | { | |
2905 | m_reoptimizationRetryCounter++; | |
2906 | if (m_reoptimizationRetryCounter > Options::reoptimizationRetryCounterMax()) | |
2907 | m_reoptimizationRetryCounter = Options::reoptimizationRetryCounterMax(); | |
2908 | } | |
2909 | ||
81345200 A |
2910 | unsigned CodeBlock::numberOfDFGCompiles() |
2911 | { | |
2912 | ASSERT(JITCode::isBaselineCode(jitType())); | |
2913 | if (Options::testTheFTL()) { | |
2914 | if (m_didFailFTLCompilation) | |
2915 | return 1000000; | |
2916 | return (m_hasBeenCompiledWithFTL ? 1 : 0) + m_reoptimizationRetryCounter; | |
2917 | } | |
2918 | return (JITCode::isOptimizingJIT(replacement()->jitType()) ? 1 : 0) + m_reoptimizationRetryCounter; | |
2919 | } | |
2920 | ||
93a37866 | 2921 | int32_t CodeBlock::codeTypeThresholdMultiplier() const |
6fe7ccc8 | 2922 | { |
93a37866 A |
2923 | if (codeType() == EvalCode) |
2924 | return Options::evalThresholdMultiplier(); | |
2925 | ||
2926 | return 1; | |
2927 | } | |
2928 | ||
2929 | double CodeBlock::optimizationThresholdScalingFactor() | |
2930 | { | |
2931 | // This expression arises from doing a least-squares fit of | |
2932 | // | |
2933 | // F[x_] =: a * Sqrt[x + b] + Abs[c * x] + d | |
2934 | // | |
2935 | // against the data points: | |
2936 | // | |
2937 | // x F[x_] | |
2938 | // 10 0.9 (smallest reasonable code block) | |
2939 | // 200 1.0 (typical small-ish code block) | |
2940 | // 320 1.2 (something I saw in 3d-cube that I wanted to optimize) | |
2941 | // 1268 5.0 (something I saw in 3d-cube that I didn't want to optimize) | |
2942 | // 4000 5.5 (random large size, used to cause the function to converge to a shallow curve of some sort) | |
2943 | // 10000 6.0 (similar to above) | |
2944 | // | |
2945 | // I achieve the minimization using the following Mathematica code: | |
2946 | // | |
2947 | // MyFunctionTemplate[x_, a_, b_, c_, d_] := a*Sqrt[x + b] + Abs[c*x] + d | |
2948 | // | |
2949 | // samples = {{10, 0.9}, {200, 1}, {320, 1.2}, {1268, 5}, {4000, 5.5}, {10000, 6}} | |
2950 | // | |
2951 | // solution = | |
2952 | // Minimize[Plus @@ ((MyFunctionTemplate[#[[1]], a, b, c, d] - #[[2]])^2 & /@ samples), | |
2953 | // {a, b, c, d}][[2]] | |
2954 | // | |
2955 | // And the code below (to initialize a, b, c, d) is generated by: | |
2956 | // | |
2957 | // Print["const double " <> ToString[#[[1]]] <> " = " <> | |
2958 | // If[#[[2]] < 0.00001, "0.0", ToString[#[[2]]]] <> ";"] & /@ solution | |
2959 | // | |
2960 | // We've long known the following to be true: | |
2961 | // - Small code blocks are cheap to optimize and so we should do it sooner rather | |
2962 | // than later. | |
2963 | // - Large code blocks are expensive to optimize and so we should postpone doing so, | |
2964 | // and sometimes have a large enough threshold that we never optimize them. | |
2965 | // - The difference in cost is not totally linear because (a) just invoking the | |
2966 | // DFG incurs some base cost and (b) for large code blocks there is enough slop | |
2967 | // in the correlation between instruction count and the actual compilation cost | |
2968 | // that for those large blocks, the instruction count should not have a strong | |
2969 | // influence on our threshold. | |
2970 | // | |
2971 | // I knew the goals but I didn't know how to achieve them; so I picked an interesting | |
2972 | // example where the heuristics were right (code block in 3d-cube with instruction | |
2973 | // count 320, which got compiled early as it should have been) and one where they were | |
2974 | // totally wrong (code block in 3d-cube with instruction count 1268, which was expensive | |
2975 | // to compile and didn't run often enough to warrant compilation in my opinion), and | |
2976 | // then threw in additional data points that represented my own guess of what our | |
2977 | // heuristics should do for some round-numbered examples. | |
2978 | // | |
2979 | // The expression to which I decided to fit the data arose because I started with an | |
2980 | // affine function, and then did two things: put the linear part in an Abs to ensure | |
2981 | // that the fit didn't end up choosing a negative value of c (which would result in | |
2982 | // the function turning over and going negative for large x) and I threw in a Sqrt | |
2983 | // term because Sqrt represents my intution that the function should be more sensitive | |
2984 | // to small changes in small values of x, but less sensitive when x gets large. | |
2985 | ||
2986 | // Note that the current fit essentially eliminates the linear portion of the | |
2987 | // expression (c == 0.0). | |
2988 | const double a = 0.061504; | |
2989 | const double b = 1.02406; | |
2990 | const double c = 0.0; | |
2991 | const double d = 0.825914; | |
2992 | ||
2993 | double instructionCount = this->instructionCount(); | |
2994 | ||
2995 | ASSERT(instructionCount); // Make sure this is called only after we have an instruction stream; otherwise it'll just return the value of d, which makes no sense. | |
2996 | ||
2997 | double result = d + a * sqrt(instructionCount + b) + c * instructionCount; | |
81345200 A |
2998 | |
2999 | result *= codeTypeThresholdMultiplier(); | |
3000 | ||
3001 | if (Options::verboseOSR()) { | |
3002 | dataLog( | |
3003 | *this, ": instruction count is ", instructionCount, | |
3004 | ", scaling execution counter by ", result, " * ", codeTypeThresholdMultiplier(), | |
3005 | "\n"); | |
3006 | } | |
3007 | return result; | |
93a37866 | 3008 | } |
6fe7ccc8 | 3009 | |
93a37866 A |
3010 | static int32_t clipThreshold(double threshold) |
3011 | { | |
3012 | if (threshold < 1.0) | |
3013 | return 1; | |
3014 | ||
3015 | if (threshold > static_cast<double>(std::numeric_limits<int32_t>::max())) | |
3016 | return std::numeric_limits<int32_t>::max(); | |
3017 | ||
3018 | return static_cast<int32_t>(threshold); | |
3019 | } | |
3020 | ||
81345200 | 3021 | int32_t CodeBlock::adjustedCounterValue(int32_t desiredThreshold) |
93a37866 A |
3022 | { |
3023 | return clipThreshold( | |
81345200 | 3024 | static_cast<double>(desiredThreshold) * |
93a37866 A |
3025 | optimizationThresholdScalingFactor() * |
3026 | (1 << reoptimizationRetryCounter())); | |
3027 | } | |
3028 | ||
3029 | bool CodeBlock::checkIfOptimizationThresholdReached() | |
3030 | { | |
81345200 A |
3031 | #if ENABLE(DFG_JIT) |
3032 | if (DFG::Worklist* worklist = DFG::existingGlobalDFGWorklistOrNull()) { | |
3033 | if (worklist->compilationState(DFG::CompilationKey(this, DFG::DFGMode)) | |
3034 | == DFG::Worklist::Compiled) { | |
3035 | optimizeNextInvocation(); | |
3036 | return true; | |
3037 | } | |
3038 | } | |
3039 | #endif | |
3040 | ||
93a37866 A |
3041 | return m_jitExecuteCounter.checkIfThresholdCrossedAndSet(this); |
3042 | } | |
3043 | ||
3044 | void CodeBlock::optimizeNextInvocation() | |
3045 | { | |
81345200 A |
3046 | if (Options::verboseOSR()) |
3047 | dataLog(*this, ": Optimizing next invocation.\n"); | |
93a37866 A |
3048 | m_jitExecuteCounter.setNewThreshold(0, this); |
3049 | } | |
3050 | ||
3051 | void CodeBlock::dontOptimizeAnytimeSoon() | |
3052 | { | |
81345200 A |
3053 | if (Options::verboseOSR()) |
3054 | dataLog(*this, ": Not optimizing anytime soon.\n"); | |
93a37866 A |
3055 | m_jitExecuteCounter.deferIndefinitely(); |
3056 | } | |
3057 | ||
3058 | void CodeBlock::optimizeAfterWarmUp() | |
3059 | { | |
81345200 A |
3060 | if (Options::verboseOSR()) |
3061 | dataLog(*this, ": Optimizing after warm-up.\n"); | |
3062 | #if ENABLE(DFG_JIT) | |
3063 | m_jitExecuteCounter.setNewThreshold( | |
3064 | adjustedCounterValue(Options::thresholdForOptimizeAfterWarmUp()), this); | |
3065 | #endif | |
93a37866 A |
3066 | } |
3067 | ||
3068 | void CodeBlock::optimizeAfterLongWarmUp() | |
3069 | { | |
81345200 A |
3070 | if (Options::verboseOSR()) |
3071 | dataLog(*this, ": Optimizing after long warm-up.\n"); | |
3072 | #if ENABLE(DFG_JIT) | |
3073 | m_jitExecuteCounter.setNewThreshold( | |
3074 | adjustedCounterValue(Options::thresholdForOptimizeAfterLongWarmUp()), this); | |
3075 | #endif | |
93a37866 A |
3076 | } |
3077 | ||
3078 | void CodeBlock::optimizeSoon() | |
3079 | { | |
81345200 A |
3080 | if (Options::verboseOSR()) |
3081 | dataLog(*this, ": Optimizing soon.\n"); | |
3082 | #if ENABLE(DFG_JIT) | |
3083 | m_jitExecuteCounter.setNewThreshold( | |
3084 | adjustedCounterValue(Options::thresholdForOptimizeSoon()), this); | |
3085 | #endif | |
93a37866 A |
3086 | } |
3087 | ||
81345200 A |
3088 | void CodeBlock::forceOptimizationSlowPathConcurrently() |
3089 | { | |
3090 | if (Options::verboseOSR()) | |
3091 | dataLog(*this, ": Forcing slow path concurrently.\n"); | |
3092 | m_jitExecuteCounter.forceSlowPathConcurrently(); | |
3093 | } | |
3094 | ||
3095 | #if ENABLE(DFG_JIT) | |
3096 | void CodeBlock::setOptimizationThresholdBasedOnCompilationResult(CompilationResult result) | |
3097 | { | |
3098 | JITCode::JITType type = jitType(); | |
3099 | if (type != JITCode::BaselineJIT) { | |
3100 | dataLog(*this, ": expected to have baseline code but have ", type, "\n"); | |
3101 | RELEASE_ASSERT_NOT_REACHED(); | |
3102 | } | |
3103 | ||
3104 | CodeBlock* theReplacement = replacement(); | |
3105 | if ((result == CompilationSuccessful) != (theReplacement != this)) { | |
3106 | dataLog(*this, ": we have result = ", result, " but "); | |
3107 | if (theReplacement == this) | |
3108 | dataLog("we are our own replacement.\n"); | |
3109 | else | |
3110 | dataLog("our replacement is ", pointerDump(theReplacement), "\n"); | |
3111 | RELEASE_ASSERT_NOT_REACHED(); | |
3112 | } | |
3113 | ||
3114 | switch (result) { | |
3115 | case CompilationSuccessful: | |
3116 | RELEASE_ASSERT(JITCode::isOptimizingJIT(replacement()->jitType())); | |
3117 | optimizeNextInvocation(); | |
3118 | return; | |
3119 | case CompilationFailed: | |
3120 | dontOptimizeAnytimeSoon(); | |
3121 | return; | |
3122 | case CompilationDeferred: | |
3123 | // We'd like to do dontOptimizeAnytimeSoon() but we cannot because | |
3124 | // forceOptimizationSlowPathConcurrently() is inherently racy. It won't | |
3125 | // necessarily guarantee anything. So, we make sure that even if that | |
3126 | // function ends up being a no-op, we still eventually retry and realize | |
3127 | // that we have optimized code ready. | |
3128 | optimizeAfterWarmUp(); | |
3129 | return; | |
3130 | case CompilationInvalidated: | |
3131 | // Retry with exponential backoff. | |
3132 | countReoptimization(); | |
3133 | optimizeAfterWarmUp(); | |
3134 | return; | |
3135 | } | |
3136 | ||
3137 | dataLog("Unrecognized result: ", static_cast<int>(result), "\n"); | |
3138 | RELEASE_ASSERT_NOT_REACHED(); | |
3139 | } | |
3140 | ||
3141 | #endif | |
3142 | ||
93a37866 A |
3143 | uint32_t CodeBlock::adjustedExitCountThreshold(uint32_t desiredThreshold) |
3144 | { | |
81345200 | 3145 | ASSERT(JITCode::isOptimizingJIT(jitType())); |
93a37866 A |
3146 | // Compute this the lame way so we don't saturate. This is called infrequently |
3147 | // enough that this loop won't hurt us. | |
3148 | unsigned result = desiredThreshold; | |
3149 | for (unsigned n = baselineVersion()->reoptimizationRetryCounter(); n--;) { | |
3150 | unsigned newResult = result << 1; | |
3151 | if (newResult < result) | |
3152 | return std::numeric_limits<uint32_t>::max(); | |
3153 | result = newResult; | |
3154 | } | |
3155 | return result; | |
3156 | } | |
3157 | ||
3158 | uint32_t CodeBlock::exitCountThresholdForReoptimization() | |
3159 | { | |
3160 | return adjustedExitCountThreshold(Options::osrExitCountForReoptimization() * codeTypeThresholdMultiplier()); | |
3161 | } | |
3162 | ||
3163 | uint32_t CodeBlock::exitCountThresholdForReoptimizationFromLoop() | |
3164 | { | |
3165 | return adjustedExitCountThreshold(Options::osrExitCountForReoptimizationFromLoop() * codeTypeThresholdMultiplier()); | |
3166 | } | |
3167 | ||
3168 | bool CodeBlock::shouldReoptimizeNow() | |
3169 | { | |
3170 | return osrExitCounter() >= exitCountThresholdForReoptimization(); | |
3171 | } | |
3172 | ||
3173 | bool CodeBlock::shouldReoptimizeFromLoopNow() | |
3174 | { | |
3175 | return osrExitCounter() >= exitCountThresholdForReoptimizationFromLoop(); | |
3176 | } | |
6fe7ccc8 A |
3177 | #endif |
3178 | ||
93a37866 A |
3179 | ArrayProfile* CodeBlock::getArrayProfile(unsigned bytecodeOffset) |
3180 | { | |
3181 | for (unsigned i = 0; i < m_arrayProfiles.size(); ++i) { | |
3182 | if (m_arrayProfiles[i].bytecodeOffset() == bytecodeOffset) | |
3183 | return &m_arrayProfiles[i]; | |
3184 | } | |
3185 | return 0; | |
3186 | } | |
3187 | ||
3188 | ArrayProfile* CodeBlock::getOrAddArrayProfile(unsigned bytecodeOffset) | |
3189 | { | |
3190 | ArrayProfile* result = getArrayProfile(bytecodeOffset); | |
3191 | if (result) | |
3192 | return result; | |
3193 | return addArrayProfile(bytecodeOffset); | |
3194 | } | |
3195 | ||
81345200 | 3196 | void CodeBlock::updateAllPredictionsAndCountLiveness(unsigned& numberOfLiveNonArgumentValueProfiles, unsigned& numberOfSamplesInProfiles) |
93a37866 | 3197 | { |
81345200 A |
3198 | ConcurrentJITLocker locker(m_lock); |
3199 | ||
93a37866 A |
3200 | numberOfLiveNonArgumentValueProfiles = 0; |
3201 | numberOfSamplesInProfiles = 0; // If this divided by ValueProfile::numberOfBuckets equals numberOfValueProfiles() then value profiles are full. | |
6fe7ccc8 A |
3202 | for (unsigned i = 0; i < totalNumberOfValueProfiles(); ++i) { |
3203 | ValueProfile* profile = getFromAllValueProfiles(i); | |
3204 | unsigned numSamples = profile->totalNumberOfSamples(); | |
3205 | if (numSamples > ValueProfile::numberOfBuckets) | |
3206 | numSamples = ValueProfile::numberOfBuckets; // We don't want profiles that are extremely hot to be given more weight. | |
3207 | numberOfSamplesInProfiles += numSamples; | |
3208 | if (profile->m_bytecodeOffset < 0) { | |
81345200 | 3209 | profile->computeUpdatedPrediction(locker); |
6fe7ccc8 A |
3210 | continue; |
3211 | } | |
93a37866 | 3212 | if (profile->numberOfSamples() || profile->m_prediction != SpecNone) |
6fe7ccc8 | 3213 | numberOfLiveNonArgumentValueProfiles++; |
81345200 | 3214 | profile->computeUpdatedPrediction(locker); |
6fe7ccc8 | 3215 | } |
93a37866 A |
3216 | |
3217 | #if ENABLE(DFG_JIT) | |
81345200 | 3218 | m_lazyOperandValueProfiles.computeUpdatedPredictions(locker); |
93a37866 A |
3219 | #endif |
3220 | } | |
3221 | ||
81345200 | 3222 | void CodeBlock::updateAllValueProfilePredictions() |
93a37866 A |
3223 | { |
3224 | unsigned ignoredValue1, ignoredValue2; | |
81345200 | 3225 | updateAllPredictionsAndCountLiveness(ignoredValue1, ignoredValue2); |
93a37866 A |
3226 | } |
3227 | ||
81345200 | 3228 | void CodeBlock::updateAllArrayPredictions() |
93a37866 | 3229 | { |
81345200 A |
3230 | ConcurrentJITLocker locker(m_lock); |
3231 | ||
93a37866 | 3232 | for (unsigned i = m_arrayProfiles.size(); i--;) |
81345200 | 3233 | m_arrayProfiles[i].computeUpdatedPrediction(locker, this); |
93a37866 A |
3234 | |
3235 | // Don't count these either, for similar reasons. | |
3236 | for (unsigned i = m_arrayAllocationProfiles.size(); i--;) | |
3237 | m_arrayAllocationProfiles[i].updateIndexingType(); | |
3238 | } | |
3239 | ||
81345200 | 3240 | void CodeBlock::updateAllPredictions() |
93a37866 | 3241 | { |
81345200 A |
3242 | updateAllValueProfilePredictions(); |
3243 | updateAllArrayPredictions(); | |
93a37866 A |
3244 | } |
3245 | ||
3246 | bool CodeBlock::shouldOptimizeNow() | |
3247 | { | |
81345200 A |
3248 | if (Options::verboseOSR()) |
3249 | dataLog("Considering optimizing ", *this, "...\n"); | |
93a37866 A |
3250 | |
3251 | if (m_optimizationDelayCounter >= Options::maximumOptimizationDelay()) | |
3252 | return true; | |
3253 | ||
3254 | updateAllArrayPredictions(); | |
3255 | ||
3256 | unsigned numberOfLiveNonArgumentValueProfiles; | |
3257 | unsigned numberOfSamplesInProfiles; | |
81345200 | 3258 | updateAllPredictionsAndCountLiveness(numberOfLiveNonArgumentValueProfiles, numberOfSamplesInProfiles); |
6fe7ccc8 | 3259 | |
81345200 A |
3260 | if (Options::verboseOSR()) { |
3261 | dataLogF( | |
3262 | "Profile hotness: %lf (%u / %u), %lf (%u / %u)\n", | |
3263 | (double)numberOfLiveNonArgumentValueProfiles / numberOfValueProfiles(), | |
3264 | numberOfLiveNonArgumentValueProfiles, numberOfValueProfiles(), | |
3265 | (double)numberOfSamplesInProfiles / ValueProfile::numberOfBuckets / numberOfValueProfiles(), | |
3266 | numberOfSamplesInProfiles, ValueProfile::numberOfBuckets * numberOfValueProfiles()); | |
3267 | } | |
6fe7ccc8 | 3268 | |
93a37866 A |
3269 | if ((!numberOfValueProfiles() || (double)numberOfLiveNonArgumentValueProfiles / numberOfValueProfiles() >= Options::desiredProfileLivenessRate()) |
3270 | && (!totalNumberOfValueProfiles() || (double)numberOfSamplesInProfiles / ValueProfile::numberOfBuckets / totalNumberOfValueProfiles() >= Options::desiredProfileFullnessRate()) | |
3271 | && static_cast<unsigned>(m_optimizationDelayCounter) + 1 >= Options::minimumOptimizationDelay()) | |
6fe7ccc8 A |
3272 | return true; |
3273 | ||
3274 | ASSERT(m_optimizationDelayCounter < std::numeric_limits<uint8_t>::max()); | |
3275 | m_optimizationDelayCounter++; | |
3276 | optimizeAfterWarmUp(); | |
3277 | return false; | |
3278 | } | |
6fe7ccc8 A |
3279 | |
3280 | #if ENABLE(DFG_JIT) | |
3281 | void CodeBlock::tallyFrequentExitSites() | |
3282 | { | |
81345200 A |
3283 | ASSERT(JITCode::isOptimizingJIT(jitType())); |
3284 | ASSERT(alternative()->jitType() == JITCode::BaselineJIT); | |
6fe7ccc8 A |
3285 | |
3286 | CodeBlock* profiledBlock = alternative(); | |
3287 | ||
81345200 A |
3288 | switch (jitType()) { |
3289 | case JITCode::DFGJIT: { | |
3290 | DFG::JITCode* jitCode = m_jitCode->dfg(); | |
3291 | for (unsigned i = 0; i < jitCode->osrExit.size(); ++i) { | |
3292 | DFG::OSRExit& exit = jitCode->osrExit[i]; | |
3293 | ||
3294 | if (!exit.considerAddingAsFrequentExitSite(profiledBlock)) | |
3295 | continue; | |
3296 | } | |
3297 | break; | |
3298 | } | |
3299 | ||
3300 | #if ENABLE(FTL_JIT) | |
3301 | case JITCode::FTLJIT: { | |
3302 | // There is no easy way to avoid duplicating this code since the FTL::JITCode::osrExit | |
3303 | // vector contains a totally different type, that just so happens to behave like | |
3304 | // DFG::JITCode::osrExit. | |
3305 | FTL::JITCode* jitCode = m_jitCode->ftl(); | |
3306 | for (unsigned i = 0; i < jitCode->osrExit.size(); ++i) { | |
3307 | FTL::OSRExit& exit = jitCode->osrExit[i]; | |
3308 | ||
3309 | if (!exit.considerAddingAsFrequentExitSite(profiledBlock)) | |
3310 | continue; | |
3311 | } | |
3312 | break; | |
3313 | } | |
6fe7ccc8 | 3314 | #endif |
81345200 A |
3315 | |
3316 | default: | |
3317 | RELEASE_ASSERT_NOT_REACHED(); | |
3318 | break; | |
6fe7ccc8 A |
3319 | } |
3320 | } | |
3321 | #endif // ENABLE(DFG_JIT) | |
3322 | ||
3323 | #if ENABLE(VERBOSE_VALUE_PROFILE) | |
3324 | void CodeBlock::dumpValueProfiles() | |
3325 | { | |
93a37866 | 3326 | dataLog("ValueProfile for ", *this, ":\n"); |
6fe7ccc8 A |
3327 | for (unsigned i = 0; i < totalNumberOfValueProfiles(); ++i) { |
3328 | ValueProfile* profile = getFromAllValueProfiles(i); | |
3329 | if (profile->m_bytecodeOffset < 0) { | |
3330 | ASSERT(profile->m_bytecodeOffset == -1); | |
93a37866 | 3331 | dataLogF(" arg = %u: ", i); |
6fe7ccc8 | 3332 | } else |
93a37866 A |
3333 | dataLogF(" bc = %d: ", profile->m_bytecodeOffset); |
3334 | if (!profile->numberOfSamples() && profile->m_prediction == SpecNone) { | |
3335 | dataLogF("<empty>\n"); | |
6fe7ccc8 A |
3336 | continue; |
3337 | } | |
3338 | profile->dump(WTF::dataFile()); | |
93a37866 | 3339 | dataLogF("\n"); |
6fe7ccc8 | 3340 | } |
93a37866 | 3341 | dataLog("RareCaseProfile for ", *this, ":\n"); |
6fe7ccc8 A |
3342 | for (unsigned i = 0; i < numberOfRareCaseProfiles(); ++i) { |
3343 | RareCaseProfile* profile = rareCaseProfile(i); | |
93a37866 | 3344 | dataLogF(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); |
6fe7ccc8 | 3345 | } |
93a37866 | 3346 | dataLog("SpecialFastCaseProfile for ", *this, ":\n"); |
6fe7ccc8 A |
3347 | for (unsigned i = 0; i < numberOfSpecialFastCaseProfiles(); ++i) { |
3348 | RareCaseProfile* profile = specialFastCaseProfile(i); | |
93a37866 | 3349 | dataLogF(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); |
6fe7ccc8 A |
3350 | } |
3351 | } | |
93a37866 | 3352 | #endif // ENABLE(VERBOSE_VALUE_PROFILE) |
6fe7ccc8 | 3353 | |
81345200 A |
3354 | unsigned CodeBlock::frameRegisterCount() |
3355 | { | |
3356 | switch (jitType()) { | |
3357 | case JITCode::InterpreterThunk: | |
3358 | return LLInt::frameRegisterCountFor(this); | |
3359 | ||
3360 | #if ENABLE(JIT) | |
3361 | case JITCode::BaselineJIT: | |
3362 | return JIT::frameRegisterCountFor(this); | |
3363 | #endif // ENABLE(JIT) | |
3364 | ||
3365 | #if ENABLE(DFG_JIT) | |
3366 | case JITCode::DFGJIT: | |
3367 | case JITCode::FTLJIT: | |
3368 | return jitCode()->dfgCommon()->frameRegisterCount; | |
3369 | #endif // ENABLE(DFG_JIT) | |
3370 | ||
3371 | default: | |
3372 | RELEASE_ASSERT_NOT_REACHED(); | |
3373 | return 0; | |
3374 | } | |
3375 | } | |
3376 | ||
3377 | int CodeBlock::stackPointerOffset() | |
3378 | { | |
3379 | return virtualRegisterForLocal(frameRegisterCount() - 1).offset(); | |
3380 | } | |
3381 | ||
6fe7ccc8 A |
3382 | size_t CodeBlock::predictedMachineCodeSize() |
3383 | { | |
93a37866 | 3384 | // This will be called from CodeBlock::CodeBlock before either m_vm or the |
6fe7ccc8 A |
3385 | // instructions have been initialized. It's OK to return 0 because what will really |
3386 | // matter is the recomputation of this value when the slow path is triggered. | |
93a37866 | 3387 | if (!m_vm) |
6fe7ccc8 A |
3388 | return 0; |
3389 | ||
93a37866 | 3390 | if (!m_vm->machineCodeBytesPerBytecodeWordForBaselineJIT) |
6fe7ccc8 A |
3391 | return 0; // It's as good of a prediction as we'll get. |
3392 | ||
3393 | // Be conservative: return a size that will be an overestimation 84% of the time. | |
93a37866 A |
3394 | double multiplier = m_vm->machineCodeBytesPerBytecodeWordForBaselineJIT.mean() + |
3395 | m_vm->machineCodeBytesPerBytecodeWordForBaselineJIT.standardDeviation(); | |
6fe7ccc8 A |
3396 | |
3397 | // Be paranoid: silently reject bogus multipiers. Silently doing the "wrong" thing | |
3398 | // here is OK, since this whole method is just a heuristic. | |
3399 | if (multiplier < 0 || multiplier > 1000) | |
3400 | return 0; | |
3401 | ||
3402 | double doubleResult = multiplier * m_instructions.size(); | |
3403 | ||
3404 | // Be even more paranoid: silently reject values that won't fit into a size_t. If | |
3405 | // the function is so huge that we can't even fit it into virtual memory then we | |
3406 | // should probably have some other guards in place to prevent us from even getting | |
3407 | // to this point. | |
3408 | if (doubleResult > std::numeric_limits<size_t>::max()) | |
3409 | return 0; | |
3410 | ||
3411 | return static_cast<size_t>(doubleResult); | |
3412 | } | |
3413 | ||
3414 | bool CodeBlock::usesOpcode(OpcodeID opcodeID) | |
3415 | { | |
93a37866 | 3416 | Interpreter* interpreter = vm()->interpreter; |
6fe7ccc8 A |
3417 | Instruction* instructionsBegin = instructions().begin(); |
3418 | unsigned instructionCount = instructions().size(); | |
3419 | ||
3420 | for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount; ) { | |
3421 | switch (interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode)) { | |
3422 | #define DEFINE_OP(curOpcode, length) \ | |
3423 | case curOpcode: \ | |
3424 | if (curOpcode == opcodeID) \ | |
3425 | return true; \ | |
3426 | bytecodeOffset += length; \ | |
3427 | break; | |
3428 | FOR_EACH_OPCODE_ID(DEFINE_OP) | |
3429 | #undef DEFINE_OP | |
3430 | default: | |
93a37866 | 3431 | RELEASE_ASSERT_NOT_REACHED(); |
6fe7ccc8 A |
3432 | break; |
3433 | } | |
3434 | } | |
3435 | ||
3436 | return false; | |
3437 | } | |
3438 | ||
81345200 | 3439 | String CodeBlock::nameForRegister(VirtualRegister virtualRegister) |
93a37866 | 3440 | { |
81345200 A |
3441 | ConcurrentJITLocker locker(symbolTable()->m_lock); |
3442 | SymbolTable::Map::iterator end = symbolTable()->end(locker); | |
3443 | for (SymbolTable::Map::iterator ptr = symbolTable()->begin(locker); ptr != end; ++ptr) { | |
3444 | if (ptr->value.getIndex() == virtualRegister.offset()) { | |
3445 | // FIXME: This won't work from the compilation thread. | |
3446 | // https://bugs.webkit.org/show_bug.cgi?id=115300 | |
93a37866 | 3447 | return String(ptr->key); |
81345200 | 3448 | } |
93a37866 | 3449 | } |
81345200 | 3450 | if (needsActivation() && virtualRegister == activationRegister()) |
93a37866 | 3451 | return ASCIILiteral("activation"); |
81345200 | 3452 | if (virtualRegister == thisRegister()) |
93a37866 A |
3453 | return ASCIILiteral("this"); |
3454 | if (usesArguments()) { | |
81345200 | 3455 | if (virtualRegister == argumentsRegister()) |
93a37866 | 3456 | return ASCIILiteral("arguments"); |
81345200 | 3457 | if (unmodifiedArgumentsRegister(argumentsRegister()) == virtualRegister) |
93a37866 A |
3458 | return ASCIILiteral("real arguments"); |
3459 | } | |
81345200 A |
3460 | if (virtualRegister.isArgument()) |
3461 | return String::format("arguments[%3d]", virtualRegister.toArgument()).impl(); | |
3462 | ||
93a37866 A |
3463 | return ""; |
3464 | } | |
3465 | ||
81345200 A |
3466 | namespace { |
3467 | ||
3468 | struct VerifyCapturedDef { | |
3469 | void operator()(CodeBlock* codeBlock, Instruction* instruction, OpcodeID opcodeID, int operand) | |
3470 | { | |
3471 | unsigned bytecodeOffset = instruction - codeBlock->instructions().begin(); | |
3472 | ||
3473 | if (codeBlock->isConstantRegisterIndex(operand)) { | |
3474 | codeBlock->beginValidationDidFail(); | |
3475 | dataLog(" At bc#", bytecodeOffset, " encountered a definition of a constant.\n"); | |
3476 | codeBlock->endValidationDidFail(); | |
3477 | return; | |
3478 | } | |
3479 | ||
3480 | switch (opcodeID) { | |
3481 | case op_enter: | |
3482 | case op_captured_mov: | |
3483 | case op_init_lazy_reg: | |
3484 | case op_create_arguments: | |
3485 | case op_new_captured_func: | |
3486 | return; | |
3487 | default: | |
3488 | break; | |
3489 | } | |
3490 | ||
3491 | VirtualRegister virtualReg(operand); | |
3492 | if (!virtualReg.isLocal()) | |
3493 | return; | |
3494 | ||
3495 | if (codeBlock->captureCount() && codeBlock->symbolTable()->isCaptured(operand)) { | |
3496 | codeBlock->beginValidationDidFail(); | |
3497 | dataLog(" At bc#", bytecodeOffset, " encountered invalid assignment to captured variable loc", virtualReg.toLocal(), ".\n"); | |
3498 | codeBlock->endValidationDidFail(); | |
3499 | return; | |
3500 | } | |
3501 | ||
3502 | return; | |
3503 | } | |
3504 | }; | |
3505 | ||
3506 | } // anonymous namespace | |
3507 | ||
3508 | void CodeBlock::validate() | |
3509 | { | |
3510 | BytecodeLivenessAnalysis liveness(this); // Compute directly from scratch so it doesn't effect CodeBlock footprint. | |
3511 | ||
3512 | FastBitVector liveAtHead = liveness.getLivenessInfoAtBytecodeOffset(0); | |
3513 | ||
3514 | if (liveAtHead.numBits() != static_cast<size_t>(m_numCalleeRegisters)) { | |
3515 | beginValidationDidFail(); | |
3516 | dataLog(" Wrong number of bits in result!\n"); | |
3517 | dataLog(" Result: ", liveAtHead, "\n"); | |
3518 | dataLog(" Bit count: ", liveAtHead.numBits(), "\n"); | |
3519 | endValidationDidFail(); | |
3520 | } | |
3521 | ||
3522 | for (unsigned i = m_numCalleeRegisters; i--;) { | |
3523 | bool isCaptured = false; | |
3524 | VirtualRegister reg = virtualRegisterForLocal(i); | |
3525 | ||
3526 | if (captureCount()) | |
3527 | isCaptured = reg.offset() <= captureStart() && reg.offset() > captureEnd(); | |
3528 | ||
3529 | if (isCaptured) { | |
3530 | if (!liveAtHead.get(i)) { | |
3531 | beginValidationDidFail(); | |
3532 | dataLog(" Variable loc", i, " is expected to be live because it is captured, but it isn't live.\n"); | |
3533 | dataLog(" Result: ", liveAtHead, "\n"); | |
3534 | endValidationDidFail(); | |
3535 | } | |
3536 | } else { | |
3537 | if (liveAtHead.get(i)) { | |
3538 | beginValidationDidFail(); | |
3539 | dataLog(" Variable loc", i, " is expected to be dead.\n"); | |
3540 | dataLog(" Result: ", liveAtHead, "\n"); | |
3541 | endValidationDidFail(); | |
3542 | } | |
3543 | } | |
3544 | } | |
3545 | ||
3546 | for (unsigned bytecodeOffset = 0; bytecodeOffset < instructions().size();) { | |
3547 | Instruction* currentInstruction = instructions().begin() + bytecodeOffset; | |
3548 | OpcodeID opcodeID = m_vm->interpreter->getOpcodeID(currentInstruction->u.opcode); | |
3549 | ||
3550 | VerifyCapturedDef verifyCapturedDef; | |
3551 | computeDefsForBytecodeOffset(this, bytecodeOffset, verifyCapturedDef); | |
3552 | ||
3553 | bytecodeOffset += opcodeLength(opcodeID); | |
3554 | } | |
3555 | } | |
3556 | ||
3557 | void CodeBlock::beginValidationDidFail() | |
3558 | { | |
3559 | dataLog("Validation failure in ", *this, ":\n"); | |
3560 | dataLog("\n"); | |
3561 | } | |
3562 | ||
3563 | void CodeBlock::endValidationDidFail() | |
3564 | { | |
3565 | dataLog("\n"); | |
3566 | dumpBytecode(); | |
3567 | dataLog("\n"); | |
3568 | dataLog("Validation failure.\n"); | |
3569 | RELEASE_ASSERT_NOT_REACHED(); | |
3570 | } | |
3571 | ||
3572 | void CodeBlock::addBreakpoint(unsigned numBreakpoints) | |
3573 | { | |
3574 | m_numBreakpoints += numBreakpoints; | |
3575 | ASSERT(m_numBreakpoints); | |
3576 | if (JITCode::isOptimizingJIT(jitType())) | |
3577 | jettison(Profiler::JettisonDueToDebuggerBreakpoint); | |
3578 | } | |
3579 | ||
3580 | void CodeBlock::setSteppingMode(CodeBlock::SteppingMode mode) | |
3581 | { | |
3582 | m_steppingMode = mode; | |
3583 | if (mode == SteppingModeEnabled && JITCode::isOptimizingJIT(jitType())) | |
3584 | jettison(Profiler::JettisonDueToDebuggerStepping); | |
3585 | } | |
3586 | ||
3587 | RareCaseProfile* CodeBlock::rareCaseProfileForBytecodeOffset(int bytecodeOffset) | |
3588 | { | |
3589 | return tryBinarySearch<RareCaseProfile, int>( | |
3590 | m_rareCaseProfiles, m_rareCaseProfiles.size(), bytecodeOffset, | |
3591 | getRareCaseProfileBytecodeOffset); | |
3592 | } | |
3593 | ||
3594 | #if ENABLE(JIT) | |
3595 | DFG::CapabilityLevel CodeBlock::capabilityLevel() | |
3596 | { | |
3597 | DFG::CapabilityLevel result = capabilityLevelInternal(); | |
3598 | m_capabilityLevelState = result; | |
3599 | return result; | |
3600 | } | |
3601 | #endif | |
3602 | ||
9dae56ea | 3603 | } // namespace JSC |