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